diff mbox series

c++/modules: Prevent emission of really-extern vtables in importers [PR114229]

Message ID 65e7ddc4.170a0220.d647a.c749@mx.google.com
State New
Headers show
Series c++/modules: Prevent emission of really-extern vtables in importers [PR114229] | expand

Commit Message

Nathaniel Shead March 6, 2024, 3:06 a.m. UTC
Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?

-- >8 --

Currently, reading a variable definition always marks that decl as
DECL_NOT_REALLY_EXTERN, with anything else imported still being
considered external. This is not sufficient for vtables, however; for an
extern template, a vtable may be generated (and its definition emitted)
but nonetheless the vtable should only be emitted in the TU where that
template is actually instantiated.

While playing around with various settings of DECL_EXTERNAL I also
noticed that we mark variables not declared 'inline' as external, which
makes sense (there's a definition in another TU, while for vague linkage
variables we'll be importing the definition too), but we also do so for
'static constexpr' members that aren't directly marked 'inline'. This
fixes that for consistency, if nothing else (I wasn't able to cause an
actual issue exploiting this currently), but may become relevant with
https://github.com/itanium-cxx-abi/cxx-abi/issues/170.

	PR c++/114229

gcc/cp/ChangeLog:

	* module.cc (trees_out::core_bools): Count 'static constexpr'
	vars also as inline.
	(trees_out::write_var_def): Stream DECL_NOT_REALLY_EXTERN for
	vtables.
	(trees_in::read_var_def): Read it.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/virt-2_c.C:
	* g++.dg/modules/virt-3_a.C: New test.
	* g++.dg/modules/virt-3_b.C: New test.
	* g++.dg/modules/virt-3_c.C: New test.
	* g++.dg/modules/virt-3_d.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
---
 gcc/cp/module.cc                        | 15 +++++++++++++--
 gcc/testsuite/g++.dg/modules/virt-2_c.C | 14 +++++++++-----
 gcc/testsuite/g++.dg/modules/virt-3_a.C |  9 +++++++++
 gcc/testsuite/g++.dg/modules/virt-3_b.C |  6 ++++++
 gcc/testsuite/g++.dg/modules/virt-3_c.C |  3 +++
 gcc/testsuite/g++.dg/modules/virt-3_d.C |  7 +++++++
 6 files changed, 47 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/virt-3_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/virt-3_b.C
 create mode 100644 gcc/testsuite/g++.dg/modules/virt-3_c.C
 create mode 100644 gcc/testsuite/g++.dg/modules/virt-3_d.C

Comments

Jason Merrill March 6, 2024, 1:59 p.m. UTC | #1
On 3/5/24 22:06, Nathaniel Shead wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> 
> -- >8 --
> 
> Currently, reading a variable definition always marks that decl as
> DECL_NOT_REALLY_EXTERN, with anything else imported still being
> considered external. This is not sufficient for vtables, however; for an
> extern template, a vtable may be generated (and its definition emitted)
> but nonetheless the vtable should only be emitted in the TU where that
> template is actually instantiated.

Does the vtable go through import_export_decl?  I've been thinking that 
that function (and import_export_class) need to be more module-aware. 
Would it make sense to do that rather than stream DECL_NOT_REALLY_EXTERN?

Jason
diff mbox series

Patch

diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 67f132d28d7..771e56245dc 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -5418,7 +5418,7 @@  trees_out::core_bools (tree t)
 		  && !(TREE_STATIC (t)
 		       && DECL_FUNCTION_SCOPE_P (t)
 		       && DECL_DECLARED_INLINE_P (DECL_CONTEXT (t)))
-		  && !DECL_VAR_DECLARED_INLINE_P (t))
+		  && !DECL_INLINE_VAR_P (t))
 		is_external = true;
 	      break;
 
@@ -11799,6 +11799,12 @@  trees_out::write_var_def (tree decl)
 	}
       tree_node (dyn_init);
     }
+
+  /* For vtables we need to know if they're actually extern or not,
+     even if we get a definition; for other kinds of variables we
+     can assume that if we have a definition they can be emitted.  */
+  if (streaming_p () && VAR_P (decl) && DECL_VTABLE_OR_VTT_P (decl))
+    u (DECL_NOT_REALLY_EXTERN (decl));
 }
 
 void
@@ -11816,6 +11822,11 @@  trees_in::read_var_def (tree decl, tree maybe_template)
   tree dyn_init = init ? NULL_TREE : tree_node ();
   unused -= vtable;
 
+  /* Assume for most vars that if we have a definition it's not extern.  */
+  bool not_really_extern = true;
+  if (vtable)
+    not_really_extern = u ();
+
   if (get_overrun ())
     return false;
 
@@ -11826,7 +11837,7 @@  trees_in::read_var_def (tree decl, tree maybe_template)
   if (installing)
     {
       if (DECL_EXTERNAL (decl))
-	DECL_NOT_REALLY_EXTERN (decl) = true;
+	DECL_NOT_REALLY_EXTERN (decl) = not_really_extern;
       if (VAR_P (decl))
 	{
 	  DECL_INITIALIZED_P (decl) = true;
diff --git a/gcc/testsuite/g++.dg/modules/virt-2_c.C b/gcc/testsuite/g++.dg/modules/virt-2_c.C
index 7b3eeebe508..8969cb04911 100644
--- a/gcc/testsuite/g++.dg/modules/virt-2_c.C
+++ b/gcc/testsuite/g++.dg/modules/virt-2_c.C
@@ -9,8 +9,12 @@  int Foo ()
   return !(Visit (&v) == 0);
 }
 
-// We do emit Visitor vtable
-// andl also we do emit rtti here
-// { dg-final { scan-assembler {_ZTVW3foo7Visitor:} } }
-// { dg-final { scan-assembler {_ZTIW3foo7Visitor:} } }
-// { dg-final { scan-assembler {_ZTSW3foo7Visitor:} } }
+// Again, we do not emit Visitor vtable
+// but we do emit rtti here
+
+// but see https://github.com/itanium-cxx-abi/cxx-abi/issues/170:
+// we should only emit RTTI in virt-2_a.C, alongside the vtable
+
+// { dg-final { scan-assembler-not {_ZTVW3foo7Visitor:} } }
+// { dg-final { scan-assembler-not {_ZTIW3foo7Visitor:} { xfail *-*-* } } }
+// { dg-final { scan-assembler-not {_ZTSW3foo7Visitor:} { xfail *-*-* } } }
diff --git a/gcc/testsuite/g++.dg/modules/virt-3_a.C b/gcc/testsuite/g++.dg/modules/virt-3_a.C
new file mode 100644
index 00000000000..a7eae7f9d35
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/virt-3_a.C
@@ -0,0 +1,9 @@ 
+// PR c++/114229
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi modA }
+
+module;
+template<class> struct basic_streambuf { virtual void overflow() { } };
+extern template struct basic_streambuf<long>;
+export module modA;
+export basic_streambuf<long> *p;
diff --git a/gcc/testsuite/g++.dg/modules/virt-3_b.C b/gcc/testsuite/g++.dg/modules/virt-3_b.C
new file mode 100644
index 00000000000..4d87b965bbf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/virt-3_b.C
@@ -0,0 +1,6 @@ 
+// PR c++/114229
+// { dg-additional-options "-fmodules-ts -fno-module-lazy" }
+// { dg-module-cmi modB }
+
+export module modB;
+import modA;
diff --git a/gcc/testsuite/g++.dg/modules/virt-3_c.C b/gcc/testsuite/g++.dg/modules/virt-3_c.C
new file mode 100644
index 00000000000..224bb4aed49
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/virt-3_c.C
@@ -0,0 +1,3 @@ 
+// PR c++/114229
+template<class> struct basic_streambuf { virtual void overflow() { } };
+template struct basic_streambuf<long>;
diff --git a/gcc/testsuite/g++.dg/modules/virt-3_d.C b/gcc/testsuite/g++.dg/modules/virt-3_d.C
new file mode 100644
index 00000000000..b3a0aad7abe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/virt-3_d.C
@@ -0,0 +1,7 @@ 
+// PR c++/114229
+// { dg-module-do link }
+// { dg-additional-options "-fmodules-ts -fno-module-lazy" }
+
+import modA;
+import modB;
+int main() { }