[PATCH v2] c++: Endless loop with targ deduction in member tmpl [PR95888]

Jason Merrill jason@redhat.com
Thu Feb 11 20:47:20 GMT 2021


On 2/11/21 3:06 PM, Marek Polacek wrote:
> 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.

OK with that corrected.  :)

Jason



More information about the Gcc-patches mailing list