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. > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > > trunk? > > > > PR c++/91911 > > > > gcc/cp/ChangeLog: > > > > * pt.c (lookup_template_class_1): When looking up a dependent > > alias template specialization, pass tf_partial to tsubst. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp1z/class-deduction92.C: New test. > > --- > > gcc/cp/pt.c | 7 +++++- > > .../g++.dg/cpp1z/class-deduction92.C | 17 +++++++++++++ > > .../g++.dg/cpp1z/class-deduction93.C | 25 +++++++++++++++++++ > > 3 files changed, 48 insertions(+), 1 deletion(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction92.C > > create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction93.C > > > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > > index f73c7471a33..23c5f515716 100644 > > --- a/gcc/cp/pt.c > > +++ b/gcc/cp/pt.c > > @@ -9954,7 +9954,12 @@ lookup_template_class_1 (tree d1, tree arglist, tree > > in_decl, tree context, > > template-arguments for the template-parameters in the > > type-id of the alias template. */ > > - 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); > > /* Note that the call above (by indirectly calling > > register_specialization in tsubst_decl) registers the > > TYPE_DECL representing the specialization of the alias > > diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C > > b/gcc/testsuite/g++.dg/cpp1z/class-deduction92.C > > new file mode 100644 > > index 00000000000..ae3c55508b2 > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction92.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-deduction93.C > > b/gcc/testsuite/g++.dg/cpp1z/class-deduction93.C > > new file mode 100644 > > index 00000000000..eebc986832e > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction93.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 = decltype(function{F()}); > > + > > +template<class F> > > +using ReturnType = typename CallableTraitT<F>::type; > > + > > +using type = ReturnType<int(*)()>; > > +using type = int; > > > >