]> granicus.if.org Git - clang/commitdiff
Fix a bug involving deferred decl emission and PCH
authorReid Kleckner <rnk@google.com>
Fri, 22 Apr 2016 18:46:33 +0000 (18:46 +0000)
committerReid Kleckner <rnk@google.com>
Fri, 22 Apr 2016 18:46:33 +0000 (18:46 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267186 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/ModuleBuilder.cpp
test/PCH/Inputs/pr27445.h [new file with mode: 0644]
test/PCH/pr27445.cpp [new file with mode: 0644]

index 5b7201e12d1560813900d6dc3c7519a1ea997996..042712965b0066647e5ad6f2be4a214b2479b302 100644 (file)
@@ -36,13 +36,21 @@ namespace {
     const CodeGenOptions CodeGenOpts;  // Intentionally copied in.
 
     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 {
       CodeGeneratorImpl &Self;
-      HandlingTopLevelDeclRAII(CodeGeneratorImpl &Self) : Self(Self) {
+      bool EmitDeferred;
+      HandlingTopLevelDeclRAII(CodeGeneratorImpl &Self,
+                               bool EmitDeferred = true)
+          : Self(Self), EmitDeferred(EmitDeferred) {
         ++Self.HandlingTopLevelDecls;
       }
       ~HandlingTopLevelDeclRAII() {
-        if (--Self.HandlingTopLevelDecls == 0)
+        unsigned Level = --Self.HandlingTopLevelDecls;
+        if (Level == 0 && EmitDeferred)
           Self.EmitDeferredDecls();
       }
     };
@@ -185,6 +193,10 @@ namespace {
       if (Diags.hasErrorOccurred())
         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);
 
       // For MSVC compatibility, treat declarations of static data members with
@@ -214,6 +226,10 @@ namespace {
       if (Diags.hasErrorOccurred())
         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 (const RecordDecl *RD = dyn_cast<RecordDecl>(D))
           DI->completeRequiredType(RD);
diff --git a/test/PCH/Inputs/pr27445.h b/test/PCH/Inputs/pr27445.h
new file mode 100644 (file)
index 0000000..f78a1bc
--- /dev/null
@@ -0,0 +1,4 @@
+struct Info {
+  virtual ~Info();
+  void hash() {}
+};
diff --git a/test/PCH/pr27445.cpp b/test/PCH/pr27445.cpp
new file mode 100644 (file)
index 0000000..2a4af5e
--- /dev/null
@@ -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 (...)** }