[modules] When emitting an update record containing the body of a destructor,

also emit the updated 'operator delete' looked up for that destructor. Switch
from UpdateDecl to an actual update record when this happens due to implicitly
defining a special member function and unify this code path and the one for
instantiating a function definition.

llvm-svn: 215132
This commit is contained in:
Richard Smith 2014-08-07 18:53:08 +00:00
parent 54c340b76a
commit 4d2357948b
6 changed files with 38 additions and 14 deletions

View File

@ -25,8 +25,8 @@ enum DeclUpdateKind {
UPD_CXX_ADDED_IMPLICIT_MEMBER,
UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION,
UPD_CXX_ADDED_ANONYMOUS_NAMESPACE,
UPD_CXX_ADDED_FUNCTION_DEFINITION,
UPD_CXX_INSTANTIATED_STATIC_DATA_MEMBER,
UPD_CXX_INSTANTIATED_FUNCTION_DEFINITION,
UPD_CXX_INSTANTIATED_CLASS_DEFINITION,
UPD_CXX_RESOLVED_EXCEPTION_SPEC,
UPD_CXX_DEDUCED_RETURN_TYPE,

View File

@ -3273,7 +3273,7 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
Reader.ReadSourceLocation(ModuleFile, Record, Idx));
break;
case UPD_CXX_INSTANTIATED_FUNCTION_DEFINITION: {
case UPD_CXX_ADDED_FUNCTION_DEFINITION: {
FunctionDecl *FD = cast<FunctionDecl>(D);
if (Reader.PendingBodies[FD]) {
// FIXME: Maybe check for ODR violations.
@ -3296,6 +3296,10 @@ void ASTDeclReader::UpdateDecl(Decl *D, ModuleFile &ModuleFile,
if (auto *CD = dyn_cast<CXXConstructorDecl>(FD))
std::tie(CD->CtorInitializers, CD->NumCtorInitializers) =
Reader.ReadCXXCtorInitializers(ModuleFile, Record, Idx);
if (auto *DD = dyn_cast<CXXDestructorDecl>(FD))
// FIXME: Check consistency.
DD->setOperatorDelete(Reader.ReadDeclAs<FunctionDecl>(ModuleFile,
Record, Idx));
// Store the offset of the body so we can lazily load it later.
Reader.PendingBodies[FD] = GetCurrentCursorOffset();
HasPendingBody = true;

View File

@ -4629,17 +4629,17 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
Record.push_back(GetDeclRef(Update.getDecl()));
break;
case UPD_CXX_INSTANTIATED_STATIC_DATA_MEMBER:
AddSourceLocation(Update.getLoc(), Record);
break;
case UPD_CXX_INSTANTIATED_FUNCTION_DEFINITION:
case UPD_CXX_ADDED_FUNCTION_DEFINITION:
// An updated body is emitted last, so that the reader doesn't need
// to skip over the lazy body to reach statements for other records.
Record.pop_back();
HasUpdatedBody = true;
break;
case UPD_CXX_INSTANTIATED_STATIC_DATA_MEMBER:
AddSourceLocation(Update.getLoc(), Record);
break;
case UPD_CXX_INSTANTIATED_CLASS_DEFINITION: {
auto *RD = cast<CXXRecordDecl>(D);
AddUpdatedDeclContext(RD->getPrimaryContext());
@ -4709,10 +4709,12 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) {
if (HasUpdatedBody) {
const FunctionDecl *Def = cast<FunctionDecl>(D);
Record.push_back(UPD_CXX_INSTANTIATED_FUNCTION_DEFINITION);
Record.push_back(UPD_CXX_ADDED_FUNCTION_DEFINITION);
Record.push_back(Def->isInlined());
AddSourceLocation(Def->getInnerLocStart(), Record);
AddFunctionDefinition(Def, Record);
if (auto *DD = dyn_cast<CXXDestructorDecl>(Def))
Record.push_back(GetDeclRef(DD->getOperatorDelete()));
}
OffsetsRecord.push_back(GetDeclRef(D));
@ -5704,9 +5706,8 @@ void ASTWriter::CompletedImplicitDefinition(const FunctionDecl *D) {
if (!D->isFromASTFile())
return; // Declaration not imported from PCH.
// Implicit decl from a PCH was defined.
// FIXME: Should implicit definition be a separate FunctionDecl?
RewriteDecl(D);
// Implicit function decl from a PCH was defined.
DeclUpdates[D].push_back(DeclUpdate(UPD_CXX_ADDED_FUNCTION_DEFINITION));
}
void ASTWriter::FunctionDefinitionInstantiated(const FunctionDecl *D) {
@ -5714,10 +5715,8 @@ void ASTWriter::FunctionDefinitionInstantiated(const FunctionDecl *D) {
if (!D->isFromASTFile())
return;
// Since the actual instantiation is delayed, this really means that we need
// to update the instantiation location.
DeclUpdates[D].push_back(
DeclUpdate(UPD_CXX_INSTANTIATED_FUNCTION_DEFINITION));
DeclUpdate(UPD_CXX_ADDED_FUNCTION_DEFINITION));
}
void ASTWriter::StaticDataMemberInstantiated(const VarDecl *D) {

View File

@ -18,3 +18,9 @@ namespace ImplicitSpecialMembers {
C c2(c); D d2(d);
}
}
namespace OperatorDeleteLookup {
// Trigger definition of A::~A() and lookup of operator delete.
// Likewise for B<int>::~B().
inline void f() { A a; B<int> b; }
}

View File

@ -32,3 +32,9 @@ namespace ImplicitSpecialMembers {
D(int);
};
}
namespace OperatorDeleteLookup {
struct A { void operator delete(void*); virtual ~A() = default; };
template<typename T> struct B { void operator delete(void*); virtual ~B() {} typedef int t; };
typedef B<int>::t b_int_instantated;
}

View File

@ -18,6 +18,9 @@ int b = h();
// CHECK-DAG: define linkonce_odr {{signext i32|i32}} @_Z3minIiET_S0_S0_(i32
int c = min(1, 2);
// CHECK-LABEL: define {{.*}} @_ZN20OperatorDeleteLookup1AD0Ev(
// CHECK: call void @_ZN20OperatorDeleteLookup1AdlEPv(
namespace ImplicitSpecialMembers {
// CHECK-LABEL: define {{.*}} @_ZN22ImplicitSpecialMembers1DC2EOS0_(
// CHECK: call {{.*}} @_ZN22ImplicitSpecialMembers1AC1ERKS0_(
@ -45,6 +48,12 @@ namespace ImplicitSpecialMembers {
D d3(static_cast<D&&>(d1));
}
namespace OperatorDeleteLookup {
// Trigger emission of B's vtable and deleting dtor.
// This requires us to know what operator delete was selected.
void g() { f(); }
}
// CHECK: define available_externally {{signext i32|i32}} @_ZN1SIiE1fEv({{.*}} #[[ALWAYS_INLINE]] align
// CHECK: attributes #[[ALWAYS_INLINE]] = {{.*}} alwaysinline