On Fri, 6 Nov 2020, Jason Merrill wrote: > On 11/5/20 8:40 PM, Patrick Palka wrote: > > This patch (naively) extends the PR93907 fix to also apply to variadic > > concepts invoked with a type argument pack. Without this, we ICE on > > the below testcase (a variadic version of concepts-using2.C) in the same > > manner as we used to on concepts-using2.C before r10-7133. > > > > Patch series bootstrapped and regtested on x86_64-pc-linux-gnu, > > and also tested against cmcstl2 and range-v3. > > > > gcc/cp/ChangeLog: > > > > PR c++/93907 > > * constraint.cc (tsubst_parameter_mapping): Also canonicalize > > the type arguments of a TYPE_ARGUMENT_PACk. > > > > gcc/testsuite/ChangeLog: > > > > PR c++/93907 > > * g++.dg/cpp2a/concepts-using3.C: New test, based off of > > concepts-using2.C. > > --- > > gcc/cp/constraint.cc | 10 ++++ > > gcc/testsuite/g++.dg/cpp2a/concepts-using3.C | 52 ++++++++++++++++++++ > > 2 files changed, 62 insertions(+) > > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-using3.C > > > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > > index b6f6f0d02a5..c871a8ab86a 100644 > > --- a/gcc/cp/constraint.cc > > +++ b/gcc/cp/constraint.cc > > @@ -2252,6 +2252,16 @@ tsubst_parameter_mapping (tree map, tree args, > > subst_info info) > > Hmm, the > > > else if (ARGUMENT_PACK_P (arg)) > > new_arg = tsubst_argument_pack (arg, args, complain, in_decl); > > just above this seems redundant, since tsubst_template_arg handles packs just > fine. In fact, I wonder why tsubst_argument_pack is used specifically > anywhere? It seems to get some edge cases better than the code in tsubst, but > the solution to that would seem to be replacing the code in tsubst with a call > to tsubst_argument_pack; then we can remove all the other calls to the > function.
They seem interchangeable here wrt handling TYPE_ARGUMENT_PACKs, but not NONTYPE_ARGUMENT_PACKs. It looks like tsubst_template_arg ends up just issuing an error from tsubst_expr if we try using it to substitute into a NONTYPE_ARGUMENT_PACK. > > > new_arg = tsubst_template_arg (arg, args, complain, in_decl); > > if (TYPE_P (new_arg)) > > new_arg = canonicalize_type_argument (new_arg, complain); > > + if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK) > > + { > > + tree pack_args = ARGUMENT_PACK_ARGS (new_arg); > > + for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++) > > + { > > + tree& pack_arg = TREE_VEC_ELT (pack_args, i); > > + if (TYPE_P (pack_arg)) > > + pack_arg = canonicalize_type_argument (pack_arg, > > complain); > > Do we need the TYPE_P here, since we already know we're in a > TYPE_ARGUMENT_PACK? That is, can an element of a TYPE_ARGUMENT_PACK be an > invalid argument to canonicalize_type_argument? With -fconcepts-ts, the elements of a TYPE_ARGUMENT_PACK here can be TEMPLATE_DECLs, as in e.g. line 28 of concepts/template-parm3.C. > > OTOH, I wonder if we need to canonicalize non-type arguments here as well? Hmm, I'm not sure. Not doing so should at worst result in a satisfaction cache miss in release builds, and in checking builds should get caught by the hash table sanitizer. I haven't been able to come up with a testcase that demonstrates it's necessary. > > I wonder if tsubst_template_arg should canonicalize rather than leave that up > to the caller? I suppose that could do a bit more work when the result is > going to end up in convert_template_argument and get canonicalized again; I > don't know if that would be significant. That seems like it works, based on some limited testing. But there are only two users of canonicalize_template_argument outside of convert_template_argument itself, and the one use in unify is still needed even with this change (or else we get many ICEs coming from verify_unstripped_args if we try to remove it). So the benefit of such a change seems marginal at the moment. > > > } > > if (new_arg == error_mark_node) > > return error_mark_node; > > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C > > b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C > > new file mode 100644 > > index 00000000000..2c8ad40d104 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-using3.C > > @@ -0,0 +1,52 @@ > > +// PR c++/93907 > > +// { dg-options -std=gnu++20 } > > + > > +// This testcase is a variadic version of concepts-using2.C; the only > > +// difference is that 'cd' and 'ce' are now variadic concepts. > > + > > +template <int a> struct c { > > + static constexpr int d = a; > > + typedef c e; > > +}; > > +template <typename> struct f; > > +template <typename b> using g = typename f<b>::e; > > +struct b; > > +template <typename b> struct f { using e = b; }; > > +template <typename ai> struct m { typedef g<ai> aj; }; > > +template <typename b> struct n { typedef typename m<b>::aj e; }; > > +template <typename b> using an = typename n<b>::e; > > +template <typename> constexpr bool ao = c<true>::d; > > +template <typename> constexpr bool i = c<1>::d; > > +template <typename> concept bb = i<b>; > > +#ifdef __SIZEOF_INT128__ > > +using cc = __int128; > > +#else > > +using cc = long long; > > +#endif > > +template <typename...> concept cd = bb<cc>; > > +template <typename... bt> concept ce = requires { requires cd<bt...>; }; > > +template <typename bt> concept h = ce<bt>; > > +template <typename bt> concept l = h<bt>; > > +template <typename> concept cl = ao<b>; > > +template <typename b> concept cp = requires(b j) { > > + requires h<an<decltype(j.begin())>>; > > +}; > > +struct o { > > + template <cl b> requires cp<b> auto operator()(b) {} > > +}; > > +template <typename b> using cm = decltype(o{}(b())); > > +template <typename bt> concept ct = l<bt>; > > +template <typename da> concept dd = ct<cm<da>>; > > +template <typename da> concept de = dd<da>; > > +struct { > > + template <de da, typename b> void operator()(da, b); > > +} di; > > +struct p { > > + void begin(); > > +}; > > +template <typename> using df = p; > > +template <int> void q() { > > + df<int> k; > > + int d; > > + di(k, d); > > +} > > > >