Possibly another approach would be to never create 'hidden' bindings for
injected friends at all, but I wasn't able to find a good way to do
that.  In particular, we can't trust DECL_UNIQUE_FRIEND_P for whether a
decl is only seen as hidden in the current TU, since it might have
merged with a namespace-scope decl from an import (as in this testcase)
which will clear the flag.

Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?

-- >8 --

During 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]>
---
 gcc/cp/name-lookup.cc                         | 44 +++++++++++--------
 gcc/cp/name-lookup.h                          |  1 +
 gcc/cp/pt.cc                                  | 11 +++--
 .../g++.dg/modules/tpl-friend-21_a.C          | 11 +++++
 .../g++.dg/modules/tpl-friend-21_b.C          |  7 +++
 5 files changed, 49 insertions(+), 25 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-21_a.C
 create mode 100644 gcc/testsuite/g++.dg/modules/tpl-friend-21_b.C

diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index abb0d48fb66..682f2ed49e7 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 3815b8c1c96..859186a2f2c 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 3b581b3fd36..2aba3187fd4 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 00000000000..091d0f02c36
--- /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 00000000000..6bdc44ad03d
--- /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;
-- 
2.51.0

Reply via email to