From: Patrick Palka Date: Thu, 18 Jul 2024 01:02:52 +0000 (-0400) Subject: c++: prev declared hidden tmpl friend inst [PR112288] X-Git-Tag: basepoints/gcc-16~7433 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=30dd420a06ad7d2adf4a672d176caee632f8168a;p=thirdparty%2Fgcc.git c++: prev declared hidden tmpl friend inst [PR112288] When partially instantiating a previously declared hidden template friend definition (at class template scope) such as slot_allocated in the first testcase below, tsubst_friend_function needs to go through all existing specializations thereof and make them point to the new definition. But when the previous declaration was also at class template scope, old_decl is not the most general template, instead it's the partial instantiation, and since instantiations are relative to the most general template, old_decl's DECL_TEMPLATE_INSTANTIATIONS is empty. So we to consistently use the most general template here. And when adjusting DECL_TI_ARGS to match, only the innermost template arguments should be preserved; the outer ones should correspond to the new definition. Otherwise we fail a checking-only sanity check in instantiate_decl in the first testcase, and in the second/third we end up emitting multiple definitions of the template friend instantiation, resulting in a link failure. PR c++/112288 gcc/cp/ChangeLog: * pt.cc (tsubst_friend_function): When adjusting existing specializations after defining a previously declared template friend, consider the most general template and correct DECL_TI_ARGS adjustment. gcc/testsuite/ChangeLog: * g++.dg/template/friend80.C: New test. * g++.dg/template/friend81.C: New test. * g++.dg/template/friend81a.C: New test. Reviewed-by: Jason Merrill --- diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 0620c8c023a..057797f213f 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -11582,6 +11582,7 @@ tsubst_friend_function (tree decl, tree args) ; else { + tree old_template = most_general_template (old_decl); tree new_template = TI_TEMPLATE (new_friend_template_info); tree new_args = TI_ARGS (new_friend_template_info); @@ -11619,7 +11620,7 @@ tsubst_friend_function (tree decl, tree args) /* Reassign any specializations already in the hash table to the new more general template, and add the additional template args. */ - for (t = DECL_TEMPLATE_INSTANTIATIONS (old_decl); + for (t = DECL_TEMPLATE_INSTANTIATIONS (old_template); t != NULL_TREE; t = TREE_CHAIN (t)) { @@ -11632,15 +11633,15 @@ tsubst_friend_function (tree decl, tree args) decl_specializations->remove_elt (&elt); - DECL_TI_ARGS (spec) - = add_outermost_template_args (new_args, - DECL_TI_ARGS (spec)); + tree& spec_args = DECL_TI_ARGS (spec); + spec_args = add_outermost_template_args + (new_args, INNERMOST_TEMPLATE_ARGS (spec_args)); register_specialization - (spec, new_template, DECL_TI_ARGS (spec), true, 0); + (spec, new_template, spec_args, true, 0); } - DECL_TEMPLATE_INSTANTIATIONS (old_decl) = NULL_TREE; + DECL_TEMPLATE_INSTANTIATIONS (old_template) = NULL_TREE; } } diff --git a/gcc/testsuite/g++.dg/template/friend80.C b/gcc/testsuite/g++.dg/template/friend80.C new file mode 100644 index 00000000000..5c417e12dd0 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/friend80.C @@ -0,0 +1,25 @@ +// PR c++/112288 +// { dg-do compile { target c++11 } } + +template +struct slot { + template + friend constexpr bool slot_allocated(slot, U); +}; + +template +struct allocate_slot { + template + friend constexpr bool slot_allocated(slot, U) { return true; } +}; + +template{}, 42)> +constexpr int next(int) { return 1; } + +template +constexpr int next(...) { return (allocate_slot{}, 0); } + +// slot_allocated, int>() not defined yet +static_assert(next(0) == 0, ""); +// now it's defined, need to make existing spec point to defn or else ICE +static_assert(next(0) == 1, ""); diff --git a/gcc/testsuite/g++.dg/template/friend81.C b/gcc/testsuite/g++.dg/template/friend81.C new file mode 100644 index 00000000000..5d3a2889c0f --- /dev/null +++ b/gcc/testsuite/g++.dg/template/friend81.C @@ -0,0 +1,28 @@ +// PR c++/112288 +// { dg-do link } + +template struct A; +template struct B; + +A* a; +B* b; + +template +struct B { + template + friend int f(A*, B*, U); +}; + +template struct B; // f declared +int n = f(a, b, 0); // f specialized + +template +struct A { + template + friend int f(A*, B*, U) { return 42; } +}; + +template struct A; // f defined, need to make existing f point to defn +int m = f(a, b, 0); // reuses existing specialization f + +int main() { } diff --git a/gcc/testsuite/g++.dg/template/friend81a.C b/gcc/testsuite/g++.dg/template/friend81a.C new file mode 100644 index 00000000000..4557fd3c03a --- /dev/null +++ b/gcc/testsuite/g++.dg/template/friend81a.C @@ -0,0 +1,30 @@ +// PR c++/112288 +// { dg-do link } +// A version of friend81.C where A and B have a different number of template +// parameters. + +template struct A; +template struct B; + +A* a; +B* b; + +template +struct B { + template + friend int f(A*, B*, U); +}; + +template struct B; // f declared +int n = f(a, b, 0); // f specialized + +template +struct A { + template + friend int f(A*, B*, U) { return 42; } +}; + +template struct A; // f defined, need to make existing f point to defn +int m = f(a, b, 0); // reuses existing specialization f + +int main() { }