From: Richard Smith Date: Tue, 30 Jun 2015 01:28:56 +0000 (+0000) Subject: Rework parsing of pure-specifiers. Perform the grammar matching and X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e4090833ea6b55bf6f0954656c0510e04a9212aa;p=clang Rework parsing of pure-specifiers. Perform the grammar matching and disambiguation in the parser rather than trying to do it in Sema. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@241032 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 98d300ece7..8c96f65548 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; +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 |" diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index adae627223..97a0aa482d 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -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, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 8794664afb..cad4bcb2ee 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index ea67a52f1f..ab1f97d31a 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -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); } } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 6531f7b902..137e6abc6b 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -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 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 diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index bce0a37446..1dec6fc109 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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(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(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; } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 7ed9bfcb97..672fc894f1 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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(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(D)) diff --git a/test/SemaCXX/virtuals.cpp b/test/SemaCXX/virtuals.cpp index 6b8231d4e1..f8180745bd 100644 --- a/test/SemaCXX/virtuals.cpp +++ b/test/SemaCXX/virtuals.cpp @@ -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}} + }; +}