On 4/1/25 7:02 AM, Nathaniel Shead wrote:
Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
-- >8 --
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".
OK.
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>
---
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(-)
create mode 100644 gcc/testsuite/g++.dg/modules/internal-11.C
create mode 100644 gcc/testsuite/g++.dg/modules/internal-12_a.C
create mode 100644 gcc/testsuite/g++.dg/modules/internal-12_b.C
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 894c70f7225..ce22b2ece3f 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 00000000000..53eb30a62be
--- /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 00000000000..5c4e7c602b0
--- /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 00000000000..bc3edf99a11
--- /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 be97ffa471a..e5113b2e0e1 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 baf60fdafa2..f04916ecbbe 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 }