Support decltype in nested-name-specifiers.

llvm-svn: 145785
This commit is contained in:
David Blaikie 2011-12-04 05:04:18 +00:00
parent 80381f6cbf
commit 15a430a368
14 changed files with 240 additions and 52 deletions

View 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

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

View 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

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

View 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,

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

View 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;
if (Tok.is(tok::annot_decltype)) {
Result = getExprAnnotation(Tok);
EndLoc = Tok.getAnnotationEndLoc();
ConsumeToken();
if (Result.isInvalid()) {
DS.SetTypeSpecError();
return EndLoc;
}
} else {
ConsumeToken();
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();
}
// 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();
}
// Parse the expression
// 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;
}
// Match the ')'
T.consumeClose();
if (T.getCloseLocation().isInvalid())
return;
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);

View 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

View 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:

View 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

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

View 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

View 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

View File

@ -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'}}
};
}