Now with better error recovery (utilizing CXXScopeSpec's
setInvalid(SourceRange) as is done elsewhere) & support for
non-crashing when used in member ref expressions like
a->decltype(foo)::bar() (including test cases for same) based on some
test cases suggested by Chandler Carruth.

- David

On Sun, Nov 6, 2011 at 10:15 PM, David Blaikie <[email protected]> wrote:
> 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,49 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
+
+struct global {
+};
+
+namespace PR10127 {
+  struct outer {
+    struct middle {
+      struct inner {
+        int func();
+      };
+      int mfunc();
+    };
+  };
+
+  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 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}}
+  decltype()::middle::fail f; // expected-error{{expected expression}}
+  
+  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,24 @@
     *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);
+    SourceLocation DeclLoc = Tok.getLocation();
+    ParseDecltypeSpecifier(DS);
+    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:
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

Reply via email to