> Some comments follow…

Thanks for the feedback.

> 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.

> 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.

> 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;
> +}
>
> This should probably check that the type specifier actually is a decltype 
> (even if it's just an assertion).

Added an assertion.

> 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.

> 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.

> +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;").]

> 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)

>  - 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.

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.

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.

Thanks,
- 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 ad175e4..26fb66b 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3964,6 +3964,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 namespace, class, or scoped enumeration">;
 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 27730f0..9e07fb7 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -3323,6 +3323,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..7fe3848 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;
 
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 04c94a0..a501001 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -632,39 +632,84 @@ 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();
+    }
 
-  // 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 +772,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..bc0a6bd 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -847,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
@@ -1029,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
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..af5ea9e 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -676,6 +676,28 @@ 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;
+    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..4d90ae6
--- /dev/null
+++ b/test/CXX/expr/expr.prim/expr.prim.general/p8-0x.cpp
@@ -0,0 +1,59 @@
+// 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().func();
+  }
+  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{{'decltype(int())' (aka 'int') is not a namespace, class, or scoped enumeration}}
+  decltype(int())::global h; // expected-error{{'decltype(int())' (aka 'int') is not a namespace, class, 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;
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to