Fix a bug involving deferred decl emission and PCH

For various reasons, involving dllexport and class linkage compuations,
we have to wait until after the semicolon after a class declaration to
emit inline methods. These are "deferred" decls. Before this change,
finishing the tag decl would trigger us to deserialize some PCH so that
we could make a "pretty" IR-level type. Deserializing the PCH triggered
calls to HandleTopLevelDecl, which, when done, checked the deferred decl
list, and emitted some dllexported decls that weren't ready.

Avoid this re-entrancy. Deferred decls should not get emitted when a tag
is finished, they should only be emitted after a real top level decl in
the main file.

llvm-svn: 267186
This commit is contained in:
Reid Kleckner 2016-04-22 18:46:33 +00:00
parent 658d9dbe56
commit ea53dba78b
3 changed files with 36 additions and 2 deletions

View File

@ -36,13 +36,21 @@ namespace {
const CodeGenOptions CodeGenOpts; // Intentionally copied in. const CodeGenOptions CodeGenOpts; // Intentionally copied in.
unsigned HandlingTopLevelDecls; unsigned HandlingTopLevelDecls;
/// Use this when emitting decls to block re-entrant decl emission. It will
/// emit all deferred decls on scope exit. Set EmitDeferred to false if decl
/// emission must be deferred longer, like at the end of a tag definition.
struct HandlingTopLevelDeclRAII { struct HandlingTopLevelDeclRAII {
CodeGeneratorImpl &Self; CodeGeneratorImpl &Self;
HandlingTopLevelDeclRAII(CodeGeneratorImpl &Self) : Self(Self) { bool EmitDeferred;
HandlingTopLevelDeclRAII(CodeGeneratorImpl &Self,
bool EmitDeferred = true)
: Self(Self), EmitDeferred(EmitDeferred) {
++Self.HandlingTopLevelDecls; ++Self.HandlingTopLevelDecls;
} }
~HandlingTopLevelDeclRAII() { ~HandlingTopLevelDeclRAII() {
if (--Self.HandlingTopLevelDecls == 0) unsigned Level = --Self.HandlingTopLevelDecls;
if (Level == 0 && EmitDeferred)
Self.EmitDeferredDecls(); Self.EmitDeferredDecls();
} }
}; };
@ -185,6 +193,10 @@ namespace {
if (Diags.hasErrorOccurred()) if (Diags.hasErrorOccurred())
return; return;
// Don't allow re-entrant calls to CodeGen triggered by PCH
// deserialization to emit deferred decls.
HandlingTopLevelDeclRAII HandlingDecl(*this, /*EmitDeferred=*/false);
Builder->UpdateCompletedType(D); Builder->UpdateCompletedType(D);
// For MSVC compatibility, treat declarations of static data members with // For MSVC compatibility, treat declarations of static data members with
@ -214,6 +226,10 @@ namespace {
if (Diags.hasErrorOccurred()) if (Diags.hasErrorOccurred())
return; return;
// Don't allow re-entrant calls to CodeGen triggered by PCH
// deserialization to emit deferred decls.
HandlingTopLevelDeclRAII HandlingDecl(*this, /*EmitDeferred=*/false);
if (CodeGen::CGDebugInfo *DI = Builder->getModuleDebugInfo()) if (CodeGen::CGDebugInfo *DI = Builder->getModuleDebugInfo())
if (const RecordDecl *RD = dyn_cast<RecordDecl>(D)) if (const RecordDecl *RD = dyn_cast<RecordDecl>(D))
DI->completeRequiredType(RD); DI->completeRequiredType(RD);

View File

@ -0,0 +1,4 @@
struct Info {
virtual ~Info();
void hash() {}
};

View File

@ -0,0 +1,14 @@
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions -x c++ %S/Inputs/pr27445.h -emit-pch -o %t.pch
// RUN: %clang_cc1 -triple x86_64-windows-msvc -fms-extensions %s -include-pch %t.pch -emit-llvm -o - | FileCheck %s
class A;
void fn1(A &) {}
class __declspec(dllexport) A {
int operator=(A) { return field_; }
void (*on_arena_allocation_)(Info);
int field_;
};
// CHECK: %class.A = type { void (%struct.Info*)*, i32 }
// CHECK: %struct.Info = type { i32 (...)** }