Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1659,14 +1659,14 @@ def DLLExport : InheritableAttr, TargetSpecificAttr { let Spellings = [Declspec<"dllexport">, GCC<"dllexport">]; - let Subjects = SubjectList<[Function, Var]>; + let Subjects = SubjectList<[Function, Var, CXXRecord]>; let Documentation = [Undocumented]; } def DLLImport : InheritableAttr, TargetSpecificAttr { let Spellings = [Declspec<"dllimport">, GCC<"dllimport">]; - // Technically, the subjects for DllImport are Function and Var, but there is - // custom semantic handling required when MicrosoftExt is true. + // Technically, the subjects for DllImport are Function, Var and CXXRecord, + // but there is custom semantic handling required when MicrosoftExt is true. let Documentation = [Undocumented]; } Index: lib/CodeGen/CGCXX.cpp =================================================================== --- lib/CodeGen/CGCXX.cpp +++ lib/CodeGen/CGCXX.cpp @@ -44,6 +44,10 @@ if (!D->hasTrivialBody()) return true; + // For exported destructors, we need a full definition. + if (D->hasAttr()) + return true; + const CXXRecordDecl *Class = D->getParent(); // If we need to manipulate a VTT parameter, give up. Index: lib/CodeGen/CGVTables.cpp =================================================================== --- lib/CodeGen/CGVTables.cpp +++ lib/CodeGen/CGVTables.cpp @@ -651,18 +651,31 @@ // 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()) { + // Cannot discard exported functions. + DiscardableODRLinkage = NonDiscardableODRLinkage; + } else if (RD->hasAttr()) { + // Imported functions 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!"); Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1389,6 +1389,13 @@ B)); } + if (const auto *Dtor = dyn_cast_or_null(D)) { + if (getCXXABI().useThunkForDtorVariant(Dtor, GD.getDtorType())) { + // Don't dllexport/import destructor thunks. + F->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass); + } + } + if (!DontDefer) { // All MSVC dtors other than the base dtor are linkonce_odr and delegate to // each other bottoming out with the base dtor. Therefore we emit non-base Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -912,6 +912,12 @@ VTable->setInitializer(Init); VTable->setLinkage(Linkage); + + if (RD->hasAttr()) + VTable->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass); + else if (RD->hasAttr()) + VTable->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass); + CGM.setGlobalVisibility(VTable, RD); } } @@ -1217,6 +1223,11 @@ llvm::Constant *Init = llvm::ConstantArray::get(VBTableType, Offsets); GV->setInitializer(Init); + if (RD->hasAttr()) + GV->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass); + else if (RD->hasAttr()) + GV->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass); + // Set the right visibility. CGM.setGlobalVisibility(GV, RD); } Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -3836,15 +3836,19 @@ } static void handleDLLImportAttr(Sema &S, Decl *D, const AttributeList &Attr) { - // Attribute can be applied only to functions or variables. - FunctionDecl *FD = dyn_cast(D); - if (!FD && !isa(D)) { + // Attribute can be applied only to functions, variables or classes. + if (!isa(D) && !isa(D) && !isa(D)) { // Apparently Visual C++ thinks it is okay to not emit a warning // in this case, so only emit a warning when -fms-extensions is not // specified. - if (!S.getLangOpts().MicrosoftExt) - S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << Attr.getName() << ExpectedVariableOrFunction; + if (S.getLangOpts().MicrosoftExt) + return; + + unsigned Expected = S.getLangOpts().CPlusPlus + ? ExpectedFunctionVariableOrClass + : ExpectedVariableOrFunction; + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << Expected; return; } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -4346,6 +4346,78 @@ } } +/// \brief Get the dllimport/dllexport attribute from a declaration. +static Attr *getDLLAttr(Decl *D) { + assert(!(D->hasAttr() && D->hasAttr()) && + "A declaration cannot be both dllimport and dllexport."); + if (auto *Import = D->getAttr()) + return Import; + if (auto *Export = D->getAttr()) + return Export; + return nullptr; +} + +/// \brief Check dllimport/dllexport class member. +static void checkDLLClassMember(Sema &S, Decl *Member, Attr *ClassAttr, + CXXRecordDecl *ReturnedRecord) { + if (getDLLAttr(Member)) { + // FIXME: Error about importing/exporting individual members. + return; + } + + bool ClassExported = ClassAttr->getKind() == attr::DLLExport; + + if (ClassExported && ReturnedRecord && + !ReturnedRecord->hasAttr()) { + // FIXME: Warn: the returned class should probably be exported. + } + + auto *NewAttr = cast(ClassAttr->clone(S.getASTContext())); + NewAttr->setInherited(true); + Member->addAttr(NewAttr); +} + +/// \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: All bases must be exportable. MSVC doesn't seem to warn about + // this, but we should. We also need to propagate the attribute upwards to + // template specialization bases. + for (Decl *D : Class->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + checkDLLClassMember(S, VD, ClassAttr, + VD->getType()->getAsCXXRecordDecl()); + } + + if (CXXMethodDecl *MD = dyn_cast(D)) { + checkDLLClassMember(S, MD, ClassAttr, + MD->getReturnType()->getAsCXXRecordDecl()); + + if (ClassExported && !MD->isDefaulted() && !MD->isDeleted()) { + // Instantiate non-default methods. + S.MarkFunctionReferenced(Class->getLocation(), MD); + } else if (ClassExported && !MD->isDeleted() && + (!MD->isTrivial() || MD->isCopyAssignmentOperator())) { + // Also instantiate non-trivial default methods and the copy assignment + // operator. + S.MarkFunctionReferenced(Class->getLocation(), MD); + // Resolve its exception specification; CodeGen needs it. + auto *FPT = MD->getType()->getAs(); + 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. @@ -4510,6 +4582,8 @@ // 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 Index: test/CodeGenCXX/dllexport.cpp =================================================================== --- test/CodeGenCXX/dllexport.cpp +++ test/CodeGenCXX/dllexport.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple i686-pc-win32 -fno-rtti -x c++ -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple i686-pc-win32 -O1 -mconstructor-aliases -fno-rtti -x c++ -emit-llvm < %s | FileCheck %s #define DLLEXPORT __declspec(dllexport) @@ -23,6 +24,62 @@ }; -void user() { +struct DLLEXPORT T { + // Copy assignment operator: + // CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.T* @"\01??4T@@QAEAAU0@ABU0@@Z" + + void a() {} + // CHECK-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?a@T@@QAEXXZ" + + static int b; + // CHECK-DAG: @"\01?b@T@@2HA" = external dllexport global i32 + + static int c; + // CHECK-DAG: @"\01?c@T@@2HA" = dllexport global i32 0, align 4 +}; + +int T::c; + +template struct DLLEXPORT U { void foo() {} }; +// The U specialization below must cause the following to be emitted: +// CHECK-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?foo@?$U@H@@QAEXXZ" +// CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.U* @"\01??4?$U@H@@QAEAAU0@ABU0@@Z" +struct DLLEXPORT V : public U { }; + + +struct DLLEXPORT W { virtual void foo() {} }; +// Default ctor: +// CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.W* @"\01??0W@@QAE@XZ" +// Copy ctor: +// CHECK-DAG: define weak_odr dllexport x86_thiscallcc %struct.W* @"\01??0W@@QAE@ABU0@@Z" +// vftable: +// CHECK-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 DLLEXPORT X : public virtual W {}; +// vbtable: +// CHECK-DAG: @"\01??_8X@@7B@" = weak_odr dllexport unnamed_addr constant [2 x i32] [i32 0, i32 4] + +struct DLLEXPORT Y { + int x; +}; + +struct DLLEXPORT Z { virtual ~Z() {} }; +// The scalar deleting dtor does not get exported: +// CHECK-DAG: define weak_odr x86_thiscallcc void @"\01??_GZ@@UAEPAXI@Z" + +// The user-defined dtor does get exported: +// CHECK-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??1Z@@UAE@XZ" + +namespace DontUseDtorAlias { + struct DLLEXPORT A { ~A(); }; + struct DLLEXPORT B : A { ~B(); }; + A::~A() { } + B::~B() { } + // Emit a real definition of B's constructor; don't alias it to A's. + // CHECK-DAG: define dllexport x86_thiscallcc void @"\01??1B@DontUseDtorAlias@@QAE@XZ" +} + +int user() { a(); + return T::b; } Index: test/CodeGenCXX/dllimport.cpp =================================================================== --- test/CodeGenCXX/dllimport.cpp +++ test/CodeGenCXX/dllimport.cpp @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -O2 -disable-llvm-optzns -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -fno-rtti -O2 -disable-llvm-optzns -emit-llvm < %s | FileCheck %s +// RUN: %clang_cc1 -triple i686-pc-win32 -x c++ -fno-rtti -mconstructor-aliases -O2 -disable-llvm-optzns -emit-llvm < %s | FileCheck %s #define DLLIMPORT __declspec(dllimport) @@ -17,9 +18,53 @@ // CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01?a@S@@QAEXXZ" }; -void user(S* s) { +struct DLLIMPORT T { + void a() {} + // CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01?a@T@@QAEXXZ" + + static int b; + // CHECK-DAG: @"\01?b@T@@2HA" = external dllimport global i32 +}; + +template struct DLLIMPORT U { void foo() {} }; +// CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01?foo@?$U@H@@QAEXXZ" +struct DLLIMPORT V : public U { }; + +struct DLLIMPORT W { virtual void foo() {} }; +// vftable: +// CHECK-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 DLLIMPORT X : public virtual W {}; +// vbtable: +// CHECK-DAG: @"\01??_8X@@7B@" = available_externally dllimport unnamed_addr constant [2 x i32] [i32 0, i32 4] + +struct DLLIMPORT Y { + int x; +}; + +struct DLLIMPORT Z { virtual ~Z() {} }; +// User-defined dtor: +// CHECK-DAG: define available_externally dllimport x86_thiscallcc void @"\01??1Z@@UAE@XZ" + +namespace DontUseDtorAlias { + struct DLLIMPORT A { ~A(); }; + struct 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. + // CHECK-DAG: available_externally dllimport x86_thiscallcc void @"\01??1B@DontUseDtorAlias@@QAE@XZ" +} + +void user() { a(); b(); c(); - s->a(); + (void) &S::a; + (void) &T::a; + (void) T::b; + (void) &V::foo; + W w; + X x; + Z z; + DontUseDtorAlias::B b; } Index: test/SemaCXX/dllexport.cpp =================================================================== --- test/SemaCXX/dllexport.cpp +++ test/SemaCXX/dllexport.cpp @@ -16,13 +16,13 @@ // 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 @@ -210,6 +210,14 @@ //===----------------------------------------------------------------------===// +// Classes +//===----------------------------------------------------------------------===// + +class __declspec(dllexport) ClassDecl; + +class __declspec(dllexport) ClassDef { }; + +//===----------------------------------------------------------------------===// // Precedence //===----------------------------------------------------------------------===// @@ -249,3 +257,5 @@ void __declspec(dllexport) precedenceRedecl2(); void __declspec(dllimport) precedenceRedecl2() {} // expected-warning{{'dllimport' attribute ignored}} + +// FIXME: Precedence rules seem to be different with classes. Index: test/SemaCXX/dllimport.cpp =================================================================== --- test/SemaCXX/dllimport.cpp +++ test/SemaCXX/dllimport.cpp @@ -15,13 +15,13 @@ // 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 @@ -225,3 +225,11 @@ template<> __declspec(dllimport) void funcTmpl(); template<> __declspec(dllimport) void funcTmpl() {} // expected-error{{dllimport cannot be applied to non-inline function definition}} template<> __declspec(dllimport) inline void funcTmpl() {} + +//===----------------------------------------------------------------------===// +// Classes +//===----------------------------------------------------------------------===// + +class __declspec(dllimport) ClassDecl; + +class __declspec(dllimport) ClassDef { };