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