]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/modules: Walk indirectly exposed namespaces [PR122699]
authorNathaniel Shead <nathanieloshead@gmail.com>
Sat, 22 Nov 2025 11:30:32 +0000 (22:30 +1100)
committerNathaniel Shead <nathanieloshead@gmail.com>
Tue, 25 Nov 2025 11:40:23 +0000 (22:40 +1100)
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 <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/name-lookup.cc
gcc/cp/name-lookup.h
gcc/cp/pt.cc
gcc/testsuite/g++.dg/modules/tpl-friend-21_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/tpl-friend-21_b.C [new file with mode: 0644]

index abb0d48fb6660c77753b0df9f2882500455d7921..682f2ed49e7c535e85f5ce80938484dbc1050c0b 100644 (file)
@@ -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)
index 3815b8c1c968a8b5ab54b3b750160edb680c1b06..859186a2f2c5bdb34c8f53d46f641482030bcb2c 100644 (file)
@@ -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,
index c418edc69313255a12aa4f13ed45827c5d25d9ca..e74e34d814990615f49f7821c8eb7dbe02501b2d 100644 (file)
@@ -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 (file)
index 0000000..091d0f0
--- /dev/null
@@ -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 (file)
index 0000000..6bdc44a
--- /dev/null
@@ -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;