diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1786,6 +1786,7 @@ static bool classofKind(Kind K) { return K >= firstCXXRecord && K <= lastCXXRecord; } + void markAbstract() { data().Abstract = true; } }; /// Store information needed for an explicit specifier. diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1086,6 +1086,7 @@ def MicrosoftCppMacro : DiagGroup<"microsoft-cpp-macro">; def MicrosoftFixedEnum : DiagGroup<"microsoft-fixed-enum">; def MicrosoftSealed : DiagGroup<"microsoft-sealed">; +def MicrosoftAbstract : DiagGroup<"microsoft-abstract">; def MicrosoftUnqualifiedFriend : DiagGroup<"microsoft-unqualified-friend">; def MicrosoftExceptionSpec : DiagGroup<"microsoft-exception-spec">; def MicrosoftUsingDecl : DiagGroup<"microsoft-using-decl">; @@ -1123,7 +1124,7 @@ // Warnings group for warnings about Microsoft extensions. def Microsoft : DiagGroup<"microsoft", [MicrosoftCharize, MicrosoftDrectveSection, MicrosoftInclude, - MicrosoftCppMacro, MicrosoftFixedEnum, MicrosoftSealed, + MicrosoftCppMacro, MicrosoftFixedEnum, MicrosoftSealed, MicrosoftAbstract, MicrosoftUnqualifiedFriend, MicrosoftExceptionSpec, MicrosoftUsingDecl, MicrosoftMutableReference, MicrosoftPureDefinition, MicrosoftUnionMemberReference, MicrosoftExplicitConstructorCall, diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -922,10 +922,16 @@ def ext_ms_sealed_keyword : ExtWarn< "'sealed' keyword is a Microsoft extension">, InGroup; +def ext_ms_abstract_keyword : ExtWarn< + "'abstract' keyword is a Microsoft extension">, + InGroup; def err_access_specifier_interface : Error< "interface types cannot specify '%select{private|protected}0' access">; +def err_duplicate_class_virt_specifier : Error< + "class already marked '%0'">; + def err_duplicate_virt_specifier : Error< "class member already marked '%0'">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -114,6 +114,7 @@ /// Contextual keywords for Microsoft extensions. IdentifierInfo *Ident__except; mutable IdentifierInfo *Ident_sealed; + mutable IdentifierInfo *Ident_abstract; /// Ident_super - IdentifierInfo for "super", to support fast /// comparison. @@ -2912,6 +2913,7 @@ SourceLocation FriendLoc); bool isCXX11FinalKeyword() const; + bool isClassCompatibleKeyword() const; /// DeclaratorScopeObj - RAII object used in Parser::ParseDirectDeclarator to /// enter a new C++ declarator scope and exit it when the function is diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -2620,7 +2620,8 @@ VS_Final = 2, VS_Sealed = 4, // Represents the __final keyword, which is legal for gcc in pre-C++11 mode. - VS_GNU_Final = 8 + VS_GNU_Final = 8, + VS_Abstract = 16 }; VirtSpecifiers() : Specifiers(0), LastSpecifier(VS_None) { } @@ -2636,6 +2637,7 @@ bool isFinalSpecified() const { return Specifiers & (VS_Final | VS_Sealed | VS_GNU_Final); } bool isFinalSpelledSealed() const { return Specifiers & VS_Sealed; } SourceLocation getFinalLoc() const { return VS_finalLoc; } + SourceLocation getAbstractLoc() const { return VS_abstractLoc; } void clear() { Specifiers = 0; } @@ -2649,7 +2651,7 @@ unsigned Specifiers; Specifier LastSpecifier; - SourceLocation VS_overrideLoc, VS_finalLoc; + SourceLocation VS_overrideLoc, VS_finalLoc, VS_abstractLoc; SourceLocation FirstLocation; SourceLocation LastLocation; }; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3139,6 +3139,7 @@ void ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagDecl, SourceLocation FinalLoc, bool IsFinalSpelledSealed, + bool IsAbstract, SourceLocation LBraceLoc); /// ActOnTagFinishDefinition - Invoked once we have finished parsing diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -1710,7 +1710,7 @@ TUK = Sema::TUK_Reference; else if (Tok.is(tok::l_brace) || (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || - (isCXX11FinalKeyword() && + (isClassCompatibleKeyword() && (NextToken().is(tok::l_brace) || NextToken().is(tok::colon)))) { if (DS.isFriendSpecified()) { // C++ [class.friend]p2: @@ -1726,14 +1726,18 @@ // Okay, this is a class definition. TUK = Sema::TUK_Definition; } - } else if (isCXX11FinalKeyword() && (NextToken().is(tok::l_square) || - NextToken().is(tok::kw_alignas))) { + } else if (isClassCompatibleKeyword() && + (NextToken().is(tok::l_square) || + NextToken().is(tok::kw_alignas) || + isCXX11VirtSpecifier(NextToken()) != VirtSpecifiers::VS_None)) { // We can't tell if this is a definition or reference // until we skipped the 'final' and C++11 attribute specifiers. TentativeParsingAction PA(*this); - // Skip the 'final' keyword. - ConsumeToken(); + // Skip the 'final', abstract'... keywords. + while (isClassCompatibleKeyword()) { + ConsumeToken(); + } // Skip C++11 attribute specifiers. while (true) { @@ -1982,7 +1986,7 @@ if (TUK == Sema::TUK_Definition) { assert(Tok.is(tok::l_brace) || (getLangOpts().CPlusPlus && Tok.is(tok::colon)) || - isCXX11FinalKeyword()); + isClassCompatibleKeyword()); if (SkipBody.ShouldSkip) SkipCXXMemberSpecification(StartLoc, AttrFixitLoc, TagType, TagOrTempResult.get()); @@ -2249,8 +2253,10 @@ Ident_final = &PP.getIdentifierTable().get("final"); if (getLangOpts().GNUKeywords) Ident_GNU_final = &PP.getIdentifierTable().get("__final"); - if (getLangOpts().MicrosoftExt) + if (getLangOpts().MicrosoftExt) { Ident_sealed = &PP.getIdentifierTable().get("sealed"); + Ident_abstract = &PP.getIdentifierTable().get("abstract"); + } Ident_override = &PP.getIdentifierTable().get("override"); } @@ -2260,6 +2266,9 @@ if (II == Ident_sealed) return VirtSpecifiers::VS_Sealed; + if (II == Ident_abstract) + return VirtSpecifiers::VS_Abstract; + if (II == Ident_final) return VirtSpecifiers::VS_Final; @@ -2305,6 +2314,8 @@ << VirtSpecifiers::getSpecifierName(Specifier); } else if (Specifier == VirtSpecifiers::VS_Sealed) { Diag(Tok.getLocation(), diag::ext_ms_sealed_keyword); + } else if (Specifier == VirtSpecifiers::VS_Abstract) { + Diag(Tok.getLocation(), diag::ext_ms_abstract_keyword); } else if (Specifier == VirtSpecifiers::VS_GNU_Final) { Diag(Tok.getLocation(), diag::ext_warn_gnu_final); } else { @@ -2327,6 +2338,16 @@ Specifier == VirtSpecifiers::VS_Sealed; } +/// isClassCompatibleKeyword - Determine whether the next token is a C++11 +/// 'final' or Microsoft 'sealed' or 'abstract' contextual keywords. +bool Parser::isClassCompatibleKeyword() const { + VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(); + return Specifier == VirtSpecifiers::VS_Final || + Specifier == VirtSpecifiers::VS_GNU_Final || + Specifier == VirtSpecifiers::VS_Sealed || + Specifier == VirtSpecifiers::VS_Abstract; +} + /// Parse a C++ member-declarator up to, but not including, the optional /// brace-or-equal-initializer or pure-specifier. bool Parser::ParseCXXMemberDeclaratorBeforeInitializer( @@ -2893,8 +2914,13 @@ HasStaticInitializer = true; } + if (PureSpecLoc.isValid() && VS.getAbstractLoc().isValid()) { + Diag(PureSpecLoc, diag::err_duplicate_virt_specifier) << "abstract"; + } if (ThisDecl && PureSpecLoc.isValid()) Actions.ActOnPureSpecifier(ThisDecl, PureSpecLoc); + else if (ThisDecl && VS.getAbstractLoc().isValid()) + Actions.ActOnPureSpecifier(ThisDecl, VS.getAbstractLoc()); // Handle the initializer. if (HasInClassInit != ICIS_NoInit) { @@ -3279,30 +3305,52 @@ Actions.ActOnTagStartDefinition(getCurScope(), TagDecl); SourceLocation FinalLoc; + SourceLocation AbstractLoc; bool IsFinalSpelledSealed = false; + bool IsAbstract = false; // Parse the optional 'final' keyword. if (getLangOpts().CPlusPlus && Tok.is(tok::identifier)) { - VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok); - assert((Specifier == VirtSpecifiers::VS_Final || - Specifier == VirtSpecifiers::VS_GNU_Final || - Specifier == VirtSpecifiers::VS_Sealed) && + for (VirtSpecifiers::Specifier Specifier = isCXX11VirtSpecifier(Tok); + Specifier != VirtSpecifiers::VS_None; + Specifier = isCXX11VirtSpecifier(Tok)) { + if (isCXX11FinalKeyword()) { + if (FinalLoc.isValid()) { + auto Skipped = ConsumeToken(); + Diag(Skipped, diag::err_duplicate_class_virt_specifier) + << VirtSpecifiers::getSpecifierName(Specifier); + } else { + FinalLoc = ConsumeToken(); + if (Specifier == VirtSpecifiers::VS_Sealed) + IsFinalSpelledSealed = true; + } + } else { + if (AbstractLoc.isValid()) { + auto Skipped = ConsumeToken(); + Diag(Skipped, diag::err_duplicate_class_virt_specifier) + << VirtSpecifiers::getSpecifierName(Specifier); + } else { + AbstractLoc = ConsumeToken(); + IsAbstract = true; + } + } + if (TagType == DeclSpec::TST_interface) + Diag(FinalLoc, diag::err_override_control_interface) + << VirtSpecifiers::getSpecifierName(Specifier); + else if (Specifier == VirtSpecifiers::VS_Final) + Diag(FinalLoc, getLangOpts().CPlusPlus11 + ? diag::warn_cxx98_compat_override_control_keyword + : diag::ext_override_control_keyword) + << VirtSpecifiers::getSpecifierName(Specifier); + else if (Specifier == VirtSpecifiers::VS_Sealed) + Diag(FinalLoc, diag::ext_ms_sealed_keyword); + else if (Specifier == VirtSpecifiers::VS_Abstract) + Diag(AbstractLoc, diag::ext_ms_abstract_keyword); + else if (Specifier == VirtSpecifiers::VS_GNU_Final) + Diag(FinalLoc, diag::ext_warn_gnu_final); + } + assert((FinalLoc.isValid() || AbstractLoc.isValid()) && "not a class definition"); - FinalLoc = ConsumeToken(); - IsFinalSpelledSealed = Specifier == VirtSpecifiers::VS_Sealed; - - if (TagType == DeclSpec::TST_interface) - Diag(FinalLoc, diag::err_override_control_interface) - << VirtSpecifiers::getSpecifierName(Specifier); - else if (Specifier == VirtSpecifiers::VS_Final) - Diag(FinalLoc, getLangOpts().CPlusPlus11 - ? diag::warn_cxx98_compat_override_control_keyword - : diag::ext_override_control_keyword) - << VirtSpecifiers::getSpecifierName(Specifier); - else if (Specifier == VirtSpecifiers::VS_Sealed) - Diag(FinalLoc, diag::ext_ms_sealed_keyword); - else if (Specifier == VirtSpecifiers::VS_GNU_Final) - Diag(FinalLoc, diag::ext_warn_gnu_final); // Parse any C++11 attributes after 'final' keyword. // These attributes are not allowed to appear here, @@ -3375,7 +3423,7 @@ if (TagDecl) Actions.ActOnStartCXXMemberDeclarations(getCurScope(), TagDecl, FinalLoc, - IsFinalSpelledSealed, + IsFinalSpelledSealed, IsAbstract, T.getOpenLocation()); // C++ 11p3: Members of a class defined with the keyword class are private diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -1475,6 +1475,7 @@ case VS_GNU_Final: case VS_Sealed: case VS_Final: VS_finalLoc = Loc; break; + case VS_Abstract: VS_abstractLoc = Loc; break; } return false; @@ -1487,5 +1488,6 @@ case VS_Final: return "final"; case VS_GNU_Final: return "__final"; case VS_Sealed: return "sealed"; + case VS_Abstract: return "abstract"; } } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16436,6 +16436,7 @@ void Sema::ActOnStartCXXMemberDeclarations(Scope *S, Decl *TagD, SourceLocation FinalLoc, bool IsFinalSpelledSealed, + bool IsAbstract, SourceLocation LBraceLoc) { AdjustDeclIfTemplate(TagD); CXXRecordDecl *Record = cast(TagD); @@ -16445,11 +16446,14 @@ if (!Record->getIdentifier()) return; - if (FinalLoc.isValid()) + if (IsAbstract) + Record->markAbstract(); + + if (FinalLoc.isValid()) { Record->addAttr(FinalAttr::Create( Context, FinalLoc, AttributeCommonInfo::AS_Keyword, static_cast(IsFinalSpelledSealed))); - + } // C++ [class]p2: // [...] The class-name is also inserted into the scope of the // class itself; this is known as the injected-class-name. For diff --git a/clang/test/SemaCXX/MicrosoftExtensions.cpp b/clang/test/SemaCXX/MicrosoftExtensions.cpp --- a/clang/test/SemaCXX/MicrosoftExtensions.cpp +++ b/clang/test/SemaCXX/MicrosoftExtensions.cpp @@ -462,6 +462,77 @@ virtual ~SealedDestructor() sealed; // expected-warning {{class with destructor marked 'sealed' cannot be inherited from}} }; +// expected-warning@+1 {{'abstract' keyword is a Microsoft extension}} +class AbstractClass abstract { + int i; +}; + +// expected-error@+1 {{variable type 'AbstractClass' is an abstract class}} +AbstractClass abstractInstance; + +// expected-warning@+4 {{abstract class is marked 'sealed'}} +// expected-note@+3 {{'AbstractAndSealedClass' declared here}} +// expected-warning@+2 {{'abstract' keyword is a Microsoft extension}} +// expected-warning@+1 {{'sealed' keyword is a Microsoft extension}} +class AbstractAndSealedClass abstract sealed {}; // Does no really make sense, but allowed + +// expected-error@+1 {{variable type 'AbstractAndSealedClass' is an abstract class}} +AbstractAndSealedClass abstractAndSealedInstance; +// expected-error@+1 {{base 'AbstractAndSealedClass' is marked 'sealed'}} +class InheritFromAbstractAndSealed : AbstractAndSealedClass {}; + +#if __cplusplus <= 199711L +// expected-warning@+4 {{'final' keyword is a C++11 extension}} +// expected-warning@+3 {{'final' keyword is a C++11 extension}} +#endif +// expected-error@+1 {{class already marked 'final'}} +class TooManyVirtSpecifiers1 final final {}; +#if __cplusplus <= 199711L +// expected-warning@+4 {{'final' keyword is a C++11 extension}} +#endif +// expected-warning@+2 {{'sealed' keyword is a Microsoft extension}} +// expected-error@+1 {{class already marked 'sealed'}} +class TooManyVirtSpecifiers2 final sealed {}; +#if __cplusplus <= 199711L +// expected-warning@+6 {{'final' keyword is a C++11 extension}} +// expected-warning@+5 {{'final' keyword is a C++11 extension}} +#endif +// expected-warning@+3 {{abstract class is marked 'final'}} +// expected-warning@+2 {{'abstract' keyword is a Microsoft extension}} +// expected-error@+1 {{class already marked 'final'}} +class TooManyVirtSpecifiers3 final abstract final {}; +#if __cplusplus <= 199711L +// expected-warning@+6 {{'final' keyword is a C++11 extension}} +#endif +// expected-warning@+4 {{abstract class is marked 'final'}} +// expected-warning@+3 {{'abstract' keyword is a Microsoft extension}} +// expected-warning@+2 {{'abstract' keyword is a Microsoft extension}} +// expected-error@+1 {{class already marked 'abstract'}} +class TooManyVirtSpecifiers4 abstract final abstract {}; + +class Base { + virtual void i(); +}; +class AbstractFunctionInClass : public Base { + // expected-note@+2 {{unimplemented pure virtual method 'f' in 'AbstractFunctionInClass'}} + // expected-warning@+1 {{'abstract' keyword is a Microsoft extension}} + virtual void f() abstract; + // expected-warning@+1 {{'abstract' keyword is a Microsoft extension}} + void g() abstract; // expected-error {{'g' is not virtual and cannot be declared pure}} + // expected-note@+2 {{unimplemented pure virtual method 'h' in 'AbstractFunctionInClass'}} + // expected-warning@+1 {{'abstract' keyword is a Microsoft extension}} + virtual void h() abstract = 0; // expected-error {{class member already marked 'abstract'}} +#if __cplusplus <= 199711L + // expected-warning@+4 {{'override' keyword is a C++11 extension}} +#endif + // expected-note@+2 {{unimplemented pure virtual method 'i' in 'AbstractFunctionInClass'}} + // expected-warning@+1 {{'abstract' keyword is a Microsoft extension}} + virtual void i() abstract override; +}; + +// expected-error@+1 {{variable type 'AbstractFunctionInClass' is an abstract class}} +AbstractFunctionInClass abstractFunctionInClassInstance; + void AfterClassBody() { // expected-warning@+1 {{attribute 'deprecated' is ignored, place it after "struct" to apply attribute to type declaration}} struct D {} __declspec(deprecated);