Thanks again for the feedback/help.
>>> Index: include/clang/Basic/TokenKinds.def
>>> ===================================================================
>>> --- include/clang/Basic/TokenKinds.def (revision 143902)
>>> +++ include/clang/Basic/TokenKinds.def (working copy)
>>> @@ -559,6 +559,8 @@
>>> // function template specialization (not a type),
>>> // e.g., "std::swap<int>"
>>> ANNOTATION(primary_expr) // annotation for a primary expression
>>> +ANNOTATION(decltype) // annotation for a decltype expression,
>>> + // e.g., "decltype(foo.bar())"
>>>
>>> // Annotation for #pragma unused(...)
>>> // For each argument inside the parentheses the pragma handler will produce
>>>
>>> Did you consider using annot_typename for this? I ask because
>>> annot_typename handles general types (including decltype), and is already
>>> well-supported throughout the parser. It looks like you've checked for
>>> annot_decltype in several places, but others (such as the tentative parser)
>>> would be confused by this annotation token.
>>
>> No, I hadn't considered that - but at first blush I'd be concerned
>> that this might end up being /too/ permissive, but I'll have a go
>> mocking it up that way, it'll probably be tidier but I won't be sure
>> what other scenarios might be accidentally accepted.
>
> I'm fine with keeping annot_decltype. It's super-specific, so just make sure
> you're auditing the various places where we have to deal with, e.g., the
> 'decltype' keyword or the annot_typename token so you catch all of the places
> 'decltype' can show up.
OK, I've done that & added a few test cases along the way for the
various cases that have been enabled (some of them weren't so easy to
figure out or didn't have existing test cases for the normal decltype
usage (without a nested-name-specifier)).
There's one case I fixed without actually needing any test cases
(ParseTentative - isExpressionOrTypeSpecifierSimple) because the cases
(expression or type name) get handled the same for decltype anyway.
>>> Index: include/clang/Basic/DiagnosticSemaKinds.td
>>> ===================================================================
>>> --- include/clang/Basic/DiagnosticSemaKinds.td (revision 143902)
>>> +++ include/clang/Basic/DiagnosticSemaKinds.td (working copy)
>>> @@ -3957,6 +3957,7 @@
>>> "conversion function from %0 to %1 invokes a deleted function">;
>>>
>>> def err_expected_class_or_namespace : Error<"expected a class or
>>> namespace">;
>>> +def err_expected_class : Error<"expected a decltype of a class">;
>>> def err_missing_qualified_for_redecl : Error<
>>> "must qualify the name %0 to declare %q1 in this scope">;
>>> def err_invalid_declarator_scope : Error<
>>>
>>> Could you provide a better error message here? Something that, at the very
>>> least, tells the user what the actual type is?
>>> Also, enum types would also be fine in C++11, so the diagnostic isn't quite
>>> accurate.
>>
>> Sure - I was going off the (admittedly rather vague) existing message
>> above. I've rephrased this to, for example:
>>
>> 'decltype(int())' (aka 'int') is not a namespace, class, or scoped
>> enumeration
>>
>> I think it'd be helpful to use this diagnostic for the existing case
>> above. It comes up most easily if you have a typedef that resolves to
>> int for example:
>>
>> namespace foo { typedef int bar; }
>>
>> foo::bar::baz i; // "expected class or namespace" (^ points to the
>> 'b' in 'bar')
>>
>> & you don't get told what foo::bar is. Though my version is a bit more
>> verbose.
>
> It's definitely an improvement. We only want that 'or scoped enumeration' bit
> if we're in C++11.
Ah, right. Though it's not an issue just yet - the message I've added
is only used for decltype issues anyway, but I've changed it to have
the necessary select. This'll be more use if/when we fix/improve the
original diagnostic as you were demonstrating in your talk at the dev
meeting.
> I wonder if there's a phrase that could describe these kinds of entities with
> a scope without having to enumerate the examples? Nothing comes to mind yet.
Nothing immediately - perhaps we could get away with dropping the "or
scoped enumeration" bit entirely? They appear like classes in code so
would it be reasonable to gloss over the distinction & just talk about
"namespace or class"?
>>> As an aside, I wonder if GCC supports typeof(blah)::foo, since typeof is
>>> the predecessor to decltype.
>>
>> GCC 4.6 doesn't seem to support decltype or typeof in nested name
>> specifiers. I don't have 4.7 on hand to compare it.
>
> Okay! Let's just stick with decltype only.
>
>>> Index: lib/Parse/ParseDeclCXX.cpp
>>> ===================================================================
>>> --- lib/Parse/ParseDeclCXX.cpp (revision 143902)
>>> +++ lib/Parse/ParseDeclCXX.cpp (working copy)
>>> @@ -633,14 +633,38 @@
>>> /// 'decltype' ( expression )
>>> ///
>>> void Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
>>> + if (Tok.is(tok::kw_decltype)) {
>>> + AnnotateDecltypeSpecifier();
>>> + }
>>> + assert(Tok.is(tok::annot_decltype) && "Not a decltype specifier");
>>> + ExprResult Result = getExprAnnotation(Tok);
>>> + if (Result.isInvalid()) {
>>> + ConsumeToken();
>>> + DS.SetTypeSpecError();
>>> + return;
>>> + }
>>> + SourceLocation DecltypeLoc = ConsumeToken();
>>> +
>>> + const char *PrevSpec = 0;
>>> + unsigned DiagID;
>>> + if (DS.SetTypeSpecType(DeclSpec::TST_decltype, DecltypeLoc, PrevSpec,
>>> + DiagID, Result.get())) {
>>> + Diag(DecltypeLoc, DiagID) << PrevSpec;
>>> + DS.SetTypeSpecError();
>>> + }
>>> +}
>>>
>>> I'm not a big fan of this flow. It means that we go through the
>>> token-annotation logic even in the case where we're simply going to parse
>>> the decltype. Please factor this so that straight parsing of decltype() is
>>> simple, and we only annotate if we can't use the generated DeclSpec
>>> directly.
>>
>> I was modeling this off the behavior I saw in
>> Parser::ParseDeclarationSpecifiers which always annotates the
>> CXXScopeToken rather than using it directly, presumably (or so I
>> thought) out of convenience of merging the two cases (where it was
>> called without an annotation token or with one), though it did feel a
>> bit questionable.
>>
>> I've switched this back to the original implementation (though still
>> with better error recovery using SetTypeSpecError) & only annotating
>> when necessary.
>
> Thanks.
>
>>> +void Parser::AnnotateDecltypeSpecifier() {
>>> assert(Tok.is(tok::kw_decltype) && "Not a decltype specifier");
>>>
>>> + // consume 'decltype'
>>> SourceLocation StartLoc = ConsumeToken();
>>> - BalancedDelimiterTracker T(*this, tok::l_paren);
>>> - if (T.expectAndConsume(diag::err_expected_lparen_after,
>>> - "decltype", tok::r_paren)) {
>>> - return;
>>> - }
>>> + if (Tok.isNot(tok::l_paren))
>>> + Diag(Tok, diag::err_expected_lparen_after)
>>> + << "decltype"
>>> + << FixItHint::CreateInsertion(Tok.getLocation(), "(");
>>> + else
>>> + ConsumeParen();
>>>
>>> // Parse the expression
>>>
>>> Why aren't you using the BalancedDelimiterTracker here? It provides better
>>> error recovery and checking than your direct checks for l_paren/r_paren.
>>
>> Originally I'd pulled this out/removed BalancedDelimiterTracker so
>> that I could avoid consuming the trailing ')' & just mutate that into
>> the annotation token I required. But since I ended up having to
>> account for the case where I needed to insert a token to be the
>> annotation token anyway (when recovering from a lack of ')') I assume
>> I might as well just do that in all cases & keep with the old
>> implementation.
>>
>> One problem I have using the BalancedDelimiterTracker is getting the
>> last token it consumes so I can set the annotation token's end
>> correctly. Though I think the only case where the
>> BlancedDelimiterTracker doesn't set and end location is when it
>> doesn't find one & goes all the way to eof. I haven't thought too much
>> about what that situation means/how it comes out in my current code or
>> how it should come out.
>>
>> [As for error recovery, BalancedDelimiterTracker does do the extra
>> legwork to handle nesting quantities which is certainly valuable, but
>> its error recovery is a bit different (& without a FixIt). It skips
>> everything up to the next matching token, my error recovery was to
>> insert the missing token - I wonder which is more likely to be
>> correct? (it'd also be kind of nice to be able to find the location of
>> the previous token & insert the fixit for the missing paren after the
>> previous token rather than before the next token. But that's a minor
>> whitespace improvement ("decltype(x y;" -> "decltype(x) y;" rather
>> than "decltype(x )y;").]
>
> Should BalancedDelimiterTracker be improved to help handle this case?
Probably, though I'm not sure exactly in what way(s). I actually had
to add another special case to my usage of BDT in the decltype parsing
- if you see the last test case I added (which involved 'decltype;') -
that would cause an assertion failure because the locations used in
the annotation token would include the ';' which was incorrect.
Instead in that case I needed to use the source location of the
decltype itself. The BDT should probably gave an invalid OpenLoc
rather than a location of an unconsumed token like that. Plus reliable
endloc reporting & possibly different error recovery rather than
searching so far forward (though that'll require some experimentation
to see what works best).
[I found one other problem with BalancedDelimiterTracker - it's
suggesting a fixit when the opening delimiter isn't present, but it
still returns failure (actually it's Parser::ExpectAndConsume that's
the culprit). Though simply returning false after Parser.cpp:173
produced a fair bit of test noise so I'll look into that separately at
some point]
>>> A couple notes on testing:
>>>
>>> - It would be good to make sure that decltype(T)::blah does the right
>>> thing during template instantiation
>>
>> Good call - this didn't work immediately for dependent names. I've
>> added a simple test case & fixed the bug I had blocking this scenario.
>> (lib/Sema/SemaCXXScopeSpec.cpp:688 - was failing if the expression was
>> not a TagType. I've made it more permissive to allow for any dependent
>> type or non-dependent tag types)
>
> Okay.
>
>>> - I don't see any tests where decltype shows up some place where we have
>>> to use tentative parsing (for the expression-or-declaration ambiguity)
>>
>> I've added a test case for this & made a small change to fix the
>> scenario - but this change has somehow regressed a name mangling test
>> related to templates and decltype usage... I haven't had time to
>> investigate this in great detail but thought I'd show it as-is in case
>> you or anyone else has some ideas about what's going on here.
>
> I don't know off-hand what could have broken.
I found it in the end - see the changes related to ParseScope in
ParseDecl.cpp. The scope was only active within
ParseFunctionDeclarator, not the call to isCXXFunctionDeclarator which
is where the decltype was being parsed/annotated. This threw out the
scopeinfo.
I'm open to suggestions on how best to fix this - the fix I've got
here is just a basic demonstration of the problem. The code could be
restructured in a few ways (or potentially the Expr that the decltype
parses could be modified to have the right/matching scope info based
on where it was reconstituted but I'm not sure how much work that
would be)
>> Also, it seems currently that we don't support decltype in expressions
>> (looks like it's half done - Parser::isCXXSimpleTypeSpecifier has
>> decltype, but ParseCXXSimpleTypeSpecifier has a FIXME to add it). If I
>> switch to using a typename annotation this might come out basically
>> for free, perhaps.
>
> Oops! Should be an easy fix.
>
>> I haven't prototyped an implementation using typename yet, but if I
>> get a chance (and/or if I fix the name mangling issue) I'll send an
>> update.
>
> Okay! I think the annot_typename idea was a bad one on my part;
> annot_decltype seems to be the way to go.
>
> I noticed that there's another (completely separable and not necessarily your
> problem) decltype-related issue, which is that we don't parse:
>
> template<typename T>
> void f(T *ptr) {
> ptr->~decltype(*ptr);
> }
>
> at all. Should be trivial following your changes :)
I guess you probably meant to have the () in there too? (
ptr->~decltype(*ptr)() )
But yes, that's one of the other bits of N3031 we're missing - I plan
to fix all the cases given in that document before resolving the bug.
- David
diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h
index e831113..bda2d31 100644
--- a/include/clang/AST/NestedNameSpecifier.h
+++ b/include/clang/AST/NestedNameSpecifier.h
@@ -36,8 +36,9 @@ class LangOptions;
/// namespaces. For example, "foo::" in "foo::x" is a nested name
/// specifier. Nested name specifiers are made up of a sequence of
/// specifiers, each of which can be a namespace, type, identifier
-/// (for dependent names), or the global specifier ('::', must be the
-/// first specifier).
+/// (for dependent names), decltype specifier, or the global specifier ('::').
+/// The last two specifiers can only appear at the start of a
+/// nested-namespace-specifier.
class NestedNameSpecifier : public llvm::FoldingSetNode {
/// \brief Enumeration describing
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 6c5e79a..b76eff1 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3968,6 +3968,8 @@ def err_typecheck_deleted_function : Error<
"conversion function from %0 to %1 invokes a deleted function">;
def err_expected_class_or_namespace : Error<"expected a class or namespace">;
+def err_expected_class : Error<
+ "%0 is not a class%select{ or namespace|, namespace, or scoped enumeration}1">;
def err_missing_qualified_for_redecl : Error<
"must qualify the name %0 to declare %q1 in this scope">;
def err_invalid_declarator_scope : Error<
diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def
index de345af..2929732 100644
--- a/include/clang/Basic/TokenKinds.def
+++ b/include/clang/Basic/TokenKinds.def
@@ -559,6 +559,8 @@ ANNOTATION(template_id) // annotation for a C++ template-id that names a
// function template specialization (not a type),
// e.g., "std::swap<int>"
ANNOTATION(primary_expr) // annotation for a primary expression
+ANNOTATION(decltype) // annotation for a decltype expression,
+ // e.g., "decltype(foo.bar())"
// Annotation for #pragma unused(...)
// For each argument inside the parentheses the pragma handler will produce
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index a1b346e..c0f9094 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -1898,7 +1898,10 @@ private:
void ParseTypeofSpecifier(DeclSpec &DS);
- void ParseDecltypeSpecifier(DeclSpec &DS);
+ SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
+ void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
+ SourceLocation StartLoc,
+ SourceLocation EndLoc);
void ParseUnderlyingTypeSpecifier(DeclSpec &DS);
void ParseAtomicSpecifier(DeclSpec &DS);
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index d424ef4..bcf9634 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -3325,6 +3325,10 @@ public:
bool EnteringContext,
CXXScopeSpec &SS);
+ bool ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
+ const DeclSpec &DS,
+ SourceLocation ColonColonLoc);
+
bool IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
IdentifierInfo &Identifier,
SourceLocation IdentifierLoc,
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 3bddd3d..aa6fe51 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -1889,6 +1889,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
goto DoneWithDeclSpec;
// typedef-name
+ case tok::kw_decltype:
case tok::identifier: {
// In C++, check to see if this is a scope specifier like foo::bar::, if
// so handle it as such. This is important for ctor parsing.
@@ -2248,7 +2249,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
ParseTypeofSpecifier(DS);
continue;
- case tok::kw_decltype:
+ case tok::annot_decltype:
ParseDecltypeSpecifier(DS);
continue;
@@ -2370,6 +2371,7 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
if (TryAltiVecToken(DS, Loc, PrevSpec, DiagID, isInvalid))
break;
// Fall through.
+ case tok::kw_decltype:
case tok::kw_typename: // typename foo::bar
// Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get.
@@ -2532,7 +2534,7 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
return true;
// C++0x decltype support.
- case tok::kw_decltype:
+ case tok::annot_decltype:
ParseDecltypeSpecifier(DS);
return true;
@@ -3346,6 +3348,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
if (TryAltiVecVectorToken())
return true;
// Fall through.
+ case tok::kw_decltype: // decltype(T())::type
case tok::kw_typename: // typename T::type
// Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get.
@@ -3441,7 +3444,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
return true;
// C++0x decltype.
- case tok::kw_decltype:
+ case tok::annot_decltype:
return true;
// C1x _Atomic()
@@ -3968,6 +3971,10 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
while (1) {
if (Tok.is(tok::l_paren)) {
+ // Enter function-declaration scope, limiting any declarators to the
+ // function prototype scope, including parameter declarators.
+ ParseScope PrototypeScope(this,
+ Scope::FunctionPrototypeScope|Scope::DeclScope);
// The paren may be part of a C++ direct initializer, eg. "int x(1);".
// In such a case, check if we actually have a function declarator; if it
// is not, the declarator has been fully parsed.
@@ -3982,13 +3989,14 @@ void Parser::ParseDirectDeclarator(Declarator &D) {
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
ParseFunctionDeclarator(D, attrs, T);
+ PrototypeScope.Exit();
} else if (Tok.is(tok::l_square)) {
ParseBracketDeclarator(D);
} else {
break;
}
}
-}
+}
/// ParseParenDeclarator - We parsed the declarator D up to a paren. This is
/// only called before the identifier, so these are most likely just grouping
@@ -4084,7 +4092,12 @@ void Parser::ParseParenDeclarator(Declarator &D) {
// ParseFunctionDeclarator to handle of argument list.
D.SetIdentifier(0, Tok.getLocation());
+ // Enter function-declaration scope, limiting any declarators to the
+ // function prototype scope, including parameter declarators.
+ ParseScope PrototypeScope(this,
+ Scope::FunctionPrototypeScope|Scope::DeclScope);
ParseFunctionDeclarator(D, attrs, T, RequiresArg);
+ PrototypeScope.Exit();
}
/// ParseFunctionDeclarator - We are after the identifier and have parsed the
@@ -4142,11 +4155,6 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
Tracker.consumeClose();
EndLoc = Tracker.getCloseLocation();
} else {
- // Enter function-declaration scope, limiting any declarators to the
- // function prototype scope, including parameter declarators.
- ParseScope PrototypeScope(this,
- Scope::FunctionPrototypeScope|Scope::DeclScope);
-
if (Tok.isNot(tok::r_paren))
ParseParameterDeclarationClause(D, attrs, ParamInfo, EllipsisLoc);
else if (RequiresArg)
@@ -4197,9 +4205,6 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
EndLoc = Range.getEnd();
}
}
-
- // Leave prototype scope.
- PrototypeScope.Exit();
}
// Remember that we parsed a function type, and remember the attributes.
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 04c94a0..f2bdb9f 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -632,39 +632,85 @@ Decl *Parser::ParseStaticAssertDeclaration(SourceLocation &DeclEnd){
///
/// 'decltype' ( expression )
///
-void Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
- assert(Tok.is(tok::kw_decltype) && "Not a decltype specifier");
+SourceLocation Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
+ assert((Tok.is(tok::kw_decltype) || Tok.is(tok::annot_decltype))
+ && "Not a decltype specifier");
+
- SourceLocation StartLoc = ConsumeToken();
- BalancedDelimiterTracker T(*this, tok::l_paren);
- if (T.expectAndConsume(diag::err_expected_lparen_after,
- "decltype", tok::r_paren)) {
- return;
- }
+ ExprResult Result;
+ SourceLocation StartLoc = Tok.getLocation();
+ SourceLocation EndLoc;
- // Parse the expression
+ if (Tok.is(tok::annot_decltype)) {
+ Result = getExprAnnotation(Tok);
+ EndLoc = Tok.getAnnotationEndLoc();
+ ConsumeToken();
+ if (Result.isInvalid()) {
+ DS.SetTypeSpecError();
+ return EndLoc;
+ }
+ } else {
+ ConsumeToken();
- // C++0x [dcl.type.simple]p4:
- // The operand of the decltype specifier is an unevaluated operand.
- EnterExpressionEvaluationContext Unevaluated(Actions,
- Sema::Unevaluated);
- ExprResult Result = ParseExpression();
- if (Result.isInvalid()) {
- SkipUntil(tok::r_paren);
- return;
- }
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.expectAndConsume(diag::err_expected_lparen_after,
+ "decltype", tok::r_paren)) {
+ DS.SetTypeSpecError();
+ return T.getOpenLocation() == Tok.getLocation() ?
+ StartLoc : T.getOpenLocation();
+ }
- // Match the ')'
- T.consumeClose();
- if (T.getCloseLocation().isInvalid())
- return;
+ // Parse the expression
+
+ // C++0x [dcl.type.simple]p4:
+ // The operand of the decltype specifier is an unevaluated operand.
+ EnterExpressionEvaluationContext Unevaluated(Actions,
+ Sema::Unevaluated);
+ Result = ParseExpression();
+ if (Result.isInvalid()) {
+ SkipUntil(tok::r_paren, true, true);
+ DS.SetTypeSpecError();
+ return Tok.is(tok::eof) ? Tok.getLocation() : ConsumeParen();
+ }
+
+ // Match the ')'
+ T.consumeClose();
+ if (T.getCloseLocation().isInvalid()) {
+ DS.SetTypeSpecError();
+ // FIXME: this should return the location of the last token
+ // that was consumed (by "consumeClose()")
+ return T.getCloseLocation();
+ }
+
+ EndLoc = T.getCloseLocation();
+ }
const char *PrevSpec = 0;
unsigned DiagID;
// Check for duplicate type specifiers (e.g. "int decltype(a)").
if (DS.SetTypeSpecType(DeclSpec::TST_decltype, StartLoc, PrevSpec,
- DiagID, Result.release()))
+ DiagID, Result.release())) {
Diag(StartLoc, DiagID) << PrevSpec;
+ DS.SetTypeSpecError();
+ }
+ return EndLoc;
+}
+
+void Parser::AnnotateExistingDecltypeSpecifier(const DeclSpec& DS,
+ SourceLocation StartLoc,
+ SourceLocation EndLoc) {
+ // make sure we have a token we can turn into an annotation token
+ if (PP.isBacktrackEnabled())
+ PP.RevertCachedTokens(1);
+ else
+ PP.EnterToken(Tok);
+
+ Tok.setKind(tok::annot_decltype);
+ setExprAnnotation(Tok, DS.getTypeSpecType() == TST_decltype ?
+ DS.getRepAsExpr() : ExprResult());
+ Tok.setAnnotationEndLoc(EndLoc);
+ Tok.setLocation(StartLoc);
+ PP.AnnotateCachedTokens(Tok);
}
void Parser::ParseUnderlyingTypeSpecifier(DeclSpec &DS) {
@@ -727,14 +773,16 @@ Parser::TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
BaseLoc = Tok.getLocation();
// Parse decltype-specifier
- if (Tok.is(tok::kw_decltype)) {
+ // tok == kw_decltype is just error recovery, it can only happen when SS
+ // isn't empty
+ if (Tok.is(tok::kw_decltype) || Tok.is(tok::annot_decltype)) {
if (SS.isNotEmpty())
Diag(SS.getBeginLoc(), diag::err_unexpected_scope_on_base_decltype)
<< FixItHint::CreateRemoval(SS.getRange());
// Fake up a Declarator to use with ActOnTypeName.
DeclSpec DS(AttrFactory);
- ParseDecltypeSpecifier(DS);
+ ParseDecltypeSpecifier(DS);
EndLocation = DS.getSourceRange().getEnd();
Declarator DeclaratorInfo(DS, Declarator::TypeNameContext);
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 4b8fd76..93f1055 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -663,6 +663,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
ConsumeToken();
break;
+ case tok::kw_decltype:
case tok::identifier: { // primary-expression: identifier
// unqualified-id: identifier
// constant: enumeration-constant
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index dfc77e1..b8f7727 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -168,6 +168,22 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
*MayBePseudoDestructor = false;
}
+ if (Tok.is(tok::kw_decltype) || Tok.is(tok::annot_decltype)) {
+ DeclSpec DS(AttrFactory);
+ SourceLocation DeclLoc = Tok.getLocation();
+ SourceLocation EndLoc = ParseDecltypeSpecifier(DS);
+ if (Tok.isNot(tok::coloncolon)) {
+ AnnotateExistingDecltypeSpecifier(DS, DeclLoc, EndLoc);
+ return false;
+ }
+
+ SourceLocation CCLoc = ConsumeToken();
+ if (Actions.ActOnCXXNestedNameSpecifierDecltype(SS, DS, CCLoc))
+ SS.SetInvalid(SourceRange(DeclLoc, CCLoc));
+
+ HasScopeSpecifier = true;
+ }
+
while (true) {
if (HasScopeSpecifier) {
// C++ [basic.lookup.classref]p5:
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index d53839f..f6811f3 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -705,7 +705,6 @@ Parser::isExpressionOrTypeSpecifierSimple(tok::TokenKind Kind) {
case tok::kw_wchar_t:
case tok::kw_char16_t:
case tok::kw_char32_t:
- case tok::kw_decltype:
case tok::kw___underlying_type:
case tok::kw_thread_local:
case tok::kw__Decimal32:
@@ -847,13 +846,14 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
if (Next.is(tok::kw_new) || // ::new
Next.is(tok::kw_delete)) // ::delete
return TPResult::False();
-
+ }
+ // Fall through.
+ case tok::kw_decltype:
// Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get.
if (TryAnnotateTypeOrScopeToken())
return TPResult::Error();
return isCXXDeclarationSpecifier();
- }
// decl-specifier:
// storage-class-specifier
@@ -1029,7 +1029,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() {
}
// C++0x decltype support.
- case tok::kw_decltype:
+ case tok::annot_decltype:
return TPResult::True();
// C++0x type traits support
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index b4d41ef..c5d054e 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -1193,8 +1193,8 @@ TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) {
/// as the current tokens, so only call it in contexts where these are invalid.
bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext, bool NeedType) {
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)
- || Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)) &&
- "Cannot be a type or scope token!");
+ || Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)
+ || Tok.is(tok::kw_decltype)) && "Cannot be a type or scope token!");
if (Tok.is(tok::kw_typename)) {
// Parse a C++ typename-specifier, e.g., "typename T::type".
@@ -1373,8 +1373,8 @@ bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) {
assert(getLang().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
- (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)))&&
- "Cannot be a type or scope token!");
+ (Tok.is(tok::annot_template_id) && NextToken().is(tok::coloncolon)) ||
+ Tok.is(tok::kw_decltype)) && "Cannot be a type or scope token!");
CXXScopeSpec SS;
if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), EnteringContext))
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index dfd59bf..93f8325 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -676,6 +676,29 @@ bool Sema::ActOnCXXNestedNameSpecifier(Scope *S,
/*ScopeLookupResult=*/0, false);
}
+bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
+ const DeclSpec &DS,
+ SourceLocation ColonColonLoc) {
+ if (SS.isInvalid() || DS.getTypeSpecType() == DeclSpec::TST_error)
+ return true;
+
+ assert(DS.getTypeSpecType() == DeclSpec::TST_decltype);
+
+ QualType T = BuildDecltypeType(DS.getRepAsExpr(), DS.getTypeSpecTypeLoc());
+ if (!T->isDependentType() && !T->getAs<TagType>()) {
+ Diag(DS.getTypeSpecTypeLoc(), diag::err_expected_class)
+ << T << getLangOptions().CPlusPlus;
+ return true;
+ }
+
+ TypeLocBuilder TLB;
+ DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
+ DecltypeTL.setNameLoc(DS.getTypeSpecTypeLoc());
+ SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, T),
+ ColonColonLoc);
+ return false;
+}
+
/// IsInvalidUnlessNestedName - This method is used for error recovery
/// purposes to determine whether the specified identifier is only valid as
/// a nested name specifier, for example a namespace name. It is
diff --git a/test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp b/test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp
index 143ba89..b976e16 100644
--- a/test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp
+++ b/test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp
@@ -2,8 +2,7 @@
struct S {
S *p = this; // ok
- decltype(this) q; // expected-error {{invalid use of 'this' outside of a nonstatic member function}} \
- expected-error {{C++ requires a type specifier for all declarations}}
+ decltype(this) q; // expected-error {{invalid use of 'this' outside of a nonstatic member function}}
int arr[sizeof(this)]; // expected-error {{invalid use of 'this' outside of a nonstatic member function}}
int sz = sizeof(this); // ok
diff --git a/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp b/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp
new file mode 100644
index 0000000..80ae67a
--- /dev/null
+++ b/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp
@@ -0,0 +1,82 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+struct global {
+};
+
+namespace PR10127 {
+ struct outer {
+ struct middle {
+ struct inner {
+ int func();
+ int i;
+ };
+ struct inner2 {
+ };
+ struct inner3 {
+ };
+ int mfunc();
+ };
+ typedef int td_int;
+ };
+
+ struct str {
+ operator decltype(outer::middle::inner()) ();
+ operator decltype(outer::middle())::inner2 ();
+ operator decltype(outer())::middle::inner3 ();
+ str(int (decltype(outer::middle::inner())::*n)(),
+ int (decltype(outer::middle())::inner::*o)(),
+ int (decltype(outer())::middle::inner::*p)());
+ };
+
+ int decltype(outer::middle())::inner::func() {
+ return 0;
+ }
+
+ decltype(outer::middle::inner()) a;
+ void scope() {
+ a.decltype(outer::middle())::mfunc(); // expected-error{{'PR10127::outer::middle::mfunc' is not a member of class 'decltype(outer::middle::inner())'}}
+ a.decltype(outer::middle::inner())::func();
+ a.decltype(outer::middle())::inner::func();
+ a.decltype(outer())::middle::inner::func();
+
+ a.decltype(outer())::middle::inner::~inner();
+
+ decltype(outer())::middle::inner().func();
+ }
+ decltype(outer::middle())::inner b;
+ decltype(outer())::middle::inner c;
+ decltype(outer())::fail d; // expected-error{{no type named 'fail' in 'PR10127::outer'}}
+ decltype(outer())::fail::inner e; // expected-error{{no member named 'fail' in 'PR10127::outer'}}
+ decltype()::fail f; // expected-error{{expected expression}}
+ decltype()::middle::fail g; // expected-error{{expected expression}}
+
+ decltype(int()) h;
+ decltype(int())::PR10127::outer i; // expected-error{{'decltype(int())' (aka 'int') is not a class, namespace, or scoped enumeration}}
+ decltype(int())::global j; // expected-error{{'decltype(int())' (aka 'int') is not a class, namespace, or scoped enumeration}}
+
+ outer::middle k = decltype(outer())::middle();
+ outer::middle::inner l = decltype(outer())::middle::inner();
+
+ template<typename T>
+ struct templ {
+ typename decltype(T())::middle::inner x; // expected-error{{type 'decltype(int())' (aka 'int') cannot be used prior to '::' because it has no members}}
+ };
+
+ template class templ<int>; // expected-note{{in instantiation of template class 'PR10127::templ<int>' requested here}}
+ template class templ<outer>;
+
+ enum class foo {
+ bar,
+ baz
+ };
+
+ foo m = decltype(foo::bar)::baz;
+
+ enum E {
+ };
+ struct bar {
+ enum E : decltype(outer())::td_int(4);
+ enum F : decltype(outer())::td_int;
+ enum G : decltype; // expected-error{{expected '(' after 'decltype'}}
+ };
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits