Fix parsing of trailing-return-type. Types are syntactically prohibited from

being defined here: [] () -> struct S {} does not define struct S.

In passing, implement DR1318 (syntactic disambiguation of 'final').

llvm-svn: 152551
This commit is contained in:
Richard Smith 2012-03-12 08:56:40 +00:00
parent c5b0552055
commit bfdb108fc5
11 changed files with 76 additions and 30 deletions

View File

@ -1622,6 +1622,7 @@ private:
DSC_normal, // normal context
DSC_class, // class context, enables 'friend'
DSC_type_specifier, // C++ type-specifier-seq
DSC_trailing, // C++11 trailing-type-specifier in a trailing return type
DSC_top_level // top-level/namespace declaration context
};

View File

@ -1428,9 +1428,10 @@ public:
ObjCCatchContext, // Objective-C catch exception-declaration
BlockLiteralContext, // Block literal declarator.
LambdaExprContext, // Lambda-expression declarator.
TrailingReturnContext, // C++11 trailing-type-specifier.
TemplateTypeArgContext, // Template type argument.
AliasDeclContext, // C++0x alias-declaration.
AliasTemplateContext // C++0x alias-declaration template.
AliasDeclContext, // C++11 alias-declaration.
AliasTemplateContext // C++11 alias-declaration template.
};
private:
@ -1604,6 +1605,7 @@ public:
case BlockLiteralContext:
case LambdaExprContext:
case TemplateTypeArgContext:
case TrailingReturnContext:
return true;
}
llvm_unreachable("unknown context kind!");
@ -1635,6 +1637,7 @@ public:
case BlockLiteralContext:
case LambdaExprContext:
case TemplateTypeArgContext:
case TrailingReturnContext:
return false;
}
llvm_unreachable("unknown context kind!");
@ -1679,6 +1682,7 @@ public:
case BlockLiteralContext:
case LambdaExprContext:
case TemplateTypeArgContext:
case TrailingReturnContext:
return false;
}
llvm_unreachable("unknown context kind!");

View File

@ -36,9 +36,13 @@ TypeResult Parser::ParseTypeName(SourceRange *Range,
Declarator::TheContext Context,
AccessSpecifier AS,
Decl **OwnedType) {
DeclSpecContext DSC = DSC_normal;
if (Context == Declarator::TrailingReturnContext)
DSC = DSC_trailing;
// Parse the common declaration-specifiers piece.
DeclSpec DS(AttrFactory);
ParseSpecifierQualifierList(DS, AS);
ParseSpecifierQualifierList(DS, AS, DSC);
if (OwnedType)
*OwnedType = DS.isTypeSpecOwned() ? DS.getRepAsDecl() : 0;
@ -2653,8 +2657,12 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
while (Tok.is(tok::kw___declspec))
ParseMicrosoftDeclSpec(attrs);
bool AllowFixedUnderlyingType
= getLangOpts().CPlusPlus0x || getLangOpts().MicrosoftExt || getLangOpts().ObjC2;
// Enum definitions should not be parsed in a trailing-return-type.
bool AllowDeclaration = DSC != DSC_trailing;
bool AllowFixedUnderlyingType = AllowDeclaration &&
(getLangOpts().CPlusPlus0x || getLangOpts().MicrosoftExt ||
getLangOpts().ObjC2);
CXXScopeSpec &SS = DS.getTypeSpecScope();
if (getLangOpts().CPlusPlus) {
@ -2679,7 +2687,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
// Must have either 'enum name' or 'enum {...}'.
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace) &&
(AllowFixedUnderlyingType && Tok.isNot(tok::colon))) {
!(AllowFixedUnderlyingType && Tok.is(tok::colon))) {
Diag(Tok, diag::err_expected_ident_lbrace);
// Skip the rest of this declarator, up until the comma or semicolon.
@ -2785,6 +2793,8 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
Sema::TagUseKind TUK;
if (DS.isFriendSpecified())
TUK = Sema::TUK_Friend;
else if (!AllowDeclaration)
TUK = Sema::TUK_Reference;
else if (Tok.is(tok::l_brace))
TUK = Sema::TUK_Definition;
else if (Tok.is(tok::semi) && DSC != DSC_type_specifier)
@ -2850,7 +2860,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
if (!TagDecl) {
// The action failed to produce an enumeration tag. If this is a
// definition, consume the entire definition.
if (Tok.is(tok::l_brace)) {
if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) {
ConsumeBrace();
SkipUntil(tok::r_brace);
}
@ -2859,7 +2869,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS,
return;
}
if (Tok.is(tok::l_brace)) {
if (Tok.is(tok::l_brace) && TUK != Sema::TUK_Reference) {
if (TUK == Sema::TUK_Friend)
Diag(Tok, diag::err_friend_decl_defines_type)
<< SourceRange(DS.getFriendSpecLoc());

View File

@ -1114,11 +1114,16 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
if (SuppressingAccessChecks)
Actions.ActOnStopSuppressingAccessChecks();
// There are four options here. If we have 'struct foo;', then this
// is either a forward declaration or a friend declaration, which
// have to be treated differently. If we have 'struct foo {...',
// 'struct foo :...' or 'struct foo final[opt]' then this is a
// definition. Otherwise we have something like 'struct foo xyz', a reference.
// There are four options here.
// - If we are in a trailing return type, this is always just a reference,
// and we must not try to parse a definition. For instance,
// [] () -> struct S { };
// does not define a type.
// - If we have 'struct foo {...', 'struct foo :...',
// 'struct foo final :' or 'struct foo final {', then this is a definition.
// - If we have 'struct foo;', then this is either a forward declaration
// or a friend declaration, which have to be treated differently.
// - Otherwise we have something like 'struct foo xyz', a reference.
// However, in type-specifier-seq's, things look like declarations but are
// just references, e.g.
// new struct s;
@ -1126,10 +1131,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// &T::operator struct s;
// For these, DSC is DSC_type_specifier.
Sema::TagUseKind TUK;
if (Tok.is(tok::l_brace) ||
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
// FIXME: 'final' must be followed by ':' or '{' to mark a definition.
isCXX0XFinalKeyword()) {
if (DSC == DSC_trailing)
TUK = Sema::TUK_Reference;
else if (Tok.is(tok::l_brace) ||
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
(isCXX0XFinalKeyword() &&
NextToken().is(tok::l_brace) || NextToken().is(tok::colon))) {
if (DS.isFriendSpecified()) {
// C++ [class.friend]p2:
// A class shall not be defined in a friend declaration.
@ -2673,14 +2680,7 @@ TypeResult Parser::ParseTrailingReturnType(SourceRange &Range) {
ConsumeToken();
// FIXME: Need to suppress declarations when parsing this typename.
// Otherwise in this function definition:
//
// auto f() -> struct X {}
//
// struct X is parsed as class definition because of the trailing
// brace.
return ParseTypeName(&Range);
return ParseTypeName(&Range, Declarator::TrailingReturnContext);
}
/// \brief We have just started parsing the definition of a new class,

View File

@ -1832,6 +1832,9 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
case Declarator::AliasTemplateContext:
Error = 9; // Type alias
break;
case Declarator::TrailingReturnContext:
Error = 10; // Function return type
break;
case Declarator::TypeNameContext:
Error = 11; // Generic
break;
@ -1885,6 +1888,11 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
// Check the contexts where C++ forbids the declaration of a new class
// or enumeration in a type-specifier-seq.
switch (D.getContext()) {
case Declarator::TrailingReturnContext:
// Class and enumeration definitions are syntactically not allowed in
// trailing return types.
llvm_unreachable("parser should not have allowed this");
break;
case Declarator::FileContext:
case Declarator::MemberContext:
case Declarator::BlockContext:
@ -2606,6 +2614,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
case Declarator::ObjCCatchContext:
case Declarator::BlockLiteralContext:
case Declarator::LambdaExprContext:
case Declarator::TrailingReturnContext:
case Declarator::TemplateTypeArgContext:
// FIXME: We may want to allow parameter packs in block-literal contexts
// in the future.

View File

@ -44,6 +44,6 @@ struct F : auto(*)()->int {}; // expected-error{{expected class name}}
template<typename T = auto(*)()->int> struct G { };
int g();
auto (*h)() -> auto = &g; // expected-error{{'auto' not allowed here}}
auto (*h)() -> auto = &g; // expected-error{{'auto' not allowed in function return type}}
auto (*i)() = &g; // ok; auto deduced as int.
auto (*k)() -> int = i; // ok; no deduction.

View File

@ -65,4 +65,4 @@ template<typename T = auto> struct G { }; // expected-error{{'auto' not allowed
using A = auto; // expected-error{{'auto' not allowed in type alias}}
// FIXME: don't issue the second diagnostic for this error.
auto k() -> auto; // expected-error{{'auto' not allowed here}} unexpected-error{{without trailing return type}}
auto k() -> auto; // expected-error{{'auto' not allowed in function return type}} unexpected-error{{without trailing return type}}

View File

@ -1,3 +1,3 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
auto j() -> enum { e3 }; // expected-error{{can not be defined in a type specifier}}
auto j() -> enum { e3 }; // expected-error{{unnamed enumeration must be a definition}} expected-error {{requires a specifier or qualifier}} expected-error {{without trailing return type}}

View File

@ -5,15 +5,24 @@
// final 'context sensitive' mess.
namespace final {
struct S { int n; };
struct T { int n; };
namespace N {
int n;
// These declare variables named final..
extern struct S final;
extern struct S final [[]];
extern struct S final, foo;
struct S final = S();
// This defines a class, not a variable, even though it would successfully
// parse as a variable but not as a class. DR1318's wording suggests that
// this disambiguation is only performed on an ambiguity, but that was not
// the intent.
struct S final {
struct S final { // expected-note {{here}}
int(n) // expected-error {{expected ';'}}
};
// This too.
struct T final : S {}; // expected-error {{base 'S' is marked 'final'}}
}
}

View File

@ -6,3 +6,13 @@ namespace Commas {
b [[ ]],
c alignas(double);
}
struct S {};
enum E { e };
auto f() -> struct S {
return S();
}
auto g() -> enum E {
return E();
}

View File

@ -1,5 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify -std=c++11 %s
enum E { e };
class C {
int f() {
@ -19,6 +21,8 @@ class C {
[=,&foo] () {};
[&,foo] () {};
[this] () {};
[] () -> class C { return C(); };
[] () -> enum E { return e; };
[] -> int { return 0; }; // expected-error{{lambda requires '()' before return type}}
[] mutable -> int { return 0; }; // expected-error{{lambda requires '()' before 'mutable'}}
@ -37,4 +41,3 @@ class C {
int a6[1] = {[this] = 1 }; // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'C *'}}
}
};