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