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:
Hans Wennborg 2015-08-15 01:18:16 +00:00
parent 3a4a60cba5
commit 99000c24c9
6 changed files with 126 additions and 43 deletions

View File

@ -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.

View File

@ -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)

View File

@ -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),

View File

@ -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,

View File

@ -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

View File

@ -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