On Mon, Jan 3, 2022 at 10:24 AM Patrick Palka <ppa...@redhat.com> wrote:
>
> On Wed, 22 Dec 2021, Jason Merrill wrote:
>
> > On 12/21/21 14:08, Patrick Palka wrote:
> > > On Tue, Dec 21, 2021 at 2:03 PM Patrick Palka <ppa...@redhat.com> wrote:
> > > >
> > > > On Wed, Jun 30, 2021 at 4:23 PM Jason Merrill <ja...@redhat.com> wrote:
> > > > >
> > > > > On 6/30/21 4:18 PM, Patrick Palka wrote:
> > > > > > On Wed, Jun 30, 2021 at 3:51 PM Jason Merrill <ja...@redhat.com>
> > > > > > wrote:
> > > > > > >
> > > > > > > On 6/30/21 11:58 AM, Patrick Palka wrote:
> > > > > > > > On Wed, 30 Jun 2021, Patrick Palka wrote:
> > > > > > > >
> > > > > > > > > On Fri, 25 Jun 2021, Jason Merrill wrote:
> > > > > > > > >
> > > > > > > > > > On 6/25/21 1:11 PM, Patrick Palka wrote:
> > > > > > > > > > > On Fri, 25 Jun 2021, Jason Merrill wrote:
> > > > > > > > > > >
> > > > > > > > > > > > On 6/24/21 4:45 PM, Patrick Palka wrote:
> > > > > > > > > > > > > In the first testcase below, during parsing of the 
> > > > > > > > > > > > > alias
> > > > > > > > > > > > > template
> > > > > > > > > > > > > ConstSpanType, transparency of alias template
> > > > > > > > > > > > > specializations means we
> > > > > > > > > > > > > replace SpanType<T> with SpanType's substituted
> > > > > > > > > > > > > definition.  But this
> > > > > > > > > > > > > substitution lowers the level of the CTAD placeholder
> > > > > > > > > > > > > for span(T()) from
> > > > > > > > > > > > > 2 to 1, and so the later instantiantion of
> > > > > > > > > > > > > ConstSpanType<int>
> > > > > > > > > > > > > erroneously substitutes this CTAD placeholder with the
> > > > > > > > > > > > > template argument
> > > > > > > > > > > > > at level 1 index 0, i.e. with int, before we get a
> > > > > > > > > > > > > chance to perform the
> > > > > > > > > > > > > CTAD.
> > > > > > > > > > > > >
> > > > > > > > > > > > > In light of this, it seems we should avoid level
> > > > > > > > > > > > > lowering when
> > > > > > > > > > > > > substituting through through the type-id of a 
> > > > > > > > > > > > > dependent
> > > > > > > > > > > > > alias template
> > > > > > > > > > > > > specialization.  To that end this patch makes
> > > > > > > > > > > > > lookup_template_class_1
> > > > > > > > > > > > > pass tf_partial to tsubst in this situation.
> > > > > > > > > > > >
> > > > > > > > > > > > This makes sense, but what happens if SpanType is a 
> > > > > > > > > > > > member
> > > > > > > > > > > > template, so
> > > > > > > > > > > > that
> > > > > > > > > > > > the levels of it and ConstSpanType don't match?  Or the
> > > > > > > > > > > > other way around?
> > > > > > > > > > >
> > > > > > > > > > > If SpanType<T> is a member template of say the class
> > > > > > > > > > > template A<U> (and
> > > > > > > > > > > thus its level is greater than ConstSpanType):
> > > > > > > > > > >
> > > > > > > > > > >       template<class U>
> > > > > > > > > > >       struct A {
> > > > > > > > > > >         template<class T>
> > > > > > > > > > >         using SpanType = decltype(span(T()));
> > > > > > > > > > >       };
> > > > > > > > > > >
> > > > > > > > > > >       template<class T>
> > > > > > > > > > >       using ConstSpanType = span<const typename
> > > > > > > > > > > A<int>::SpanType<T>::value_type>;
> > > > > > > > > > >
> > > > > > > > > > >       using type = ConstSpanType<int>;
> > > > > > > > > > >
> > > > > > > > > > > then this case luckily works even without the patch 
> > > > > > > > > > > because
> > > > > > > > > > > instantiate_class_template now reuses the specialization
> > > > > > > > > > > A<int>::SpanType<T>
> > > > > > > > > > > that was formed earlier during instantiation of A<int>,
> > > > > > > > > > > where we
> > > > > > > > > > > substitute only a single level of template arguments, so 
> > > > > > > > > > > the
> > > > > > > > > > > level of
> > > > > > > > > > > the CTAD placeholder inside the defining-type-id of this
> > > > > > > > > > > specialization
> > > > > > > > > > > dropped from 3 to 2, so still more than the level of
> > > > > > > > > > > ConstSpanType.
> > > > > > > > > > >
> > > > > > > > > > > This luck is short-lived though, because if we replace
> > > > > > > > > > > A<int>::SpanType<T> with say A<int>::SpanType<const T> 
> > > > > > > > > > > then
> > > > > > > > > > > the testcase
> > > > > > > > > > > breaks again (without the patch) because we no longer can
> > > > > > > > > > > reuse that
> > > > > > > > > > > specialization, so we instead form it on the spot by
> > > > > > > > > > > substituting two
> > > > > > > > > > > levels of template arguments (U=int,T=T) into the
> > > > > > > > > > > defining-type-id,
> > > > > > > > > > > causing the level of the placeholder to drop to 1.  I 
> > > > > > > > > > > think
> > > > > > > > > > > the patch
> > > > > > > > > > > causes its level to remain 3 (though I guess it should
> > > > > > > > > > > really be 2).
> > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > For the other way around, if ConstSpanType<T> is a member
> > > > > > > > > > > template of
> > > > > > > > > > > say the class template B<V> (and thus its level is greater
> > > > > > > > > > > than
> > > > > > > > > > > SpanType):
> > > > > > > > > > >
> > > > > > > > > > >       template<class T>
> > > > > > > > > > >       using SpanType = decltype(span(T()));
> > > > > > > > > > >
> > > > > > > > > > >       template<class V>
> > > > > > > > > > >       struct B {
> > > > > > > > > > >         template<class T>
> > > > > > > > > > >         using ConstSpanType = span<const typename
> > > > > > > > > > > SpanType<T>::value_type>;
> > > > > > > > > > >       };
> > > > > > > > > > >
> > > > > > > > > > >       using type = B<char>::ConstSpanType<int>;
> > > > > > > > > > >
> > > > > > > > > > > then tf_partial doesn't help here at all; we end up
> > > > > > > > > > > substituting 'int'
> > > > > > > > > > > for the CTAD placeholder...  What it seems we need is to
> > > > > > > > > > > _increase_ the
> > > > > > > > > > > level of the CTAD placeholder from 2 to 3 during the
> > > > > > > > > > > dependent
> > > > > > > > > > > substitution..
> > > > > > > > > > >
> > > > > > > > > > > Hmm, rather than messing with tf_partial, which is
> > > > > > > > > > > apparently only a
> > > > > > > > > > > partial solution, maybe we should just make tsubst never
> > > > > > > > > > > substitute a
> > > > > > > > > > > CTAD placeholder -- they should always be resolved from
> > > > > > > > > > > do_class_deduction,
> > > > > > > > > > > and their level doesn't really matter otherwise.  (But 
> > > > > > > > > > > we'd
> > > > > > > > > > > still want
> > > > > > > > > > > to substitute into the CLASS_PLACEHOLDER_TEMPLATE of the
> > > > > > > > > > > placeholder in
> > > > > > > > > > > case it's a template template parm.)  Something like:
> > > > > > > > > > >
> > > > > > > > > > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> > > > > > > > > > > index 5107bfbf9d1..dead651ed84 100644
> > > > > > > > > > > --- a/gcc/cp/pt.c
> > > > > > > > > > > +++ b/gcc/cp/pt.c
> > > > > > > > > > > @@ -15552,7 +15550,8 @@ tsubst (tree t, tree args,
> > > > > > > > > > > tsubst_flags_t complain,
> > > > > > > > > > > tree in_decl)
> > > > > > > > > > >              levels = TMPL_ARGS_DEPTH (args);
> > > > > > > > > > >              if (level <= levels
> > > > > > > > > > > -      && TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)) 
> > > > > > > > > > > >
> > > > > > > > > > > 0)
> > > > > > > > > > > +      && TREE_VEC_LENGTH (TMPL_ARGS_LEVEL (args, level)) 
> > > > > > > > > > > >
> > > > > > > > > > > 0
> > > > > > > > > > > +      && !template_placeholder_p (t))
> > > > > > > > > > >                {
> > > > > > > > > > >                  arg = TMPL_ARG (args, level, idx);
> > > > > > > > > > >
> > > > > > > > > > > seems to work better.
> > > > > > > > > >
> > > > > > > > > > Makes sense.
> > > > > > > > >
> > > > > > > > > Here's a patch that implements that.  I reckon it's good to 
> > > > > > > > > have
> > > > > > > > > both
> > > > > > > > > workarounds in place because the tf_partial workaround is
> > > > > > > > > necessary to
> > > > > > > > > accept class-deduction93a.C below, and the tsubst workaround 
> > > > > > > > > is
> > > > > > > > > necessary to accept class-deduction-92b.C below.
> > > > > > > >
> > > > > > > > Whoops, forgot to git-add class-deduction93a.C:
> > > > > > > >
> > > > > > > > -- >8 --
> > > > > > > >
> > > > > > > > Subject: [PATCH] c++: CTAD within alias template [PR91911]
> > > > > > > >
> > > > > > > > In the first testcase below, during parsing of the alias 
> > > > > > > > template
> > > > > > > > ConstSpanType, transparency of alias template specializations
> > > > > > > > means we
> > > > > > > > replace SpanType<T> with SpanType's substituted definition.  But
> > > > > > > > this
> > > > > > > > substitution lowers the level of the CTAD placeholder for
> > > > > > > > span{T()} from
> > > > > > > > 2 to 1, and so the later instantiation of ConstSpanType<int>
> > > > > > > > erroneously
> > > > > > > > substitutes this CTAD placeholder with the template argument at
> > > > > > > > level 1
> > > > > > > > index 0, i.e. with int, before we get a chance to perform the
> > > > > > > > CTAD.
> > > > > > > >
> > > > > > > > In light of this, it seems we should avoid level lowering when
> > > > > > > > substituting through the type-id of a dependent alias template
> > > > > > > > specialization.  To that end this patch makes
> > > > > > > > lookup_template_class_1
> > > > > > > > pass tf_partial to tsubst in this situation.
> > > > > > > >
> > > > > > > > Unfortunately, using tf_partial alone isn't sufficient because 
> > > > > > > > the
> > > > > > > > template context in which we perform the dependent substitution
> > > > > > > > may
> > > > > > > > have more levels than the substituted alias template and so we
> > > > > > > > end up substituting the CTAD placeholder anyway, as in
> > > > > > > > class-deduction92b.C below.  (There, it seems we'd need to
> > > > > > > > _increase_
> > > > > > > > the level of the placeholder for span{T()} from 2 to 3 during 
> > > > > > > > the
> > > > > > > > dependent substitution.)  Since we never want to resolve a CTAD
> > > > > > > > placeholder outside of CTAD proper, this patch takes the
> > > > > > > > relatively
> > > > > > > > ad-hoc approach of making tsubst explicitly avoid doing so.
> > > > > > > >
> > > > > > > > This tsubst workaround doesn't obviate the tf_partial workaround
> > > > > > > > because
> > > > > > > > it's still desirable to avoid prematurely level lowering a CTAD
> > > > > > > > placeholder:
> > > > > > > > it's less work for the compiler, and it gives us a chance to
> > > > > > > > substitute
> > > > > > > > a template placeholder that's a template template parameter 
> > > > > > > > with a
> > > > > > > > concrete template template argument, as in the last testcase
> > > > > > > > below.
> > > > > > >
> > > > > > > Hmm, what if we combine the template template parameter with the
> > > > > > > level
> > > > > > > mismatch?
> > > > > >
> > > > > > So for e.g.
> > > > > >
> > > > > > template<class F, template<class> class Tmpl>
> > > > > > using CallableTraitT = CallableTrait<decltype(Tmpl{F()})>;
> > > > > >
> > > > > > template<class>
> > > > > > struct A {
> > > > > >     template<class F, template<class> class Tmpl>
> > > > > >     using ReturnType = typename CallableTraitT<F, Tmpl>::ReturnType;
> > > > > > };
> > > > > >
> > > > > > using type = A<int>::ReturnType<int(*)(), function>;
> > > > > > using type = int;
> > > > > >
> > > > > > sadly we crash, because during the dependent substitution of the
> > > > > > innermost arguments into the defining-type-id, tf_partial means we
> > > > > > don't lower the level of the CTAD placeholder and therefore don't
> > > > > > substitute into CLASS_PLACEHOLDER_TEMPLATE, so
> > > > > > CLASS_PLACEHOLDER_TEMPLATE remains a template template parm at 
> > > > > > index 1
> > > > > > level 1 (as opposed to level 2).   Later during the full
> > > > > > instantiation, there is no such template template argument at that
> > > > > > position (it's at index 1 level 2 rather).
> > > > > >
> > > > > > To handle this testcase, it seems we need a way to substitute into
> > > > > > CTAD placeholders without lowering their level I guess.
> > > > >
> > > > > Or replacing their level with the appropriate level for the args we're
> > > > > dealing with/whether tf_partial is set?
> > > >
> > > > That sounds like it might work for CTAD placeholders, since we never
> > > > want to replace them via tsubst anyway.  But I suppose a complete
> > > > solution to this problem would also need to adjust the level of 'auto'
> > > > that appears inside unevaluated lambdas (and C++23 auto(x) now too, I
> > > > guess).  And the tricky part with those is that we do sometimes want
> > > > to replace 'auto's via tsubst, in particular during
> > > > do_auto_deduction..
> > > >
> > > > I wonder if for now the v1 patch (the one consisting of just the
> > > > lookup_template_class_1 change) can go in?  I noticed that it also
> > > > fixes a slew of (essentially duplicate) PRs about simple uses of
> > > > unevaluated lambdas within alias templates: 100594, 92211, 103569,
> > > > 102680, 101315, 101013, 92707.  (The template_placeholder_p change in
> > > > the v2 patch I realized is buggy -- by avoiding substitution into the
> > > > CTAD placeholder, we fall back to level-lowering, but since level <=
> > > > levels we end up creating a CTAD
> > > placeholder with nonpositive level.
> >
> > >   -      t = tsubst (TREE_TYPE (gen_tmpl), arglist, complain, in_decl);
> > > +      /* When substituting a dependent alias template specialization,
> > > +         we pass tf_partial to avoid lowering the level of any 'auto'
> > > +         in its type-id which might correspond to CTAD placeholders.  */
> > > +      t = tsubst (TREE_TYPE (gen_tmpl), arglist,
> > > +              complain | (is_dependent_type * tf_partial),
> > > +              in_decl);
> >
> > So, if we aren't changing any containing template scopes from dependent to
> > non-dependent, we don't want to mess with levels.
> >
> > I think is_dependent_type is a bit too broad here; I'd expect this could 
> > cause
> > trouble when e.g. instantiating a class A<int> with a member template B and 
> > we
> > have both B<U> and an auto in the signature of a member template.  I think
> > what we want to check is whether the outermost args are dependent.
>
> Ah yeah, I see what you mean...
>
> >
> > It would also be safer to handle adding tf_partial for alias templates in
> > instantiate_alias_template rather than lookup_template_class. Perhaps in
> > alias_ctad_tweaks as well.
> >
> > I tried adding an assert that tf_partial is set any time we see dependent
> > outermost args; I expected to need to override that for deduction guide
> > rewriting, but also seem to hit problems in concepts and TTP.  Attached in
> > case you're interested; I don't think this is going to become a patch 
> > suitable
> > for GCC 12.  The use of uses_template_parms_level was a kludge because
> > dependent_template_arg_p returns true for null args.
>
> Interesting.  In light of this general problem, I wonder if representing
> autos as template parameters with one level greater than the template
> depth, while convenient, is ultimately not the best approach?
>
> Back to the original issue concerning CTAD within alias templates,
> here's an approach that seems to work well.  The idea is to treat
> CTAD placeholders less like template parameters, by giving them
> the special level 0, and by making tsubst avoid substituting them
> like template parameters.  With this approach the representation
> of a CTAD placeholder no longer depends on the template depth, so
> alias template transparency for alias templates that contain CTAD
> just works.
>
> I tried extending this approach to all autos (i.e. giving all
> autos a level of 0 and adjusting tsubst accordingly), and it nearly
> works modulo handling concepts TS auto... deduction:
>
>   tuple<auto...> x = tuple<int, char>{}.
>
> since unify_pack_expansion isn't prepared to see a parameter pack of
> level 0.  This is likely fixable, but I suppose it'd be good to first
> get confirmation that this is a good approach before continuing down
> this patch.
>
> Below the patch that implements this approach only for CTAD
> placeholders.  Attached is an incremental WIP diff that additionally
> extends the approach to all autos, which passes testing modulo the
> concept TS thing.

Ping.  It's probably too risky to commit to this approach for all
autos for GCC 12 at this point, but perhaps the CTAD-only patch
(inlined below) might be worth considering for GCC 12?  Seems to fix
the regression PR103672 as well (though I think we just got lucky that
we accepted this testcase previously).

>
> -- >8 --
>
> Subject: [PATCH] c++: CTAD inside alias template [PR91911]
>
> In the first testcase below, during parsing of the alias template
> ConstSpanType, transparency of alias template specializations means we
> replace SpanType<T> with its instantiated definition.  But this
> instantiation lowers the level of the CTAD placeholder for span{T()} from
> 2 to 1, and so the later instantiation of ConstSpanType<int> erroneously
> substitutes this CTAD placeholder with the template argument at level 1
> index 0, i.e. with int, before we get a chance to perform the CTAD.
>
> Although we represent CTAD placeholders as template parameters, we never
> actually want to replace them via tsubst.  So this patch adjusts tsubst
> to handle CTAD placeholders by simply substituting the template and
> returning a new CTAD placeholder.  Moreover, this means that the level
> of a CTAD placeholder doesn't matter, so we may as well give them all
> the same level.  This patch gives them the special level 0.
>
> The change in grokdeclarator makes us reject an invalid function
> return type consisting of a CTAD placeholder sooner (as in pr88187.C),
> which helps guarantee that splice_late_return_type doesn't see or need
> to handle a erroneous CTAD placeholder return type.
>
> The change in tsubst_decl removes a CHECKING_P workaround added in 2017,
> which would otherwise now get triggered for variables with CTAD placeholder
> types (since their level is 0).  Alternatively, we could just guard the
> workaround with !template_placeholder_p if that's preferable.
>
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk?
>
>         PR c++/91911
>
> gcc/cp/ChangeLog:
>
>         * decl.c (grokdeclarator): Diagnose CTAD placeholder in function
>         return type even when !funcdecl_p.
>         * pt.c (keep_template_parm): Punt on a level 0 template parm.
>         (tsubst_decl) <case VAR_DECL>: Remove CHECKING_P workaround.
>         (tsubst) <case TEMPLATE_TYPE_PARM>: Handle CTAD placeholders
>         specially.
>         (make_auto_1): Add defaulted 'level' parameter.
>         (make_template_placeholder): Pass 0 as 'level' to make_auto_1.
>
> gcc/testsuite/ChangeLog:
>
>         * g++.dg/cpp1z/class-deduction100.C: New test.
>         * g++.dg/cpp1z/class-deduction100a.C: New test.
>         * g++.dg/cpp1z/class-deduction100b.C: New test.
>         * g++.dg/cpp1z/class-deduction101.C: New test.
>         * g++.dg/cpp1z/class-deduction101a.C: New test.
>         * g++.dg/cpp1z/class-deduction101b.C: New test.
> ---
>  gcc/cp/decl.c                                 |  6 +-
>  gcc/cp/pt.c                                   | 60 ++++++++-----------
>  .../g++.dg/cpp1z/class-deduction100.C         | 17 ++++++
>  .../g++.dg/cpp1z/class-deduction100a.C        | 22 +++++++
>  .../g++.dg/cpp1z/class-deduction100b.C        | 22 +++++++
>  .../g++.dg/cpp1z/class-deduction101.C         | 25 ++++++++
>  .../g++.dg/cpp1z/class-deduction101a.C        | 27 +++++++++
>  .../g++.dg/cpp1z/class-deduction101b.C        | 30 ++++++++++
>  gcc/testsuite/g++.dg/other/pr88187.C          |  2 +-
>  9 files changed, 173 insertions(+), 38 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction100.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction100a.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction100b.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction101.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction101a.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction101b.C
>
> diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
> index 0b71c00f5ab..406a9163ffd 100644
> --- a/gcc/cp/decl.c
> +++ b/gcc/cp/decl.c
> @@ -12635,11 +12635,11 @@ grokdeclarator (const cp_declarator *declarator,
>                 if (!tmpl)
>                   if (tree late_auto = type_uses_auto (late_return_type))
>                     tmpl = CLASS_PLACEHOLDER_TEMPLATE (late_auto);
> -               if (tmpl && funcdecl_p)
> +               if (tmpl)
>                   {
> -                   if (!dguide_name_p (unqualified_id))
> +                   if (!funcdecl_p || !dguide_name_p (unqualified_id))
>                       {
> -                       error_at (declarator->id_loc, "deduced class "
> +                       error_at (typespec_loc, "deduced class "
>                                   "type %qD in function return type",
>                                   DECL_NAME (tmpl));
>                         inform (DECL_SOURCE_LOCATION (tmpl),
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index c587966adbe..08e34c7404a 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -10660,7 +10660,7 @@ keep_template_parm (tree t, void* data)
>    int level;
>    int index;
>    template_parm_level_and_index (t, &level, &index);
> -  if (level > ftpi->max_depth)
> +  if (level == 0 || level > ftpi->max_depth)
>      return 0;
>
>    if (TREE_CODE (t) == BOUND_TEMPLATE_TEMPLATE_PARM)
> @@ -14796,20 +14796,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t 
> complain)
>                 && VAR_HAD_UNKNOWN_BOUND (t)
>                 && type != error_mark_node)
>               type = strip_array_domain (type);
> -           tree sub_args = args;
> -           if (tree auto_node = type_uses_auto (type))
> -             {
> -               /* Mask off any template args past the variable's context so 
> we
> -                  don't replace the auto with an unrelated argument.  */
> -               int nouter = TEMPLATE_TYPE_LEVEL (auto_node) - 1;
> -               int extra = TMPL_ARGS_DEPTH (args) - nouter;
> -               if (extra > 0)
> -                 /* This should never happen with the new lambda 
> instantiation
> -                    model, but keep the handling just in case.  */
> -                 gcc_assert (!CHECKING_P),
> -                 sub_args = strip_innermost_template_args (args, extra);
> -             }
> -           type = tsubst (type, sub_args, complain, in_decl);
> +           type = tsubst (type, args, complain, in_decl);
>             /* Substituting the type might have recursively instantiated this
>                same alias (c++/86171).  */
>             if (gen_tmpl && DECL_ALIAS_TEMPLATE_P (gen_tmpl)
> @@ -15561,6 +15548,19 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
> tree in_decl)
>        }
>
>      case TEMPLATE_TYPE_PARM:
> +      if (template_placeholder_p (t))
> +       {
> +         tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (t);
> +         tmpl = tsubst_copy (tmpl, args, complain, in_decl);
> +         if (TREE_CODE (tmpl) == TEMPLATE_TEMPLATE_PARM)
> +           tmpl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (tmpl);
> +
> +         if (tmpl != CLASS_PLACEHOLDER_TEMPLATE (t))
> +           return make_template_placeholder (tmpl);
> +         else
> +           return t;
> +       }
> +      /* Fall through.  */
>      case TEMPLATE_TEMPLATE_PARM:
>      case BOUND_TEMPLATE_TEMPLATE_PARM:
>      case TEMPLATE_PARM_INDEX:
> @@ -15734,7 +15734,6 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
> tree in_decl)
>                  of a constrained placeholder.  */;
>             else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM
>                      && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)
> -                    && !CLASS_PLACEHOLDER_TEMPLATE (t)
>                      && (arg = TEMPLATE_TYPE_PARM_INDEX (t),
>                          r = TEMPLATE_PARM_DESCENDANTS (arg))
>                      && (TEMPLATE_PARM_LEVEL (r)
> @@ -15753,19 +15752,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, 
> tree in_decl)
>                 TYPE_REFERENCE_TO (r) = NULL_TREE;
>
>                  if (TREE_CODE (t) == TEMPLATE_TYPE_PARM)
> -                 {
> +                 if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t))
>                     /* Propagate constraints on placeholders since they are
>                        only instantiated during satisfaction.  */
> -                   if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t))
> -                     PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r) = ci;
> -                   else if (tree pl = CLASS_PLACEHOLDER_TEMPLATE (t))
> -                     {
> -                       pl = tsubst_copy (pl, args, complain, in_decl);
> -                       if (TREE_CODE (pl) == TEMPLATE_TEMPLATE_PARM)
> -                         pl = TEMPLATE_TEMPLATE_PARM_TEMPLATE_DECL (pl);
> -                       CLASS_PLACEHOLDER_TEMPLATE (r) = pl;
> -                     }
> -                 }
> +                   PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r) = ci;
>
>                 if (TREE_CODE (r) == TEMPLATE_TEMPLATE_PARM)
>                   /* We have reduced the level of the template
> @@ -28491,18 +28481,18 @@ make_args_non_dependent (vec<tree, va_gc> *args)
>  }
>
>  /* Returns a type which represents 'auto' or 'decltype(auto)'.  We use a
> -   TEMPLATE_TYPE_PARM with a level one deeper than the actual template
> -   parms.  If set_canonical is true, we set TYPE_CANONICAL on it.  */
> +   TEMPLATE_TYPE_PARM with a level one deeper than the actual template parms,
> +   by default.  If set_canonical is true, we set TYPE_CANONICAL on it.  */
>
>  static tree
> -make_auto_1 (tree name, bool set_canonical)
> +make_auto_1 (tree name, bool set_canonical,
> +            int level = current_template_depth + 1)
>  {
>    tree au = cxx_make_type (TEMPLATE_TYPE_PARM);
>    TYPE_NAME (au) = build_decl (input_location, TYPE_DECL, name, au);
>    TYPE_STUB_DECL (au) = TYPE_NAME (au);
>    TEMPLATE_TYPE_PARM_INDEX (au) = build_template_parm_index
> -    (0, current_template_depth + 1, current_template_depth + 1,
> -     TYPE_NAME (au), NULL_TREE);
> +    (0, level, level, TYPE_NAME (au), NULL_TREE);
>    if (set_canonical)
>      TYPE_CANONICAL (au) = canonical_type_parameter (au);
>    DECL_ARTIFICIAL (TYPE_NAME (au)) = 1;
> @@ -28525,12 +28515,14 @@ make_auto (void)
>    return make_auto_1 (auto_identifier, true);
>  }
>
> -/* Return a C++17 deduction placeholder for class template TMPL.  */
> +/* Return a C++17 deduction placeholder for class template TMPL.
> +   There are represented as an 'auto' with the special level 0 and
> +   CLASS_PLACEHOLDER_TEMPLATE set.  */
>
>  tree
>  make_template_placeholder (tree tmpl)
>  {
> -  tree t = make_auto_1 (auto_identifier, false);
> +  tree t = make_auto_1 (auto_identifier, false, /*level=*/0);
>    CLASS_PLACEHOLDER_TEMPLATE (t) = tmpl;
>    /* Our canonical type depends on the placeholder.  */
>    TYPE_CANONICAL (t) = canonical_type_parameter (t);
> diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction100.C 
> b/gcc/testsuite/g++.dg/cpp1z/class-deduction100.C
> new file mode 100644
> index 00000000000..379eb960da6
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction100.C
> @@ -0,0 +1,17 @@
> +// PR c++/91911
> +// { dg-do compile { target c++17 } }
> +
> +template<class T>
> +struct span {
> +  using value_type = T;
> +  span(T);
> +};
> +
> +template<class T>
> +using SpanType = decltype(span{T()});
> +
> +template<class T>
> +using ConstSpanType = span<const typename SpanType<T>::value_type>;
> +
> +using type = ConstSpanType<int>;
> +using type = span<const int>;
> diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction100a.C 
> b/gcc/testsuite/g++.dg/cpp1z/class-deduction100a.C
> new file mode 100644
> index 00000000000..958ac4f9762
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction100a.C
> @@ -0,0 +1,22 @@
> +// PR c++/91911
> +// { dg-do compile { target c++17 } }
> +// A variant of class-deduction100.C where SpanType has more levels than
> +// ConstSpanType.
> +
> +template<class T>
> +struct span {
> +  using value_type = T;
> +  span(T);
> +};
> +
> +template<class>
> +struct A {
> +  template<class T>
> +  using SpanType = decltype(span{T()});
> +};
> +
> +template<class T>
> +using ConstSpanType = span<const typename A<int>::SpanType<const 
> T>::value_type>;
> +
> +using type = ConstSpanType<int>;
> +using type = span<const int>;
> diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction100b.C 
> b/gcc/testsuite/g++.dg/cpp1z/class-deduction100b.C
> new file mode 100644
> index 00000000000..9b5c684449c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction100b.C
> @@ -0,0 +1,22 @@
> +// PR c++/91911
> +// { dg-do compile { target c++17 } }
> +// A variant of class-deduction100.C where SpanType has fewer levels than
> +// ConstSpanType.
> +
> +template<class T>
> +struct span {
> +  using value_type = T;
> +  span(T);
> +};
> +
> +template<class T>
> +using SpanType = decltype(span{T()});
> +
> +template<class>
> +struct B {
> +  template<class T>
> +  using ConstSpanType = span<const typename SpanType<T>::value_type>;
> +};
> +
> +using type = B<int>::ConstSpanType<int>;
> +using type = span<const int>;
> diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction101.C 
> b/gcc/testsuite/g++.dg/cpp1z/class-deduction101.C
> new file mode 100644
> index 00000000000..20504780d32
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction101.C
> @@ -0,0 +1,25 @@
> +// PR c++/98077
> +// { dg-do compile { target c++17 } }
> +
> +template<class R>
> +struct function {
> +  template<class T> function(T);
> +  using type = R;
> +};
> +
> +template<class T> function(T) -> function<decltype(T()())>;
> +
> +template<class T>
> +struct CallableTrait;
> +
> +template<class R>
> +struct CallableTrait<function<R>> { using ReturnType = R; };
> +
> +template<class F>
> +using CallableTraitT = CallableTrait<decltype(function{F()})>;
> +
> +template<class F>
> +using ReturnType = typename CallableTraitT<F>::ReturnType;
> +
> +using type = ReturnType<int(*)()>;
> +using type = int;
> diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction101a.C 
> b/gcc/testsuite/g++.dg/cpp1z/class-deduction101a.C
> new file mode 100644
> index 00000000000..77b05d23b62
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction101a.C
> @@ -0,0 +1,27 @@
> +// PR c++/98077
> +// { dg-do compile { target c++17 } }
> +// A variant of class-deduction101.C where the template placeholder is a 
> template
> +// template parameter.
> +
> +template<class R>
> +struct function {
> +  template<class T> function(T);
> +  using type = R;
> +};
> +
> +template<class T> function(T) -> function<decltype(T()())>;
> +
> +template<class T>
> +struct CallableTrait;
> +
> +template<class R>
> +struct CallableTrait<function<R>> { using ReturnType = R; };
> +
> +template<class F, template<class> class Tmpl>
> +using CallableTraitT = CallableTrait<decltype(Tmpl{F()})>;
> +
> +template<class F, template<class> class Tmpl>
> +using ReturnType = typename CallableTraitT<F, Tmpl>::ReturnType;
> +
> +using type = ReturnType<int(*)(), function>;
> +using type = int;
> diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction101b.C 
> b/gcc/testsuite/g++.dg/cpp1z/class-deduction101b.C
> new file mode 100644
> index 00000000000..50df934796d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction101b.C
> @@ -0,0 +1,30 @@
> +// PR c++/98077
> +// { dg-do compile { target c++17 } }
> +// A variant of class-deduction101.C where the template placeholder is a 
> template
> +// template parameter and ReturnType has more levels than CallableTraitT.
> +
> +template<class R>
> +struct function {
> +  template<class T> function(T);
> +  using type = R;
> +};
> +
> +template<class T> function(T) -> function<decltype(T()())>;
> +
> +template<class T>
> +struct CallableTrait;
> +
> +template<class R>
> +struct CallableTrait<function<R>> { using ReturnType = R; };
> +
> +template<class F, template<class> class Tmpl>
> +using CallableTraitT = CallableTrait<decltype(Tmpl{F()})>;
> +
> +template<class>
> +struct A {
> +  template<class F, template<class> class Tmpl>
> +  using ReturnType = typename CallableTraitT<F, Tmpl>::ReturnType;
> +};
> +
> +using type = A<int>::ReturnType<int(*)(), function>;
> +using type = int;
> diff --git a/gcc/testsuite/g++.dg/other/pr88187.C 
> b/gcc/testsuite/g++.dg/other/pr88187.C
> index 13466d3ce57..c33644d14ca 100644
> --- a/gcc/testsuite/g++.dg/other/pr88187.C
> +++ b/gcc/testsuite/g++.dg/other/pr88187.C
> @@ -4,4 +4,4 @@
>  template <int> struct A;
>  void f (A ()); // { dg-error "6:variable or field 'f' declared void" "" { 
> target c++14_down } }
>                 // { dg-error "missing template arguments before '\\(' token" 
> "" { target c++14_down } .-1 }
> -               // { dg-error "placeholder .A. not permitted in this context" 
> "" { target c++17 } .-2 }
> +               // { dg-error "deduced class type .A. in function return 
> type" "" { target c++17 } .-2 }
> --
> 2.34.1.390.g2ae0a9cb82

Reply via email to