https://gcc.gnu.org/g:0210bedf481a9fd248ce29650b824bcd84c3723c

commit r15-9136-g0210bedf481a9fd248ce29650b824bcd84c3723c
Author: Nathaniel Shead <nathanielosh...@gmail.com>
Date:   Tue Apr 1 16:36:30 2025 +1100

    c++/modules: Forbid exposures of TU-local entities in inline variables 
[PR119551]
    
    An inline variable has vague linkage, and needs to be conditionally
    emitted in TUs that reference it.  Unfortunately this clashes with
    [basic.link] p14.2, which says that we ignore the initialisers of all
    variables (including inline ones), since importers will not have access
    to the referenced TU-local entities to write the definition.
    
    This patch makes such exposures be ill-formed.  One case that continues
    to work is if the exposure is part of the dynamic initialiser of an
    inline variable; in such cases, the definition has been built as part of
    the module interface unit anyway, and importers don't need to write it
    out again, so such exposures are "harmless".
    
            PR c++/119551
    
    gcc/cp/ChangeLog:
    
            * module.cc (trees_out::write_var_def): Only ignore non-inline
            variable initializers.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/internal-5_a.C: Add cases that should be
            ignored.
            * g++.dg/modules/internal-5_b.C: Test these new cases, and make
            the testcase more robust.
            * g++.dg/modules/internal-11.C: New test.
            * g++.dg/modules/internal-12_a.C: New test.
            * g++.dg/modules/internal-12_b.C: New test.
    
    Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>

Diff:
---
 gcc/cp/module.cc                             |  7 ++++---
 gcc/testsuite/g++.dg/modules/internal-11.C   | 24 ++++++++++++++++++++++++
 gcc/testsuite/g++.dg/modules/internal-12_a.C | 13 +++++++++++++
 gcc/testsuite/g++.dg/modules/internal-12_b.C | 14 ++++++++++++++
 gcc/testsuite/g++.dg/modules/internal-5_a.C  |  8 +++++++-
 gcc/testsuite/g++.dg/modules/internal-5_b.C  |  6 ++++++
 6 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 894c70f7225f..ce22b2ece3f0 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -12679,9 +12679,10 @@ trees_in::read_function_def (tree decl, tree 
maybe_template)
 void
 trees_out::write_var_def (tree decl)
 {
-  /* The initializer of a variable or variable template is ignored for
-     determining exposures.  */
-  auto ovr = make_temp_override (dep_hash->ignore_tu_local, VAR_P (decl));
+  /* The initializer of a non-inline variable or variable template is
+     ignored for determining exposures.  */
+  auto ovr = make_temp_override (dep_hash->ignore_tu_local,
+                                VAR_P (decl) && !DECL_INLINE_VAR_P (decl));
 
   tree init = DECL_INITIAL (decl);
   tree_node (init);
diff --git a/gcc/testsuite/g++.dg/modules/internal-11.C 
b/gcc/testsuite/g++.dg/modules/internal-11.C
new file mode 100644
index 000000000000..53eb30a62be2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/internal-11.C
@@ -0,0 +1,24 @@
+// PR c++/119551
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi !M }
+
+export module M;
+
+static int tu_local = 5;
+static int* foo() { return &tu_local; }
+
+// For implementation reasons, we adjust [basic.link] p14.2 to restrict ignored
+// exposures to non-inline variables, since for inline variables without
+// dynamic initialisation we need to emit their initialiser for importer use.
+
+int* a = &tu_local;  // OK
+inline int* b = &tu_local;  // { dg-error "exposes TU-local entity" }
+
+// But dynamic initialisers are fine, importers will just treat them as 
external.
+inline int* c = foo();  // OK
+
+// For consistency, we follow the same rules with templates, noting that
+// we still need to emit definitions with dynamic initializers so we error.
+template <typename T> int* d = &tu_local;  // OK
+template <typename T> inline int* e = &tu_local;  // { dg-error "exposes 
TU-local entity" }
+template <typename T> inline int* f = foo();  // { dg-error "exposes TU-local 
entity" }
diff --git a/gcc/testsuite/g++.dg/modules/internal-12_a.C 
b/gcc/testsuite/g++.dg/modules/internal-12_a.C
new file mode 100644
index 000000000000..5c4e7c602b0e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/internal-12_a.C
@@ -0,0 +1,13 @@
+// PR c++/119551
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi M }
+// Test that emitting variables referencing TU-local entities
+// builds and runs correctly.
+
+export module M;
+
+static int tu_local_var = 5;
+static int* tu_local_func() { return &tu_local_var; }
+
+export int* a = &tu_local_var;
+export inline int* b = tu_local_func();
diff --git a/gcc/testsuite/g++.dg/modules/internal-12_b.C 
b/gcc/testsuite/g++.dg/modules/internal-12_b.C
new file mode 100644
index 000000000000..bc3edf99a114
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/internal-12_b.C
@@ -0,0 +1,14 @@
+// PR c++/119551
+// { dg-module-do run }
+// { dg-additional-options "-fmodules" }
+
+import M;
+
+int main() {
+  if (*a != 5)
+    __builtin_abort();
+  if (*b != 5)
+    __builtin_abort();
+  if (a != b)
+    __builtin_abort();
+}
diff --git a/gcc/testsuite/g++.dg/modules/internal-5_a.C 
b/gcc/testsuite/g++.dg/modules/internal-5_a.C
index be97ffa471a9..e5113b2e0e1b 100644
--- a/gcc/testsuite/g++.dg/modules/internal-5_a.C
+++ b/gcc/testsuite/g++.dg/modules/internal-5_a.C
@@ -37,7 +37,7 @@ template void function_tmpl<ok_inst_tag>();
 template <> void function_tmpl<ok_inst_tag*>() {}
 
 
-// The initializer for a variable or variable template
+// The initializer for a (non-inline) variable or variable template
 export int var
   = (internal_t{}, internal_tmpl_t<int>{},
      internal_ovl(internal_x), internal_tmpl<int>(), 0);
@@ -53,9 +53,15 @@ template <typename T> int var_tmpl<T*>  // { dg-warning 
"refers to TU-local enti
 template int var_tmpl<ok_inst_tag>;
 template <> int var_tmpl<ok_inst_tag*> = 0;
 
+export int* ptr = &internal_x;
+export template <typename T> int* ptr_tmpl = &internal_x;  // { dg-warning 
"refers to TU-local entity" }
+
 export int& constant_ref = internal_x;
 static_assert (&constant_ref == &internal_x);
 
+// Support exposures in inline vars with dynamic initialisers
+export inline int dynamic_var = (internal_ovl(internal_x), 0);
+
 
 // Friend declarations in a class definition
 export struct klass {  // { dg-bogus "TU-local" }
diff --git a/gcc/testsuite/g++.dg/modules/internal-5_b.C 
b/gcc/testsuite/g++.dg/modules/internal-5_b.C
index baf60fdafa25..f04916ecbbe2 100644
--- a/gcc/testsuite/g++.dg/modules/internal-5_b.C
+++ b/gcc/testsuite/g++.dg/modules/internal-5_b.C
@@ -15,11 +15,14 @@ int main() {
   function_tmpl<ok_inst_tag*>();
   int b = var_tmpl<ok_inst_tag>;
   int c = var_tmpl<ok_inst_tag*>;
+  int d = *ptr;
+  int e = dynamic_var;
 
   // But don't ignore exposures in these cases
   function_tmpl<int>();  // { dg-message "required from here" }
   int x = var_tmpl<int>;  // { dg-message "required from here" }
   int y = var_tmpl<int*>;  // { dg-message "required from here" }
+  int z = *ptr_tmpl<int>;  // { dg-message "required from here" }
 
   // And decls initialized to a TU-local value are not constant here
   // Unfortunately the error does not currently point to this decl
@@ -27,4 +30,7 @@ int main() {
   // { dg-error "is not a constant expression" "" { target *-*-* } 0 }
 }
 
+// The errors occur in a different file, so we just test that all the
+// needed "required from here"s are found above.
 // { dg-error "instantiation exposes TU-local entity" "" { target *-*-* } 0 }
+// { dg-bogus "required from here" "" { target *-*-* } 0 }

Reply via email to