On Thu, Feb 11, 2021 at 02:24:22PM -0500, Marek Polacek via Gcc-patches wrote:
> On Thu, Feb 11, 2021 at 11:30:07AM -0500, Jason Merrill via Gcc-patches wrote:
> > On 2/9/21 5:41 PM, Marek Polacek wrote:
> > > My r10-7007 patch tweaked tsubst not to reduce the template level of
> > > template parameters when tf_partial.  That caused infinite looping in
> > > is_specialization_of: we ended up with a class template specialization
> > > whose TREE_TYPE (CLASSTYPE_TI_TEMPLATE (t)) == t, so the second for
> > > loop in is_specialization_of never finished.
> > > 
> > > There's a lot going on in this test, but essentially: the template fn
> > > here has two template parameters, we call it with one explicitly
> > > provided, the other one has to be deduced.  So we'll find ourselves
> > > in fn_type_unification which uses tf_partial when tsubsting the
> > > *explicit* template arguments into the function type.  That leads to
> > > tsubstituting the return type, C<T>.  C is a member template; its
> > > most general template is
> > > 
> > >    template<class U> template<class V> struct B<U>::C
> > > 
> > > we figure out (tsubst_template_args) that the template argument list
> > > is <int, int>.  They come from different levels, one comes from B<int>,
> > > the other one from fn<int>.
> > > 
> > > So now we lookup_template_class to see if we have C<int, int>.  We
> > > do the
> > >    /* This is a full instantiation of a member template.  Find
> > >       the partial instantiation of which this is an instance.  */
> > >    TREE_VEC_LENGTH (arglist)--;
> > >    // arglist is now <int>, not <int, int>
> > >    found = tsubst (gen_tmpl, arglist, complain, NULL_TREE);
> > >    TREE_VEC_LENGTH (arglist)++;
> > > 
> > > magic which is looking for the partial instantiation, in this case,
> > > that would be template<class V> struct B<int>::C.  Note we're still
> > > in a tf_partial context!  So the tsubst_template_args in the tsubst
> > > (which tries to substitute <int> into <U, V>) returns <int, V>, but
> > > V's template level hasn't been reduced!  After tsubst_template_args,
> > > tsubst_template_decl looks to see if we already have this specialization:
> > > 
> > >    // t = template_decl C
> > >    // full_args = <int, V>
> > >    spec = retrieve_specialization (t, full_args, hash);
> > > 
> > > but doesn't find the one we created a while ago, when processing
> > > B<int> b; in the test, because V's levels don't match.  Whereupon
> > > tsubst_template_decl creates a new TEMPLATE_DECL, one that leads to
> > > the infinite looping problem.
> > > 
> > > I think let's clear tf_partial when looking for an existing partial
> > > instantiation.
> > > 
> > > It also occurred to me that I should be able to trigger a similar
> > > problem with 'auto', since r10-7007 removed an is_auto check.  And lo,
> > > I constructed deduce10.C which exhibits the same issue with pre-r10-7007
> > > compilers.  This patch fixes that problem as well.  I'm ecstatic.
> > > 
> > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10?  Also
> > > built cmcstl2.
> > 
> > Perhaps the mistake here is using the complain parm at all; this
> > substitution is not in the "immediate context" of deduction.  Either tf_none
> > or tf_warning_or_error should be fine, as we know this substitution has
> > previously succeeded.
> 
> Yeah, that makes sense to me too:
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/10?
> 
> -- >8 --
> My r10-7007 patch tweaked tsubst not to reduce the template level of
> template parameters when tf_partial.  That caused infinite looping in
> is_specialization_of: we ended up with a class template specialization
> whose TREE_TYPE (CLASSTYPE_TI_TEMPLATE (t)) == t, so the second for
> loop in is_specialization_of never finished.
> 
> There's a lot going on in this test, but essentially: the template fn
> here has two template parameters, we call it with one explicitly
> provided, the other one has to be deduced.  So we'll find ourselves
> in fn_type_unification which uses tf_partial when tsubsting the
> *explicit* template arguments into the function type.  That leads to
> tsubstituting the return type, C<T>.  C is a member template; its
> most general template is
> 
>   template<class U> template<class V> struct B<U>::C
> 
> we figure out (tsubst_template_args) that the template argument list
> is <int, int>.  They come from different levels, one comes from B<int>,
> the other one from fn<int>.
> 
> So now we lookup_template_class to see if we have C<int, int>.  We
> do the
>   /* This is a full instantiation of a member template.  Find
>      the partial instantiation of which this is an instance.  */
>   TREE_VEC_LENGTH (arglist)--;
>   // arglist is now <int>, not <int, int>
>   found = tsubst (gen_tmpl, arglist, complain, NULL_TREE);
>   TREE_VEC_LENGTH (arglist)++;
> 
> magic which is looking for the partial instantiation, in this case,
> that would be template<class V> struct B<int>::C.  Note we're still
> in a tf_partial context!  So the tsubst_template_args in the tsubst
> (which tries to substitute <int> into <U, V>) returns <int, V>, but
> V's template level hasn't been reduced!  After tsubst_template_args,
> tsubst_template_decl looks to see if we already have this specialization:
> 
>   // t = template_decl C
>   // full_args = <int, V>
>   spec = retrieve_specialization (t, full_args, hash);
> 
> but doesn't find the one we created a while ago, when processing
> B<int> b; in the test, because V's levels don't match.  Whereupon
> tsubst_template_decl creates a new TEMPLATE_DECL, one that leads to
> the infinite looping problem.
> 
> Fixed by using tf_none when looking for an existing partial instantiation.
> 
> It also occurred to me that I should be able to trigger a similar
> problem with 'auto', since r10-7007 removed an is_auto check.  And lo,
> I constructed deduce10.C which exhibits the same issue with pre-r10-7007
> compilers.  This patch fixes that problem as well.  I'm ecstatic.
> 
> gcc/cp/ChangeLog:
> 
>       PR c++/95888
>       * pt.c (lookup_template_class_1): Clear tf_partial when looking for
>       the partial instantiation.
And clearly this CL needs to be updated to reflect the changes in v2.

Marek

Reply via email to