Here's a patch that adds support for decltype in
nested-namespace-specifiers. I've included test cases for use of this
feature in both declarations and expressions (open to suggestions for
other contexts that might benefit from coverage).
I've also improved error recovery of the existing use of decltype (for
declarations) & these new uses by calling DeclSpec
setTypeSpecTypeError when errors are encountered while trying to parse
a decltype-specifier. There's probably still some error recovery to
improve, though, as I'm not currently doing anything with
ActOnCXXNestedNameSpecifierDecltype's return value as I'm not sure
what the best error recovery would be there. I suppose the
CXXScopeSpec should at least be failed.
- David
Index: test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp
===================================================================
--- test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp (revision 143906)
+++ test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp (working copy)
@@ -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
Index: test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp
===================================================================
--- test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp (revision 0)
+++ test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp (revision 0)
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+struct global {
+};
+
+namespace PR10127 {
+ struct outer {
+ struct middle {
+ struct inner {
+ };
+ };
+ };
+
+ decltype(outer::middle::inner()) a;
+ decltype(outer::middle())::inner b;
+ decltype(outer())::middle::inner c;
+ decltype(outer())::fail c; // expected-error{{no type named 'fail' in 'PR10127::outer'}}
+ decltype(outer())::fail::inner d; // expected-error{{no member named 'fail' in 'PR10127::outer'}}
+ decltype()::fail e; // expected-error{{expected expression}} expected-error{{unknown type name 'fail'}}
+ decltype()::middle::fail f; // expected-error{{expected expression}} expected-error{{use of undeclared identifier 'middle'}}
+
+ decltype(int()) g;
+ decltype(int())::PR10127::outer h; // expected-error{{expected a decltype of a class}}
+ decltype(int())::global i; // expected-error{{expected a decltype of a class}}
+
+ outer::middle k = decltype(outer())::middle();
+ outer::middle::inner l = decltype(outer())::middle::inner();
+
+ enum class foo {
+ bar,
+ baz
+ };
+
+ foo m = decltype(foo::bar)::baz;
+}
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
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<
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h (revision 143902)
+++ include/clang/Sema/Sema.h (working copy)
@@ -3320,6 +3320,10 @@
bool EnteringContext,
CXXScopeSpec &SS);
+ bool ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
+ const DeclSpec &DS,
+ SourceLocation ColonColonLoc);
+
bool IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
IdentifierInfo &Identifier,
SourceLocation IdentifierLoc,
Index: include/clang/AST/NestedNameSpecifier.h
===================================================================
--- include/clang/AST/NestedNameSpecifier.h (revision 143902)
+++ include/clang/AST/NestedNameSpecifier.h (working copy)
@@ -36,8 +36,9 @@
/// 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
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h (revision 143902)
+++ include/clang/Parse/Parser.h (working copy)
@@ -1884,6 +1884,7 @@
void ParseTypeofSpecifier(DeclSpec &DS);
void ParseDecltypeSpecifier(DeclSpec &DS);
+ void AnnotateDecltypeSpecifier();
void ParseUnderlyingTypeSpecifier(DeclSpec &DS);
void ParseAtomicSpecifier(DeclSpec &DS);
Index: lib/Sema/SemaCXXScopeSpec.cpp
===================================================================
--- lib/Sema/SemaCXXScopeSpec.cpp (revision 143902)
+++ lib/Sema/SemaCXXScopeSpec.cpp (working copy)
@@ -676,6 +676,27 @@
/*ScopeLookupResult=*/0, false);
}
+bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
+ const DeclSpec &DS,
+ SourceLocation ColonColonLoc) {
+ if (SS.isInvalid() || DS.getTypeSpecType() == DeclSpec::TST_error)
+ return true;
+
+ QualType Q = DS.getRepAsExpr()->getType();
+ if (!Q->getAs<TagType>()) {
+ Diag(DS.getTypeSpecTypeLoc(), diag::err_expected_class);
+ return true;
+ }
+
+ QualType T = BuildDecltypeType(DS.getRepAsExpr(), DS.getTypeSpecTypeLoc());
+ 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
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp (revision 143902)
+++ lib/Parse/ParseDecl.cpp (working copy)
@@ -1888,6 +1888,7 @@
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.
@@ -2247,7 +2248,7 @@
ParseTypeofSpecifier(DS);
continue;
- case tok::kw_decltype:
+ case tok::annot_decltype:
ParseDecltypeSpecifier(DS);
continue;
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp (revision 143902)
+++ lib/Parse/ParseExpr.cpp (working copy)
@@ -663,6 +663,7 @@
ConsumeToken();
break;
+ case tok::kw_decltype:
case tok::identifier: { // primary-expression: identifier
// unqualified-id: identifier
// constant: enumeration-constant
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();
+ }
+}
+
+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
@@ -649,22 +673,25 @@
EnterExpressionEvaluationContext Unevaluated(Actions,
Sema::Unevaluated);
ExprResult Result = ParseExpression();
- if (Result.isInvalid()) {
- SkipUntil(tok::r_paren);
- return;
- }
+ if (Result.isInvalid())
+ SkipUntil(tok::r_paren, true, true);
// Match the ')'
- T.consumeClose();
- if (T.getCloseLocation().isInvalid())
- return;
+ if (Tok.isNot(tok::r_paren)) {
+ Diag(Tok, diag::err_expected_rparen)
+ << FixItHint::CreateInsertion(Tok.getLocation(), ")");
+ // make sure we have a token we can turn into an annotation token
+ if (PP.isBacktrackEnabled())
+ PP.RevertCachedTokens(1);
+ else
+ PP.EnterToken(Tok);
+ }
- 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()))
- Diag(StartLoc, DiagID) << PrevSpec;
+ Tok.setKind(tok::annot_decltype);
+ setExprAnnotation(Tok, Result);
+ Tok.setAnnotationEndLoc(Tok.getLocation());
+ Tok.setLocation(StartLoc);
+ PP.AnnotateCachedTokens(Tok);
}
void Parser::ParseUnderlyingTypeSpecifier(DeclSpec &DS) {
@@ -727,14 +754,16 @@
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);
Index: lib/Parse/ParseExprCXX.cpp
===================================================================
--- lib/Parse/ParseExprCXX.cpp (revision 143907)
+++ lib/Parse/ParseExprCXX.cpp (working copy)
@@ -168,6 +168,19 @@
*MayBePseudoDestructor = false;
}
+ if (Tok.is(tok::kw_decltype))
+ AnnotateDecltypeSpecifier();
+
+ if (Tok.is(tok::annot_decltype)) {
+ if (NextToken().isNot(tok::coloncolon)) {
+ return false;
+ }
+
+ DeclSpec DS(AttrFactory);
+ ParseDecltypeSpecifier(DS);
+ Actions.ActOnCXXNestedNameSpecifierDecltype(SS, DS, ConsumeToken());
+ }
+
while (true) {
if (HasScopeSpecifier) {
// C++ [basic.lookup.classref]p5:
Index: lib/Parse/Parser.cpp
===================================================================
--- lib/Parse/Parser.cpp (revision 143902)
+++ lib/Parse/Parser.cpp (working copy)
@@ -1193,8 +1193,8 @@
/// 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".
@@ -1372,8 +1372,8 @@
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))
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits