diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 4eb852e4cfd1ad513270d2898b656086f3fdf3d1..11a30b9010d55d4fe3f4902d671e478cd3974600 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -1659,13 +1659,13 @@ def MsStruct : InheritableAttr { def DLLExport : InheritableAttr, TargetSpecificAttr<TargetWindows> { let Spellings = [Declspec<"dllexport">, GCC<"dllexport">]; - let Subjects = SubjectList<[Function, Var]>; + let Subjects = SubjectList<[Function, Var, CXXRecord]>; let Documentation = [Undocumented]; } def DLLImport : InheritableAttr, TargetSpecificAttr<TargetWindows> { let Spellings = [Declspec<"dllimport">, GCC<"dllimport">]; - let Subjects = SubjectList<[Function, Var]>; + let Subjects = SubjectList<[Function, Var, CXXRecord]>; let Documentation = [Undocumented]; } diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp index 55c06f67865e4623e1d944a3d09ded3b92da87bc..24dc89d551fb8163e9877a53fdd0178e749a9c22 100644 --- a/lib/CodeGen/CGCXX.cpp +++ b/lib/CodeGen/CGCXX.cpp @@ -44,6 +44,10 @@ bool CodeGenModule::TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D) { if (!D->hasTrivialBody()) return true; + // For exported destructors, we need a full definition. + if (D->hasAttr<DLLExportAttr>()) + return true; + const CXXRecordDecl *Class = D->getParent(); // If we need to manipulate a VTT parameter, give up. diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 97bea977349166a4d5e9435ccb328bc701fe74a3..e82ac1df53c468383d3d965b3aab383ac79a0e32 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -1425,8 +1425,8 @@ namespace { /// in reverse order of their construction. void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, CXXDtorType DtorType) { - assert(!DD->isTrivial() && - "Should not emit dtor epilogue for trivial dtor!"); + assert((!DD->isTrivial() || DD->hasAttr<DLLExportAttr>()) && + "Should not emit dtor epilogue for non-exported trivial dtor!"); // The deleting-destructor phase just needs to call the appropriate // operator delete that Sema picked up. diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index d10db5a68392f5a08aa131b008b323ff6be12b3e..8984a707c4fbb8acea524cebca05be2058c6b6eb 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -651,18 +651,31 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) { // internal linkage. if (Context.getLangOpts().AppleKext) return llvm::Function::InternalLinkage; - + + llvm::GlobalVariable::LinkageTypes DiscardableODRLinkage = + llvm::GlobalValue::LinkOnceODRLinkage; + llvm::GlobalVariable::LinkageTypes NonDiscardableODRLinkage = + llvm::GlobalValue::WeakODRLinkage; + if (RD->hasAttr<DLLExportAttr>()) { + // Cannot discard exported vtables. + DiscardableODRLinkage = NonDiscardableODRLinkage; + } else if (RD->hasAttr<DLLImportAttr>()) { + // Imported vtables are available externally. + DiscardableODRLinkage = llvm::GlobalVariable::AvailableExternallyLinkage; + NonDiscardableODRLinkage = llvm::GlobalVariable::AvailableExternallyLinkage; + } + switch (RD->getTemplateSpecializationKind()) { case TSK_Undeclared: case TSK_ExplicitSpecialization: case TSK_ImplicitInstantiation: - return llvm::GlobalVariable::LinkOnceODRLinkage; + return DiscardableODRLinkage; case TSK_ExplicitInstantiationDeclaration: llvm_unreachable("Should not have been asked to emit this"); case TSK_ExplicitInstantiationDefinition: - return llvm::GlobalVariable::WeakODRLinkage; + return NonDiscardableODRLinkage; } llvm_unreachable("Invalid TemplateSpecializationKind!"); diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index dee565a873c92a378f783427a4b0345a4b3a7a2f..5fd9e979487a2f63af403eaf4bb2c91170a47642 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -203,6 +203,9 @@ public: void setThunkLinkage(llvm::Function *Thunk, bool ForVTable) override { Thunk->setLinkage(llvm::GlobalValue::WeakAnyLinkage); + + // Never dllimport/dllexport thunks. + Thunk->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass); } llvm::Value *performThisAdjustment(CodeGenFunction &CGF, llvm::Value *This, @@ -913,6 +916,7 @@ void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT, VTable->setInitializer(Init); VTable->setLinkage(Linkage); + CGM.setGlobalVisibility(VTable, RD); } } @@ -994,6 +998,10 @@ llvm::GlobalVariable *MicrosoftCXXABI::getAddrOfVTable(const CXXRecordDecl *RD, VTable = CGM.CreateOrReplaceCXXRuntimeVariable( Name.str(), ArrayType, llvm::GlobalValue::ExternalLinkage); VTable->setUnnamedAddr(true); + if (RD->hasAttr<DLLImportAttr>()) + VTable->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass); + else if (RD->hasAttr<DLLExportAttr>()) + VTable->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass); break; } @@ -1169,6 +1177,12 @@ MicrosoftCXXABI::getAddrOfVBTable(const VPtrInfo &VBT, const CXXRecordDecl *RD, llvm::GlobalVariable *GV = CGM.CreateOrReplaceCXXRuntimeVariable(Name, VBTableType, Linkage); GV->setUnnamedAddr(true); + + if (RD->hasAttr<DLLImportAttr>()) + GV->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass); + else if (RD->hasAttr<DLLExportAttr>()) + GV->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass); + return GV; } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index da93b044826bcf908e10a11e2e799de6f2f1b866..48c2c5d3559e45ffdd9a656497912216d3402bfa 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -4350,6 +4350,8 @@ static void CheckAbstractClassUsage(AbstractUsageInfo &Info, /// \brief Return a DLL attribute from the declaration. static InheritableAttr *getDLLAttr(Decl *D) { + assert(!(D->hasAttr<DLLImportAttr>() && D->hasAttr<DLLExportAttr>()) && + "A declaration cannot be both dllimport and dllexport."); if (auto *Import = D->getAttr<DLLImportAttr>()) return Import; if (auto *Export = D->getAttr<DLLExportAttr>()) @@ -4357,6 +4359,59 @@ static InheritableAttr *getDLLAttr(Decl *D) { return nullptr; } +/// \brief Check class-level dllimport/dllexport attribute. +static void checkDLLAttribute(Sema &S, CXXRecordDecl *Class) { + Attr *ClassAttr = getDLLAttr(Class); + if (!ClassAttr) + return; + + bool ClassExported = ClassAttr->getKind() == attr::DLLExport; + + // Force declaration of implicit members so they can inherit the attribute. + S.ForceDeclarationOfImplicitMembers(Class); + + // FIXME: MSVC's docs say all bases must be exportable, but this doesn't + // seem to be true in practice? + + // FIXME: We also need to propagate the attribute upwards to class template + // specialization bases. + + for (Decl *Member : Class->decls()) { + if (getDLLAttr(Member)) { + // FIXME: Error about importing/exporting individual members. + } + + if (!isa<CXXMethodDecl>(Member) && !isa<VarDecl>(Member)) + continue; + + auto *NewAttr = cast<InheritableAttr>(ClassAttr->clone(S.getASTContext())); + NewAttr->setInherited(true); + Member->addAttr(NewAttr); + + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member)) { + if (ClassExported) { + if (MD->isDeleted()) + continue; + + if (MD->isUserProvided()) { + // Instantiate non-default methods. + S.MarkFunctionReferenced(Class->getLocation(), MD); + } else if (!MD->isTrivial() || MD->isExplicitlyDefaulted() || + MD->isCopyAssignmentOperator() || + MD->isMoveAssignmentOperator()) { + // Instantiate non-trival or explicitly defaulted methods, and the + // copy assignment / move assignment operators. + S.MarkFunctionReferenced(Class->getLocation(), MD); + // Resolve its exception specification; CodeGen needs it. + auto *FPT = MD->getType()->getAs<FunctionProtoType>(); + S.ResolveExceptionSpec(Class->getLocation(), FPT); + S.ActOnFinishInlineMethodDef(MD); + } + } + } + } +} + /// \brief Perform semantic checks on a class definition that has been /// completing, introducing implicitly-declared members, checking for /// abstract types, etc. @@ -4521,6 +4576,8 @@ void Sema::CheckCompletedCXXClass(CXXRecordDecl *Record) { // instantiated (e.g. meta-functions). This doesn't apply to classes that // have inheriting constructors. DeclareInheritingConstructors(Record); + + checkDLLAttribute(*this, Record); } /// Look up the special member function that would be called by a special diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index be00c0e8b37fc2bebeae22fe52b43bc831ce6e92..d767b3c8e79e134758577f36564cb151349acb0d 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -11284,7 +11284,7 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { Constructor = cast<CXXConstructorDecl>(Constructor->getFirstDecl()); if (Constructor->isDefaulted() && !Constructor->isDeleted()) { if (Constructor->isDefaultConstructor()) { - if (Constructor->isTrivial()) + if (Constructor->isTrivial() && !Constructor->hasAttr<DLLExportAttr>()) return; DefineImplicitDefaultConstructor(Loc, Constructor); } else if (Constructor->isCopyConstructor()) { diff --git a/test/CodeGenCXX/dllexport.cpp b/test/CodeGenCXX/dllexport.cpp index cd448049c645de25b8d77f69cf3f39c109fc582b..df8524d03f01efaf54bbc60a430a32cbb1bf25c4 100644 --- a/test/CodeGenCXX/dllexport.cpp +++ b/test/CodeGenCXX/dllexport.cpp @@ -3,6 +3,8 @@ // RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -std=c++1y -O0 -o - %s | FileCheck --check-prefix=GNU --check-prefix=G32 %s // RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -std=c++1y -O0 -o - %s | FileCheck --check-prefix=GNU --check-prefix=G64 %s +// RUN: %clang_cc1 -triple i686-pc-win32 -O1 -mconstructor-aliases -std=c++1y -emit-llvm -o - %s | FileCheck %s --check-prefix=MSC --check-prefix=M32 + // Helper structs to make templates more expressive. struct ImplicitInst_Exported {}; struct ExplicitDecl_Exported {}; @@ -447,3 +449,91 @@ void __declspec(dllexport) precedenceRedecl1() {} // GNU-DAG: define dllexport void @_Z17precedenceRedecl2v() void __declspec(dllexport) precedenceRedecl2(); void __declspec(dllimport) precedenceRedecl2() {} + + + +//===----------------------------------------------------------------------===// +// Classes +//===----------------------------------------------------------------------===// + +struct S { + void __declspec(dllexport) a() {} + // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?a@S@@QAEXXZ" + + struct T { + void __declspec(dllexport) a() {} + // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?a@T@S@@QAEXXZ" + }; +}; + + +struct __declspec(dllexport) T { + // Copy assignment operator: + // M32-DAG: define weak_odr dllexport x86_thiscallcc nonnull %struct.T* @"\01??4T@@QAEAAU0@ABU0@@Z" + + // Explicitly defaulted copy constructur: + T(const T&) = default; + // M32-DAG: define weak_odr dllexport x86_thiscallcc %struct.T* @"\01??0T@@QAE@ABU0@@Z" + + void a() {} + // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?a@T@@QAEXXZ" + + static int b; + // M32-DAG: @"\01?b@T@@2HA" = external dllexport global i32 + + static int c; + // M32-DAG: @"\01?c@T@@2HA" = dllexport global i32 0, align 4 +}; + +USEVAR(T::b) +int T::c; + +template <typename T> struct __declspec(dllexport) U { void foo() {} }; +// The U<int> specialization below must cause the following to be emitted: +// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?foo@?$U@H@@QAEXXZ" +// M32-DAG: define weak_odr dllexport x86_thiscallcc nonnull %struct.U* @"\01??4?$U@H@@QAEAAU0@ABU0@@Z" +struct __declspec(dllexport) V : public U<int> { }; + + +struct __declspec(dllexport) W { virtual void foo() {} }; +// Default ctor: +// M32-DAG: define weak_odr dllexport x86_thiscallcc %struct.W* @"\01??0W@@QAE@XZ" +// Copy ctor: +// M32-DAG: define weak_odr dllexport x86_thiscallcc %struct.W* @"\01??0W@@QAE@ABU0@@Z" +// vftable: +// M32-DAG: @"\01??_7W@@6B@" = weak_odr dllexport unnamed_addr constant [1 x i8*] [i8* bitcast (void (%struct.W*)* @"\01?foo@W@@UAEXXZ" to i8*)] + +struct __declspec(dllexport) X : public virtual W {}; +// vbtable: +// M32-DAG: @"\01??_8X@@7B@" = weak_odr dllexport unnamed_addr constant [2 x i32] [i32 0, i32 4] + +struct __declspec(dllexport) Y { + // Move assignment operator: + // M32-DAG: define weak_odr dllexport x86_thiscallcc nonnull %struct.Y* @"\01??4Y@@QAEAAU0@$$QAU0@@Z" + + int x; +}; + +struct __declspec(dllexport) Z { virtual ~Z() {} }; +// The scalar deleting dtor does not get exported: +// M32-DAG: define linkonce_odr x86_thiscallcc void @"\01??_GZ@@UAEPAXI@Z" + + +// The user-defined dtor does get exported: +// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??1Z@@UAE@XZ" + +namespace DontUseDtorAlias { + struct __declspec(dllexport) A { ~A(); }; + struct __declspec(dllexport) B : A { ~B(); }; + A::~A() { } + B::~B() { } + // Emit a real definition of B's constructor; don't alias it to A's. + // M32-DAG: define dllexport x86_thiscallcc void @"\01??1B@DontUseDtorAlias@@QAE@XZ" +} + +struct __declspec(dllexport) DefaultedCtorsDtors { + DefaultedCtorsDtors() = default; + // M32-DAG: define weak_odr dllexport x86_thiscallcc %struct.DefaultedCtorsDtors* @"\01??0DefaultedCtorsDtors@@QAE@XZ" + ~DefaultedCtorsDtors() = default; + // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??1DefaultedCtorsDtors@@QAE@XZ" +}; diff --git a/test/CodeGenCXX/dllimport.cpp b/test/CodeGenCXX/dllimport.cpp index 87f3c884ea9fbcebb24778fa99e138e055f475d4..a0d153e408da1d6fcb4330f3df3eef4c06940ffb 100644 --- a/test/CodeGenCXX/dllimport.cpp +++ b/test/CodeGenCXX/dllimport.cpp @@ -1,9 +1,9 @@ -// RUN: %clang_cc1 -triple i686-windows-msvc -emit-llvm -std=c++1y -O0 -o - %s -DMSABI | FileCheck --check-prefix=MSC --check-prefix=M32 %s -// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm -std=c++1y -O0 -o - %s -DMSABI | FileCheck --check-prefix=MSC --check-prefix=M64 %s -// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -std=c++1y -O0 -o - %s | FileCheck --check-prefix=GNU --check-prefix=G32 %s -// RUN: %clang_cc1 -triple x86_64-windows-gnu -emit-llvm -std=c++1y -O0 -o - %s | FileCheck --check-prefix=GNU --check-prefix=G64 %s -// RUN: %clang_cc1 -triple i686-windows-msvc -emit-llvm -std=c++1y -O1 -o - %s -DMSABI | FileCheck --check-prefix=MO1 %s -// RUN: %clang_cc1 -triple i686-windows-gnu -emit-llvm -std=c++1y -O1 -o - %s | FileCheck --check-prefix=GO1 %s +// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -DMSABI | FileCheck --check-prefix=MSC --check-prefix=M32 %s +// RUN: %clang_cc1 -triple x86_64-windows-msvc -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s -DMSABI | FileCheck --check-prefix=MSC --check-prefix=M64 %s +// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s | FileCheck --check-prefix=GNU --check-prefix=G32 %s +// RUN: %clang_cc1 -triple x86_64-windows-gnu -fno-rtti -emit-llvm -std=c++1y -O0 -o - %s | FileCheck --check-prefix=GNU --check-prefix=G64 %s +// RUN: %clang_cc1 -triple i686-windows-msvc -fno-rtti -emit-llvm -std=c++1y -O1 -o - %s -DMSABI | FileCheck --check-prefix=MO1 %s +// RUN: %clang_cc1 -triple i686-windows-gnu -fno-rtti -emit-llvm -std=c++1y -O1 -o - %s | FileCheck --check-prefix=GO1 %s // Helper structs to make templates more expressive. struct ImplicitInst_Imported {}; @@ -21,8 +21,8 @@ struct ExplicitSpec_NotImported {}; #define USEVARTYPE(type, var) type UNIQ(use)() { return var; } #define USEVAR(var) USEVARTYPE(int, var) #define USE(func) void UNIQ(use)() { func(); } - - +#define USEMEMFUNC(class, func) void (class::*UNIQ(use)())() { return &class::func; } +#define USECLASS(class) void UNIQ(USE)() { class x; } //===----------------------------------------------------------------------===// // Globals @@ -501,3 +501,69 @@ USE(funcTmpl<ExplicitSpec_Imported>) // GO1-DAG: define available_externally dllimport void @_Z8funcTmplI31ExplicitSpec_InlineDef_ImportedEvv() template<> __declspec(dllimport) inline void funcTmpl<ExplicitSpec_InlineDef_Imported>() {} USE(funcTmpl<ExplicitSpec_InlineDef_Imported>) + + + +//===----------------------------------------------------------------------===// +// Classes +//===----------------------------------------------------------------------===// + +struct __declspec(dllimport) T { + void a() {} + // MO1-DAG: define available_externally dllimport x86_thiscallcc void @"\01?a@T@@QAEXXZ" + + static int b; + // MO1-DAG: @"\01?b@T@@2HA" = external dllimport global i32 +}; +USEMEMFUNC(T, a) +USEVAR(T::b) + +template <typename T> struct __declspec(dllimport) U { void foo() {} }; +// MO1-DAG: define available_externally dllimport x86_thiscallcc void @"\01?foo@?$U@H@@QAEXXZ" +struct __declspec(dllimport) V : public U<int> { }; +USEMEMFUNC(V, foo) + +struct __declspec(dllimport) W { virtual void foo() {} }; +USECLASS(W) +// vftable: +// MO1-DAG: @"\01??_7W@@6B@" = available_externally dllimport unnamed_addr constant [1 x i8*] [i8* bitcast (void (%struct.W*)* @"\01?foo@W@@UAEXXZ" to i8*)] + +struct __declspec(dllimport) X : public virtual W {}; +USECLASS(X) +// vbtable: +// MO1-DAG: @"\01??_8X@@7B@" = available_externally dllimport unnamed_addr constant [2 x i32] [i32 0, i32 4] + +struct __declspec(dllimport) Y { + int x; +}; + +struct __declspec(dllimport) Z { virtual ~Z() {} }; +USECLASS(Z) +// User-defined dtor: +// MO1-DAG: define available_externally dllimport x86_thiscallcc void @"\01??1Z@@UAE@XZ" + +namespace DontUseDtorAlias { + struct __declspec(dllimport) A { ~A(); }; + struct __declspec(dllimport) B : A { ~B(); }; + inline A::~A() { } + inline B::~B() { } + // Emit a real definition of B's constructor; don't alias it to A's. + // MO1-DAG: available_externally dllimport x86_thiscallcc void @"\01??1B@DontUseDtorAlias@@QAE@XZ" + USECLASS(B) +} + +namespace Vtordisp { + // Don't dllimport the vtordisp. + // MO1-DAG: define weak x86_thiscallcc void @"\01?f@?$C@D@Vtordisp@@$4PPPPPPPM@A@AEXXZ" + + class Base { + virtual void f() {} + }; + template <typename T> + class __declspec(dllimport) C : virtual public Base { + public: + C() {} + virtual void f() {} + }; + template class C<char>; +} diff --git a/test/SemaCXX/dllexport.cpp b/test/SemaCXX/dllexport.cpp index c637e63f8e02efd168a1b6cb6f4e1dce7b805429..75f9fa9d6dff256682830038410372c26f201c5e 100644 --- a/test/SemaCXX/dllexport.cpp +++ b/test/SemaCXX/dllexport.cpp @@ -16,13 +16,13 @@ struct External { int v; }; // Invalid usage. -__declspec(dllexport) typedef int typedef1; // expected-warning{{'dllexport' attribute only applies to variables and functions}} -typedef __declspec(dllexport) int typedef2; // expected-warning{{'dllexport' attribute only applies to variables and functions}} -typedef int __declspec(dllexport) typedef3; // expected-warning{{'dllexport' attribute only applies to variables and functions}} -typedef __declspec(dllexport) void (*FunTy)(); // expected-warning{{'dllexport' attribute only applies to variables and functions}} -enum __declspec(dllexport) Enum {}; // expected-warning{{'dllexport' attribute only applies to variables and functions}} +__declspec(dllexport) typedef int typedef1; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} +typedef __declspec(dllexport) int typedef2; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} +typedef int __declspec(dllexport) typedef3; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} +typedef __declspec(dllexport) void (*FunTy)(); // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} +enum __declspec(dllexport) Enum {}; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} #if __has_feature(cxx_strong_enums) - enum class __declspec(dllexport) EnumClass {}; // expected-warning{{'dllexport' attribute only applies to variables and functions}} + enum class __declspec(dllexport) EnumClass {}; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} #endif @@ -310,6 +310,14 @@ template<> __declspec(dllexport) inline void funcTmpl<ExplicitSpec_InlineDef_Exp +//===----------------------------------------------------------------------===// +// Classes +//===----------------------------------------------------------------------===// + +class __declspec(dllexport) ClassDecl; + +class __declspec(dllexport) ClassDef { }; + //===----------------------------------------------------------------------===// // Precedence //===----------------------------------------------------------------------===// @@ -385,7 +393,7 @@ private: __declspec(dllexport) void privateDef(); public: - __declspec(dllexport) int Field; // expected-warning{{'dllexport' attribute only applies to variables and functions}} + __declspec(dllexport) int Field; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} __declspec(dllexport) static int StaticField; __declspec(dllexport) static int StaticFieldDef; __declspec(dllexport) static const int StaticConstField; @@ -771,7 +779,7 @@ private: __declspec(dllexport) void privateDef(); public: - __declspec(dllexport) int Field; // expected-warning{{'dllexport' attribute only applies to variables and functions}} + __declspec(dllexport) int Field; // expected-warning{{'dllexport' attribute only applies to variables, functions and classes}} __declspec(dllexport) static int StaticField; __declspec(dllexport) static int StaticFieldDef; __declspec(dllexport) static const int StaticConstField; @@ -907,3 +915,5 @@ template<typename T> template<typename U> __declspec(dllexport) int CTMT template<typename T> template<typename U> __declspec(dllexport) const int CTMTR<T>::StaticConstField = 1; // expected-error{{redeclaration of 'CTMTR::StaticConstField' cannot add 'dllexport' attribute}} template<typename T> template<typename U> __declspec(dllexport) constexpr int CTMTR<T>::ConstexprField; // expected-error{{redeclaration of 'CTMTR::ConstexprField' cannot add 'dllexport' attribute}} #endif // __has_feature(cxx_variable_templates) + +// FIXME: Precedence rules seem to be different for classes. diff --git a/test/SemaCXX/dllimport.cpp b/test/SemaCXX/dllimport.cpp index 1f53f7ab5929b60e84e7987185561371e6c5567d..8f7eb5bc4b26d07fdb0623d0a95b1e504ab2aa7b 100644 --- a/test/SemaCXX/dllimport.cpp +++ b/test/SemaCXX/dllimport.cpp @@ -15,13 +15,13 @@ namespace { struct Internal {}; } // Invalid usage. -__declspec(dllimport) typedef int typedef1; // expected-warning{{'dllimport' attribute only applies to variables and functions}} -typedef __declspec(dllimport) int typedef2; // expected-warning{{'dllimport' attribute only applies to variables and functions}} -typedef int __declspec(dllimport) typedef3; // expected-warning{{'dllimport' attribute only applies to variables and functions}} -typedef __declspec(dllimport) void (*FunTy)(); // expected-warning{{'dllimport' attribute only applies to variables and functions}} -enum __declspec(dllimport) Enum {}; // expected-warning{{'dllimport' attribute only applies to variables and functions}} +__declspec(dllimport) typedef int typedef1; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} +typedef __declspec(dllimport) int typedef2; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} +typedef int __declspec(dllimport) typedef3; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} +typedef __declspec(dllimport) void (*FunTy)(); // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} +enum __declspec(dllimport) Enum {}; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} #if __has_feature(cxx_strong_enums) - enum class __declspec(dllimport) EnumClass {}; // expected-warning{{'dllimport' attribute only applies to variables and functions}} + enum class __declspec(dllimport) EnumClass {}; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} #endif @@ -362,7 +362,6 @@ template<> __declspec(dllimport) void funcTmpl<ExplicitSpec_Def_Imported>() {} / template<> __declspec(dllimport) inline void funcTmpl<ExplicitSpec_InlineDef_Imported>() {} - //===----------------------------------------------------------------------===// // Class members //===----------------------------------------------------------------------===// @@ -396,7 +395,7 @@ private: __declspec(dllimport) void privateDecl(); public: - __declspec(dllimport) int Field; // expected-warning{{'dllimport' attribute only applies to variables and functions}} + __declspec(dllimport) int Field; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} __declspec(dllimport) static int StaticField; __declspec(dllimport) static int StaticFieldDef; // expected-note{{attribute is here}} __declspec(dllimport) static const int StaticConstField; @@ -794,7 +793,7 @@ private: __declspec(dllimport) void privateDecl(); public: - __declspec(dllimport) int Field; // expected-warning{{'dllimport' attribute only applies to variables and functions}} + __declspec(dllimport) int Field; // expected-warning{{'dllimport' attribute only applies to variables, functions and classes}} __declspec(dllimport) static int StaticField; __declspec(dllimport) static int StaticFieldDef; // expected-note{{attribute is here}} __declspec(dllimport) static const int StaticConstField; @@ -945,3 +944,13 @@ template<typename T> template<typename U> __declspec(dllimport) constexpr int CT // expected-error@-1{{definition of dllimport static field not allowed}} // expected-note@-2{{attribute is here}} #endif // __has_feature(cxx_variable_templates) + + + +//===----------------------------------------------------------------------===// +// Classes +//===----------------------------------------------------------------------===// + +class __declspec(dllimport) ClassDecl; + +class __declspec(dllimport) ClassDef { };