r245139 - Delay emitting members of dllexport classes until the class is fully parsed (PR23542)
Hans Wennborg via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 14 18:18:16 PDT 2015
Author: hans
Date: Fri Aug 14 20:18:16 2015
New Revision: 245139
URL: http://llvm.org/viewvc/llvm-project?rev=245139&view=rev
Log:
Delay emitting members of dllexport classes until the class is fully parsed (PR23542)
This enables Clang to correctly handle code such as:
struct __declspec(dllexport) S {
int x = 42;
};
where it would otherwise error due to trying to generate the default
constructor before the in-class initializer for x has been parsed.
Differential Revision: http://reviews.llvm.org/D11850
Modified:
cfe/trunk/include/clang/Sema/Sema.h
cfe/trunk/lib/Parse/ParseDeclCXX.cpp
cfe/trunk/lib/Sema/Sema.cpp
cfe/trunk/lib/Sema/SemaDeclCXX.cpp
cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
cfe/trunk/test/CodeGenCXX/dllexport.cpp
Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=245139&r1=245138&r2=245139&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Fri Aug 14 20:18:16 2015
@@ -5222,7 +5222,7 @@ public:
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 @@ public:
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<CXXRecordDecl*, 4> DelayedDllExportClasses;
};
/// \brief RAII object that enters a new expression evaluation context.
Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=245139&r1=245138&r2=245139&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Fri Aug 14 20:18:16 2015
@@ -3106,7 +3106,7 @@ void Parser::ParseCXXMemberSpecification
// We've finished parsing everything, including default argument
// initializers.
- Actions.ActOnFinishCXXMemberDefaultArgs(TagDecl);
+ Actions.ActOnFinishCXXNonNestedClass(TagDecl);
}
if (TagDecl)
Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=245139&r1=245138&r2=245139&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Fri Aug 14 20:18:16 2015
@@ -691,6 +691,9 @@ void Sema::ActOnEndOfTranslationUnit() {
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),
Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=245139&r1=245138&r2=245139&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Fri Aug 14 20:18:16 2015
@@ -4682,6 +4682,60 @@ static void CheckAbstractClassUsage(Abst
}
}
+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<CXXMethodDecl>(Member);
+ if (!MD)
+ continue;
+
+ if (Member->getAttr<DLLExportAttr>()) {
+ 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 @@ void Sema::checkClassLevelDLLAttribute(C
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 @@ static void getDefaultArgExprsForConstru
}
}
-void Sema::ActOnFinishCXXMemberDefaultArgs(Decl *D) {
+void Sema::ActOnFinishCXXNonNestedClass(Decl *D) {
auto *RD = dyn_cast<CXXRecordDecl>(D);
// Default constructors that are annotated with __declspec(dllexport) which
@@ -9487,6 +9506,15 @@ void Sema::ActOnFinishCXXMemberDefaultAr
// 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<CXXRecordDecl *, 4> WorkList;
+ std::swap(DelayedDllExportClasses, WorkList);
+ for (CXXRecordDecl *Class : WorkList)
+ ReferenceDllExportedMethods(*this, Class);
+ }
}
void Sema::AdjustDestructorExceptionSpec(CXXRecordDecl *ClassDecl,
Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=245139&r1=245138&r2=245139&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Fri Aug 14 20:18:16 2015
@@ -2050,7 +2050,7 @@ Sema::InstantiateClass(SourceLocation Po
// 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
Modified: cfe/trunk/test/CodeGenCXX/dllexport.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/dllexport.cpp?rev=245139&r1=245138&r2=245139&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/dllexport.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/dllexport.cpp Fri Aug 14 20:18:16 2015
@@ -544,6 +544,7 @@ struct A {
struct __declspec(dllexport) B {
B(A = 0) {}
};
+
}
//
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01??_FB at PR23801@@QAEXXZ"({{.*}}) comdat
@@ -611,7 +612,6 @@ namespace UseDtorAlias {
B::~B() { }
// Emit a alias definition of B's constructor.
// M32-DAG: @"\01??1B at UseDtorAlias@@QAE at XZ" = dllexport alias {{.*}} @"\01??1A at UseDtorAlias@@QAE at XZ"
-
}
struct __declspec(dllexport) DefaultedCtorsDtors {
@@ -729,6 +729,54 @@ extern template struct PR23770DerivedTem
template struct __declspec(dllexport) PR23770DerivedTemplate<int>;
// M32-DAG: define weak_odr dllexport x86_thiscallcc void @"\01?f@?$PR23770BaseTemplate at H@@QAEXXZ"
+namespace InClassInits {
+
+struct __declspec(dllexport) S {
+ int x = 42;
+};
+// M32-DAG: define weak_odr dllexport x86_thiscallcc %"struct.InClassInits::S"* @"\01??0S at InClassInits@@QAE at XZ"
+
+// dllexport an already instantiated class template.
+template <typename T> struct Base {
+ int x = 42;
+};
+Base<int> base;
+struct __declspec(dllexport) T : Base<int> { };
+// M32-DAG: define weak_odr dllexport x86_thiscallcc %"struct.InClassInits::Base"* @"\01??0?$Base at H@InClassInits@@QAE at 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 at InClassInits@@QAE at UA@1@@Z"
+
+struct Evil {
+ template <typename T> struct Base {
+ int x = 0;
+ };
+ struct S : Base<int> {};
+ // The already instantiated Base<int> 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<int> {};
+};
+// M32-DAG: define weak_odr dllexport x86_thiscallcc %"struct.InClassInits::Evil::Base"* @"\01??0?$Base at H@Evil at InClassInits@@QAE at XZ"
+
+template <typename T> struct Foo {};
+template <typename T> struct Bar {
+ Bar<T> &operator=(Foo<T>) {}
+};
+struct __declspec(dllexport) Baz {
+ Bar<int> n;
+};
+// After parsing Baz, in ActOnFinishCXXNonNestedClass we would synthesize
+// Baz's operator=, causing instantiation of Foo<int> 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 at InClassInits@@QAEAAU01 at ABU01@@Z"
+}
+
//===----------------------------------------------------------------------===//
// Classes with template base classes
More information about the cfe-commits
mailing list