Properly parse the 'default' and 'delete' keywords.

They are actually grammatically considered definitions and parsed
accordingly.

This fixes the outstanding bugs regarding defaulting functions after
their declarations.

We now really nicely diagnose the following construct (try it!)

int foo() = delete, bar;

Still todo: Defaulted functions other than default constructors
            Test cases (including for the above construct)

llvm-svn: 131228
This commit is contained in:
Alexis Hunt 2011-05-12 06:15:49 +00:00
parent f92197cf96
commit 5a7fa250ab
11 changed files with 213 additions and 79 deletions

View File

@ -56,6 +56,8 @@ def err_default_special_members : Error<
"only special member functions may be defaulted">;
def err_friends_define_only_namespace_scope : Error<
"cannot define a function with non-namespace scope in a friend declaration">;
def err_deleted_non_function : Error<
"only functions can have deleted definitions">;
// Sema && Lex
def ext_longlong : Extension<

View File

@ -343,6 +343,9 @@ def err_operator_string_not_empty : Error<
// Classes.
def err_anon_type_definition : Error<
"declaration of anonymous %0 must be a definition">;
def err_default_delete_in_multiple_declaration : Error<
"'= %select{default|delete}0' is a function definition and must occur in a "
"standalone declaration">;
def err_cxx0x_attribute_forbids_arguments : Error<
"C++0x attribute '%0' cannot have an argument list">;

View File

@ -553,8 +553,6 @@ def err_type_defined_in_alias_template : Error<
def note_pure_virtual_function : Note<
"unimplemented pure virtual method %0 in %1">;
def err_deleted_non_function : Error<
"only functions can have deleted definitions">;
def err_deleted_decl_not_first : Error<
"deleted definition must be first declaration">;

View File

@ -1000,7 +1000,7 @@ private:
DeclGroupPtrTy ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
ParsingDeclSpec *DS = 0);
bool isDeclarationAfterDeclarator() const;
bool isDeclarationAfterDeclarator();
bool isStartOfFunctionDefinition(const ParsingDeclarator &Declarator);
DeclGroupPtrTy ParseDeclarationOrFunctionDefinition(ParsedAttributes &attrs,
AccessSpecifier AS = AS_none);

View File

@ -931,12 +931,12 @@ public:
SourceLocation NameLoc,
const Token &NextToken);
Decl *ActOnDeclarator(Scope *S, Declarator &D);
Decl *ActOnDeclarator(Scope *S, Declarator &D,
bool IsFunctionDefintion = false);
Decl *HandleDeclarator(Scope *S, Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
bool IsFunctionDefinition,
SourceLocation DefaultLoc = SourceLocation());
bool IsFunctionDefinition);
void RegisterLocallyScopedExternCDecl(NamedDecl *ND,
const LookupResult &Previous,
Scope *S);
@ -964,8 +964,7 @@ public:
LookupResult &Previous,
MultiTemplateParamsArg TemplateParamLists,
bool IsFunctionDefinition,
bool &Redeclaration,
SourceLocation DefaultLoc = SourceLocation());
bool &Redeclaration);
bool AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD);
void DiagnoseHiddenVirtualMethods(CXXRecordDecl *DC, CXXMethodDecl *MD);
void CheckFunctionDeclaration(Scope *S,
@ -997,6 +996,7 @@ public:
void ActOnInitializerError(Decl *Dcl);
void ActOnCXXForRangeDecl(Decl *D);
void SetDeclDeleted(Decl *dcl, SourceLocation DelLoc);
void SetDeclDefaulted(Decl *dcl, SourceLocation DefaultLoc);
void FinalizeDeclaration(Decl *D);
DeclGroupPtrTy FinalizeDeclaratorGroup(Scope *S, const DeclSpec &DS,
Decl **Group,
@ -3126,9 +3126,7 @@ public:
Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
Expr *BitfieldWidth, const VirtSpecifiers &VS,
Expr *Init, bool IsDefinition,
bool Deleted = false,
SourceLocation DefaultLoc = SourceLocation());
Expr *Init, bool IsDefinition);
MemInitResult ActOnMemInitializer(Decl *ConstructorD,
Scope *S,

View File

@ -25,8 +25,9 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo,
const VirtSpecifiers& VS, ExprResult& Init) {
assert(D.isFunctionDeclarator() && "This isn't a function declarator!");
assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try)) &&
"Current token not a '{', ':' or 'try'!");
assert((Tok.is(tok::l_brace) || Tok.is(tok::colon) || Tok.is(tok::kw_try) ||
Tok.is(tok::equal)) &&
"Current token not a '{', ':', '=', or 'try'!");
MultiTemplateParamsArg TemplateParams(Actions,
TemplateInfo.TemplateParams ? TemplateInfo.TemplateParams->data() : 0,
@ -48,6 +49,40 @@ Decl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, ParsingDeclarator &D,
D.complete(FnD);
if (Tok.is(tok::equal)) {
ConsumeToken();
bool Delete = false;
SourceLocation KWLoc;
if (Tok.is(tok::kw_delete)) {
if (!getLang().CPlusPlus0x)
Diag(Tok, diag::warn_deleted_function_accepted_as_extension);
KWLoc = ConsumeToken();
Actions.SetDeclDeleted(FnD, KWLoc);
Delete = true;
} else if (Tok.is(tok::kw_default)) {
if (!getLang().CPlusPlus0x)
Diag(Tok, diag::warn_defaulted_function_accepted_as_extension);
KWLoc = ConsumeToken();
Actions.SetDeclDefaulted(FnD, KWLoc);
} else {
llvm_unreachable("function definition after = not 'delete' or 'default'");
}
if (Tok.is(tok::comma)) {
Diag(KWLoc, diag::err_default_delete_in_multiple_declaration)
<< Delete;
SkipUntil(tok::semi);
} else {
ExpectAndConsume(tok::semi, diag::err_expected_semi_after,
Delete ? "delete" : "default", tok::semi);
}
return FnD;
}
// In delayed template parsing mode, if we are within a class template
// or if we are about to parse function member template then consume
// the tokens and store them for parsing at the end of the translation unit.

View File

@ -968,14 +968,17 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D,
diag::err_invalid_equalequal_after_declarator)) {
ConsumeToken();
if (Tok.is(tok::kw_delete)) {
SourceLocation DelLoc = ConsumeToken();
if (!getLang().CPlusPlus0x)
Diag(DelLoc, diag::warn_deleted_function_accepted_as_extension);
Actions.SetDeclDeleted(ThisDecl, DelLoc);
if (D.isFunctionDeclarator())
Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
<< 1 /* delete */;
else
Diag(ConsumeToken(), diag::err_deleted_non_function);
} else if (Tok.is(tok::kw_default)) {
Diag(ConsumeToken(), diag::err_default_special_members);
if (D.isFunctionDeclarator())
Diag(Tok, diag::err_default_delete_in_multiple_declaration)
<< 1 /* delete */;
else
Diag(ConsumeToken(), diag::err_default_special_members);
} else {
if (getLang().CPlusPlus && D.getCXXScopeSpec().isSet()) {
EnterScope(0);

View File

@ -1614,10 +1614,21 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
SkipUntil(tok::comma, true, true);
}
bool IsDefinition = false;
// function-definition:
if (Tok.is(tok::l_brace)
|| (DeclaratorInfo.isFunctionDeclarator() &&
(Tok.is(tok::colon) || Tok.is(tok::kw_try)))) {
if (Tok.is(tok::l_brace)) {
IsDefinition = true;
} else if (DeclaratorInfo.isFunctionDeclarator()) {
if (Tok.is(tok::colon) || Tok.is(tok::kw_try)) {
IsDefinition = true;
} else if (Tok.is(tok::equal)) {
const Token &KW = NextToken();
if (KW.is(tok::kw_default) || KW.is(tok::kw_delete))
IsDefinition = true;
}
}
if (IsDefinition) {
if (!DeclaratorInfo.isFunctionDeclarator()) {
Diag(Tok, diag::err_func_def_no_params);
ConsumeBrace();
@ -1644,9 +1655,11 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
}
ParseCXXInlineMethodDef(AS, DeclaratorInfo, TemplateInfo, VS, Init);
// Consume the optional ';'
if (Tok.is(tok::semi))
// Consume the ';' - it's optional unless we have a delete or default
if (Tok.is(tok::semi)) {
ConsumeToken();
}
return;
}
@ -1658,8 +1671,6 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
llvm::SmallVector<Decl *, 8> DeclsInGroup;
ExprResult BitfieldSize;
bool Deleted = false;
SourceLocation DefaultLoc;
while (1) {
// member-declarator:
@ -1687,14 +1698,17 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
if (Tok.is(tok::equal)) {
ConsumeToken();
if (Tok.is(tok::kw_delete)) {
if (!getLang().CPlusPlus0x)
Diag(Tok, diag::warn_deleted_function_accepted_as_extension);
ConsumeToken();
Deleted = true;
if (DeclaratorInfo.isFunctionDeclarator())
Diag(ConsumeToken(), diag::err_default_delete_in_multiple_declaration)
<< 1 /* delete */;
else
Diag(ConsumeToken(), diag::err_deleted_non_function);
} else if (Tok.is(tok::kw_default)) {
if (!getLang().CPlusPlus0x)
Diag(Tok, diag::warn_defaulted_function_accepted_as_extension);
DefaultLoc = ConsumeToken();
if (DeclaratorInfo.isFunctionDeclarator())
Diag(Tok, diag::err_default_delete_in_multiple_declaration)
<< 1 /* delete */;
else
Diag(ConsumeToken(), diag::err_default_special_members);
} else {
Init = ParseInitializer();
if (Init.isInvalid())
@ -1722,9 +1736,6 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
Decl *ThisDecl = 0;
if (DS.isFriendSpecified()) {
if (DefaultLoc.isValid())
Diag(DefaultLoc, diag::err_default_special_members);
// TODO: handle initializers, bitfields, 'delete'
ThisDecl = Actions.ActOnFriendFunctionDecl(getCurScope(), DeclaratorInfo,
/*IsDefinition*/ false,
@ -1734,9 +1745,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DeclaratorInfo,
move(TemplateParams),
BitfieldSize.release(),
VS, Init.release(),
/*IsDefinition*/Deleted,
Deleted, DefaultLoc);
VS, Init.release(), false);
}
if (ThisDecl)
DeclsInGroup.push_back(ThisDecl);
@ -1762,8 +1771,6 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
VS.clear();
BitfieldSize = 0;
Init = 0;
Deleted = false;
DefaultLoc = SourceLocation();
// Attributes are only allowed on the second declarator.
MaybeParseGNUAttributes(DeclaratorInfo);

View File

@ -677,7 +677,14 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
/// \brief Determine whether the current token, if it occurs after a
/// declarator, continues a declaration or declaration list.
bool Parser::isDeclarationAfterDeclarator() const {
bool Parser::isDeclarationAfterDeclarator() {
// Check for '= delete' or '= default'
if (getLang().CPlusPlus && Tok.is(tok::equal)) {
const Token &KW = NextToken();
if (KW.is(tok::kw_default) || KW.is(tok::kw_delete))
return false;
}
return Tok.is(tok::equal) || // int X()= -> not a function def
Tok.is(tok::comma) || // int X(), -> not a function def
Tok.is(tok::semi) || // int X(); -> not a function def
@ -698,6 +705,11 @@ bool Parser::isStartOfFunctionDefinition(const ParsingDeclarator &Declarator) {
if (!getLang().CPlusPlus &&
Declarator.getFunctionTypeInfo().isKNRPrototype())
return isDeclarationSpecifier();
if (getLang().CPlusPlus && Tok.is(tok::equal)) {
const Token &KW = NextToken();
return KW.is(tok::kw_default) || KW.is(tok::kw_delete);
}
return Tok.is(tok::colon) || // X() : Base() {} (used for ctors)
Tok.is(tok::kw_try); // X() try { ... }
@ -820,6 +832,73 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D,
if (FTI.isKNRPrototype())
ParseKNRParamDeclarations(D);
if (Tok.is(tok::equal)) {
assert(getLang().CPlusPlus && "Only C++ function definitions have '='");
ConsumeToken();
Decl *Decl = 0;
// Here we complete the declaration as if it were normal
switch (TemplateInfo.Kind) {
case ParsedTemplateInfo::NonTemplate:
Decl = Actions.ActOnDeclarator(getCurScope(), D, true);
break;
case ParsedTemplateInfo::Template:
case ParsedTemplateInfo::ExplicitSpecialization:
Decl = Actions.ActOnTemplateDeclarator(getCurScope(),
MultiTemplateParamsArg(Actions,
TemplateInfo.TemplateParams->data(),
TemplateInfo.TemplateParams->size()),
D);
break;
case ParsedTemplateInfo::ExplicitInstantiation: {
DeclResult Result
= Actions.ActOnExplicitInstantiation(getCurScope(),
TemplateInfo.ExternLoc,
TemplateInfo.TemplateLoc,
D);
if (Result.isInvalid()) {
SkipUntil(tok::semi);
return 0;
}
Decl = Result.get();
break;
}
}
bool Delete = false;
SourceLocation KWLoc;
if (Tok.is(tok::kw_delete)) {
if (!getLang().CPlusPlus0x)
Diag(Tok, diag::warn_deleted_function_accepted_as_extension);
KWLoc = ConsumeToken();
Actions.SetDeclDeleted(Decl, KWLoc);
Delete = true;
} else if (Tok.is(tok::kw_default)) {
if (!getLang().CPlusPlus0x)
Diag(Tok, diag::warn_defaulted_function_accepted_as_extension);
KWLoc = ConsumeToken();
Actions.SetDeclDefaulted(Decl, KWLoc);
} else {
llvm_unreachable("function definition after = not 'delete' or 'default'");
}
if (Tok.is(tok::comma)) {
Diag(KWLoc, diag::err_default_delete_in_multiple_declaration)
<< Delete;
SkipUntil(tok::semi);
} else {
ExpectAndConsume(tok::semi, diag::err_expected_semi_after,
Delete ? "delete" : "default", tok::semi);
}
return Decl;
}
// We should have either an opening brace or, in a C++ constructor,
// we may have a colon.
if (Tok.isNot(tok::l_brace) &&

View File

@ -2913,8 +2913,10 @@ static bool RebuildDeclaratorInCurrentInstantiation(Sema &S, Declarator &D,
return false;
}
Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) {
return HandleDeclarator(S, D, MultiTemplateParamsArg(*this), false);
Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D,
bool IsFunctionDefinition) {
return HandleDeclarator(S, D, MultiTemplateParamsArg(*this),
IsFunctionDefinition);
}
/// DiagnoseClassNameShadow - Implement C++ [class.mem]p13:
@ -2939,8 +2941,7 @@ bool Sema::DiagnoseClassNameShadow(DeclContext *DC,
Decl *Sema::HandleDeclarator(Scope *S, Declarator &D,
MultiTemplateParamsArg TemplateParamLists,
bool IsFunctionDefinition,
SourceLocation DefaultLoc) {
bool IsFunctionDefinition) {
// TODO: consider using NameInfo for diagnostic.
DeclarationNameInfo NameInfo = GetNameForDeclarator(D);
DeclarationName Name = NameInfo.getName();
@ -3141,9 +3142,6 @@ Decl *Sema::HandleDeclarator(Scope *S, Declarator &D,
bool Redeclaration = false;
if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef) {
if (DefaultLoc.isValid())
Diag(DefaultLoc, diag::err_default_special_members);
if (TemplateParamLists.size()) {
Diag(D.getIdentifierLoc(), diag::err_template_typedef);
return 0;
@ -3153,10 +3151,8 @@ Decl *Sema::HandleDeclarator(Scope *S, Declarator &D,
} else if (R->isFunctionType()) {
New = ActOnFunctionDeclarator(S, D, DC, R, TInfo, Previous,
move(TemplateParamLists),
IsFunctionDefinition, Redeclaration,
DefaultLoc);
IsFunctionDefinition, Redeclaration);
} else {
assert(!DefaultLoc.isValid() && "We should have caught this in a caller");
New = ActOnVariableDeclarator(S, D, DC, R, TInfo, Previous,
move(TemplateParamLists),
Redeclaration);
@ -4028,8 +4024,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
QualType R, TypeSourceInfo *TInfo,
LookupResult &Previous,
MultiTemplateParamsArg TemplateParamLists,
bool IsFunctionDefinition, bool &Redeclaration,
SourceLocation DefaultLoc) {
bool IsFunctionDefinition, bool &Redeclaration) {
assert(R.getTypePtr()->isFunctionType());
// TODO: consider using NameInfo for diagnostic.
@ -4086,8 +4081,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
bool isFunctionTemplateSpecialization = false;
if (!getLangOptions().CPlusPlus) {
assert(!DefaultLoc.isValid() && "Defaulted functions are a C++ feature");
// Determine whether the function was written with a
// prototype. This true when:
// - there is a prototype in the declarator, or
@ -4689,7 +4682,7 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
} else if (!IsFunctionDefinition && D.getCXXScopeSpec().isSet() &&
!isFriend && !isFunctionTemplateSpecialization &&
!isExplicitSpecialization && !DefaultLoc.isValid()) {
!isExplicitSpecialization) {
// An out-of-line member function declaration must also be a
// definition (C++ [dcl.meaning]p1).
// Note that this is not the case for explicit specializations of
@ -4700,8 +4693,8 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
<< D.getCXXScopeSpec().getRange();
}
}
// Handle attributes. We need to have merged decls when handling attributes
// (for example to check for conflicts, etc).
// FIXME: This needs to happen before we merge declarations. Then,
@ -4774,16 +4767,6 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC,
}
}
if (DefaultLoc.isValid()) {
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(NewFD);
if (MD && getSpecialMember(MD) != CXXInvalid) {
MD->setExplicitlyDefaulted();
MD->setDefaulted();
} else {
Diag(DefaultLoc, diag::err_default_special_members);
}
}
return NewFD;
}

View File

@ -1025,8 +1025,7 @@ Decl *
Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
MultiTemplateParamsArg TemplateParameterLists,
ExprTy *BW, const VirtSpecifiers &VS,
ExprTy *InitExpr, bool IsDefinition,
bool Deleted, SourceLocation DefaultLoc) {
ExprTy *InitExpr, bool IsDefinition) {
const DeclSpec &DS = D.getDeclSpec();
DeclarationNameInfo NameInfo = GetNameForDeclarator(D);
DeclarationName Name = NameInfo.getName();
@ -1091,9 +1090,6 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
if (isInstField) {
CXXScopeSpec &SS = D.getCXXScopeSpec();
if (DefaultLoc.isValid())
Diag(DefaultLoc, diag::err_default_special_members);
if (SS.isSet() && !SS.isInvalid()) {
// The user provided a superfluous scope specifier inside a class
// definition:
@ -1118,8 +1114,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
AS);
assert(Member && "HandleField never returns null");
} else {
Member = HandleDeclarator(S, D, move(TemplateParameterLists), IsDefinition,
DefaultLoc);
Member = HandleDeclarator(S, D, move(TemplateParameterLists), IsDefinition);
if (!Member) {
return 0;
}
@ -1189,8 +1184,6 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
if (Init)
AddInitializerToDecl(Member, Init, false,
DS.getTypeSpecType() == DeclSpec::TST_auto);
if (Deleted) // FIXME: Source location is not very good.
SetDeclDeleted(Member, D.getSourceRange().getBegin());
FinalizeDeclaration(Member);
@ -7865,6 +7858,39 @@ void Sema::SetDeclDeleted(Decl *Dcl, SourceLocation DelLoc) {
Fn->setDeletedAsWritten();
}
void Sema::SetDeclDefaulted(Decl *Dcl, SourceLocation DefaultLoc) {
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Dcl);
if (MD) {
CXXSpecialMember Member = getSpecialMember(MD);
if (Member == CXXInvalid) {
Diag(DefaultLoc, diag::err_default_special_members);
return;
}
MD->setDefaulted();
MD->setExplicitlyDefaulted();
// We'll check it when the record is done
if (MD == MD->getCanonicalDecl())
return;
switch (Member) {
case CXXDefaultConstructor: {
CXXConstructorDecl *CD = cast<CXXConstructorDecl>(MD);
CheckExplicitlyDefaultedDefaultConstructor(CD);
DefineImplicitDefaultConstructor(DefaultLoc, CD);
break;
}
default:
// FIXME: Do the rest once we have functions
break;
}
} else {
Diag(DefaultLoc, diag::err_default_special_members);
}
}
static void SearchForReturnInStmt(Sema &Self, Stmt *S) {
for (Stmt::child_range CI = S->children(); CI; ++CI) {
Stmt *SubStmt = *CI;