aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2025-03-29 08:56:09 -0400
committerJason Merrill <jason@redhat.com>2025-03-29 11:18:06 -0400
commiteb26b667518c951d06f3c51118a1d41dcdda8b99 (patch)
tree4f4b7cd2200ec46a80fb45d9382aeacb3ab9c2cf /gcc
parent9018336252463ffed28f01badfdea2a3ca3ba5c8 (diff)
downloadgcc-eb26b667518c951d06f3c51118a1d41dcdda8b99.zip
gcc-eb26b667518c951d06f3c51118a1d41dcdda8b99.tar.gz
gcc-eb26b667518c951d06f3c51118a1d41dcdda8b99.tar.bz2
c++/modules: unexported friend template
Here we were failing to match the injected friend declaration to the definition because the latter isn't exported. But the friend is attached to the module, so we need to look for any reachable declaration in that module, not just the exports. The duplicate_decls change is to avoid clobbering DECL_MODULE_IMPORT_P on the imported definition; matching an injected friend doesn't change that it's imported. I considered checking get_originating_module == 0 or !decl_defined_p instead of DECL_UNIQUE_FRIEND_P there, but I think this situation is specific to friends. I removed an assert because we have a test for the same condition a few lines above. gcc/cp/ChangeLog: * decl.cc (duplicate_decls): Don't clobber DECL_MODULE_IMPORT_P with an injected friend. * name-lookup.cc (check_module_override): Look at all reachable decls in decl's originating module. gcc/testsuite/ChangeLog: * g++.dg/modules/friend-9_a.C: New test. * g++.dg/modules/friend-9_b.C: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/decl.cc6
-rw-r--r--gcc/cp/name-lookup.cc19
-rw-r--r--gcc/testsuite/g++.dg/modules/friend-9_a.C13
-rw-r--r--gcc/testsuite/g++.dg/modules/friend-9_b.C13
4 files changed, 40 insertions, 11 deletions
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index a785d5e..7d10b22 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -2539,8 +2539,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
}
/* Propagate purviewness and importingness as with
- set_instantiating_module. */
- if (modules_p () && DECL_LANG_SPECIFIC (new_result))
+ set_instantiating_module, unless newdecl is a friend injection. */
+ if (modules_p () && DECL_LANG_SPECIFIC (new_result)
+ && !(TREE_CODE (new_result) == FUNCTION_DECL
+ && DECL_UNIQUE_FRIEND_P (new_result)))
{
if (DECL_MODULE_PURVIEW_P (new_result))
DECL_MODULE_PURVIEW_P (old_result) = true;
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index df033ed..7fadbcc 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -3777,6 +3777,10 @@ check_module_override (tree decl, tree mvec, bool hiding,
any reachable declaration, so we should check for overriding here too. */
bool any_reachable = deduction_guide_p (decl);
+ /* DECL might have an originating module if it's an instantiation of a
+ friend; we want to look at all reachable decls in that module. */
+ unsigned decl_mod = get_originating_module (decl);
+
if (BINDING_VECTOR_SLOTS_PER_CLUSTER == BINDING_SLOTS_FIXED)
{
cluster++;
@@ -3789,18 +3793,15 @@ check_module_override (tree decl, tree mvec, bool hiding,
/* Are we importing this module? */
if (cluster->indices[jx].span != 1)
continue;
- if (!cluster->indices[jx].base)
+ unsigned cluster_mod = cluster->indices[jx].base;
+ if (!cluster_mod)
continue;
- if (!any_reachable
- && !bitmap_bit_p (imports, cluster->indices[jx].base))
+ bool c_any_reachable = (any_reachable || cluster_mod == decl_mod);
+ if (!c_any_reachable && !bitmap_bit_p (imports, cluster_mod))
continue;
/* Is it loaded? */
if (cluster->slots[jx].is_lazy ())
- {
- gcc_assert (cluster->indices[jx].span == 1);
- lazy_load_binding (cluster->indices[jx].base,
- scope, name, &cluster->slots[jx]);
- }
+ lazy_load_binding (cluster_mod, scope, name, &cluster->slots[jx]);
tree bind = cluster->slots[jx];
if (!bind)
/* Errors could cause there to be nothing. */
@@ -3812,7 +3813,7 @@ check_module_override (tree decl, tree mvec, bool hiding,
/* If there was a matching STAT_TYPE here then xref_tag
should have found it, but we need to check anyway because
a conflicting using-declaration may exist. */
- if (any_reachable)
+ if (c_any_reachable)
{
type = STAT_TYPE (bind);
bind = STAT_DECL (bind);
diff --git a/gcc/testsuite/g++.dg/modules/friend-9_a.C b/gcc/testsuite/g++.dg/modules/friend-9_a.C
new file mode 100644
index 0000000..ca95027
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/friend-9_a.C
@@ -0,0 +1,13 @@
+// { dg-additional-options -fmodules }
+// { dg-module-cmi M }
+// { dg-module-do link }
+
+export module M;
+
+export template <class T> struct A
+{
+ template <class U> friend void f (U);
+};
+
+template <class U>
+void f(U u) { }
diff --git a/gcc/testsuite/g++.dg/modules/friend-9_b.C b/gcc/testsuite/g++.dg/modules/friend-9_b.C
new file mode 100644
index 0000000..9f58379
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/friend-9_b.C
@@ -0,0 +1,13 @@
+// { dg-additional-options -fmodules }
+
+// Check that f and A are mangled as attached to M.
+// void f@M<A@M<main::loc> >(A@M<main::loc>)
+// { dg-final { scan-assembler "_ZW1M1fIS_1AIZ4mainE3locEEvT_" } }
+
+import M;
+
+int main()
+{
+ struct loc {};
+ f(A<loc>());
+}