On Fri, 9 Aug 2024, Jason Merrill wrote:
> On 8/9/24 2:32 PM, Patrick Palka wrote:
> > On Fri, 9 Aug 2024, Patrick Palka wrote:
> >
> > > On Fri, 9 Aug 2024, Jason Merrill wrote:
> > >
> > > > On 8/8/24 1:00 PM, Patrick Palka wrote:
> > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> > > > > look OK for trunk/14?
> > > > >
> > > > > -- >8 --
> > > > >
> > > > > This implements the inherited vs non-inherited guide tiebreaker
> > > > > specified by P2582R1. In order to track inherited-ness of a guide
> > > > > it seems natural to reuse the lang_decl_fn::context field that already
> > > > > tracks inherited-ness of a constructor.
> > > > >
> > > > > This patch also works around CLASSTYPE_CONSTRUCTORS apparently not
> > > > > always containing all inherited constructors, by iterating over
> > > > > TYPE_FIELDS instead.
> > > > >
> > > > > This patch also makes us recognize another written form of inherited
> > > > > constructor, 'using Base<T>::Base::Base' whose USING_DECL_SCOPE is a
> > > > > TYPENAME_TYPE.
> > > > >
> > > > > PR c++/116276
> > > > >
> > > > > gcc/cp/ChangeLog:
> > > > >
> > > > > * call.cc (joust): Implement P2582R1 inherited vs
> > > > > non-inherited
> > > > > guide tiebreaker.
> > > > > * cp-tree.h (lang_decl_fn::context): Document usage in
> > > > > deduction_guide_p FUNCTION_DECLs.
> > > > > (inherited_guide_p): Declare.
> > > > > * pt.cc (inherited_guide_p): Define.
> > > > > (set_inherited_guide_context): Define.
> > > > > (alias_ctad_tweaks): Use set_inherited_guide_context.
> > > > > (inherited_ctad_tweaks): Recognize some inherited constructors
> > > > > whose scope is a TYPENAME_TYPE.
> > > > > (ctor_deduction_guides_for): For C++23 inherited CTAD, loop
> > > > > over TYPE_FIELDS instead of using CLASSTYPE_CONSTRUCTORS to
> > > > > recognize all relevant using-decls.
> > > > >
> > > > > gcc/testsuite/ChangeLog:
> > > > >
> > > > > * g++.dg/cpp23/class-deduction-inherited4.C: Extend test.
> > > > > * g++.dg/cpp23/class-deduction-inherited5.C: New test.
> > > > > ---
> > > > > gcc/cp/call.cc | 22 +++++++++
> > > > > gcc/cp/cp-tree.h | 8 +++-
> > > > > gcc/cp/pt.cc | 45
> > > > > +++++++++++++++----
> > > > > .../g++.dg/cpp23/class-deduction-inherited4.C | 15 ++++++-
> > > > > .../g++.dg/cpp23/class-deduction-inherited5.C | 25 +++++++++++
> > > > > 5 files changed, 103 insertions(+), 12 deletions(-)
> > > > > create mode 100644
> > > > > gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C
> > > > >
> > > > > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > > > > index a75e2e5e3af..3287f77b59b 100644
> > > > > --- a/gcc/cp/call.cc
> > > > > +++ b/gcc/cp/call.cc
> > > > > @@ -13261,6 +13261,28 @@ joust (struct z_candidate *cand1, struct
> > > > > z_candidate *cand2, bool warn,
> > > > > else if (cand2->rewritten ())
> > > > > return 1;
> > > > > + /* F1 and F2 are generated from class template argument
> > > > > deduction for a
> > > > > class
> > > > > + D, and F2 is generated from inheriting constructors from a base
> > > > > class
> > > > > of D
> > > > > + while F1 is not, and for each explicit function argument, the
> > > > > corresponding
> > > > > + parameters of F1 and F2 are either both ellipses or have the
> > > > > same type
> > > > > */
> > > > > + if (deduction_guide_p (cand1->fn))
> > > > > + {
> > > > > + bool inherited1 = inherited_guide_p (cand1->fn);
> > > > > + bool inherited2 = inherited_guide_p (cand2->fn);
> > > > > + if (int diff = inherited2 - inherited1)
> > > > > + {
> > > > > + for (i = 0; i < len; ++i)
> > > > > + {
> > > > > + conversion *t1 = cand1->convs[i + off1];
> > > > > + conversion *t2 = cand2->convs[i + off2];
> > > > > + if (!same_type_p (t1->type, t2->type))
> > > >
> > > > I'm not sure this comparison distinguishes between ellipse and
> > > > non-ellipse?
> > > > There doesn't seem to be a testcase for that.
> > >
> > > Unfortunately I haven't been able to come up with a testcase for
> > > the ellipses logic :/ It seems the earlier ICS comparison would always
> > > break the tie first if one parameter is an ellipsis but not the other?
>
> Just mention that in a comment, then.
>
> > > Is there a known example that perhaps motivated the wording?
>
> I'm failing to find anything, it appeared between R0 and R1 without a
> corresponding comment on the wiki.
>
> > > Relatidely I noticed that unlike the inherited guide tiebreaker, the
> > > inherited ctor tiebreaker doesn't mention ellipses, which seems
> > > surprising: https://eel.is/c++draft/over.match.best#general-2.7
> >
> > Here's v2 which is just a tidied up version of v1 along with an extra
> > test (no additional functional change):
> >
> > -- >8 --
> >
> > Subject: [PATCH] c++: inherited CTAD fixes [PR116276]
> >
> > This implements the overlooked inherited vs non-inherited guide
> > tiebreaker from P2582R1. In order to track inherited-ness of a guide
> > it seems natural to reuse the lang_decl_fn::context field which also
> > tracks inherited-ness of a constructor.
> >
> > This patch also works around CLASSTYPE_CONSTRUCTORS not reliably
> > returning all inherited constructors, by iterating over TYPE_FIELDS
> > instead.
>
> Did you investigate why that is?
For e.g.
template<class T>
struct B { };
template<class T>
struct A : B<T> {
A(); // target_bval
using B<T>::B; // decl
};
it's because of an early exit in push_class_level_binding:
else if (TREE_CODE (decl) == USING_DECL
&& DECL_DEPENDENT_P (decl)
&& OVL_P (target_bval))
/* The new dependent using beats an old overload. */
old_decl = bval;
...
if (old_decl && binding->scope == class_binding_level)
{
binding->value = x;
/* It is always safe to clear INHERITED_VALUE_BINDING_P
here. This function is only used to register bindings
from with the class definition itself. */
INHERITED_VALUE_BINDING_P (binding) = 0;
return true;
}
and so we don't call supplement_binding which seems to be what's
responsible for augmenting the binding properly. After the early
exit, CLASSTYPE_CONSTRUCTORS still returns only A().
Note that if we swap the order of the two member declarations above
then CLASSTYPE_CONSTRUCTORS at the end will return only the using.
So the result of name lookup in this case is sensitive to member
declaration order which seems like a bug.
I'm afraid I don't understand the name lookup code enough to
fix this issue..
>
> > This patch also makes us recognize another written form of inherited
> > constructor, 'using Base<T>::Base::Base' whose USING_DECL_SCOPE is a
> > TYPENAME_TYPE.
> >
> > PR c++/116276
> >
> > gcc/cp/ChangeLog:
> >
> > * call.cc (joust): Implement P2582R1 inherited vs non-inherited
> > guide tiebreaker.
> > * cp-tree.h (lang_decl_fn::context): Document usage in
> > deduction_guide_p FUNCTION_DECLs.
> > (inherited_guide_p): Declare.
> > * pt.cc (inherited_guide_p): Define.
> > (set_inherited_guide_context): Define.
> > (alias_ctad_tweaks): Use set_inherited_guide_context.
> > (inherited_ctad_tweaks): Recognize some inherited constructors
> > whose scope is a TYPENAME_TYPE.
> > (ctor_deduction_guides_for): For C++23 inherited CTAD, loop
> > over TYPE_FIELDS instead of using CLASSTYPE_CONSTRUCTORS to
> > recognize all relevant using-decls.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * g++.dg/cpp23/class-deduction-inherited5.C: New test.
> > * g++.dg/cpp23/class-deduction-inherited6.C: New test.
> > ---
> > gcc/cp/call.cc | 23 +++++++++-
> > gcc/cp/cp-tree.h | 8 +++-
> > gcc/cp/pt.cc | 44 ++++++++++++++----
> > .../g++.dg/cpp23/class-deduction-inherited4.C | 4 +-
> > .../g++.dg/cpp23/class-deduction-inherited5.C | 25 ++++++++++
> > .../g++.dg/cpp23/class-deduction-inherited6.C | 46 +++++++++++++++++++
> > 6 files changed, 137 insertions(+), 13 deletions(-)
> > create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C
> > create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C
> >
> > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > index 67d38e2a78a..8ae1312a370 100644
> > --- a/gcc/cp/call.cc
> > +++ b/gcc/cp/call.cc
> > @@ -13262,10 +13262,31 @@ joust (struct z_candidate *cand1, struct
> > z_candidate *cand2, bool warn,
> > else if (cand2->rewritten ())
> > return 1;
> > - /* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
> > if (deduction_guide_p (cand1->fn))
> > {
> > gcc_assert (deduction_guide_p (cand2->fn));
> > +
> > + /* F1 and F2 are generated from class template argument deduction for
> > a
> > + class D, and F2 is generated from inheriting constructors from a base
> > + class of D while F1 is not, and for each explicit function argument,
> > + the corresponding parameters of F1 and F2 are either both ellipses or
> > + have the same type. */
> > + bool inherited1 = inherited_guide_p (cand1->fn);
> > + bool inherited2 = inherited_guide_p (cand2->fn);
> > + if (int diff = inherited2 - inherited1)
> > + {
> > + for (i = 0; i < len; ++i)
> > + {
> > + conversion *t1 = cand1->convs[i + off1];
> > + conversion *t2 = cand2->convs[i + off2];
> > + if (!same_type_p (t1->type, t2->type))
> > + break;
> > + }
> > + if (i == len)
> > + return diff;
> > + }
> > +
> > + /* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
> > /* We distinguish between candidates from an explicit deduction
> > guide and
> > candidates built from a constructor based on DECL_ARTIFICIAL. */
> > int art1 = DECL_ARTIFICIAL (cand1->fn);
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index 5807f4e0edb..3e218793ec7 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -2973,8 +2973,11 @@ struct GTY(()) lang_decl_fn {
> > chained here. This pointer thunks to return pointer thunks
> > will be chained on the return pointer thunk.
> > For a DECL_CONSTUCTOR_P FUNCTION_DECL, this is the base from
> > - whence we inherit. Otherwise, it is the class in which a
> > - (namespace-scope) friend is defined (if any). */
> > + whence we inherit.
> > + For a deduction_guide_p FUNCTION_DECL, this is the base class
> > + from whence we inherited the guide (if any).
> > + Otherwise, it is the class in which a (namespace-scope) friend
> > + is defined (if any). */
> > tree context;
> > union lang_decl_u5
> > @@ -7662,6 +7665,7 @@ extern bool deduction_guide_p
> > (const_tree);
> > extern bool copy_guide_p (const_tree);
> > extern bool template_guide_p (const_tree);
> > extern bool builtin_guide_p (const_tree);
> > +extern bool inherited_guide_p (const_tree);
> > extern void store_explicit_specifier (tree, tree);
> > extern tree lookup_explicit_specifier (tree);
> > extern tree lookup_imported_hidden_friend (tree);
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index c1d4cdc7e25..d8def23ef3c 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -29716,6 +29716,25 @@ builtin_guide_p (const_tree fn)
> > return true;
> > }
> > +/* True if FN is a C++23 inherited guide. */
> > +
> > +bool
> > +inherited_guide_p (const_tree fn)
> > +{
> > + gcc_assert (deduction_guide_p (fn));
> > + return LANG_DECL_FN_CHECK (fn)->context != NULL_TREE;
> > +}
> > +
> > +/* Set the base class BASE from which the transformed guide FN
> > + was inherited as part of C++23 inherited CTAD. */
> > +
> > +static void
> > +set_inherited_guide_context (const_tree fn, tree base)
> > +{
> > + gcc_assert (deduction_guide_p (fn));
> > + LANG_DECL_FN_CHECK (fn)->context = base;
> > +}
> > +
> > /* OLDDECL is a _DECL for a template parameter. Return a similar
> > parameter at
> > LEVEL:INDEX, using tsubst_args and complain for substitution into
> > non-type
> > template parameter types. Note that the handling of template template
> > @@ -30486,6 +30505,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
> > TREE_TYPE (fprime) = fntype;
> > if (TREE_CODE (fprime) == TEMPLATE_DECL)
> > TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype;
> > + set_inherited_guide_context (fprime, utype);
> > }
> > aguides = lookup_add (fprime, aguides);
> > @@ -30517,11 +30537,14 @@ inherited_ctad_tweaks (tree tmpl, tree ctor,
> > tsubst_flags_t complain)
> > template specialization with the template argument list of A but with
> > C as
> > the template. */
> > - /* FIXME: Also recognize inherited constructors of the form 'using
> > C::B::B',
> > - which seem to be represented with TYPENAME_TYPE C::B as
> > USING_DECL_SCOPE?
> > - And recognize constructors inherited from a non-dependent base class,
> > which
> > - seem to be missing from the overload set entirely? */
> > tree scope = USING_DECL_SCOPE (ctor);
> > + if (TREE_CODE (scope) == TYPENAME_TYPE
> > + && (TYPE_IDENTIFIER (TYPE_CONTEXT (scope))
> > + == TYPENAME_TYPE_FULLNAME (scope)))
> > + /* Recognize using B<T>::B::B as an inherited constructor. */
> > + /* FIXME: Also recognize using C::B::B? We might have to call
> > + resolve_typename_type for that. */
> > + scope = TYPE_CONTEXT (scope);
> > if (!CLASS_TYPE_P (scope)
> > || !CLASSTYPE_TEMPLATE_INFO (scope)
> > || !PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (scope)))
> > @@ -30655,10 +30678,15 @@ ctor_deduction_guides_for (tree tmpl,
> > tsubst_flags_t complain)
> > }
> > if (cxx_dialect >= cxx23)
> > - for (tree ctor : ovl_range (CLASSTYPE_CONSTRUCTORS (type)))
> > - if (TREE_CODE (ctor) == USING_DECL)
> > - {
> > - tree uguides = inherited_ctad_tweaks (tmpl, ctor, complain);
> > + /* FIXME: CLASSTYPE_CONSTRUCTORS doesn't contain (dependent) inherited
> > + constructors if e.g. there's a user-defined constructor. So instead
> > + iterate over TYPE_FIELDS manually to robustly find all relevant
> > using
> > + decls. */
> > + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN
> > (field))
> > + if (TREE_CODE (field) == USING_DECL
> > + && DECL_NAME (field) == ctor_identifier)
> > + {
> > + tree uguides = inherited_ctad_tweaks (tmpl, field, complain);
> > if (uguides)
> > cands = lookup_add (uguides, cands);
> > }
> > diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> > b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> > index 5e3a7f42919..806f0167a4e 100644
> > --- a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> > +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> > @@ -13,10 +13,10 @@ using ty1 = B<int>;
> > template<class T=void>
> > struct C : A<int> {
> > - using A<int>::A; // FIXME: we don't notice this one either
> > + using A<int>::A;
> > };
> > -using ty2 = decltype(C(0)); // { dg-bogus "" "" { xfail *-*-* } }
> > +using ty2 = decltype(C(0));
> > using ty2 = C<void>;
> > template<class T>
> > diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C
> > b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C
> > new file mode 100644
> > index 00000000000..d835acb0b79
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C
> > @@ -0,0 +1,25 @@
> > +// PR c++/116276
> > +// { dg-do compile { target c++20 } }
> > +
> > +template<class T>
> > +struct Base1 { };
> > +
> > +template<class T>
> > +struct Base2 { };
> > +
> > +template<class T = int>
> > +struct Derived : public Base1<T>, Base2<T> {
> > + using Base1<T>::Base1;
> > + using Base2<T>::Base2;
> > +};
> > +
> > +Derived d;
> > +
> > +template<class T = int>
> > +struct Derived2 : public Base1<T>, Base2<T> {
> > + using Base1<T>::Base1::Base1;
> > + using Base2<T>::Base2::Base2;
> > + Derived2();
> > +};
> > +
> > +Derived2 d2;
> > diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C
> > b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C
> > new file mode 100644
> > index 00000000000..df8199cd3e4
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C
> > @@ -0,0 +1,46 @@
> > +// PR c++/116276
> > +// { dg-do compile { target c++23 } }
> > +
> > +template<class T>
> > +struct Base1 {
> > + Base1();
> > + Base1(T);
> > +};
> > +
> > +template<class T>
> > +struct Base2 {
> > + Base2();
> > + Base2(T*);
> > +};
> > +
> > +template<class T = int>
> > +struct Derived : public Base1<T>, Base2<T> {
> > + using Base1<T>::Base1;
> > + using Base2<T>::Base2;
> > +};
> > +
> > +using ty1 = decltype(Derived{});
> > +using ty1 = Derived<int>;
> > +
> > +using ty2 = decltype(Derived{true});
> > +using ty2 = Derived<bool>;
> > +
> > +using ty3 = decltype(Derived{(char*)nullptr});
> > +using ty3 = Derived<char>;
> > +
> > +template<class T = int>
> > +struct Derived2 : public Base1<T>, Base2<T> {
> > + using Base1<T>::Base1;
> > + using Base2<T>::Base2;
> > + Derived2();
> > + Derived2(T);
> > +};
> > +
> > +using ty4 = decltype(Derived2{});
> > +using ty4 = Derived2<int>;
> > +
> > +using ty5 = decltype(Derived2{true});
> > +using ty5 = Derived2<bool>;
> > +
> > +using ty6 = decltype(Derived2{(char*)nullptr});
> > +using ty6 = Derived2<char>;
>
>