mstorsjo created this revision.
mstorsjo added reviewers: rnk, hans, smeenai.
Herald added a project: clang.

An explicit template instantiation declaration used to let callers assume both 
outer and nested classes instantiations were defined in a different translation 
unit.

If the instantiation is marked dllexport, only the outer class is exported, but 
the caller will try to reference the instantiation of both outer and inner 
classes.

This makes MinGW mode match both MSVC and Windows Itanium, by having 
instantations only cover the outer class, and locally emitting definitions of 
the nested classes. Windows Itanium was changed to use this behavious in SVN 
r300804.

This deviates from what GCC does, but should be safe (and only inflate the 
object file size a bit, but MSVC and Windows Itanium modes do the same), and 
fixes cases where inner classes aren't dllexported.

This fixes missing references in combination with dllexported/imported template 
intantiations.

GCC suffers from the same issue, reported at [1], but the issue is still 
unresolved there. The issue can probably be solved either by making dllexport 
cover all nested classes as well, or this way (matching MSVC).

[1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89087


Repository:
  rC Clang

https://reviews.llvm.org/D61175

Files:
  lib/Sema/SemaTemplateInstantiate.cpp
  test/CodeGenCXX/mingw-template-dllexport.cpp


Index: test/CodeGenCXX/mingw-template-dllexport.cpp
===================================================================
--- test/CodeGenCXX/mingw-template-dllexport.cpp
+++ test/CodeGenCXX/mingw-template-dllexport.cpp
@@ -1,5 +1,10 @@
 // RUN: %clang_cc1 -emit-llvm -triple i686-mingw32 %s -o - | FileCheck %s
 
+#define JOIN2(x, y) x##y
+#define JOIN(x, y) JOIN2(x, y)
+#define UNIQ(name) JOIN(name, __LINE__)
+#define USEMEMFUNC(class, func) void (class::*UNIQ(use)())() { return 
&class::func; }
+
 template <class T>
 class c {
   void f();
@@ -36,3 +41,10 @@
 
 // CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1fEv
 // CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1fEv
+
+extern template class __declspec(dllimport) outer<char>;
+USEMEMFUNC(outer<char>, f)
+USEMEMFUNC(outer<char>::inner, f)
+
+// CHECK: declare dllimport {{.*}} @_ZN5outerIcE1fEv
+// CHECK: define {{.*}} @_ZN5outerIcE5inner1fEv
Index: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp
+++ lib/Sema/SemaTemplateInstantiate.cpp
@@ -2681,10 +2681,14 @@
         continue;
 
       if ((Context.getTargetInfo().getCXXABI().isMicrosoft() ||
-           Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment()) 
&&
+           Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment() ||
+           Context.getTargetInfo().getTriple().isWindowsGNUEnvironment()) &&
           TSK == TSK_ExplicitInstantiationDeclaration) {
         // In MSVC and Windows Itanium mode, explicit instantiation decl of the
         // outer class doesn't affect the inner class.
+        // In GNU mode, inner classes aren't dllexported. Don't let the
+        // instantiation cover the inner class, to avoid undefined references
+        // to inner classes that weren't exported.
         continue;
       }
 


Index: test/CodeGenCXX/mingw-template-dllexport.cpp
===================================================================
--- test/CodeGenCXX/mingw-template-dllexport.cpp
+++ test/CodeGenCXX/mingw-template-dllexport.cpp
@@ -1,5 +1,10 @@
 // RUN: %clang_cc1 -emit-llvm -triple i686-mingw32 %s -o - | FileCheck %s
 
+#define JOIN2(x, y) x##y
+#define JOIN(x, y) JOIN2(x, y)
+#define UNIQ(name) JOIN(name, __LINE__)
+#define USEMEMFUNC(class, func) void (class::*UNIQ(use)())() { return &class::func; }
+
 template <class T>
 class c {
   void f();
@@ -36,3 +41,10 @@
 
 // CHECK: define {{.*}} dllexport {{.*}} @_ZN5outerIiE1fEv
 // CHECK-NOT: define {{.*}} dllexport {{.*}} @_ZN5outerIiE5inner1fEv
+
+extern template class __declspec(dllimport) outer<char>;
+USEMEMFUNC(outer<char>, f)
+USEMEMFUNC(outer<char>::inner, f)
+
+// CHECK: declare dllimport {{.*}} @_ZN5outerIcE1fEv
+// CHECK: define {{.*}} @_ZN5outerIcE5inner1fEv
Index: lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- lib/Sema/SemaTemplateInstantiate.cpp
+++ lib/Sema/SemaTemplateInstantiate.cpp
@@ -2681,10 +2681,14 @@
         continue;
 
       if ((Context.getTargetInfo().getCXXABI().isMicrosoft() ||
-           Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment()) &&
+           Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment() ||
+           Context.getTargetInfo().getTriple().isWindowsGNUEnvironment()) &&
           TSK == TSK_ExplicitInstantiationDeclaration) {
         // In MSVC and Windows Itanium mode, explicit instantiation decl of the
         // outer class doesn't affect the inner class.
+        // In GNU mode, inner classes aren't dllexported. Don't let the
+        // instantiation cover the inner class, to avoid undefined references
+        // to inner classes that weren't exported.
         continue;
       }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to