> 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