From e402e72273cde2a64fa6097c1fe93f500038675d Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 25 Sep 2012 07:32:39 +0000 Subject: [PATCH] Fix for r163013 regression and further __interface enhancement. Patch by Andy Gibbs! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@164590 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclCXX.h | 4 +- include/clang/Basic/DiagnosticParseKinds.td | 5 ++ include/clang/Basic/DiagnosticSemaKinds.td | 8 +++ include/clang/Parse/Parser.h | 16 +++-- lib/Parse/ParseDeclCXX.cpp | 56 ++++++++++++---- lib/Sema/SemaDecl.cpp | 11 ++-- lib/Sema/SemaDeclCXX.cpp | 63 ++++++++++++++++-- test/CodeGenCXX/microsoft-interface.cpp | 43 ++++++++++++ test/SemaCXX/ms-interface.cpp | 73 +++++++++++++++++++++ 9 files changed, 249 insertions(+), 30 deletions(-) create mode 100644 test/CodeGenCXX/microsoft-interface.cpp create mode 100644 test/SemaCXX/ms-interface.cpp diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 76be4086c9..26f91dd42d 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -1565,9 +1565,9 @@ public: CXXMethodDecl *CD = cast(const_cast(this)->getCanonicalDecl()); - // Methods declared in interfaces are automatically (pure) virtual + // Methods declared in interfaces are automatically (pure) virtual. if (CD->isVirtualAsWritten() || - CD->getParent()->getTagKind() == TTK_Interface) + (CD->getParent()->isInterface() && CD->isUserProvided())) return true; return (CD->begin_overridden_methods() != CD->end_overridden_methods()); diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 6d308ff70d..7f46bf0ce0 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -644,6 +644,11 @@ def ext_override_control_keyword : ExtWarn< def warn_cxx98_compat_override_control_keyword : Warning< "'%0' keyword is incompatible with C++98">, InGroup, DefaultIgnore; +def err_override_control_interface : Error< + "'%0' keyword not permitted with interface types">; + +def err_access_specifier_interface : Error< + "interface types cannot specify '%select{private|protected}0' access">; def err_duplicate_virt_specifier : Error< "class member already marked '%0'">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c314cf78d9..6c92a6d23f 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -812,6 +812,14 @@ def err_friend_def_in_local_class : Error< def err_friend_not_first_in_declaration : Error< "'friend' must appear first in a non-function declaration">; +def err_invalid_member_in_interface : Error< + "%select{data member |non-public member function |static member function |" + "user-declared constructor|user-declared destructor|operator |" + "nested class }0%1 is not permitted within an interface type">; +def err_invalid_base_in_interface : Error< + "interface type cannot inherit from " + "%select{'struct|non-public 'interface|'class}0 %1'">; + def err_abstract_type_in_decl : Error< "%select{return|parameter|variable|field|instance variable}0 type %1 is an abstract class">; def err_allocation_of_abstract_type : Error< diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index b5c1a41650..d143a1a0c5 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -925,9 +925,9 @@ private: /// any member function declarations or definitions that need to be /// parsed after the corresponding top-level class is complete. struct ParsingClass { - ParsingClass(Decl *TagOrTemplate, bool TopLevelClass) + ParsingClass(Decl *TagOrTemplate, bool TopLevelClass, bool IsInterface) : TopLevelClass(TopLevelClass), TemplateScope(false), - TagOrTemplate(TagOrTemplate) { } + IsInterface(IsInterface), TagOrTemplate(TagOrTemplate) { } /// \brief Whether this is a "top-level" class, meaning that it is /// not nested within another class. @@ -938,6 +938,9 @@ private: /// othewise, it is a tag declaration. bool TemplateScope : 1; + /// \brief Whether this class is an __interface. + bool IsInterface : 1; + /// \brief The class or class template whose definition we are parsing. Decl *TagOrTemplate; @@ -964,9 +967,10 @@ private: Sema::ParsingClassState State; public: - ParsingClassDefinition(Parser &P, Decl *TagOrTemplate, bool TopLevelClass) + ParsingClassDefinition(Parser &P, Decl *TagOrTemplate, bool TopLevelClass, + bool IsInterface) : P(P), Popped(false), - State(P.PushParsingClass(TagOrTemplate, TopLevelClass)) { + State(P.PushParsingClass(TagOrTemplate, TopLevelClass, IsInterface)) { } /// \brief Pop this class of the stack. @@ -1054,7 +1058,7 @@ private: void LateTemplateParser(const FunctionDecl *FD); Sema::ParsingClassState - PushParsingClass(Decl *TagOrTemplate, bool TopLevelClass); + PushParsingClass(Decl *TagOrTemplate, bool TopLevelClass, bool IsInterface); void DeallocateParsedClasses(ParsingClass *Class); void PopParsingClass(Sema::ParsingClassState); @@ -1931,7 +1935,7 @@ private: VirtSpecifiers::Specifier isCXX0XVirtSpecifier() const { return isCXX0XVirtSpecifier(Tok); } - void ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS); + void ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS, bool IsInterface); bool isCXX0XFinalKeyword() const; diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 64a3ef0deb..6fee37dc02 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -18,6 +18,7 @@ #include "clang/Sema/Scope.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/PrettyDeclStackTrace.h" +#include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/SmallString.h" #include "RAIIObjectsForParser.h" using namespace clang; @@ -1668,7 +1669,8 @@ VirtSpecifiers::Specifier Parser::isCXX0XVirtSpecifier(const Token &Tok) const { /// virt-specifier-seq: /// virt-specifier /// virt-specifier-seq virt-specifier -void Parser::ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS) { +void Parser::ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS, + bool IsInterface) { while (true) { VirtSpecifiers::Specifier Specifier = isCXX0XVirtSpecifier(); if (Specifier == VirtSpecifiers::VS_None) @@ -1682,10 +1684,15 @@ void Parser::ParseOptionalCXX0XVirtSpecifierSeq(VirtSpecifiers &VS) { << PrevSpec << FixItHint::CreateRemoval(Tok.getLocation()); - Diag(Tok.getLocation(), getLangOpts().CPlusPlus0x ? - diag::warn_cxx98_compat_override_control_keyword : - diag::ext_override_control_keyword) - << VirtSpecifiers::getSpecifierName(Specifier); + if (IsInterface && Specifier == VirtSpecifiers::VS_Final) { + Diag(Tok.getLocation(), diag::err_override_control_interface) + << VirtSpecifiers::getSpecifierName(Specifier); + } else { + Diag(Tok.getLocation(), getLangOpts().CPlusPlus0x ? + diag::warn_cxx98_compat_override_control_keyword : + diag::ext_override_control_keyword) + << VirtSpecifiers::getSpecifierName(Specifier); + } ConsumeToken(); } } @@ -1906,7 +1913,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, return; } - ParseOptionalCXX0XVirtSpecifierSeq(VS); + ParseOptionalCXX0XVirtSpecifierSeq(VS, getCurrentClass().IsInterface); // If attributes exist after the declarator, but before an '{', parse them. MaybeParseGNUAttributes(DeclaratorInfo, &LateParsedAttrs); @@ -2027,7 +2034,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, // FIXME: When g++ adds support for this, we'll need to check whether it // goes before or after the GNU attributes and __asm__. - ParseOptionalCXX0XVirtSpecifierSeq(VS); + ParseOptionalCXX0XVirtSpecifierSeq(VS, getCurrentClass().IsInterface); InClassInitStyle HasInClassInit = ICIS_NoInit; if ((Tok.is(tok::equal) || Tok.is(tok::l_brace)) && !HasInitializer) { @@ -2256,6 +2263,15 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, if (S->isClassScope()) { // We're inside a class scope, so this is a nested class. NonNestedClass = false; + + // The Microsoft extension __interface does not permit nested classes. + if (getCurrentClass().IsInterface) { + Diag(RecordLoc, diag::err_invalid_member_in_interface) + << /*ErrorType=*/6 + << (isa(TagDecl) + ? cast(TagDecl)->getQualifiedNameAsString() + : ""); + } break; } @@ -2276,7 +2292,8 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, ParseScope ClassScope(this, Scope::ClassScope|Scope::DeclScope); // Note that we are parsing a new (potentially-nested) class definition. - ParsingClassDefinition ParsingDef(*this, TagDecl, NonNestedClass); + ParsingClassDefinition ParsingDef(*this, TagDecl, NonNestedClass, + TagType == DeclSpec::TST_interface); if (TagDecl) Actions.ActOnTagStartDefinition(getCurScope(), TagDecl); @@ -2288,9 +2305,14 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, assert(isCXX0XFinalKeyword() && "not a class definition"); FinalLoc = ConsumeToken(); - Diag(FinalLoc, getLangOpts().CPlusPlus0x ? - diag::warn_cxx98_compat_override_control_keyword : - diag::ext_override_control_keyword) << "final"; + if (TagType == DeclSpec::TST_interface) { + Diag(FinalLoc, diag::err_override_control_interface) + << "final"; + } else { + Diag(FinalLoc, getLangOpts().CPlusPlus0x ? + diag::warn_cxx98_compat_override_control_keyword : + diag::ext_override_control_keyword) << "final"; + } } if (Tok.is(tok::colon)) { @@ -2375,6 +2397,13 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc, << FixItHint::CreateInsertion(EndLoc, ":"); } + // The Microsoft extension __interface does not permit non-public + // access specifiers. + if (TagType == DeclSpec::TST_interface && CurAS != AS_public) { + Diag(ASLoc, diag::err_access_specifier_interface) + << (CurAS == AS_protected); + } + if (Actions.ActOnAccessSpecifier(AS, ASLoc, EndLoc, AccessAttrs.getList())) { // found another attribute than only annotations @@ -2754,10 +2783,11 @@ TypeResult Parser::ParseTrailingReturnType(SourceRange &Range) { /// so push that class onto our stack of classes that is currently /// being parsed. Sema::ParsingClassState -Parser::PushParsingClass(Decl *ClassDecl, bool NonNestedClass) { +Parser::PushParsingClass(Decl *ClassDecl, bool NonNestedClass, + bool IsInterface) { assert((NonNestedClass || !ClassStack.empty()) && "Nested class without outer class"); - ClassStack.push(new ParsingClass(ClassDecl, NonNestedClass)); + ClassStack.push(new ParsingClass(ClassDecl, NonNestedClass, IsInterface)); return Actions.PushParsingClass(); } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index fc2d1d6ae6..01aaf8be32 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5210,11 +5210,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, NewFD->setImplicitlyInline(); } - // if this is a method defined in an __interface, set pure - // (isVirtual will already return true) - if (CXXRecordDecl *Parent = dyn_cast( - NewFD->getDeclContext())) { - if (Parent->getTagKind() == TTK_Interface) + // If this is a method defined in an __interface, and is not a constructor + // or an overloaded operator, then set the pure flag (isVirtual will already + // return true). + if (const CXXRecordDecl *Parent = + dyn_cast(NewFD->getDeclContext())) { + if (Parent->isInterface() && cast(NewFD)->isUserProvided()) NewFD->setPure(true); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index a4ddba3740..dccce6ef90 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1178,10 +1178,21 @@ bool Sema::AttachBaseSpecifiers(CXXRecordDecl *Class, CXXBaseSpecifier **Bases, // Okay, add this new base class. KnownBase = Bases[idx]; Bases[NumGoodBases++] = Bases[idx]; - if (const RecordType *Record = NewBaseType->getAs()) - if (const CXXRecordDecl *RD = cast(Record->getDecl())) - if (RD->hasAttr()) - Class->addAttr(::new (Context) WeakAttr(SourceRange(), Context)); + if (const RecordType *Record = NewBaseType->getAs()) { + const CXXRecordDecl *RD = cast(Record->getDecl()); + if (Class->isInterface() && + (!RD->isInterface() || + KnownBase->getAccessSpecifier() != AS_public)) { + // The Microsoft extension __interface does not permit bases that + // are not themselves public interfaces. + Diag(KnownBase->getLocStart(), diag::err_invalid_base_in_interface) + << getRecordDiagFromTagKind(RD->getTagKind()) << RD->getName() + << RD->getSourceRange(); + Invalid = true; + } + if (RD->hasAttr()) + Class->addAttr(::new (Context) WeakAttr(SourceRange(), Context)); + } } } @@ -1512,6 +1523,50 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, bool isFunc = D.isDeclarationOfFunction(); + if (cast(CurContext)->isInterface()) { + // The Microsoft extension __interface only permits public member functions + // and prohibits constructors, destructors, operators, non-public member + // functions, static methods and data members. + unsigned InvalidDecl; + bool ShowDeclName = true; + if (!isFunc) + InvalidDecl = (DS.getStorageClassSpec() == DeclSpec::SCS_typedef) ? 0 : 1; + else if (AS != AS_public) + InvalidDecl = 2; + else if (DS.getStorageClassSpec() == DeclSpec::SCS_static) + InvalidDecl = 3; + else switch (Name.getNameKind()) { + case DeclarationName::CXXConstructorName: + InvalidDecl = 4; + ShowDeclName = false; + break; + + case DeclarationName::CXXDestructorName: + InvalidDecl = 5; + ShowDeclName = false; + break; + + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXConversionFunctionName: + InvalidDecl = 6; + break; + + default: + InvalidDecl = 0; + break; + } + + if (InvalidDecl) { + if (ShowDeclName) + Diag(Loc, diag::err_invalid_member_in_interface) + << (InvalidDecl-1) << Name; + else + Diag(Loc, diag::err_invalid_member_in_interface) + << (InvalidDecl-1) << ""; + return 0; + } + } + // C++ 9.2p6: A member shall not be declared to have automatic storage // duration (auto, register) or with the extern storage-class-specifier. // C++ 7.1.1p8: The mutable specifier can be applied only to names of class diff --git a/test/CodeGenCXX/microsoft-interface.cpp b/test/CodeGenCXX/microsoft-interface.cpp new file mode 100644 index 0000000000..3887d18df1 --- /dev/null +++ b/test/CodeGenCXX/microsoft-interface.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -std=c++11 -fms-extensions -Wno-microsoft -triple=i386-pc-win32 -emit-llvm %s -o - | FileCheck %s + +__interface I { + int test() { + return 1; + } +}; + +struct S : I { + virtual int test() override { + return I::test(); + } +}; + +int fn() { + S s; + return s.test(); +} + +// CHECK: @_ZTV1S = linkonce_odr unnamed_addr constant [3 x i8*] [i8* null, i8* bitcast ({ i8*, i8*, i8* }* @_ZTI1S to i8*), i8* bitcast (i32 (%struct.S*)* @_ZN1S4testEv to i8*)] + +// CHECK: define i32 @_Z2fnv() +// CHECK: call void @_ZN1SC1Ev(%struct.S* %s) +// CHECK: %call = call i32 @_ZN1S4testEv(%struct.S* %s) + +// CHECK: define linkonce_odr void @_ZN1SC1Ev(%struct.S* %this) +// CHECK: call void @_ZN1SC2Ev(%struct.S* %this1) + +// CHECK: define linkonce_odr i32 @_ZN1S4testEv(%struct.S* %this) +// CHECK: %call = call i32 @_ZN1I4testEv(%__interface.I* %0) + +// CHECK: define linkonce_odr i32 @_ZN1I4testEv(%__interface.I* %this) +// CHECK: ret i32 1 + +// CHECK: define linkonce_odr void @_ZN1SC2Ev(%struct.S* %this) +// CHECK: call void @_ZN1IC2Ev(%__interface.I* %0) +// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1S, i64 0, i64 2), i8*** %1 + +// CHECK: define linkonce_odr void @_ZN1IC2Ev(%__interface.I* %this) +// CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1I, i64 0, i64 2), i8*** %0 + +// CHECK-NOT: define linkonce_odr %__interface.I* @_ZN1IaSERKS_(%__interface.I* %this, %__interface.I*) +// CHECK-NOT: define linkonce_odr %__interface.I* @_ZN1IaSEOS_(%__interface.I* %this, %__interface.I*) diff --git a/test/SemaCXX/ms-interface.cpp b/test/SemaCXX/ms-interface.cpp new file mode 100644 index 0000000000..62ae25482f --- /dev/null +++ b/test/SemaCXX/ms-interface.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 %s -fsyntax-only -verify -fms-extensions -Wno-microsoft -std=c++11 + +__interface I1 { + // expected-error@+1 {{user-declared constructor is not permitted within an interface type}} + I1(); + // expected-error@+1 {{user-declared destructor is not permitted within an interface type}} + ~I1(); + virtual void fn1() const; + // expected-error@+1 {{operator 'operator!' is not permitted within an interface type}} + bool operator!(); + // expected-error@+1 {{operator 'operator int' is not permitted within an interface type}} + operator int(); + // expected-error@+1 {{nested class I1:: is not permitted within an interface type}} + struct { int a; }; + void fn2() { + struct A { }; // should be ignored: not a nested class + } +protected: // expected-error {{interface types cannot specify 'protected' access}} + typedef void void_t; + using int_t = int; +private: // expected-error {{interface types cannot specify 'private' access}} + static_assert(true, "oops"); +}; + +__interface I2 { + // expected-error@+1 {{data member 'i' is not permitted within an interface type}} + int i; + // expected-error@+1 {{static member function 'fn1' is not permitted within an interface type}} + static int fn1(); +private: // expected-error {{interface types cannot specify 'private' access}} + // expected-error@+1 {{non-public member function 'fn2' is not permitted within an interface type}} + void fn2(); +protected: // expected-error {{interface types cannot specify 'protected' access}} + // expected-error@+1 {{non-public member function 'fn3' is not permitted within an interface type}} + void fn3(); +public: + void fn4(); +}; + +// expected-error@+1 {{'final' keyword not permitted with interface types}} +__interface I3 final { +}; + +__interface I4 : I1, I2 { + void fn1() const override; + // expected-error@+1 {{'final' keyword not permitted with interface types}} + void fn2() final; +}; + +// expected-error@+1 {{interface type cannot inherit from non-public 'interface I1'}} +__interface I5 : private I1 { +}; + +template +__interface I6 : X { +}; + +struct S { }; +class C { }; +__interface I { }; + +// expected-error@55 {{interface type cannot inherit from 'struct S'}} +// expected-note@+1 {{in instantiation of template class 'I6' requested here}} +struct S1 : I6 { +}; + +// expected-error@55 {{interface type cannot inherit from 'class C'}} +// expected-note@+1 {{in instantiation of template class 'I6' requested here}} +class C1 : I6 { +}; + +class C2 : I6 { +}; -- 2.40.0