diff options
author | Nathaniel Shead <nathanieloshead@gmail.com> | 2025-04-01 16:36:30 +1100 |
---|---|---|
committer | Nathaniel Shead <nathanieloshead@gmail.com> | 2025-04-02 07:20:36 +1100 |
commit | 0210bedf481a9fd248ce29650b824bcd84c3723c (patch) | |
tree | 75993b058b0de1185be3854fc993a043f1bc8252 /gcc | |
parent | 9aa78d771222cd9aec463ee28f9039d306ab5834 (diff) | |
download | gcc-0210bedf481a9fd248ce29650b824bcd84c3723c.zip gcc-0210bedf481a9fd248ce29650b824bcd84c3723c.tar.gz gcc-0210bedf481a9fd248ce29650b824bcd84c3723c.tar.bz2 |
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 <nathanieloshead@gmail.com>
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/module.cc | 7 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/modules/internal-11.C | 24 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/modules/internal-12_a.C | 13 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/modules/internal-12_b.C | 14 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/modules/internal-5_a.C | 8 | ||||
-rw-r--r-- | 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 894c70f..ce22b2e 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 0000000..53eb30a --- /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 0000000..5c4e7c6 --- /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 0000000..bc3edf9 --- /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 be97ffa..e5113b2 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 baf60fd..f04916e 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 } |