Rework parsing of pure-specifiers. Perform the grammar matching and

disambiguation in the parser rather than trying to do it in Sema.

llvm-svn: 241032
This commit is contained in:
Richard Smith 2015-06-30 01:28:56 +00:00
parent a5cb389337
commit 9ba0fec83e
8 changed files with 90 additions and 72 deletions

View File

@ -1051,6 +1051,7 @@ def ext_friend_tag_redecl_outside_namespace : ExtWarn<
"unqualified friend declaration referring to type outside of the nearest "
"enclosing namespace is a Microsoft extension; add a nested name specifier">,
InGroup<Microsoft>;
def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">;
def err_invalid_member_in_interface : Error<
"%select{data member |non-public member function |static member function |"

View File

@ -1182,7 +1182,7 @@ private:
ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo,
const VirtSpecifiers& VS,
ExprResult& Init);
SourceLocation PureSpecLoc);
void ParseCXXNonStaticMemberInitializer(Decl *VarD);
void ParseLexedAttributes(ParsingClass &Class);
void ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,

View File

@ -1690,6 +1690,7 @@ public:
bool TypeMayContainAuto);
void ActOnUninitializedDecl(Decl *dcl, bool TypeMayContainAuto);
void ActOnInitializerError(Decl *Dcl);
void ActOnPureSpecifier(Decl *D, SourceLocation PureSpecLoc);
void ActOnCXXForRangeDecl(Decl *D);
StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
IdentifierInfo *Ident,

View File

@ -27,7 +27,7 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS,
ParsingDeclarator &D,
const ParsedTemplateInfo &TemplateInfo,
const VirtSpecifiers& VS,
ExprResult& Init) {
SourceLocation PureSpecLoc) {
assert(D.isFunctionDeclarator() && "This isn't a function declarator!");
assert(Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try, tok::equal) &&
"Current token not a '{', ':', '=', or 'try'!");
@ -47,12 +47,8 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS,
VS, ICIS_NoInit);
if (FnD) {
Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs);
bool TypeSpecContainsAuto = D.getDeclSpec().containsPlaceholderType();
if (Init.isUsable())
Actions.AddInitializerToDecl(FnD, Init.get(), false,
TypeSpecContainsAuto);
else
Actions.ActOnUninitializedDecl(FnD, TypeSpecContainsAuto);
if (PureSpecLoc.isValid())
Actions.ActOnPureSpecifier(FnD, PureSpecLoc);
}
}

View File

@ -2372,8 +2372,28 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
LateParsedAttrList LateParsedAttrs;
SourceLocation EqualLoc;
bool HasInitializer = false;
ExprResult Init;
SourceLocation PureSpecLoc;
auto TryConsumePureSpecifier = [&] (bool AllowDefinition = false) {
if (Tok.isNot(tok::equal))
return false;
auto &Zero = NextToken();
SmallString<8> Buffer;
if (Zero.isNot(tok::numeric_constant) || Zero.getLength() != 1 ||
PP.getSpelling(Zero, Buffer) != "0")
return false;
auto &After = GetLookAheadToken(2);
if (!After.isOneOf(tok::semi, tok::comma) &&
!(AllowDefinition &&
After.isOneOf(tok::l_brace, tok::colon, tok::kw_try)))
return false;
EqualLoc = ConsumeToken();
PureSpecLoc = ConsumeToken();
return true;
};
SmallVector<Decl *, 8> DeclsInGroup;
ExprResult BitfieldSize;
@ -2390,16 +2410,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
if (BitfieldSize.isUnset()) {
// MSVC permits pure specifier on inline functions defined at class scope.
// Hence check for =0 before checking for function definition.
if (getLangOpts().MicrosoftExt && Tok.is(tok::equal) &&
DeclaratorInfo.isFunctionDeclarator() &&
NextToken().is(tok::numeric_constant)) {
EqualLoc = ConsumeToken();
Init = ParseInitializer();
if (Init.isInvalid())
SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
else
HasInitializer = true;
}
if (getLangOpts().MicrosoftExt && DeclaratorInfo.isDeclarationOfFunction())
TryConsumePureSpecifier(/*AllowDefinition*/ true);
FunctionDefinitionKind DefinitionKind = FDK_Declaration;
// function-definition:
@ -2453,7 +2465,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
Decl *FunDecl =
ParseCXXInlineMethodDef(AS, AccessAttrs, DeclaratorInfo, TemplateInfo,
VS, Init);
VS, PureSpecLoc);
if (FunDecl) {
for (unsigned i = 0, ni = CommonLateParsedAttrs.size(); i < ni; ++i) {
@ -2479,16 +2491,25 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
while (1) {
InClassInitStyle HasInClassInit = ICIS_NoInit;
if (Tok.isOneOf(tok::equal, tok::l_brace) && !HasInitializer) {
bool HasStaticInitializer = false;
if (Tok.isOneOf(tok::equal, tok::l_brace) && PureSpecLoc.isInvalid()) {
if (BitfieldSize.get()) {
Diag(Tok, diag::err_bitfield_member_init);
SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
} else if (DeclaratorInfo.isDeclarationOfFunction()) {
// It's a pure-specifier.
if (!TryConsumePureSpecifier(/*AllowFunctionDefinition*/ false))
// Parse it as an expression so that Sema can diagnose it.
HasStaticInitializer = true;
} else if (DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
DeclSpec::SCS_static &&
DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
DeclSpec::SCS_typedef &&
!DS.isFriendSpecified()) {
// It's a default member initializer.
HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit;
} else {
HasInitializer = true;
if (!DeclaratorInfo.isDeclarationOfFunction() &&
DeclaratorInfo.getDeclSpec().getStorageClassSpec()
!= DeclSpec::SCS_typedef)
HasInClassInit = Tok.is(tok::equal) ? ICIS_CopyInit : ICIS_ListInit;
HasStaticInitializer = true;
}
}
@ -2528,10 +2549,20 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs);
}
// Handle the initializer.
// Error recovery might have converted a non-static member into a static
// member.
if (HasInClassInit != ICIS_NoInit &&
DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
DeclSpec::SCS_static) {
DeclaratorInfo.getDeclSpec().getStorageClassSpec() ==
DeclSpec::SCS_static) {
HasInClassInit = ICIS_NoInit;
HasStaticInitializer = true;
}
if (ThisDecl && PureSpecLoc.isValid())
Actions.ActOnPureSpecifier(ThisDecl, PureSpecLoc);
// Handle the initializer.
if (HasInClassInit != ICIS_NoInit) {
// The initializer was deferred; parse it and cache the tokens.
Diag(Tok, getLangOpts().CPlusPlus11
? diag::warn_cxx98_compat_nonstatic_member_init
@ -2551,11 +2582,10 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
ThisDecl->setInvalidDecl();
} else
ParseCXXNonStaticMemberInitializer(ThisDecl);
} else if (HasInitializer) {
} else if (HasStaticInitializer) {
// Normal initializer.
if (!Init.isUsable())
Init = ParseCXXMemberInitializer(
ThisDecl, DeclaratorInfo.isDeclarationOfFunction(), EqualLoc);
ExprResult Init = ParseCXXMemberInitializer(
ThisDecl, DeclaratorInfo.isDeclarationOfFunction(), EqualLoc);
if (Init.isInvalid())
SkipUntil(tok::comma, StopAtSemi | StopBeforeMatch);
@ -2608,8 +2638,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
DeclaratorInfo.clear();
VS.clear();
BitfieldSize = ExprResult(/*Invalid=*/false);
Init = ExprResult(/*Invalid=*/false);
HasInitializer = false;
EqualLoc = PureSpecLoc = SourceLocation();
DeclaratorInfo.setCommaLoc(CommaLoc);
// GNU attributes are allowed before the second and subsequent declarator.
@ -2632,13 +2661,11 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS,
Actions.FinalizeDeclaratorGroup(getCurScope(), DS, DeclsInGroup);
}
/// ParseCXXMemberInitializer - Parse the brace-or-equal-initializer or
/// pure-specifier. Also detect and reject any attempted defaulted/deleted
/// function definition. The location of the '=', if any, will be placed in
/// EqualLoc.
/// ParseCXXMemberInitializer - Parse the brace-or-equal-initializer.
/// Also detect and reject any attempted defaulted/deleted function definition.
/// The location of the '=', if any, will be placed in EqualLoc.
///
/// pure-specifier:
/// '= 0'
/// This does not check for a pure-specifier; that's handled elsewhere.
///
/// brace-or-equal-initializer:
/// '=' initializer-expression

View File

@ -8783,19 +8783,6 @@ namespace {
}
}
/// Determine whether the given expression was formed from the token '0'. This
/// test is necessary to determine whether an initializer is really a
/// pure-specifier.
static bool isZeroToken(Sema &S, Expr *E) {
auto *IL = dyn_cast<IntegerLiteral>(E);
if (!IL || !!IL->getValue() ||
!IL->getType()->isSpecificBuiltinType(BuiltinType::Int))
return false;
SmallString<8> Buffer;
return S.PP.getSpelling(E->getLocStart(), Buffer) == "0";
}
/// AddInitializerToDecl - Adds the initializer Init to the
/// declaration dcl. If DirectInit is true, this is C++ direct
/// initialization rather than copy initialization.
@ -8809,20 +8796,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init,
}
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(RealDecl)) {
// With declarators parsed the way they are, the parser cannot
// distinguish between a normal initializer and a pure-specifier.
// Thus this grotesque test.
//
// FIXME: The parser should instead treat anything that looks like a
// pure-specifier as a pure-specifier, and Sema should convert it to an
// initializer when necessary, rather than doing things this way around.
if (!DirectInit && isZeroToken(*this, Init))
CheckPureMethod(Method, Init->getSourceRange());
else {
Diag(Method->getLocation(), diag::err_member_function_initialization)
<< Method->getDeclName() << Init->getSourceRange();
Method->setInvalidDecl();
}
// Pure-specifiers are handled in ActOnPureSpecifier.
Diag(Method->getLocation(), diag::err_member_function_initialization)
<< Method->getDeclName() << Init->getSourceRange();
Method->setInvalidDecl();
return;
}

View File

@ -2186,9 +2186,6 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
assert(Member && "HandleField never returns null");
}
} else {
assert(InitStyle == ICIS_NoInit ||
D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static);
Member = HandleDeclarator(S, D, TemplateParameterLists);
if (!Member)
return nullptr;
@ -13060,6 +13057,15 @@ bool Sema::CheckPureMethod(CXXMethodDecl *Method, SourceRange InitRange) {
return true;
}
void Sema::ActOnPureSpecifier(Decl *D, SourceLocation ZeroLoc) {
if (D->getFriendObjectKind())
Diag(D->getLocation(), diag::err_pure_friend);
else if (auto *M = dyn_cast<CXXMethodDecl>(D))
CheckPureMethod(M, ZeroLoc);
else
Diag(D->getLocation(), diag::err_illegal_initializer);
}
/// \brief Determine whether the given declaration is a static data member.
static bool isStaticDataMember(const Decl *D) {
if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(D))

View File

@ -51,3 +51,13 @@ namespace pr8264 {
virtual virtual void func(); // expected-warning {{duplicate 'virtual' declaration specifier}}
};
}
namespace VirtualFriend {
// DR (filed but no number yet): reject meaningless pure-specifier on a friend declaration.
struct A { virtual int f(); };
struct B { friend int A::f() = 0; }; // expected-error {{friend declaration cannot have a pure-specifier}}
struct C {
virtual int f();
friend int C::f() = 0; // expected-error {{friend declaration cannot have a pure-specifier}}
};
}