forked from OSchip/llvm-project
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 llvm-svn: 245139
This commit is contained in:
parent
3a4a60cba5
commit
99000c24c9
|
@ -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.
|
||||
|
|
|
@ -3106,7 +3106,7 @@ void Parser::ParseCXXMemberSpecification(SourceLocation RecordLoc,
|
|||
|
||||
// We've finished parsing everything, including default argument
|
||||
// initializers.
|
||||
Actions.ActOnFinishCXXMemberDefaultArgs(TagDecl);
|
||||
Actions.ActOnFinishCXXNonNestedClass(TagDecl);
|
||||
}
|
||||
|
||||
if (TagDecl)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -4682,6 +4682,60 @@ static void CheckAbstractClassUsage(AbstractUsageInfo &Info,
|
|||
}
|
||||
}
|
||||
|
||||
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(CXXRecordDecl *Class) {
|
|||
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 getDefaultArgExprsForConstructors(Sema &S, CXXRecordDecl *Class) {
|
|||
}
|
||||
}
|
||||
|
||||
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::ActOnFinishCXXMemberDefaultArgs(Decl *D) {
|
|||
// 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,
|
||||
|
|
|
@ -2050,7 +2050,7 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation,
|
|||
|
||||
// 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
|
||||
|
|
|
@ -544,6 +544,7 @@ struct A {
|
|||
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 @@ namespace UseDtorAlias {
|
|||
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 @@ extern template struct PR23770DerivedTemplate<int>;
|
|||
template struct __declspec(dllexport) PR23770DerivedTemplate<int>;
|
||||
// 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 <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@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 <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@H@Evil@InClassInits@@QAE@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@InClassInits@@QAEAAU01@ABU01@@Z"
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Classes with template base classes
|
||||
|
|
Loading…
Reference in New Issue