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?

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

Reply via email to