Index: cfe/trunk/include/clang/Sema/Sema.h =================================================================== --- cfe/trunk/include/clang/Sema/Sema.h +++ cfe/trunk/include/clang/Sema/Sema.h @@ -5222,7 +5222,7 @@ SourceLocation RBrac, AttributeList *AttrList); void ActOnFinishCXXMemberDecls(); - void ActOnFinishCXXMemberDefaultArgs(Decl *D); + void ActOnFinishCXXNonNestedClass(Decl *D); void ActOnReenterCXXMethodParameter(Scope *S, ParmVarDecl *Param); unsigned ActOnReenterTemplateScope(Scope *S, Decl *Template); @@ -9022,6 +9022,10 @@ return NumArgs + 1 > NumParams; // If so, we view as an extra argument. return NumArgs > NumParams; } + + // Emitting members of dllexported classes is delayed until the class + // (including field initializers) is fully parsed. + SmallVector DelayedDllExportClasses; }; /// \brief RAII object that enters a new expression evaluation context. Index: cfe/trunk/lib/Parse/ParseDeclCXX.cpp =================================================================== --- cfe/trunk/lib/Parse/ParseDeclCXX.cpp +++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp @@ -3106,7 +3106,7 @@ // We've finished parsing everything, including default argument // initializers. - Actions.ActOnFinishCXXMemberDefaultArgs(TagDecl); + Actions.ActOnFinishCXXNonNestedClass(TagDecl); } if (TagDecl) Index: cfe/trunk/lib/Sema/Sema.cpp =================================================================== --- cfe/trunk/lib/Sema/Sema.cpp +++ cfe/trunk/lib/Sema/Sema.cpp @@ -691,6 +691,9 @@ assert(DelayedDefaultedMemberExceptionSpecs.empty()); assert(DelayedExceptionSpecChecks.empty()); + // All dllexport classes should have been processed already. + assert(DelayedDllExportClasses.empty()); + // Remove file scoped decls that turned out to be used. UnusedFileScopedDecls.erase( std::remove_if(UnusedFileScopedDecls.begin(nullptr, true), Index: cfe/trunk/lib/Sema/SemaDeclCXX.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp @@ -4682,6 +4682,60 @@ } } +static void ReferenceDllExportedMethods(Sema &S, CXXRecordDecl *Class) { + Attr *ClassAttr = getDLLAttr(Class); + if (!ClassAttr) + return; + + assert(ClassAttr->getKind() == attr::DLLExport); + + TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind(); + + if (TSK == TSK_ExplicitInstantiationDeclaration) + // Don't go any further if this is just an explicit instantiation + // declaration. + return; + + for (Decl *Member : Class->decls()) { + auto *MD = dyn_cast(Member); + if (!MD) + continue; + + if (Member->getAttr()) { + if (MD->isUserProvided()) { + // Instantiate non-default class member functions ... + + // .. except for certain kinds of template specializations. + if (TSK == TSK_ImplicitInstantiation && !ClassAttr->isInherited()) + continue; + + S.MarkFunctionReferenced(Class->getLocation(), MD); + + // The function will be passed to the consumer when its definition is + // encountered. + } else if (!MD->isTrivial() || MD->isExplicitlyDefaulted() || + MD->isCopyAssignmentOperator() || + MD->isMoveAssignmentOperator()) { + // Synthesize and instantiate non-trivial implicit methods, explicitly + // defaulted methods, and the copy and move assignment operators. The + // latter are exported even if they are trivial, because the address of + // an operator can be taken and should compare equal accross libraries. + DiagnosticErrorTrap Trap(S.Diags); + S.MarkFunctionReferenced(Class->getLocation(), MD); + if (Trap.hasErrorOccurred()) { + S.Diag(ClassAttr->getLocation(), diag::note_due_to_dllexported_class) + << Class->getName() << !S.getLangOpts().CPlusPlus11; + break; + } + + // There is no later point when we will see the definition of this + // function, so pass it to the consumer now. + S.Consumer.HandleTopLevelDecl(DeclGroupRef(MD)); + } + } + } +} + /// \brief Check class-level dllimport/dllexport attribute. void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { Attr *ClassAttr = getDLLAttr(Class); @@ -4783,45 +4837,10 @@ NewAttr->setInherited(true); Member->addAttr(NewAttr); } - - if (MD && ClassExported) { - if (TSK == TSK_ExplicitInstantiationDeclaration) - // Don't go any further if this is just an explicit instantiation - // declaration. - continue; - - if (MD->isUserProvided()) { - // Instantiate non-default class member functions ... - - // .. except for certain kinds of template specializations. - if (TSK == TSK_ImplicitInstantiation && !ClassAttr->isInherited()) - continue; - - MarkFunctionReferenced(Class->getLocation(), MD); - - // The function will be passed to the consumer when its definition is - // encountered. - } else if (!MD->isTrivial() || MD->isExplicitlyDefaulted() || - MD->isCopyAssignmentOperator() || - MD->isMoveAssignmentOperator()) { - // Synthesize and instantiate non-trivial implicit methods, explicitly - // defaulted methods, and the copy and move assignment operators. The - // latter are exported even if they are trivial, because the address of - // an operator can be taken and should compare equal accross libraries. - DiagnosticErrorTrap Trap(Diags); - MarkFunctionReferenced(Class->getLocation(), MD); - if (Trap.hasErrorOccurred()) { - Diag(ClassAttr->getLocation(), diag::note_due_to_dllexported_class) - << Class->getName() << !getLangOpts().CPlusPlus11; - break; - } - - // There is no later point when we will see the definition of this - // function, so pass it to the consumer now. - Consumer.HandleTopLevelDecl(DeclGroupRef(MD)); - } - } } + + if (ClassExported) + DelayedDllExportClasses.push_back(Class); } /// \brief Perform propagation of DLL attributes from a derived class to a @@ -9479,7 +9498,7 @@ } } -void Sema::ActOnFinishCXXMemberDefaultArgs(Decl *D) { +void Sema::ActOnFinishCXXNonNestedClass(Decl *D) { auto *RD = dyn_cast(D); // Default constructors that are annotated with __declspec(dllexport) which @@ -9487,6 +9506,15 @@ // wrapped with a thunk called the default constructor closure. if (RD && Context.getTargetInfo().getCXXABI().isMicrosoft()) getDefaultArgExprsForConstructors(*this, RD); + + if (!DelayedDllExportClasses.empty()) { + // Calling ReferenceDllExportedMethods might cause the current function to + // be called again, so use a local copy of DelayedDllExportClasses. + SmallVector WorkList; + std::swap(DelayedDllExportClasses, WorkList); + for (CXXRecordDecl *Class : WorkList) + ReferenceDllExportedMethods(*this, Class); + } } void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl, Index: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp +++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp @@ -2050,7 +2050,7 @@ // Default arguments are parsed, if not instantiated. We can go instantiate // default arg exprs for default constructors if necessary now. - ActOnFinishCXXMemberDefaultArgs(Instantiation); + ActOnFinishCXXNonNestedClass(Instantiation); // Instantiate late parsed attributes, and attach them to their decls. // See Sema::InstantiateAttrs Index: cfe/trunk/test/CodeGenCXX/dllexport.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/dllexport.cpp +++ cfe/trunk/test/CodeGenCXX/dllexport.cpp @@ -544,6 +544,7 @@ struct __declspec(dllexport) B { B(A = 0) {} }; + } // // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_FB@PR23801@@QAEXXZ"({{.*}}) comdat @@ -611,7 +612,6 @@ B::~B() { } // Emit a alias definition of B's constructor. // M32-DAG: @"\01??1B@UseDtorAlias@@QAE@XZ" = dllexport alias {{.*}} @"\01??1A@UseDtorAlias@@QAE@XZ" - } struct __declspec(dllexport) DefaultedCtorsDtors { @@ -729,6 +729,54 @@ template struct __declspec(dllexport) PR23770DerivedTemplate; // M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?f@?$PR23770BaseTemplate@H@@QAEXXZ" +namespace InClassInits { + +struct __declspec(dllexport) S { + int x = 42; +}; +// M32-DAG: define weak_odr dllexport x86_thiscallcc %"struct.InClassInits::S"* @"\01??0S@InClassInits@@QAE@XZ" + +// dllexport an already instantiated class template. +template struct Base { + int x = 42; +}; +Base base; +struct __declspec(dllexport) T : Base { }; +// M32-DAG: define weak_odr dllexport x86_thiscallcc %"struct.InClassInits::Base"* @"\01??0?$Base@H@InClassInits@@QAE@XZ" + +struct A { A(int); }; +struct __declspec(dllexport) U { + // Class with both default constructor closure and in-class initializer. + U(A = 0) {} + int x = 0; +}; +// M32-DAG: define weak_odr dllexport x86_thiscallcc %"struct.InClassInits::U"* @"\01??0U@InClassInits@@QAE@UA@1@@Z" + +struct Evil { + template struct Base { + int x = 0; + }; + struct S : Base {}; + // The already instantiated Base becomes dllexported below, but the + // in-class initializer for Base<>::x still hasn't been parsed, so emitting + // the default ctor must still be delayed. + struct __declspec(dllexport) T : Base {}; +}; +// M32-DAG: define weak_odr dllexport x86_thiscallcc %"struct.InClassInits::Evil::Base"* @"\01??0?$Base@H@Evil@InClassInits@@QAE@XZ" + +template struct Foo {}; +template struct Bar { + Bar &operator=(Foo) {} +}; +struct __declspec(dllexport) Baz { + Bar n; +}; +// After parsing Baz, in ActOnFinishCXXNonNestedClass we would synthesize +// Baz's operator=, causing instantiation of Foo after which +// ActOnFinishCXXNonNestedClass is called, and we would bite our own tail. +// M32-DAG: define weak_odr dllexport x86_thiscallcc dereferenceable(1) %"struct.InClassInits::Baz"* @"\01??4Baz@InClassInits@@QAEAAU01@ABU01@@Z" +} + //===----------------------------------------------------------------------===// // Classes with template base classes