https://gcc.gnu.org/g:2359344af53a5fc844d108fcf9f0e8bddd84a6b5
commit r16-5298-g2359344af53a5fc844d108fcf9f0e8bddd84a6b5 Author: Jason Merrill <[email protected]> Date: Wed Nov 12 15:03:46 2025 +0530 c++/modules: friend void foo<bar>() 23_containers/mdspan/layouts/padded.cc was failing because on load we were wrongly treating the __get_static_stride friends as equivalent between layout_left_padded and layout_right_padded. This happened because we were wrongly pushing these declarations into namespace scope even though we don't yet know what template they instantiate. Fixed by using the same MK_local_friend mechanism as template friends. gcc/cp/ChangeLog: * decl.cc (grokfndecl): Set DECL_CHAIN of a friend f<>. * module.cc (trees_out::get_merge_kind): Give it MK_local_friend. (trees_out::decl_container): Its container is the befriender. (trees_out::key_mergeable): Expand comment. * cp-tree.h (decl_specialization_friend_p): New. * friend.cc (do_friend): Use it. * pt.cc (tsubst_friend_function): Likewise. gcc/testsuite/ChangeLog: * g++.dg/modules/friend-11_a.C: New test. * g++.dg/modules/friend-11_b.C: New test. Diff: --- gcc/cp/cp-tree.h | 12 ++++++++++++ gcc/cp/decl.cc | 5 +++++ gcc/cp/friend.cc | 4 ++-- gcc/cp/module.cc | 9 ++++++--- gcc/cp/pt.cc | 4 +--- gcc/testsuite/g++.dg/modules/friend-11_a.C | 22 ++++++++++++++++++++++ gcc/testsuite/g++.dg/modules/friend-11_b.C | 9 +++++++++ 7 files changed, 57 insertions(+), 8 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5e8d1c9644c5..c6e284d060ca 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7658,6 +7658,18 @@ extern tree implicitly_declare_fn (special_function_kind, tree, bool, tree, tree); extern tree type_order_value (tree, tree); +/* True iff DECL represents a declaration of a friend template + specialization, e.g. friend void f<>(). */ + +inline bool +decl_specialization_friend_p (tree decl) +{ + return (TREE_CODE (decl) == FUNCTION_DECL + && DECL_UNIQUE_FRIEND_P (decl) + && DECL_IMPLICIT_INSTANTIATION (decl) + && TREE_CODE (DECL_TI_TEMPLATE (decl)) != TEMPLATE_DECL); +} + /* In module.cc */ class module_state; /* Forward declare. */ inline bool modules_p () { return flag_modules != 0; } diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 5f990ea56b2c..29165e447b38 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -12097,6 +12097,11 @@ grokfndecl (tree ctype, gcc_assert (identifier_p (fns) || OVL_P (fns)); DECL_TEMPLATE_INFO (decl) = build_template_info (fns, args); + /* Remember the befriending class like push_template_decl does for + template friends. */ + gcc_checking_assert (!DECL_CHAIN (decl)); + DECL_CHAIN (decl) = current_scope (); + for (t = TYPE_ARG_TYPES (TREE_TYPE (decl)); t; t = TREE_CHAIN (t)) if (TREE_PURPOSE (t) && TREE_CODE (TREE_PURPOSE (t)) == DEFERRED_PARSE) diff --git a/gcc/cp/friend.cc b/gcc/cp/friend.cc index 014af618088c..e0afc1728ac6 100644 --- a/gcc/cp/friend.cc +++ b/gcc/cp/friend.cc @@ -662,8 +662,8 @@ do_friend (tree scope, tree declarator, tree decl, if (decl == error_mark_node) return error_mark_node; - if (!class_template_depth && DECL_IMPLICIT_INSTANTIATION (decl) - && TREE_CODE (DECL_TI_TEMPLATE (decl)) != TEMPLATE_DECL) + if (!class_template_depth + && decl_specialization_friend_p (decl)) /* "[if no non-template match is found,] each remaining function template is replaced with the specialization chosen by deduction from the friend declaration or discarded if deduction fails." diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index ccabd640757d..1578674614e7 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -11492,7 +11492,8 @@ trees_out::get_merge_kind (tree decl, depset *dep) } if (TREE_CODE (decl) == TEMPLATE_DECL - && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) + ? DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl) + : decl_specialization_friend_p (decl)) { mk = MK_local_friend; break; @@ -11568,7 +11569,8 @@ trees_out::decl_container (tree decl) tree container = NULL_TREE; if (TREE_CODE (decl) == TEMPLATE_DECL - && DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl)) + ? DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl) + : decl_specialization_friend_p (decl)) container = DECL_CHAIN (decl); else container = CP_DECL_CONTEXT (decl); @@ -11751,7 +11753,8 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, case MK_local_friend: { - /* Find by index on the class's DECL_LIST */ + /* Find by index on the class's DECL_LIST. We set TREE_CHAIN to + point to the class in push_template_decl or grokfndecl. */ unsigned ix = 0; for (tree decls = CLASSTYPE_DECL_LIST (TREE_CHAIN (decl)); decls; decls = TREE_CHAIN (decls)) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index bbbf49363e8f..b10442b09609 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -11660,9 +11660,7 @@ tsubst_friend_function (tree decl, tree args) { tree new_friend; - if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_TEMPLATE_INSTANTIATION (decl) - && TREE_CODE (DECL_TI_TEMPLATE (decl)) != TEMPLATE_DECL) + if (decl_specialization_friend_p (decl)) /* This was a friend declared with an explicit template argument list, e.g.: diff --git a/gcc/testsuite/g++.dg/modules/friend-11_a.C b/gcc/testsuite/g++.dg/modules/friend-11_a.C new file mode 100644 index 000000000000..2382f4c4898a --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/friend-11_a.C @@ -0,0 +1,22 @@ +// { dg-additional-options "-fmodules" } + +export module M; + +export { +template <class T> +int fn() { + return T::mem; +} + +template <class T> +class A { + inline static int mem = 42; + friend int fn<A>(); +}; + +template <class T> +class B { + inline static int mem = 24; + friend int fn<B>(); +}; +} diff --git a/gcc/testsuite/g++.dg/modules/friend-11_b.C b/gcc/testsuite/g++.dg/modules/friend-11_b.C new file mode 100644 index 000000000000..e8baeee66c92 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/friend-11_b.C @@ -0,0 +1,9 @@ +// { dg-additional-options "-fmodules" } + +import M; + +int main() +{ + fn<A<int>>(); + fn<B<int>>(); +}
