On Wed, 14 May 2025, Patrick Palka wrote:

> On Wed, 14 May 2025, Jason Merrill wrote:
> 
> > On 5/12/25 7:53 PM, Patrick Palka wrote:
> > > Bootstrapped and regtested on x86-64-pc-linux-gnu, does this look OK
> > > for trunk/15/14?
> > > 
> > > -- >8 --
> > > 
> > > Here unification of P=Wrap<int>::type, A=Wrap<long>::type wrongly
> > > succeeds ever since r14-4112 which made the RECORD_TYPE case of unify
> > > no longer recurse into template arguments for non-primary templates
> > > (since they're a non-deduced context) and so the int/long mismatch that
> > > makes the two types distinct goes unnoticed.
> > > 
> > > In the case of (comparing specializations of) a non-primary template,
> > > unify should still go on to compare the types directly before returning
> > > success.
> > 
> > Should the PRIMARY_TEMPLATE_P check instead move up to join the
> > CLASSTYPE_TEMPLATE_INFO check?  try_class_deduction also doesn't seem
> > applicable to non-primary templates.
> 
> I don't think that'd work, for either the CLASSTYPE_TEMPLATE_INFO (parm) check
> or the earlier CLASSTYPE_TEMPLATE_INFO (arg) check.
> 
> While try_class_deduction directly doesn't apply to non-primary templates,
> get_template_base still might, so if we move up the PRIMARY_TEMPLATE_P to join
> the C_T_I (parm) check, then we wouldn't try get_template_base anymore which
> would  break e.g.
> 
>     template<class T> struct B { };
> 
>     template<class T>
>     struct A {
>       struct C : B<int> { };
>     };
> 
>     template<class T> void f(B<T>*);
> 
>     int main() {
>       A<int>::C c;
>       f(&c);
>     }
> 
> If we move the PRIMARY_TEMPLATE_P check up to the C_T_I (arg) check, then
> that'd mean we still don't check same_type_p on the two types in the
> non-primary case, which seems wrong (although it'd fix the PR thanks to the
> parm == arg early exit in unify).

FWIW it seems part of the weird/subtle logic here is due to the fact
that when unifying e.g. P=C<T> with A=C<int>, we do it twice, first via
try_class_deduction using a copy of 'targs', and if that succeeds we do
it again with the real 'targs'.  I think the logic could simultaneously
be simplified and made memory efficient if we made it so that if the
trial unification from try_class_deduction succeeds we just use its
'targs' instead of having to repeat the unification.

> 
> > 
> > >   PR c++/120161
> > > 
> > > gcc/cp/ChangeLog:
> > > 
> > >   * pt.cc (unify) <case RECORD_TYPE>: When comparing specializations
> > >   of a non-primary template, still perform a type comparison.
> > > 
> > > gcc/testsuite/ChangeLog:
> > > 
> > >   * g++.dg/template/unify13.C: New test.
> > > ---
> > >   gcc/cp/pt.cc                            |  6 +++---
> > >   gcc/testsuite/g++.dg/template/unify13.C | 18 ++++++++++++++++++
> > >   2 files changed, 21 insertions(+), 3 deletions(-)
> > >   create mode 100644 gcc/testsuite/g++.dg/template/unify13.C
> > > 
> > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > > index 0d64a1cfb128..868dd0e2b3ff 100644
> > > --- a/gcc/cp/pt.cc
> > > +++ b/gcc/cp/pt.cc
> > > @@ -25785,10 +25785,10 @@ unify (tree tparms, tree targs, tree parm, tree
> > > arg, int strict,
> > >                             INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS 
> > > (parm)),
> > >                             INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS 
> > > (t)),
> > >                             UNIFY_ALLOW_NONE, explain_p);
> > > -   else
> > > -     return unify_success (explain_p);
> > > +   gcc_checking_assert (t == arg);
> > >           }
> > > -      else if (!same_type_ignoring_top_level_qualifiers_p (parm, arg))
> > > +
> > > +      if (!same_type_ignoring_top_level_qualifiers_p (parm, arg))
> > >           return unify_type_mismatch (explain_p, parm, arg);
> > >         return unify_success (explain_p);
> > >   diff --git a/gcc/testsuite/g++.dg/template/unify13.C
> > > b/gcc/testsuite/g++.dg/template/unify13.C
> > > new file mode 100644
> > > index 000000000000..ec7ca9d17a44
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.dg/template/unify13.C
> > > @@ -0,0 +1,18 @@
> > > +// PR c++/120161
> > > +
> > > +template<class T, class U>
> > > +struct mp_list { };
> > > +
> > > +template<class T>
> > > +struct Wrap { struct type { }; };
> > > +
> > > +struct A : mp_list<Wrap<int>::type, void>
> > > +         , mp_list<Wrap<long>::type, void> { };
> > > +
> > > +template<class U>
> > > +void f(mp_list<Wrap<int>::type, U>*);
> > > +
> > > +int main() {
> > > +  A a;
> > > +  f(&a);
> > > +}
> > 
> > 
> 

Reply via email to