https://gcc.gnu.org/g:e0ac5e1c8a69fec114fc5896cf90b4c0b05240c5
commit r16-5584-ge0ac5e1c8a69fec114fc5896cf90b4c0b05240c5 Author: Nathaniel Shead <[email protected]> Date: Sat Nov 22 22:30:32 2025 +1100 c++/modules: Walk indirectly exposed namespaces [PR122699] In some situations, such as friend injection, we may add an entity to a namespace without ever explicitly opening that namespace in this TU. We currently have an additional loop to make sure the namespace is considered purview, but this isn't sufficient to make walk_module_binding find it, since the namspace itself is not in the current TU's symbol table. This patch ensures we still process the (hidden) binding for the injected friend in this TU. PR c++/122699 gcc/cp/ChangeLog: * name-lookup.h (expose_existing_namespace): Declare. * name-lookup.cc (expose_existing_namespace): New function. (push_namespace): Call it. * pt.cc (tsubst_friend_function): Likewise. gcc/testsuite/ChangeLog: * g++.dg/modules/tpl-friend-21_a.C: New test. * g++.dg/modules/tpl-friend-21_b.C: New test. Signed-off-by: Nathaniel Shead <[email protected]> Reviewed-by: Jason Merrill <[email protected]> Diff: --- gcc/cp/name-lookup.cc | 44 +++++++++++++++----------- gcc/cp/name-lookup.h | 1 + gcc/cp/pt.cc | 11 +++---- gcc/testsuite/g++.dg/modules/tpl-friend-21_a.C | 11 +++++++ gcc/testsuite/g++.dg/modules/tpl-friend-21_b.C | 7 ++++ 5 files changed, 49 insertions(+), 25 deletions(-) diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index abb0d48fb666..682f2ed49e7c 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -9259,6 +9259,28 @@ make_namespace_finish (tree ns, tree *slot, bool from_import = false) add_using_namespace (NAMESPACE_LEVEL (ctx)->using_directives, ns); } +/* NS is a possibly-imported namespace that is now needed for + a declaration. Add it to the current TU's binding slot. */ + +void +expose_existing_namespace (tree ns) +{ + if (!modules_p ()) + return; + + tree bind = *find_namespace_slot (CP_DECL_CONTEXT (ns), DECL_NAME (ns)); + if (bind != ns) + { + auto &cluster = BINDING_VECTOR_CLUSTER (bind, 0); + binding_slot &slot = cluster.slots[BINDING_SLOT_CURRENT]; + gcc_checking_assert (!(tree)slot || (tree)slot == ns); + slot = ns; + } + + if (module_purview_p ()) + DECL_MODULE_PURVIEW_P (ns) = true; +} + /* Push into the scope of the NAME namespace. If NAME is NULL_TREE, then we enter an anonymous namespace. If MAKE_INLINE is true, then we create an inline namespace (it is up to the caller to check upon @@ -9340,25 +9362,9 @@ push_namespace (tree name, bool make_inline) /* DR2061. NS might be a member of an inline namespace. We need to push into those namespaces. */ if (modules_p ()) - { - for (tree parent, ctx = ns; ctx != current_namespace; - ctx = parent) - { - parent = CP_DECL_CONTEXT (ctx); - - tree bind = *find_namespace_slot (parent, DECL_NAME (ctx), false); - if (bind != ctx) - { - auto &cluster = BINDING_VECTOR_CLUSTER (bind, 0); - binding_slot &slot = cluster.slots[BINDING_SLOT_CURRENT]; - gcc_checking_assert (!(tree)slot || (tree)slot == ctx); - slot = ctx; - } - - if (module_purview_p ()) - DECL_MODULE_PURVIEW_P (ctx) = true; - } - } + for (tree ctx = ns; ctx != current_namespace; + ctx = CP_DECL_CONTEXT (ctx)) + expose_existing_namespace (ctx); count += push_inline_namespaces (CP_DECL_CONTEXT (ns)); if (DECL_SOURCE_LOCATION (ns) == BUILTINS_LOCATION) diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 3815b8c1c968..859186a2f2c5 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -494,6 +494,7 @@ extern void pop_from_top_level (void); extern bool maybe_push_to_top_level (tree); extern void maybe_pop_from_top_level (bool); extern void push_using_decl_bindings (tree, tree); +extern void expose_existing_namespace (tree); /* Lower level interface for modules. */ extern tree *mergeable_namespace_slots (tree ns, tree name, bool is_attached, diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index c418edc69313..e74e34d81499 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -11899,14 +11899,13 @@ tsubst_friend_function (tree decl, tree args) new_friend = old_decl; } - /* We've just introduced a namespace-scope function in the purview - without necessarily having opened the enclosing namespace, so - make sure the namespace is in the purview now too. */ - if (modules_p () - && DECL_MODULE_PURVIEW_P (STRIP_TEMPLATE (new_friend))) + /* We've just introduced a namespace-scope function without + necessarily having opened the enclosing namespace, so + make sure the namespace is declared in this TU as well. */ + if (modules_p ()) for (tree ctx = DECL_CONTEXT (new_friend); TREE_CODE (ctx) == NAMESPACE_DECL; ctx = DECL_CONTEXT (ctx)) - DECL_MODULE_PURVIEW_P (ctx) = true; + expose_existing_namespace (ctx); } else { diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-21_a.C b/gcc/testsuite/g++.dg/modules/tpl-friend-21_a.C new file mode 100644 index 000000000000..091d0f02c363 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-21_a.C @@ -0,0 +1,11 @@ +// PR c++/122699 +// { dg-additional-options "-fmodules" } +// { dg-module-cmi M } + +export module M; +export namespace ns::inner { + template <typename T> struct S { + inline friend void f(); + }; + void f(); +} diff --git a/gcc/testsuite/g++.dg/modules/tpl-friend-21_b.C b/gcc/testsuite/g++.dg/modules/tpl-friend-21_b.C new file mode 100644 index 000000000000..6bdc44ad03d8 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/tpl-friend-21_b.C @@ -0,0 +1,7 @@ +// PR c++/122699 +// { dg-additional-options "-fmodules" } +// { dg-module-cmi X } + +export module X; +import M; +ns::inner::S<int> s;
