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_normal, // normal context
DSC_class, // class context, enables 'friend' DSC_class, // class context, enables 'friend'
DSC_type_specifier, // C++ type-specifier-seq 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 DSC_top_level // top-level/namespace declaration context
}; };

View File

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

View File

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

View File

@ -1114,11 +1114,16 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
if (SuppressingAccessChecks) if (SuppressingAccessChecks)
Actions.ActOnStopSuppressingAccessChecks(); Actions.ActOnStopSuppressingAccessChecks();
// There are four options here. If we have 'struct foo;', then this // There are four options here.
// is either a forward declaration or a friend declaration, which // - If we are in a trailing return type, this is always just a reference,
// have to be treated differently. If we have 'struct foo {...', // and we must not try to parse a definition. For instance,
// 'struct foo :...' or 'struct foo final[opt]' then this is a // [] () -> struct S { };
// definition. Otherwise we have something like 'struct foo xyz', a reference. // 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 // However, in type-specifier-seq's, things look like declarations but are
// just references, e.g. // just references, e.g.
// new struct s; // new struct s;
@ -1126,10 +1131,12 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind,
// &T::operator struct s; // &T::operator struct s;
// For these, DSC is DSC_type_specifier. // For these, DSC is DSC_type_specifier.
Sema::TagUseKind TUK; Sema::TagUseKind TUK;
if (Tok.is(tok::l_brace) || if (DSC == DSC_trailing)
TUK = Sema::TUK_Reference;
else if (Tok.is(tok::l_brace) ||
(getLangOpts().CPlusPlus && Tok.is(tok::colon)) || (getLangOpts().CPlusPlus && Tok.is(tok::colon)) ||
// FIXME: 'final' must be followed by ':' or '{' to mark a definition. (isCXX0XFinalKeyword() &&
isCXX0XFinalKeyword()) { NextToken().is(tok::l_brace) || NextToken().is(tok::colon))) {
if (DS.isFriendSpecified()) { if (DS.isFriendSpecified()) {
// C++ [class.friend]p2: // C++ [class.friend]p2:
// A class shall not be defined in a friend declaration. // A class shall not be defined in a friend declaration.
@ -2673,14 +2680,7 @@ TypeResult Parser::ParseTrailingReturnType(SourceRange &Range) {
ConsumeToken(); ConsumeToken();
// FIXME: Need to suppress declarations when parsing this typename. return ParseTypeName(&Range, Declarator::TrailingReturnContext);
// Otherwise in this function definition:
//
// auto f() -> struct X {}
//
// struct X is parsed as class definition because of the trailing
// brace.
return ParseTypeName(&Range);
} }
/// \brief We have just started parsing the definition of a new class, /// \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: case Declarator::AliasTemplateContext:
Error = 9; // Type alias Error = 9; // Type alias
break; break;
case Declarator::TrailingReturnContext:
Error = 10; // Function return type
break;
case Declarator::TypeNameContext: case Declarator::TypeNameContext:
Error = 11; // Generic Error = 11; // Generic
break; break;
@ -1885,6 +1888,11 @@ static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state,
// Check the contexts where C++ forbids the declaration of a new class // Check the contexts where C++ forbids the declaration of a new class
// or enumeration in a type-specifier-seq. // or enumeration in a type-specifier-seq.
switch (D.getContext()) { 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::FileContext:
case Declarator::MemberContext: case Declarator::MemberContext:
case Declarator::BlockContext: case Declarator::BlockContext:
@ -2606,6 +2614,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
case Declarator::ObjCCatchContext: case Declarator::ObjCCatchContext:
case Declarator::BlockLiteralContext: case Declarator::BlockLiteralContext:
case Declarator::LambdaExprContext: case Declarator::LambdaExprContext:
case Declarator::TrailingReturnContext:
case Declarator::TemplateTypeArgContext: case Declarator::TemplateTypeArgContext:
// FIXME: We may want to allow parameter packs in block-literal contexts // FIXME: We may want to allow parameter packs in block-literal contexts
// in the future. // 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 { }; template<typename T = auto(*)()->int> struct G { };
int 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 (*i)() = &g; // ok; auto deduced as int.
auto (*k)() -> int = i; // ok; no deduction. 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}} using A = auto; // expected-error{{'auto' not allowed in type alias}}
// FIXME: don't issue the second diagnostic for this error. // 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 // 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. // final 'context sensitive' mess.
namespace final { namespace final {
struct S { int n; }; struct S { int n; };
struct T { int n; };
namespace N { namespace N {
int 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 // 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 // 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 // this disambiguation is only performed on an ambiguity, but that was not
// the intent. // the intent.
struct S final { struct S final { // expected-note {{here}}
int(n) // expected-error {{expected ';'}} 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 [[ ]], b [[ ]],
c alignas(double); 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 // RUN: %clang_cc1 -fsyntax-only -Wno-unused-value -verify -std=c++11 %s
enum E { e };
class C { class C {
int f() { int f() {
@ -19,6 +21,8 @@ class C {
[=,&foo] () {}; [=,&foo] () {};
[&,foo] () {}; [&,foo] () {};
[this] () {}; [this] () {};
[] () -> class C { return C(); };
[] () -> enum E { return e; };
[] -> int { return 0; }; // expected-error{{lambda requires '()' before return type}} [] -> int { return 0; }; // expected-error{{lambda requires '()' before return type}}
[] mutable -> int { return 0; }; // expected-error{{lambda requires '()' before 'mutable'}} [] 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 *'}} int a6[1] = {[this] = 1 }; // expected-error{{integral constant expression must have integral or unscoped enumeration type, not 'C *'}}
} }
}; };