On Wed, 19 Jan 2022, Jason Merrill wrote: > On 1/3/22 10:24, Patrick Palka 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. > > > > -- >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), > > This seems like it could go in separately.
Like so? -- >8 Subject: [PATCH] c++: consistently diagnose bare CTAD placeholder as fn return type Relax slightly the existing code for diagnosing a bare CTAD placeholder as the return type of a function declarator to also handle the abstract declarator case. gcc/cp/ChangeLog: * decl.cc (grokdeclarator): Diagnose CTAD placeholder in function return type even when !funcdecl_p. gcc/testsuite/ChangeLog: * g++.dg/other/pr88187.C: Adjust expected diagnostic accordingly. --- gcc/cp/decl.cc | 6 +++--- gcc/testsuite/g++.dg/other/pr88187.C | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 2dade75c489..1cbe9a34be0 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -12647,11 +12647,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/testsuite/g++.dg/other/pr88187.C b/gcc/testsuite/g++.dg/other/pr88187.C index 13466d3ce57..7812e3fd5b3 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.35.0.rc1 -- >8 -- > > > 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) > > Isn't this code still needed for non-CTAD autos? Probably only in theory -- the safeguard was added over 4 years ago so I presume it's dead code by now. Shall we keep it and guard it with !template_placeholder_p (auto_node) instead? > > > @@ -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 } > >