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);
> > +}
> > 
> 
> 

Reply via email to