On 12/12/22 12:20, Patrick Palka wrote:
When instantiating a constrained hidden template friend, we need to substitute into its constraints for sake of declaration matching.
Hmm, we shouldn't need to do declaration matching when there's only a single declaration.
For this substitution we use a full argument vector whose outer levels correspond to the class's arguments and innermost level corresponds to the template's level-lowered generic arguments. But for A<int>::f here, for which the relevant argument vector is {{int}, {Us...}}, this substitution triggers the assert in use_pack_expansion_extra_args_p since one argument is a pack expansion and the other isn't. And for A<int, int>::f, for which the relevant argument vector is {{int, int}, {Us...}}, the use_pack_expansion_extra_args_p assert would also trigger but we first get a bogus "mismatched argument pack lengths" error from tsubst_pack_expansion. These might ultimately be bugs in tsubst_pack_expansion, but it seems we can work around them by tweaking the constraint substitution in maybe_substitute_reqs_for to only use the friend's outer arguments in the case of a template friend.
Yes, this is how we normally substitute a member template during class instantiation: with the class' template args, not its own. The assert seems to be enforcing that.
This should be otherwise equivalent to substituting using the full arguments, since the template's innermost arguments are just its generic arguments with level=1. Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk/12? PR c++/108066 PR c++/108067 gcc/cp/ChangeLog: * constraint.cc (maybe_substitute_reqs_for): For a template friend, substitute using only its outer arguments. Remove dead uses_template_parms test.
I don't see any removal?
gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-friend12.C: New test. --- gcc/cp/constraint.cc | 8 +++++++ .../g++.dg/cpp2a/concepts-friend12.C | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-friend12.C diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index d4cd92ec3b4..f9d1009c9fe 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1352,6 +1352,14 @@ maybe_substitute_reqs_for (tree reqs, const_tree decl) tree tmpl = DECL_TI_TEMPLATE (decl); tree gargs = generic_targs_for (tmpl); processing_template_decl_sentinel s; + if (PRIMARY_TEMPLATE_P (tmpl)) + { + if (TEMPLATE_ARGS_DEPTH (gargs) == 1) + return reqs; + ++processing_template_decl; + gargs = copy_node (gargs); + --TREE_VEC_LENGTH (gargs);
Maybe instead of messing with TEMPLATE_ARGS_DEPTH we want to grab the targs for DECL_FRIEND_CONTEXT instead of decl itself?
+ } if (uses_template_parms (gargs)) ++processing_template_decl; reqs = tsubst_constraint (reqs, gargs, diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-friend12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-friend12.C new file mode 100644 index 00000000000..95973842afb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-friend12.C @@ -0,0 +1,22 @@ +// PR c++/108066 +// PR c++/108067 +// { dg-do compile { target c++20 } } + +template<class T, class U> +concept C = __is_same(T, U); + +template<class... Ts> +struct A { + template<class... Us> + requires (... && C<Ts, Us>) + friend void f(A, A<Us...>) { } +}; + +int main() { + A<int> x; + f(x, x); + A<int, int> y; + f(y, y); + A<char> z; + f(x, z); // { dg-error "no match" } +}