Re: [PATCH] c++: CTAD within alias template [PR91911]

2022-01-19 Thread Jason Merrill via Gcc-patches

On 1/19/22 17:32, Patrick Palka wrote:

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  wrote:


On Wed, Jun 30, 2021 at 4:23 PM Jason Merrill 
wrote:


On 6/30/21 4:18 PM, Patrick Palka wrote:

On Wed, Jun 30, 2021 at 3:51 PM Jason Merrill 
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 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
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 is a member template of say the class
template A (and
thus its level is greater than ConstSpanType):

template
struct A {
  template
  using SpanType = decltype(span(T()));
};

template
using ConstSpanType = span::SpanType::value_type>;

using type = ConstSpanType;

then this case luckily works even without the patch
because
instantiate_class_template now reuses the specialization
A::SpanType
that was formed earlier during instantiation of A,
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::SpanType with say A::SpanType
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 is a
member
template of
say the class template B (and thus its level is
greater
than
SpanType):

template
using SpanType = decltype(span(T()));

template
struct B {
  template
  using ConstSpanType = span::value_type>;
};

using type = B::ConstSpanType;

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 with SpanType's substituted definition.
But

Re: [PATCH] c++: CTAD within alias template [PR91911]

2022-01-19 Thread Patrick Palka via Gcc-patches
> > > > > > thus its level is greater than ConstSpanType):
> > > > > > > > > > > > 
> > > > > > > > > > > >template
> > > > > > > > > > > >struct A {
> > > > > > > > > > > >  template
> > > > > > > > > > > >  using SpanType = decltype(span(T()));
> > > > > > > > > > > >};
> > > > > > > > > > > > 
> > > > > > > > > > > >template
> > > > > > > > > > > >using ConstSpanType = span > > > > > > > > > > > A::SpanType::value_type>;
> > > > > > > > > > > > 
> > > > > > > > > > > >using type = ConstSpanType;
> > > > > > > > > > > > 
> > > > > > > > > > > > then this case luckily works even without the patch
> > > > > > > > > > > > because
> > > > > > > > > > > > instantiate_class_template now reuses the specialization
> > > > > > > > > > > > A::SpanType
> > > > > > > > > > > > that was formed earlier during instantiation of A,
> > > > > > > > > > > > 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::SpanType with say A::SpanType
> > > > > > > > > > > > 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 is a
> > > > > > > > > > > > member
> > > > > > > > > > > > template of
> > > > > > > > > > > > say the class template B (and thus its level is
> > > > > > > > > > > > greater
> > > > > > > > > > > > than
> > > > > > > > > > > > SpanType):
> > > > > > > > > > > > 
> > > > > > > > > > > >template
> > > > > > > > > > > >using SpanType = decltype(span(T()));
> > > > > > > > > > > > 
> > > > > > > > > > > >template
> > > > > > > > > > > >struct B {
> > > > > > > > > > > >  template
> > > > > > > > > > > >

Re: [PATCH] c++: CTAD within alias template [PR91911]

2022-01-19 Thread Jason Merrill via Gcc-patches

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  wrote:


On Wed, Jun 30, 2021 at 4:23 PM Jason Merrill  wrote:


On 6/30/21 4:18 PM, Patrick Palka wrote:

On Wed, Jun 30, 2021 at 3:51 PM Jason Merrill 
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 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
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 is a member template of say the class
template A (and
thus its level is greater than ConstSpanType):

   template
   struct A {
 template
 using SpanType = decltype(span(T()));
   };

   template
   using ConstSpanType = span::SpanType::value_type>;

   using type = ConstSpanType;

then this case luckily works even without the patch because
instantiate_class_template now reuses the specialization
A::SpanType
that was formed earlier during instantiation of A,
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::SpanType with say A::SpanType 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 is a member
template of
say the class template B (and thus its level is greater
than
SpanType):

   template
   using SpanType = decltype(span(T()));

   template
   struct B {
 template
 using ConstSpanType = span::value_type>;
   };

   using type = B::ConstSpanType;

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 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 lat

Re: [PATCH] c++: CTAD within alias template [PR91911]

2022-01-19 Thread Patrick Palka via Gcc-patches
; > > > > >
> > > > > > > > > > >   template
> > > > > > > > > > >   using ConstSpanType = span > > > > > > > > > > A::SpanType::value_type>;
> > > > > > > > > > >
> > > > > > > > > > >   using type = ConstSpanType;
> > > > > > > > > > >
> > > > > > > > > > > then this case luckily works even without the patch 
> > > > > > > > > > > because
> > > > > > > > > > > instantiate_class_template now reuses the specialization
> > > > > > > > > > > A::SpanType
> > > > > > > > > > > that was formed earlier during instantiation of A,
> > > > > > > > > > > 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::SpanType with say A::SpanType 
> > > > > > > > > > > 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 is a member
> > > > > > > > > > > template of
> > > > > > > > > > > say the class template B (and thus its level is greater
> > > > > > > > > > > than
> > > > > > > > > > > SpanType):
> > > > > > > > > > >
> > > > > > > > > > >   template
> > > > > > > > > > >   using SpanType = decltype(span(T()));
> > > > > > > > > > >
> > > > > > > > > > >   template
> > > > > > > > > > >   struct B {
> > > > > > > > > > > template
> > > > > > > > > > > using ConstSpanType = span > > > > > > > > > > SpanType::value_type>;
> > > > > > > > > > >   };
> > > > > > > > > > >
> > > > > > > > > > >   using type = B::ConstSpanType;
> > > > > > > > > > >
> > > > > > > > > > > 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..
> > > > > > > &

Re: [PATCH] c++: CTAD within alias template [PR91911]

2022-01-06 Thread Patrick Palka via Gcc-patches
t; > > > 
> > > > > > > > > > >   template
> > > > > > > > > > >   using ConstSpanType = span > > > > > > > > > > A::SpanType::value_type>;
> > > > > > > > > > > 
> > > > > > > > > > >   using type = ConstSpanType;
> > > > > > > > > > > 
> > > > > > > > > > > then this case luckily works even without the patch 
> > > > > > > > > > > because
> > > > > > > > > > > instantiate_class_template now reuses the specialization
> > > > > > > > > > > A::SpanType
> > > > > > > > > > > that was formed earlier during instantiation of A,
> > > > > > > > > > > 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::SpanType with say A::SpanType 
> > > > > > > > > > > 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 is a member
> > > > > > > > > > > template of
> > > > > > > > > > > say the class template B (and thus its level is greater
> > > > > > > > > > > than
> > > > > > > > > > > SpanType):
> > > > > > > > > > > 
> > > > > > > > > > >   template
> > > > > > > > > > >   using SpanType = decltype(span(T()));
> > > > > > > > > > > 
> > > > > > > > > > >   template
> > > > > > > > > > >   struct B {
> > > > > > > > > > > template
> > > > > > > > > > > using ConstSpanType = span > > > > > > > > > > SpanType::value_type>;
> > > > > > > > > > >   };
> > > > > > > > > > > 
> > > > > > > > > > >   using type = B::ConstSpanType;
> > > > > > > > > > > 
> > > > > > > > > > > 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..
> > > > > > &g

Re: [PATCH] c++: CTAD within alias template [PR91911]

2022-01-03 Thread Patrick Palka via Gcc-patches
ecause
> > > > > > > > > > instantiate_class_template now reuses the specialization
> > > > > > > > > > A::SpanType
> > > > > > > > > > that was formed earlier during instantiation of A,
> > > > > > > > > > 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::SpanType with say A::SpanType 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 is a member
> > > > > > > > > > template of
> > > > > > > > > > say the class template B (and thus its level is greater
> > > > > > > > > > than
> > > > > > > > > > SpanType):
> > > > > > > > > > 
> > > > > > > > > >   template
> > > > > > > > > >       using SpanType = decltype(span(T()));
> > > > > > > > > > 
> > > > > > > > > >   template
> > > > > > > > > >   struct B {
> > > > > > > > > > template
> > > > > > > > > > using ConstSpanType = span > > > > > > > > > SpanType::value_type>;
> > > > > > > > > >   };
> > > > > > > > > > 
> > > > > > > > > >   using type = B::ConstSpanType;
> > > > > > > > > > 
> > > > > > > > > > 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:
> > > > > > > > > &g

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-12-21 Thread Jason Merrill via Gcc-patches

On 12/21/21 14:08, Patrick Palka wrote:

On Tue, Dec 21, 2021 at 2:03 PM Patrick Palka  wrote:


On Wed, Jun 30, 2021 at 4:23 PM Jason Merrill  wrote:


On 6/30/21 4:18 PM, Patrick Palka wrote:

On Wed, Jun 30, 2021 at 3:51 PM Jason Merrill  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 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
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 is a member template of say the class template A (and
thus its level is greater than ConstSpanType):

  template
  struct A {
template
using SpanType = decltype(span(T()));
  };

  template
  using ConstSpanType = span::SpanType::value_type>;

  using type = ConstSpanType;

then this case luckily works even without the patch because
instantiate_class_template now reuses the specialization A::SpanType
that was formed earlier during instantiation of A, 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::SpanType with say A::SpanType 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 is a member template of
say the class template B (and thus its level is greater than
SpanType):

  template
  using SpanType = decltype(span(T()));

  template
  struct B {
template
using ConstSpanType = span::value_type>;
  };

  using type = B::ConstSpanType;

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 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 erroneously
substitutes this CTAD placeholder with the template argument

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-12-21 Thread Patrick Palka via Gcc-patches
l 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 is a member template of
> > >>>>>> say the class template B (and thus its level is greater than
> > >>>>>> SpanType):
> > >>>>>>
> > >>>>>>  template
> > >>>>>>  using SpanType = decltype(span(T()));
> > >>>>>>
> > >>>>>>  template
> > >>>>>>  struct B {
> > >>>>>>template
> > >>>>>>using ConstSpanType = span > >>>>>> SpanType::value_type>;
> > >>>>>>  };
> > >>>>>>
> > >>>>>>  using type = B::ConstSpanType;
> > >>>>>>
> > >>>>>> 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 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 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 l

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-12-21 Thread Patrick Palka via Gcc-patches
ype = decltype(span(T()));
> >>>>>>
> >>>>>>  template
> >>>>>>  struct B {
> >>>>>>template
> >>>>>>using ConstSpanType = span >>>>>> SpanType::value_type>;
> >>>>>>  };
> >>>>>>
> >>>>>>  using type = B::ConstSpanType;
> >>>>>>
> >>>>>> 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 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 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 tsubs

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-30 Thread Jason Merrill via Gcc-patches

On 6/30/21 4:18 PM, Patrick Palka wrote:

On Wed, Jun 30, 2021 at 3:51 PM Jason Merrill  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 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
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 is a member template of say the class template A (and
thus its level is greater than ConstSpanType):

 template
 struct A {
   template
   using SpanType = decltype(span(T()));
 };

 template
 using ConstSpanType = span::SpanType::value_type>;

 using type = ConstSpanType;

then this case luckily works even without the patch because
instantiate_class_template now reuses the specialization A::SpanType
that was formed earlier during instantiation of A, 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::SpanType with say A::SpanType 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 is a member template of
say the class template B (and thus its level is greater than
SpanType):

 template
 using SpanType = decltype(span(T()));

 template
 struct B {
   template
   using ConstSpanType = span::value_type>;
 };

 using type = B::ConstSpanType;

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 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 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
substitutin

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-30 Thread Patrick Palka via Gcc-patches
 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 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 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 Tmpl>
using CallableTraitT = CallableTrait;

template
struct A {
  template class Tmpl>
  using ReturnType = typename CallableTraitT::ReturnType;
};

using type = A::ReturnType;
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.


>
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> > trunk?
> >
> 

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-30 Thread Jason Merrill via Gcc-patches

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 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
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 is a member template of say the class template A (and
thus its level is greater than ConstSpanType):

template
struct A {
  template
  using SpanType = decltype(span(T()));
};

template
using ConstSpanType = span::SpanType::value_type>;

using type = ConstSpanType;

then this case luckily works even without the patch because
instantiate_class_template now reuses the specialization A::SpanType
that was formed earlier during instantiation of A, 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::SpanType with say A::SpanType 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 is a member template of
say the class template B (and thus its level is greater than
SpanType):

template
using SpanType = decltype(span(T()));

template
struct B {
  template
  using ConstSpanType = span::value_type>;
};

using type = B::ConstSpanType;

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 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 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
pas

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-30 Thread Patrick Palka via Gcc-patches
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 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
> > > > > 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 is a member template of say the class template A (and
> > > thus its level is greater than ConstSpanType):
> > > 
> > >template
> > >struct A {
> > >  template
> > >  using SpanType = decltype(span(T()));
> > >};
> > > 
> > >template
> > >using ConstSpanType = span > > A::SpanType::value_type>;
> > > 
> > >using type = ConstSpanType;
> > > 
> > > then this case luckily works even without the patch because
> > > instantiate_class_template now reuses the specialization 
> > > A::SpanType
> > > that was formed earlier during instantiation of A, 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::SpanType with say A::SpanType 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 is a member template of
> > > say the class template B (and thus its level is greater than
> > > SpanType):
> > > 
> > >template
> > >using SpanType = decltype(span(T()));
> > > 
> > >template
> > >struct B {
> > >  template
> > >  using ConstSpanType = span::value_type>;
> > >};
> > > 
> > >using type = B::ConstSpanType;
> > > 
> > > 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_fl

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-30 Thread Patrick Palka via Gcc-patches
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 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
> > > > 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 is a member template of say the class template A (and
> > thus its level is greater than ConstSpanType):
> > 
> >template
> >struct A {
> >  template
> >  using SpanType = decltype(span(T()));
> >};
> > 
> >template
> >using ConstSpanType = span > A::SpanType::value_type>;
> > 
> >using type = ConstSpanType;
> > 
> > then this case luckily works even without the patch because
> > instantiate_class_template now reuses the specialization A::SpanType
> > that was formed earlier during instantiation of A, 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::SpanType with say A::SpanType 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 is a member template of
> > say the class template B (and thus its level is greater than
> > SpanType):
> > 
> >template
> >using SpanType = decltype(span(T()));
> > 
> >template
> >struct B {
> >  template
> >  using ConstSpanType = span::value_type>;
> >};
> > 
> >using type = B::ConstSpanType;
> > 
> > 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 th

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-25 Thread Jason Merrill via Gcc-patches

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 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
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 is a member template of say the class template A (and
thus its level is greater than ConstSpanType):

   template
   struct A {
 template
 using SpanType = decltype(span(T()));
   };

   template
   using ConstSpanType = span::SpanType::value_type>;

   using type = ConstSpanType;

then this case luckily works even without the patch because
instantiate_class_template now reuses the specialization A::SpanType
that was formed earlier during instantiation of A, 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::SpanType with say A::SpanType 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 is a member template of
say the class template B (and thus its level is greater than
SpanType):

   template
   using SpanType = decltype(span(T()));

   template
   struct B {
 template
 using ConstSpanType = span::value_type>;
   };

   using type = B::ConstSpanType;

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.




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 it

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-25 Thread Patrick Palka via Gcc-patches
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 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
> > 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 is a member template of say the class template A (and
thus its level is greater than ConstSpanType):

  template
  struct A {
template
using SpanType = decltype(span(T()));
  };

  template
  using ConstSpanType = span::SpanType::value_type>;

  using type = ConstSpanType;

then this case luckily works even without the patch because
instantiate_class_template now reuses the specialization A::SpanType
that was formed earlier during instantiation of A, 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::SpanType with say A::SpanType 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 is a member template of
say the class template B (and thus its level is greater than
SpanType):

  template
  using SpanType = decltype(span(T()));

  template
  struct B {
template
using ConstSpanType = span::value_type>;
  };

  using type = B::ConstSpanType;

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

Re: [PATCH] c++: CTAD within alias template [PR91911]

2021-06-25 Thread Jason Merrill via Gcc-patches

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 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
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?



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 000..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
+struct span {
+  using value_type = T;
+  span(T);
+};
+
+template
+using SpanType = decltype(span(T()));
+
+template
+using ConstSpanType = span::value_type>;
+
+using type = ConstSpanType;
+using type = span;
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 000..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
+struct function {
+  template function(T) { }
+  using type = R;
+};
+
+template function(T) -> function;
+
+template
+struct CallableTrait;
+
+template
+struct CallableTrait> { using ReturnType = R; };
+
+template
+using CallableTraitT = decltype(function{F()});
+
+template
+using ReturnType = typename CallableTraitT::type;
+
+using type = ReturnType;
+using type = int;





[PATCH] c++: CTAD within alias template [PR91911]

2021-06-24 Thread Patrick Palka via Gcc-patches
In the first testcase below, during parsing of the alias template
ConstSpanType, transparency of alias template specializations means we
replace SpanType 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
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.

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 000..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
+struct span {
+  using value_type = T;
+  span(T);
+};
+
+template
+using SpanType = decltype(span(T()));
+
+template
+using ConstSpanType = span::value_type>;
+
+using type = ConstSpanType;
+using type = span;
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 000..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
+struct function {
+  template function(T) { }
+  using type = R;
+};
+
+template function(T) -> function;
+
+template
+struct CallableTrait;
+
+template
+struct CallableTrait> { using ReturnType = R; };
+
+template
+using CallableTraitT = decltype(function{F()});
+
+template
+using ReturnType = typename CallableTraitT::type;
+
+using type = ReturnType;
+using type = int;
-- 
2.32.0.93.g670b81a890