]> granicus.if.org Git - clang/commitdiff
Support decltype in nested-name-specifiers.
authorDavid Blaikie <dblaikie@gmail.com>
Sun, 4 Dec 2011 05:04:18 +0000 (05:04 +0000)
committerDavid Blaikie <dblaikie@gmail.com>
Sun, 4 Dec 2011 05:04:18 +0000 (05:04 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145785 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
include/clang/AST/NestedNameSpecifier.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/TokenKinds.def
include/clang/Parse/Parser.h
include/clang/Sema/Sema.h
lib/Parse/ParseDecl.cpp
lib/Parse/ParseDeclCXX.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseExprCXX.cpp
lib/Parse/ParseTentative.cpp
lib/Parse/Parser.cpp
lib/Sema/SemaCXXScopeSpec.cpp
test/CXX/expr/expr.prim/expr.prim.general/p4-0x.cpp
test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp [new file with mode: 0644]

index e831113863e781079fbf617113ada74f3123ffad..bda2d31fd5a4892389f9a1594cd16d39e78cb4a7 100644 (file)
@@ -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
index 42430912913164a25f5e67ee3803357441a8789f..fa2fabcdeb8841be23cb01c215d62fd745697729 100644 (file)
@@ -3993,6 +3993,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<
index 99ccc9ac4e0b33b18357473cad399785c56bed41..e9b772cddedf5fb73b8845f8a6c3d2600af3729c 100644 (file)
@@ -560,6 +560,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
index 2c8f7ef9868953666d0f284a077d23a825cec471..5dfb99ec07620d070ce8c8d86674335b9cf229ea 100644 (file)
@@ -1901,7 +1901,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);
 
index ab644e2c65b877933fda974746c9cd1be3d86fcd..adb00dfcb3a19afc74c83e61b403428cd3e05ebc 100644 (file)
@@ -3326,6 +3326,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,
index 3bddd3de13d06a49389aca61a76fa6ef3eb0b061..0c5a4879aedbe577446d4562a96fa08a3ff8fd85 100644 (file)
@@ -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
@@ -4109,6 +4122,8 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
                                      ParsedAttributes &attrs,
                                      BalancedDelimiterTracker &Tracker,
                                      bool RequiresArg) {
+  assert(getCurScope()->isFunctionPrototypeScope() && 
+         "Should call from a Function scope");
   // lparen is already consumed!
   assert(D.isPastIdentifier() && "Should not call before identifier!");
 
@@ -4142,11 +4157,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 +4207,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.
index 04c94a053d6be6c094e05e3241cce3fb78c33888..f2bdb9f7bd55fb5785872a6e30242a682b6f9995 100644 (file)
@@ -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);
index 75fc9be5aa43e1e85b62eea4331c7cfe9e09db27..dcbee4f9e6ac3102fb73af5055bb3a991df74e8f 100644 (file)
@@ -664,6 +664,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression,
     ConsumeToken();
     break;
       
+  case tok::kw_decltype:
   case tok::identifier: {      // primary-expression: identifier
                                // unqualified-id: identifier
                                // constant: enumeration-constant
index d9983d4167695cb0a7902fa66add17d002caf5a7..79d2ec21ab09cd8b3a9e224b21cb0dd6bf443464 100644 (file)
@@ -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:
index e682cb421895390edb222f7eb015cdb073be1be8..7c280ff50be9e1937648c48c9b1edab05eab18c3 100644 (file)
@@ -706,7 +706,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:
@@ -848,13 +847,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
@@ -1030,7 +1030,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
index e74c46e2fda18f50fb15cf04299a9441a0570482..112d7a2d12c3861613ef30728560f4434771a45d 100644 (file)
@@ -1202,8 +1202,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".
@@ -1382,8 +1382,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))
index dfd59bfe4fd5b165a3d2c8ee21ccadc0bee07815..93f83256b018daa55a80b00e2e1a43d585ca6168 100644 (file)
@@ -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
index 143ba897ae95edfa83b467de1547b8e9f2efe690..b976e1664e1fe59ee95c7c194d4c2f1953b37722 100644 (file)
@@ -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 (file)
index 0000000..80ae67a
--- /dev/null
@@ -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'}}
+  };
+}