diff mbox series

c++: Implement P2582R1, CTAD from inherited constructors

Message ID 20231122220731.1121607-1-ppalka@redhat.com
State New
Headers show
Series c++: Implement P2582R1, CTAD from inherited constructors | expand

Commit Message

Patrick Palka Nov. 22, 2023, 10:07 p.m. UTC
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?

-- >8 --

This patch implements C++23 class template argument deduction from
inherited constructors, which is specified in terms of C++20 alias
CTAD which we already fully support.  The rule for transforming
the return type of an inherited guide is specified in terms of a
partially specialized class template, but this patch implements it
in a simpler way, performing ahead of time deduction instead of
instantiation time deduction.  I wasn't able to find an example for
which this implementation strategy makes a difference, but I didn't
look very hard.  Support seems good enough to advertise as complete,
and there should be no functional change before C++23 mode.

There's a couple of FIXMEs, one in inherited_ctad_tweaks for recognizing
more forms of inherited constructors, and one in deduction_guides_for for
making the cache aware of base-class dependencies.

There doesn't seem to be a feature-test macro update for this paper.

gcc/cp/ChangeLog:

	* cp-tree.h (type_targs_deducible_from): Adjust declaration.
	* pt.cc (alias_ctad_tweaks): Handle C++23 inherited CTAD.
	(inherited_ctad_tweaks): Define.
	(type_targs_deducible_from): Add defaulted 'targs_out' parameter.
	Handle 'tmpl' being a TREE_LIST representing a synthetic alias
	template.  Set 'targs_out' upon success.
	(ctor_deduction_guides_for): Do inherited_ctad_tweaks for each
	USING_DECL in C++23 mode.
	(deduction_guides_for): Add FIXME for stale cache entries in
	light of inherited CTAD.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1z/class-deduction67.C: Accept in C++23 mode.
	* g++.dg/cpp23/class-deduction-inherited1.C: New test.
	* g++.dg/cpp23/class-deduction-inherited2.C: New test.
	* g++.dg/cpp23/class-deduction-inherited3.C: New test.
---
 gcc/cp/cp-tree.h                              |   2 +-
 gcc/cp/pt.cc                                  | 176 +++++++++++++++---
 .../g++.dg/cpp1z/class-deduction67.C          |   5 +-
 .../g++.dg/cpp23/class-deduction-inherited1.C |  36 ++++
 .../g++.dg/cpp23/class-deduction-inherited2.C |  26 +++
 .../g++.dg/cpp23/class-deduction-inherited3.C |  16 ++
 6 files changed, 231 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C

Comments

Patrick Palka Nov. 24, 2023, 6:18 p.m. UTC | #1
On Wed, 22 Nov 2023, Patrick Palka wrote:

> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk?
> 
> -- >8 --
> 
> This patch implements C++23 class template argument deduction from
> inherited constructors, which is specified in terms of C++20 alias
> CTAD which we already fully support.  The rule for transforming
> the return type of an inherited guide is specified in terms of a
> partially specialized class template, but this patch implements it
> in a simpler way, performing ahead of time deduction instead of
> instantiation time deduction.  I wasn't able to find an example for
> which this implementation strategy makes a difference, but I didn't
> look very hard.  Support seems good enough to advertise as complete,
> and there should be no functional change before C++23 mode.
> 
> There's a couple of FIXMEs, one in inherited_ctad_tweaks for recognizing
> more forms of inherited constructors, and one in deduction_guides_for for
> making the cache aware of base-class dependencies.
> 
> There doesn't seem to be a feature-test macro update for this paper.
> 

Here's v2 with some minor changes:

  * set processing_template_decl when rewriting the return type of
    a template guide
  * rather than adding an out parameter to type_targs_deducible_from,
    just make it return NULL_TREE or the deduced args
  * add a testcase demonstrating each of the FIXMEs

-- >8 --

gcc/cp/ChangeLog:

	* cp-tree.h (type_targs_deducible_from): Adjust return type.
	* pt.cc (alias_ctad_tweaks): Handle C++23 inherited CTAD.
	(inherited_ctad_tweaks): Define.
	(type_targs_deducible_from): Return the deduced arguments or
	NULL_TREE instead of a bool.  Handle 'tmpl' being a TREE_LIST
	representing a synthetic alias template.
	(ctor_deduction_guides_for): Do inherited_ctad_tweaks for each
	USING_DECL in C++23 mode.
	(deduction_guides_for): Add FIXME for stale cache entries in
	light of inherited CTAD.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1z/class-deduction67.C: Accept in C++23 mode.
	* g++.dg/cpp23/class-deduction-inherited1.C: New test.
	* g++.dg/cpp23/class-deduction-inherited2.C: New test.
	* g++.dg/cpp23/class-deduction-inherited3.C: New test.
	* g++.dg/cpp23/class-deduction-inherited4.C: New test.
---
 gcc/cp/cp-tree.h                              |   2 +-
 gcc/cp/pt.cc                                  | 186 +++++++++++++++---
 .../g++.dg/cpp1z/class-deduction67.C          |   5 +-
 .../g++.dg/cpp23/class-deduction-inherited1.C |  38 ++++
 .../g++.dg/cpp23/class-deduction-inherited2.C |  26 +++
 .../g++.dg/cpp23/class-deduction-inherited3.C |  16 ++
 .../g++.dg/cpp23/class-deduction-inherited4.C |  32 +++
 7 files changed, 272 insertions(+), 33 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7b0b7c6a17e..abc467fb290 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7457,7 +7457,7 @@ extern tree fn_type_unification			(tree, tree, tree,
 						 bool, bool);
 extern void mark_decl_instantiated		(tree, int);
 extern int more_specialized_fn			(tree, tree, int);
-extern bool type_targs_deducible_from		(tree, tree);
+extern tree type_targs_deducible_from		(tree, tree);
 extern void do_decl_instantiation		(tree, tree);
 extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
 extern bool always_instantiate_p		(tree);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 092e6fdfd36..8b7aa96cf01 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -223,6 +223,9 @@ static void instantiate_body (tree pattern, tree args, tree d, bool nested);
 static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
 static void mark_template_arguments_used (tree, tree);
 static bool uses_outer_template_parms (tree);
+static tree alias_ctad_tweaks (tree, tree);
+static tree inherited_ctad_tweaks (tree, tree, tsubst_flags_t);
+static tree deduction_guides_for (tree, bool&, tsubst_flags_t);
 
 /* Make the current scope suitable for access checking when we are
    processing T.  T can be FUNCTION_DECL for instantiated function
@@ -29736,8 +29739,6 @@ is_spec_or_derived (tree etype, tree tmpl)
   return !err;
 }
 
-static tree alias_ctad_tweaks (tree, tree);
-
 /* Return a C++20 aggregate deduction candidate for TYPE initialized from
    INIT.  */
 
@@ -29842,7 +29843,13 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
 }
 
 /* UGUIDES are the deduction guides for the underlying template of alias
-   template TMPL; adjust them to be deduction guides for TMPL.  */
+   template TMPL; adjust them to be deduction guides for TMPL.
+
+   This routine also handles C++23 inherited CTAD, in which case TMPL is a
+   TREE_LIST representing a synthetic alias template whose TREE_PURPOSE is
+   the template parameter list of the alias template (equivalently, of the
+   derived class) and TREE_VALUE the defining-type-id (equivalently, the
+   base whose guides we're inheriting).  UGUIDES are the base's guides.  */
 
 static tree
 alias_ctad_tweaks (tree tmpl, tree uguides)
@@ -29886,13 +29893,27 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
      * The explicit-specifier of f' is the explicit-specifier of g (if
      any).  */
 
+  enum { alias, inherited } ctad_kind;
+  tree atype, fullatparms, utype;
+  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
+    {
+      ctad_kind = alias;
+      atype = TREE_TYPE (tmpl);
+      fullatparms = DECL_TEMPLATE_PARMS (tmpl);
+      utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
+    }
+  else
+    {
+      ctad_kind = inherited;
+      atype = NULL_TREE;
+      fullatparms = TREE_PURPOSE (tmpl);
+      utype = TREE_VALUE (tmpl);
+    }
+
   tsubst_flags_t complain = tf_warning_or_error;
-  tree atype = TREE_TYPE (tmpl);
   tree aguides = NULL_TREE;
-  tree fullatparms = DECL_TEMPLATE_PARMS (tmpl);
   tree atparms = INNERMOST_TEMPLATE_PARMS (fullatparms);
   unsigned natparms = TREE_VEC_LENGTH (atparms);
-  tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
   for (ovl_iterator iter (uguides); iter; ++iter)
     {
       tree f = *iter;
@@ -29930,7 +29951,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 
 	  /* Set current_template_parms as in build_deduction_guide.  */
 	  auto ctp = make_temp_override (current_template_parms);
-	  current_template_parms = copy_node (DECL_TEMPLATE_PARMS (tmpl));
+	  current_template_parms = copy_node (fullatparms);
 	  TREE_VALUE (current_template_parms) = gtparms;
 
 	  j = 0;
@@ -30006,9 +30027,10 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	  /* Add a constraint that the return type matches the instantiation of
 	     A with the same template arguments.  */
 	  ret = TREE_TYPE (TREE_TYPE (fprime));
-	  if (!same_type_p (atype, ret)
-	      /* FIXME this should mean they don't compare as equivalent.  */
-	      || dependent_alias_template_spec_p (atype, nt_opaque))
+	  if (ctad_kind == alias
+	      && (!same_type_p (atype, ret)
+		  /* FIXME this should mean they don't compare as equivalent.  */
+		  || dependent_alias_template_spec_p (atype, nt_opaque)))
 	    {
 	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
 	      ci = append_constraint (ci, same);
@@ -30025,43 +30047,134 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	  /* For a non-template deduction guide, if the arguments of A aren't
 	     deducible from the return type, don't add the candidate.  */
 	non_template:
-	  if (!type_targs_deducible_from (tmpl, ret))
+	  if (ctad_kind == alias
+	      && !type_targs_deducible_from (tmpl, ret))
 	    continue;
 	}
 
+      /* Rewrite the return type of the inherited guide in terms of the
+	 derived class.  This is specified as replacing the return type R
+	 with typename CC<R>::type where the partially specialized CC maps a
+	 base class specialization to a specialization of the derived class
+	 having such a base (inducing substitution failure if no such derived
+	 class exists).
+
+	 As specified this mapping would be done at instantiation time using
+	 non-dependent template arguments, but we do it ahead of time using
+	 the generic arguments.  This seems to be good enough since generic
+	 deduction should succeed only if concrete deduction would.  */
+      if (ctad_kind == inherited)
+	{
+	  processing_template_decl_sentinel ptds (/*reset*/false);
+	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
+	    ++processing_template_decl;
+
+	  if (TREE_CODE (f) != TEMPLATE_DECL)
+	    fprime = copy_decl (fprime);
+	  else
+	    /* Our fprime is already a copy.  */;
+
+	  tree fntype = TREE_TYPE (fprime);
+	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
+				       in_decl, NULL_TREE, false, complain);
+	  fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
+	  TREE_TYPE (fprime) = fntype;
+	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
+	    TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype;
+	}
+
       aguides = lookup_add (fprime, aguides);
     }
 
   return aguides;
 }
 
-/* True iff template arguments for TMPL can be deduced from TYPE.
+/* CTOR is a using-declaration inheriting the constructors of some base of the
+   class template TMPL; adjust the base's guides be deduction guides for TMPL.  */
+
+static tree
+inherited_ctad_tweaks (tree tmpl, tree ctor, tsubst_flags_t complain)
+{
+  /* [over.match.class.deduct]: In addition, if C is defined and inherits
+     constructors ([namespace.udecl]) from a direct base class denoted in the
+     base-specifier-list by a class-or-decltype B, let A be an alias template
+     whose template parameter list is that of C and whose defining-type-id is
+     B.  If A is a deducible template ([dcl.type.simple]), the set contains the
+     guides of A with the return type R of each guide replaced with typename
+     CC::type given a class template
+
+     template <typename> class CC;
+
+     whose primary template is not defined and with a single partial
+     specialization whose template parameter list is that of A and whose
+     template argument list is a specialization of A with the template argument
+     list of A ([temp.dep.type]) having a member typedef type designating a
+     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 (!CLASS_TYPE_P (scope)
+      || !CLASSTYPE_TEMPLATE_INFO (scope))
+    return NULL_TREE;
+
+  tree t = build_tree_list (DECL_TEMPLATE_PARMS (tmpl), scope);
+  bool any_dguides_p;
+  tree uguides = deduction_guides_for (TYPE_TI_TEMPLATE (scope),
+				       any_dguides_p, complain);
+  return alias_ctad_tweaks (t, uguides);
+}
+
+/* If template arguments for TMPL can be deduced from TYPE, return
+   the deduced arguments, otherwise return NULL_TREE.
    Used to implement CPTK_IS_DEDUCIBLE for alias CTAD according to
    [over.match.class.deduct].
 
    This check is specified in terms of partial specialization, so the behavior
    should be parallel to that of get_partial_spec_bindings.  */
 
-bool
+tree
 type_targs_deducible_from (tree tmpl, tree type)
 {
-  tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+  tree tparms, ttype;
+  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
+    {
+      /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
+	 specialization of TMPL.  */
+      if (DECL_CLASS_TEMPLATE_P (tmpl))
+	{
+	  if (CLASS_TYPE_P (type)
+	      && CLASSTYPE_TEMPLATE_INFO (type)
+	      && CLASSTYPE_TI_TEMPLATE (type) == tmpl)
+	    return INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
+	  else
+	    return NULL_TREE;
+	}
+
+      /* Otherwise it's an alias template.  */
+      tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+      ttype = TREE_TYPE (tmpl);
+    }
+  else
+    {
+      /* TMPL is a synthetic alias template represented as a TREE_LIST as
+	 per alias_ctad_tweaks.  */
+      tparms = INNERMOST_TEMPLATE_PARMS (TREE_PURPOSE (tmpl));
+      ttype = TREE_VALUE (tmpl);
+      tmpl = TI_TEMPLATE (TYPE_TEMPLATE_INFO_MAYBE_ALIAS (ttype));
+    }
+
   int len = TREE_VEC_LENGTH (tparms);
   tree targs = make_tree_vec (len);
   bool tried_array_deduction = (cxx_dialect < cxx17);
 
-  /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
-     specialization of TMPL.  */
-  if (DECL_CLASS_TEMPLATE_P (tmpl))
-    return (CLASS_TYPE_P (type)
-	    && CLASSTYPE_TEMPLATE_INFO (type)
-	    && CLASSTYPE_TI_TEMPLATE (type) == tmpl);
-
-  /* Otherwise it's an alias template.  */
  again:
-  if (unify (tparms, targs, TREE_TYPE (tmpl), type,
+  if (unify (tparms, targs, ttype, type,
 	     UNIFY_ALLOW_NONE, false))
-    return false;
+    return NULL_TREE;
 
   /* We don't fail on an undeduced targ the second time through (like
      get_partial_spec_bindings) because we're going to try defaults.  */
@@ -30074,7 +30187,7 @@ type_targs_deducible_from (tree tmpl, tree type)
 	if (!tried_array_deduction
 	    && TREE_CODE (tparm) == TYPE_DECL)
 	  {
-	    try_array_deduction (tparms, targs, TREE_TYPE (tmpl));
+	    try_array_deduction (tparms, targs, ttype);
 	    tried_array_deduction = true;
 	    if (TREE_VEC_ELT (targs, i))
 	      goto again;
@@ -30103,12 +30216,15 @@ type_targs_deducible_from (tree tmpl, tree type)
      partial specialization can't have default targs.  */
   targs = coerce_template_parms (tparms, targs, tmpl, tf_none);
   if (targs == error_mark_node)
-    return false;
+    return NULL_TREE;
 
   /* I believe we don't need the template_template_parm_bindings_ok_p call
      because coerce_template_parms did coerce_template_template_parms.  */
 
-  return constraints_satisfied_p (tmpl, targs);
+  if (!constraints_satisfied_p (tmpl, targs))
+    return NULL_TREE;
+
+  return targs;
 }
 
 /* Return artificial deduction guides built from the constructors of class
@@ -30124,7 +30240,7 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
 
   for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter)
     {
-      /* Skip inherited constructors.  */
+      /* We handle C++23 inherited CTAD below.  */
       if (iter.using_p ())
 	continue;
 
@@ -30132,6 +30248,15 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
       cands = lookup_add (guide, cands);
     }
 
+  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);
+	  if (uguides)
+	    cands = lookup_add (uguides, cands);
+	}
+
   /* Add implicit default constructor deduction guide.  */
   if (!TYPE_HAS_USER_CONSTRUCTOR (type))
     {
@@ -30181,7 +30306,10 @@ deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
     }
 
   /* Cache the deduction guides for a template.  We also remember the result of
-     lookup, and rebuild everything if it changes; should be very rare.  */
+     lookup, and rebuild everything if it changes; should be very rare.
+
+     FIXME: Also rebuild if this is a class template that inherits guides from a
+     base class, and lookup for the latter changed.  */
   tree_pair_p cache = NULL;
   if (tree_pair_p &r
       = hash_map_safe_get_or_insert<hm_ggc> (dguide_cache, tmpl))
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
index 4624794c4b7..74f92325d7a 100644
--- a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
@@ -1,5 +1,4 @@
-// Deduction from inherited constructors isn't supported yet, but we shouldn't
-// crash.  It may well be supported in C++23.
+// Deduction from inherited constructors isn't supported before C++23.
 
 //{ dg-do compile { target c++17 } }
 
@@ -17,5 +16,5 @@ int main()
 {
   B b = 42;			// { dg-line init }
   // { dg-prune-output "no matching function" }
-  // { dg-error "class template argument deduction" "" { target *-*-* } init }
+  // { dg-error "class template argument deduction" "" { target c++23_down } init }
 }
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
new file mode 100644
index 00000000000..5fd1270e819
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
@@ -0,0 +1,38 @@
+// Modified example from P2582R1
+// { dg-do compile { target c++23 } }
+
+template <typename T> struct B {
+  B(T);
+};
+B(bool) -> B<char>;
+template <typename T> struct C : public B<T> {
+  using B<T>::B;
+};
+template <typename T> struct D : public B<T> {};
+
+C c(42);            // OK, deduces C<int>
+using ty1 = decltype(c);
+using ty1 = C<int>;
+
+D d(42);            // { dg-error "deduction|no match" }
+
+C c2(true);           // OK, deduces C<char>
+using ty2 = decltype(c2);
+using ty2 = C<char>;
+
+template <typename T> struct E : public B<int> {
+  using B<int>::B;
+};
+
+E e(42);            // { dg-error "deduction|no match" }
+
+template <typename T, typename U, typename V> struct F {
+  F(T, U, V);
+};
+template <typename T, typename U> struct G : F<U, T, int> {
+  using F<U, T, int>::F;
+};
+
+G g(true, 'a', 1);  // OK, deduces G<char, bool>
+using ty3 = decltype(g);
+using ty3 = G<char, bool>;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
new file mode 100644
index 00000000000..cb3c595f316
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
@@ -0,0 +1,26 @@
+// { dg-do compile { target c++23 } }
+
+template<class T, class U, class V> struct F {
+  F(T, U, V);       // #1
+  F(T*, U*, V*);    // #2
+  template<class W>
+  F(int, int, W);   // #3
+};
+
+F(bool, bool, bool) -> F<bool*, void*, int>;
+
+template<class T, class U> struct G : F<U, T, int> {
+  using F<U, T, int>::F;
+};
+
+using ty1 = decltype(G(true, 'a', 1)); // uses #1
+using ty1 = G<char, bool>;
+
+using ty2 = decltype(G((bool*)0, (char*)0, (int*)0)); // uses #2
+using ty2 = G<char, bool>;
+
+using ty3 = decltype(G(0, 0, 0)); // uses #3
+using ty3 = G<int, int>;
+
+using ty4 = decltype(G(true, true, true)); // uses #4
+using ty4 = G<void*, bool*>;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
new file mode 100644
index 00000000000..57e323b5124
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++23 } }
+
+template<class T>
+struct A {
+  A(T);
+  template<class U> A(T, U);
+};
+
+template<class T>
+struct B : A<const T> {
+  using A<const T>::A;
+};
+
+using type = decltype(B(0));
+using type = decltype(B(0, 0));
+using type = B<int>;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
new file mode 100644
index 00000000000..f706ebe9bb7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
@@ -0,0 +1,32 @@
+// { dg-do compile { target c++23 } }
+
+template<class T>
+struct A { A(T); };
+
+template<class T>
+struct B : A<T> {
+  using B::A::A; // FIXME: we don't notice this inherited ctor during CTAD
+};
+
+using ty1 = decltype(B(0)); // { dg-bogus "" "" { xfail *-*-* } }
+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 ty2 = decltype(C(0)); // { dg-bogus "" "" { xfail *-*-* } }
+using ty2 = C<void>;
+
+template<class T>
+struct D : A<T> {
+  using A<T>::A;
+};
+
+using ty3 = decltype(D(0));
+using ty3 = D<int>;
+
+A(int) -> A<char>; // FIXME: we need to rebuild the guides of D
+using ty4 = decltype(D(0));
+using ty4 = D<char>; // { dg-bogus "conflicting" "" { xfail *-*-* } }
Patrick Palka Nov. 27, 2023, 3:58 p.m. UTC | #2
On Fri, 24 Nov 2023, Patrick Palka wrote:

> On Wed, 22 Nov 2023, Patrick Palka wrote:
> 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> > trunk?
> > 
> > -- >8 --
> > 
> > This patch implements C++23 class template argument deduction from
> > inherited constructors, which is specified in terms of C++20 alias
> > CTAD which we already fully support.  The rule for transforming
> > the return type of an inherited guide is specified in terms of a
> > partially specialized class template, but this patch implements it
> > in a simpler way, performing ahead of time deduction instead of
> > instantiation time deduction.  I wasn't able to find an example for
> > which this implementation strategy makes a difference, but I didn't
> > look very hard.  Support seems good enough to advertise as complete,
> > and there should be no functional change before C++23 mode.
> > 
> > There's a couple of FIXMEs, one in inherited_ctad_tweaks for recognizing
> > more forms of inherited constructors, and one in deduction_guides_for for
> > making the cache aware of base-class dependencies.
> > 
> > There doesn't seem to be a feature-test macro update for this paper.
> > 
> 
> Here's v2 with some minor changes:
> 
>   * set processing_template_decl when rewriting the return type of
>     a template guide
>   * rather than adding an out parameter to type_targs_deducible_from,
>     just make it return NULL_TREE or the deduced args
>   * add a testcase demonstrating each of the FIXMEs
> 
> -- >8 --
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (type_targs_deducible_from): Adjust return type.
> 	* pt.cc (alias_ctad_tweaks): Handle C++23 inherited CTAD.
> 	(inherited_ctad_tweaks): Define.
> 	(type_targs_deducible_from): Return the deduced arguments or
> 	NULL_TREE instead of a bool.  Handle 'tmpl' being a TREE_LIST
> 	representing a synthetic alias template.
> 	(ctor_deduction_guides_for): Do inherited_ctad_tweaks for each
> 	USING_DECL in C++23 mode.
> 	(deduction_guides_for): Add FIXME for stale cache entries in
> 	light of inherited CTAD.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp1z/class-deduction67.C: Accept in C++23 mode.
> 	* g++.dg/cpp23/class-deduction-inherited1.C: New test.
> 	* g++.dg/cpp23/class-deduction-inherited2.C: New test.
> 	* g++.dg/cpp23/class-deduction-inherited3.C: New test.
> 	* g++.dg/cpp23/class-deduction-inherited4.C: New test.
> ---
>  gcc/cp/cp-tree.h                              |   2 +-
>  gcc/cp/pt.cc                                  | 186 +++++++++++++++---
>  .../g++.dg/cpp1z/class-deduction67.C          |   5 +-
>  .../g++.dg/cpp23/class-deduction-inherited1.C |  38 ++++
>  .../g++.dg/cpp23/class-deduction-inherited2.C |  26 +++
>  .../g++.dg/cpp23/class-deduction-inherited3.C |  16 ++
>  .../g++.dg/cpp23/class-deduction-inherited4.C |  32 +++
>  7 files changed, 272 insertions(+), 33 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
>  create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 7b0b7c6a17e..abc467fb290 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7457,7 +7457,7 @@ extern tree fn_type_unification			(tree, tree, tree,
>  						 bool, bool);
>  extern void mark_decl_instantiated		(tree, int);
>  extern int more_specialized_fn			(tree, tree, int);
> -extern bool type_targs_deducible_from		(tree, tree);
> +extern tree type_targs_deducible_from		(tree, tree);
>  extern void do_decl_instantiation		(tree, tree);
>  extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
>  extern bool always_instantiate_p		(tree);
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 092e6fdfd36..8b7aa96cf01 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -223,6 +223,9 @@ static void instantiate_body (tree pattern, tree args, tree d, bool nested);
>  static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
>  static void mark_template_arguments_used (tree, tree);
>  static bool uses_outer_template_parms (tree);
> +static tree alias_ctad_tweaks (tree, tree);
> +static tree inherited_ctad_tweaks (tree, tree, tsubst_flags_t);
> +static tree deduction_guides_for (tree, bool&, tsubst_flags_t);
>  
>  /* Make the current scope suitable for access checking when we are
>     processing T.  T can be FUNCTION_DECL for instantiated function
> @@ -29736,8 +29739,6 @@ is_spec_or_derived (tree etype, tree tmpl)
>    return !err;
>  }
>  
> -static tree alias_ctad_tweaks (tree, tree);
> -
>  /* Return a C++20 aggregate deduction candidate for TYPE initialized from
>     INIT.  */
>  
> @@ -29842,7 +29843,13 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
>  }
>  
>  /* UGUIDES are the deduction guides for the underlying template of alias
> -   template TMPL; adjust them to be deduction guides for TMPL.  */
> +   template TMPL; adjust them to be deduction guides for TMPL.
> +
> +   This routine also handles C++23 inherited CTAD, in which case TMPL is a
> +   TREE_LIST representing a synthetic alias template whose TREE_PURPOSE is
> +   the template parameter list of the alias template (equivalently, of the
> +   derived class) and TREE_VALUE the defining-type-id (equivalently, the
> +   base whose guides we're inheriting).  UGUIDES are the base's guides.  */
>  
>  static tree
>  alias_ctad_tweaks (tree tmpl, tree uguides)
> @@ -29886,13 +29893,27 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>       * The explicit-specifier of f' is the explicit-specifier of g (if
>       any).  */
>  
> +  enum { alias, inherited } ctad_kind;
> +  tree atype, fullatparms, utype;
> +  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
> +    {
> +      ctad_kind = alias;
> +      atype = TREE_TYPE (tmpl);
> +      fullatparms = DECL_TEMPLATE_PARMS (tmpl);
> +      utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
> +    }
> +  else
> +    {
> +      ctad_kind = inherited;
> +      atype = NULL_TREE;
> +      fullatparms = TREE_PURPOSE (tmpl);
> +      utype = TREE_VALUE (tmpl);
> +    }
> +
>    tsubst_flags_t complain = tf_warning_or_error;
> -  tree atype = TREE_TYPE (tmpl);
>    tree aguides = NULL_TREE;
> -  tree fullatparms = DECL_TEMPLATE_PARMS (tmpl);
>    tree atparms = INNERMOST_TEMPLATE_PARMS (fullatparms);
>    unsigned natparms = TREE_VEC_LENGTH (atparms);
> -  tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
>    for (ovl_iterator iter (uguides); iter; ++iter)
>      {
>        tree f = *iter;
> @@ -29930,7 +29951,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>  
>  	  /* Set current_template_parms as in build_deduction_guide.  */
>  	  auto ctp = make_temp_override (current_template_parms);
> -	  current_template_parms = copy_node (DECL_TEMPLATE_PARMS (tmpl));
> +	  current_template_parms = copy_node (fullatparms);
>  	  TREE_VALUE (current_template_parms) = gtparms;
>  
>  	  j = 0;
> @@ -30006,9 +30027,10 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>  	  /* Add a constraint that the return type matches the instantiation of
>  	     A with the same template arguments.  */
>  	  ret = TREE_TYPE (TREE_TYPE (fprime));
> -	  if (!same_type_p (atype, ret)
> -	      /* FIXME this should mean they don't compare as equivalent.  */
> -	      || dependent_alias_template_spec_p (atype, nt_opaque))
> +	  if (ctad_kind == alias
> +	      && (!same_type_p (atype, ret)
> +		  /* FIXME this should mean they don't compare as equivalent.  */
> +		  || dependent_alias_template_spec_p (atype, nt_opaque)))
>  	    {
>  	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
>  	      ci = append_constraint (ci, same);
> @@ -30025,43 +30047,134 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>  	  /* For a non-template deduction guide, if the arguments of A aren't
>  	     deducible from the return type, don't add the candidate.  */
>  	non_template:
> -	  if (!type_targs_deducible_from (tmpl, ret))
> +	  if (ctad_kind == alias
> +	      && !type_targs_deducible_from (tmpl, ret))
>  	    continue;
>  	}
>  
> +      /* Rewrite the return type of the inherited guide in terms of the
> +	 derived class.  This is specified as replacing the return type R
> +	 with typename CC<R>::type where the partially specialized CC maps a
> +	 base class specialization to a specialization of the derived class
> +	 having such a base (inducing substitution failure if no such derived
> +	 class exists).
> +
> +	 As specified this mapping would be done at instantiation time using
> +	 non-dependent template arguments, but we do it ahead of time using
> +	 the generic arguments.  This seems to be good enough since generic
> +	 deduction should succeed only if concrete deduction would.  */
> +      if (ctad_kind == inherited)
> +	{
> +	  processing_template_decl_sentinel ptds (/*reset*/false);
> +	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
> +	    ++processing_template_decl;
> +
> +	  if (TREE_CODE (f) != TEMPLATE_DECL)
> +	    fprime = copy_decl (fprime);
> +	  else
> +	    /* Our fprime is already a copy.  */;
> +
> +	  tree fntype = TREE_TYPE (fprime);
> +	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
> +				       in_decl, NULL_TREE, false, complain);

Oops, the local variable targs is missing from this block due to a
botched git commit --amend.  Here's the corrected v2 patch:

-- >8 --

gcc/cp/ChangeLog:

	* cp-tree.h (type_targs_deducible_from): Adjust return type.
	* pt.cc (alias_ctad_tweaks): Handle C++23 inherited CTAD.
	(inherited_ctad_tweaks): Define.
	(type_targs_deducible_from): Return the deduced arguments or
	NULL_TREE instead of a bool.  Handle 'tmpl' being a TREE_LIST
	representing a synthetic alias template.
	(ctor_deduction_guides_for): Do inherited_ctad_tweaks for each
	USING_DECL in C++23 mode.
	(deduction_guides_for): Add FIXME for stale cache entries in
	light of inherited CTAD.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1z/class-deduction67.C: Accept in C++23 mode.
	* g++.dg/cpp23/class-deduction-inherited1.C: New test.
	* g++.dg/cpp23/class-deduction-inherited2.C: New test.
	* g++.dg/cpp23/class-deduction-inherited3.C: New test.
	* g++.dg/cpp23/class-deduction-inherited4.C: New test.
---
 gcc/cp/cp-tree.h                              |   2 +-
 gcc/cp/pt.cc                                  | 187 +++++++++++++++---
 .../g++.dg/cpp1z/class-deduction67.C          |   5 +-
 .../g++.dg/cpp23/class-deduction-inherited1.C |  38 ++++
 .../g++.dg/cpp23/class-deduction-inherited2.C |  26 +++
 .../g++.dg/cpp23/class-deduction-inherited3.C |  16 ++
 .../g++.dg/cpp23/class-deduction-inherited4.C |  32 +++
 7 files changed, 273 insertions(+), 33 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7b0b7c6a17e..abc467fb290 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7457,7 +7457,7 @@ extern tree fn_type_unification			(tree, tree, tree,
 						 bool, bool);
 extern void mark_decl_instantiated		(tree, int);
 extern int more_specialized_fn			(tree, tree, int);
-extern bool type_targs_deducible_from		(tree, tree);
+extern tree type_targs_deducible_from		(tree, tree);
 extern void do_decl_instantiation		(tree, tree);
 extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
 extern bool always_instantiate_p		(tree);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 092e6fdfd36..5b982d1d641 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -223,6 +223,9 @@ static void instantiate_body (tree pattern, tree args, tree d, bool nested);
 static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
 static void mark_template_arguments_used (tree, tree);
 static bool uses_outer_template_parms (tree);
+static tree alias_ctad_tweaks (tree, tree);
+static tree inherited_ctad_tweaks (tree, tree, tsubst_flags_t);
+static tree deduction_guides_for (tree, bool&, tsubst_flags_t);
 
 /* Make the current scope suitable for access checking when we are
    processing T.  T can be FUNCTION_DECL for instantiated function
@@ -29736,8 +29739,6 @@ is_spec_or_derived (tree etype, tree tmpl)
   return !err;
 }
 
-static tree alias_ctad_tweaks (tree, tree);
-
 /* Return a C++20 aggregate deduction candidate for TYPE initialized from
    INIT.  */
 
@@ -29842,7 +29843,13 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
 }
 
 /* UGUIDES are the deduction guides for the underlying template of alias
-   template TMPL; adjust them to be deduction guides for TMPL.  */
+   template TMPL; adjust them to be deduction guides for TMPL.
+
+   This routine also handles C++23 inherited CTAD, in which case TMPL is a
+   TREE_LIST representing a synthetic alias template whose TREE_PURPOSE is
+   the template parameter list of the alias template (equivalently, of the
+   derived class) and TREE_VALUE the defining-type-id (equivalently, the
+   base whose guides we're inheriting).  UGUIDES are the base's guides.  */
 
 static tree
 alias_ctad_tweaks (tree tmpl, tree uguides)
@@ -29886,13 +29893,27 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
      * The explicit-specifier of f' is the explicit-specifier of g (if
      any).  */
 
+  enum { alias, inherited } ctad_kind;
+  tree atype, fullatparms, utype;
+  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
+    {
+      ctad_kind = alias;
+      atype = TREE_TYPE (tmpl);
+      fullatparms = DECL_TEMPLATE_PARMS (tmpl);
+      utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
+    }
+  else
+    {
+      ctad_kind = inherited;
+      atype = NULL_TREE;
+      fullatparms = TREE_PURPOSE (tmpl);
+      utype = TREE_VALUE (tmpl);
+    }
+
   tsubst_flags_t complain = tf_warning_or_error;
-  tree atype = TREE_TYPE (tmpl);
   tree aguides = NULL_TREE;
-  tree fullatparms = DECL_TEMPLATE_PARMS (tmpl);
   tree atparms = INNERMOST_TEMPLATE_PARMS (fullatparms);
   unsigned natparms = TREE_VEC_LENGTH (atparms);
-  tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
   for (ovl_iterator iter (uguides); iter; ++iter)
     {
       tree f = *iter;
@@ -29930,7 +29951,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 
 	  /* Set current_template_parms as in build_deduction_guide.  */
 	  auto ctp = make_temp_override (current_template_parms);
-	  current_template_parms = copy_node (DECL_TEMPLATE_PARMS (tmpl));
+	  current_template_parms = copy_node (fullatparms);
 	  TREE_VALUE (current_template_parms) = gtparms;
 
 	  j = 0;
@@ -30006,9 +30027,10 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	  /* Add a constraint that the return type matches the instantiation of
 	     A with the same template arguments.  */
 	  ret = TREE_TYPE (TREE_TYPE (fprime));
-	  if (!same_type_p (atype, ret)
-	      /* FIXME this should mean they don't compare as equivalent.  */
-	      || dependent_alias_template_spec_p (atype, nt_opaque))
+	  if (ctad_kind == alias
+	      && (!same_type_p (atype, ret)
+		  /* FIXME this should mean they don't compare as equivalent.  */
+		  || dependent_alias_template_spec_p (atype, nt_opaque)))
 	    {
 	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
 	      ci = append_constraint (ci, same);
@@ -30025,8 +30047,41 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
 	  /* For a non-template deduction guide, if the arguments of A aren't
 	     deducible from the return type, don't add the candidate.  */
 	non_template:
-	  if (!type_targs_deducible_from (tmpl, ret))
+	  if (ctad_kind == alias
+	      && !type_targs_deducible_from (tmpl, ret))
+	    continue;
+	}
+
+      /* Rewrite the return type of the inherited guide in terms of the
+	 derived class.  This is specified as replacing the return type R
+	 with typename CC<R>::type where the partially specialized CC maps a
+	 base class specialization to a specialization of the derived class
+	 having such a base (inducing substitution failure if no such derived
+	 class exists).
+
+	 As specified this mapping would be done at instantiation time using
+	 non-dependent template arguments, but we do it ahead of time using
+	 the generic arguments.  This seems to be good enough since generic
+	 deduction should succeed only if concrete deduction would.  */
+      if (ctad_kind == inherited)
+	{
+	  processing_template_decl_sentinel ptds (/*reset*/false);
+	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
+	    ++processing_template_decl;
+
+	  tree targs = type_targs_deducible_from (tmpl, ret);
+	  if (!targs)
 	    continue;
+
+	  if (TREE_CODE (f) != TEMPLATE_DECL)
+	    fprime = copy_decl (fprime);
+	  tree fntype = TREE_TYPE (fprime);
+	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
+				       in_decl, NULL_TREE, false, complain);
+	  fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
+	  TREE_TYPE (fprime) = fntype;
+	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
+	    TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype;
 	}
 
       aguides = lookup_add (fprime, aguides);
@@ -30035,33 +30090,92 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
   return aguides;
 }
 
-/* True iff template arguments for TMPL can be deduced from TYPE.
+/* CTOR is a using-declaration inheriting the constructors of some base of the
+   class template TMPL; adjust the base's guides be deduction guides for TMPL.  */
+
+static tree
+inherited_ctad_tweaks (tree tmpl, tree ctor, tsubst_flags_t complain)
+{
+  /* [over.match.class.deduct]: In addition, if C is defined and inherits
+     constructors ([namespace.udecl]) from a direct base class denoted in the
+     base-specifier-list by a class-or-decltype B, let A be an alias template
+     whose template parameter list is that of C and whose defining-type-id is
+     B.  If A is a deducible template ([dcl.type.simple]), the set contains the
+     guides of A with the return type R of each guide replaced with typename
+     CC::type given a class template
+
+     template <typename> class CC;
+
+     whose primary template is not defined and with a single partial
+     specialization whose template parameter list is that of A and whose
+     template argument list is a specialization of A with the template argument
+     list of A ([temp.dep.type]) having a member typedef type designating a
+     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 (!CLASS_TYPE_P (scope)
+      || !CLASSTYPE_TEMPLATE_INFO (scope))
+    return NULL_TREE;
+
+  tree t = build_tree_list (DECL_TEMPLATE_PARMS (tmpl), scope);
+  bool any_dguides_p;
+  tree uguides = deduction_guides_for (TYPE_TI_TEMPLATE (scope),
+				       any_dguides_p, complain);
+  return alias_ctad_tweaks (t, uguides);
+}
+
+/* If template arguments for TMPL can be deduced from TYPE, return
+   the deduced arguments, otherwise return NULL_TREE.
    Used to implement CPTK_IS_DEDUCIBLE for alias CTAD according to
    [over.match.class.deduct].
 
    This check is specified in terms of partial specialization, so the behavior
    should be parallel to that of get_partial_spec_bindings.  */
 
-bool
+tree
 type_targs_deducible_from (tree tmpl, tree type)
 {
-  tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+  tree tparms, ttype;
+  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
+    {
+      /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
+	 specialization of TMPL.  */
+      if (DECL_CLASS_TEMPLATE_P (tmpl))
+	{
+	  if (CLASS_TYPE_P (type)
+	      && CLASSTYPE_TEMPLATE_INFO (type)
+	      && CLASSTYPE_TI_TEMPLATE (type) == tmpl)
+	    return INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
+	  else
+	    return NULL_TREE;
+	}
+
+      /* Otherwise it's an alias template.  */
+      tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+      ttype = TREE_TYPE (tmpl);
+    }
+  else
+    {
+      /* TMPL is a synthetic alias template represented as a TREE_LIST as
+	 per alias_ctad_tweaks.  */
+      tparms = INNERMOST_TEMPLATE_PARMS (TREE_PURPOSE (tmpl));
+      ttype = TREE_VALUE (tmpl);
+      tmpl = TI_TEMPLATE (TYPE_TEMPLATE_INFO_MAYBE_ALIAS (ttype));
+    }
+
   int len = TREE_VEC_LENGTH (tparms);
   tree targs = make_tree_vec (len);
   bool tried_array_deduction = (cxx_dialect < cxx17);
 
-  /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
-     specialization of TMPL.  */
-  if (DECL_CLASS_TEMPLATE_P (tmpl))
-    return (CLASS_TYPE_P (type)
-	    && CLASSTYPE_TEMPLATE_INFO (type)
-	    && CLASSTYPE_TI_TEMPLATE (type) == tmpl);
-
-  /* Otherwise it's an alias template.  */
  again:
-  if (unify (tparms, targs, TREE_TYPE (tmpl), type,
+  if (unify (tparms, targs, ttype, type,
 	     UNIFY_ALLOW_NONE, false))
-    return false;
+    return NULL_TREE;
 
   /* We don't fail on an undeduced targ the second time through (like
      get_partial_spec_bindings) because we're going to try defaults.  */
@@ -30074,7 +30188,7 @@ type_targs_deducible_from (tree tmpl, tree type)
 	if (!tried_array_deduction
 	    && TREE_CODE (tparm) == TYPE_DECL)
 	  {
-	    try_array_deduction (tparms, targs, TREE_TYPE (tmpl));
+	    try_array_deduction (tparms, targs, ttype);
 	    tried_array_deduction = true;
 	    if (TREE_VEC_ELT (targs, i))
 	      goto again;
@@ -30103,12 +30217,15 @@ type_targs_deducible_from (tree tmpl, tree type)
      partial specialization can't have default targs.  */
   targs = coerce_template_parms (tparms, targs, tmpl, tf_none);
   if (targs == error_mark_node)
-    return false;
+    return NULL_TREE;
 
   /* I believe we don't need the template_template_parm_bindings_ok_p call
      because coerce_template_parms did coerce_template_template_parms.  */
 
-  return constraints_satisfied_p (tmpl, targs);
+  if (!constraints_satisfied_p (tmpl, targs))
+    return NULL_TREE;
+
+  return targs;
 }
 
 /* Return artificial deduction guides built from the constructors of class
@@ -30124,7 +30241,7 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
 
   for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter)
     {
-      /* Skip inherited constructors.  */
+      /* We handle C++23 inherited CTAD below.  */
       if (iter.using_p ())
 	continue;
 
@@ -30132,6 +30249,15 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
       cands = lookup_add (guide, cands);
     }
 
+  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);
+	  if (uguides)
+	    cands = lookup_add (uguides, cands);
+	}
+
   /* Add implicit default constructor deduction guide.  */
   if (!TYPE_HAS_USER_CONSTRUCTOR (type))
     {
@@ -30181,7 +30307,10 @@ deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
     }
 
   /* Cache the deduction guides for a template.  We also remember the result of
-     lookup, and rebuild everything if it changes; should be very rare.  */
+     lookup, and rebuild everything if it changes; should be very rare.
+
+     FIXME: Also rebuild if this is a class template that inherits guides from a
+     base class, and lookup for the latter changed.  */
   tree_pair_p cache = NULL;
   if (tree_pair_p &r
       = hash_map_safe_get_or_insert<hm_ggc> (dguide_cache, tmpl))
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
index 4624794c4b7..74f92325d7a 100644
--- a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
@@ -1,5 +1,4 @@
-// Deduction from inherited constructors isn't supported yet, but we shouldn't
-// crash.  It may well be supported in C++23.
+// Deduction from inherited constructors isn't supported before C++23.
 
 //{ dg-do compile { target c++17 } }
 
@@ -17,5 +16,5 @@ int main()
 {
   B b = 42;			// { dg-line init }
   // { dg-prune-output "no matching function" }
-  // { dg-error "class template argument deduction" "" { target *-*-* } init }
+  // { dg-error "class template argument deduction" "" { target c++23_down } init }
 }
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
new file mode 100644
index 00000000000..5fd1270e819
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
@@ -0,0 +1,38 @@
+// Modified example from P2582R1
+// { dg-do compile { target c++23 } }
+
+template <typename T> struct B {
+  B(T);
+};
+B(bool) -> B<char>;
+template <typename T> struct C : public B<T> {
+  using B<T>::B;
+};
+template <typename T> struct D : public B<T> {};
+
+C c(42);            // OK, deduces C<int>
+using ty1 = decltype(c);
+using ty1 = C<int>;
+
+D d(42);            // { dg-error "deduction|no match" }
+
+C c2(true);           // OK, deduces C<char>
+using ty2 = decltype(c2);
+using ty2 = C<char>;
+
+template <typename T> struct E : public B<int> {
+  using B<int>::B;
+};
+
+E e(42);            // { dg-error "deduction|no match" }
+
+template <typename T, typename U, typename V> struct F {
+  F(T, U, V);
+};
+template <typename T, typename U> struct G : F<U, T, int> {
+  using F<U, T, int>::F;
+};
+
+G g(true, 'a', 1);  // OK, deduces G<char, bool>
+using ty3 = decltype(g);
+using ty3 = G<char, bool>;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
new file mode 100644
index 00000000000..cb3c595f316
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
@@ -0,0 +1,26 @@
+// { dg-do compile { target c++23 } }
+
+template<class T, class U, class V> struct F {
+  F(T, U, V);       // #1
+  F(T*, U*, V*);    // #2
+  template<class W>
+  F(int, int, W);   // #3
+};
+
+F(bool, bool, bool) -> F<bool*, void*, int>;
+
+template<class T, class U> struct G : F<U, T, int> {
+  using F<U, T, int>::F;
+};
+
+using ty1 = decltype(G(true, 'a', 1)); // uses #1
+using ty1 = G<char, bool>;
+
+using ty2 = decltype(G((bool*)0, (char*)0, (int*)0)); // uses #2
+using ty2 = G<char, bool>;
+
+using ty3 = decltype(G(0, 0, 0)); // uses #3
+using ty3 = G<int, int>;
+
+using ty4 = decltype(G(true, true, true)); // uses #4
+using ty4 = G<void*, bool*>;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
new file mode 100644
index 00000000000..57e323b5124
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++23 } }
+
+template<class T>
+struct A {
+  A(T);
+  template<class U> A(T, U);
+};
+
+template<class T>
+struct B : A<const T> {
+  using A<const T>::A;
+};
+
+using type = decltype(B(0));
+using type = decltype(B(0, 0));
+using type = B<int>;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
new file mode 100644
index 00000000000..5e3a7f42919
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
@@ -0,0 +1,32 @@
+// { dg-do compile { target c++23 } }
+
+template<class T>
+struct A { A(T); };
+
+template<class T>
+struct B : A<T> {
+  using B::A::A; // FIXME: we don't notice this inherited ctor
+};
+
+using ty1 = decltype(B(0)); // { dg-bogus "" "" { xfail *-*-* } }
+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 ty2 = decltype(C(0)); // { dg-bogus "" "" { xfail *-*-* } }
+using ty2 = C<void>;
+
+template<class T>
+struct D : A<T> {
+  using A<T>::A;
+};
+
+using ty3 = decltype(D(0));
+using ty3 = D<int>;
+
+A(int) -> A<char>; // FIXME: we need to rebuild the guides of D
+using ty4 = decltype(D(0));
+using ty4 = D<char>; // { dg-bogus "conflicting" "" { xfail *-*-* } }
Jason Merrill Dec. 14, 2023, 1:48 a.m. UTC | #3
On 11/27/23 10:58, Patrick Palka wrote:
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (type_targs_deducible_from): Adjust return type.
> 	* pt.cc (alias_ctad_tweaks): Handle C++23 inherited CTAD.
> 	(inherited_ctad_tweaks): Define.
> 	(type_targs_deducible_from): Return the deduced arguments or
> 	NULL_TREE instead of a bool.  Handle 'tmpl' being a TREE_LIST
> 	representing a synthetic alias template.
> 	(ctor_deduction_guides_for): Do inherited_ctad_tweaks for each
> 	USING_DECL in C++23 mode.
> 	(deduction_guides_for): Add FIXME for stale cache entries in
> 	light of inherited CTAD.

check_GNU_style.py notices a few too-long lines in comments:

> === ERROR type #2: lines should not exceed 80 characters (3 error(s)) ===
> gcc/cp/pt.cc:30076:80:                  /* FIXME this should mean they don't compare as equivalent.  */
> gcc/cp/pt.cc:30138:80:   class template TMPL; adjust the base's guides be deduction guides for TMPL.  */
> gcc/cp/pt.cc:30190:80:      /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a

OK with those fixed.

> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp1z/class-deduction67.C: Accept in C++23 mode.
> 	* g++.dg/cpp23/class-deduction-inherited1.C: New test.
> 	* g++.dg/cpp23/class-deduction-inherited2.C: New test.
> 	* g++.dg/cpp23/class-deduction-inherited3.C: New test.
> 	* g++.dg/cpp23/class-deduction-inherited4.C: New test.
> ---
>   gcc/cp/cp-tree.h                              |   2 +-
>   gcc/cp/pt.cc                                  | 187 +++++++++++++++---
>   .../g++.dg/cpp1z/class-deduction67.C          |   5 +-
>   .../g++.dg/cpp23/class-deduction-inherited1.C |  38 ++++
>   .../g++.dg/cpp23/class-deduction-inherited2.C |  26 +++
>   .../g++.dg/cpp23/class-deduction-inherited3.C |  16 ++
>   .../g++.dg/cpp23/class-deduction-inherited4.C |  32 +++
>   7 files changed, 273 insertions(+), 33 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> 
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 7b0b7c6a17e..abc467fb290 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7457,7 +7457,7 @@ extern tree fn_type_unification			(tree, tree, tree,
>   						 bool, bool);
>   extern void mark_decl_instantiated		(tree, int);
>   extern int more_specialized_fn			(tree, tree, int);
> -extern bool type_targs_deducible_from		(tree, tree);
> +extern tree type_targs_deducible_from		(tree, tree);
>   extern void do_decl_instantiation		(tree, tree);
>   extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
>   extern bool always_instantiate_p		(tree);
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 092e6fdfd36..5b982d1d641 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -223,6 +223,9 @@ static void instantiate_body (tree pattern, tree args, tree d, bool nested);
>   static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
>   static void mark_template_arguments_used (tree, tree);
>   static bool uses_outer_template_parms (tree);
> +static tree alias_ctad_tweaks (tree, tree);
> +static tree inherited_ctad_tweaks (tree, tree, tsubst_flags_t);
> +static tree deduction_guides_for (tree, bool&, tsubst_flags_t);
>   
>   /* Make the current scope suitable for access checking when we are
>      processing T.  T can be FUNCTION_DECL for instantiated function
> @@ -29736,8 +29739,6 @@ is_spec_or_derived (tree etype, tree tmpl)
>     return !err;
>   }
>   
> -static tree alias_ctad_tweaks (tree, tree);
> -
>   /* Return a C++20 aggregate deduction candidate for TYPE initialized from
>      INIT.  */
>   
> @@ -29842,7 +29843,13 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
>   }
>   
>   /* UGUIDES are the deduction guides for the underlying template of alias
> -   template TMPL; adjust them to be deduction guides for TMPL.  */
> +   template TMPL; adjust them to be deduction guides for TMPL.
> +
> +   This routine also handles C++23 inherited CTAD, in which case TMPL is a
> +   TREE_LIST representing a synthetic alias template whose TREE_PURPOSE is
> +   the template parameter list of the alias template (equivalently, of the
> +   derived class) and TREE_VALUE the defining-type-id (equivalently, the
> +   base whose guides we're inheriting).  UGUIDES are the base's guides.  */
>   
>   static tree
>   alias_ctad_tweaks (tree tmpl, tree uguides)
> @@ -29886,13 +29893,27 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>        * The explicit-specifier of f' is the explicit-specifier of g (if
>        any).  */
>   
> +  enum { alias, inherited } ctad_kind;
> +  tree atype, fullatparms, utype;
> +  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
> +    {
> +      ctad_kind = alias;
> +      atype = TREE_TYPE (tmpl);
> +      fullatparms = DECL_TEMPLATE_PARMS (tmpl);
> +      utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
> +    }
> +  else
> +    {
> +      ctad_kind = inherited;
> +      atype = NULL_TREE;
> +      fullatparms = TREE_PURPOSE (tmpl);
> +      utype = TREE_VALUE (tmpl);
> +    }
> +
>     tsubst_flags_t complain = tf_warning_or_error;
> -  tree atype = TREE_TYPE (tmpl);
>     tree aguides = NULL_TREE;
> -  tree fullatparms = DECL_TEMPLATE_PARMS (tmpl);
>     tree atparms = INNERMOST_TEMPLATE_PARMS (fullatparms);
>     unsigned natparms = TREE_VEC_LENGTH (atparms);
> -  tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
>     for (ovl_iterator iter (uguides); iter; ++iter)
>       {
>         tree f = *iter;
> @@ -29930,7 +29951,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>   
>   	  /* Set current_template_parms as in build_deduction_guide.  */
>   	  auto ctp = make_temp_override (current_template_parms);
> -	  current_template_parms = copy_node (DECL_TEMPLATE_PARMS (tmpl));
> +	  current_template_parms = copy_node (fullatparms);
>   	  TREE_VALUE (current_template_parms) = gtparms;
>   
>   	  j = 0;
> @@ -30006,9 +30027,10 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>   	  /* Add a constraint that the return type matches the instantiation of
>   	     A with the same template arguments.  */
>   	  ret = TREE_TYPE (TREE_TYPE (fprime));
> -	  if (!same_type_p (atype, ret)
> -	      /* FIXME this should mean they don't compare as equivalent.  */
> -	      || dependent_alias_template_spec_p (atype, nt_opaque))
> +	  if (ctad_kind == alias
> +	      && (!same_type_p (atype, ret)
> +		  /* FIXME this should mean they don't compare as equivalent.  */
> +		  || dependent_alias_template_spec_p (atype, nt_opaque)))
>   	    {
>   	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
>   	      ci = append_constraint (ci, same);
> @@ -30025,8 +30047,41 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>   	  /* For a non-template deduction guide, if the arguments of A aren't
>   	     deducible from the return type, don't add the candidate.  */
>   	non_template:
> -	  if (!type_targs_deducible_from (tmpl, ret))
> +	  if (ctad_kind == alias
> +	      && !type_targs_deducible_from (tmpl, ret))
> +	    continue;
> +	}
> +
> +      /* Rewrite the return type of the inherited guide in terms of the
> +	 derived class.  This is specified as replacing the return type R
> +	 with typename CC<R>::type where the partially specialized CC maps a
> +	 base class specialization to a specialization of the derived class
> +	 having such a base (inducing substitution failure if no such derived
> +	 class exists).
> +
> +	 As specified this mapping would be done at instantiation time using
> +	 non-dependent template arguments, but we do it ahead of time using
> +	 the generic arguments.  This seems to be good enough since generic
> +	 deduction should succeed only if concrete deduction would.  */
> +      if (ctad_kind == inherited)
> +	{
> +	  processing_template_decl_sentinel ptds (/*reset*/false);
> +	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
> +	    ++processing_template_decl;
> +
> +	  tree targs = type_targs_deducible_from (tmpl, ret);
> +	  if (!targs)
>   	    continue;
> +
> +	  if (TREE_CODE (f) != TEMPLATE_DECL)
> +	    fprime = copy_decl (fprime);
> +	  tree fntype = TREE_TYPE (fprime);
> +	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
> +				       in_decl, NULL_TREE, false, complain);
> +	  fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
> +	  TREE_TYPE (fprime) = fntype;
> +	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
> +	    TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype;
>   	}
>   
>         aguides = lookup_add (fprime, aguides);
> @@ -30035,33 +30090,92 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
>     return aguides;
>   }
>   
> -/* True iff template arguments for TMPL can be deduced from TYPE.
> +/* CTOR is a using-declaration inheriting the constructors of some base of the
> +   class template TMPL; adjust the base's guides be deduction guides for TMPL.  */
> +
> +static tree
> +inherited_ctad_tweaks (tree tmpl, tree ctor, tsubst_flags_t complain)
> +{
> +  /* [over.match.class.deduct]: In addition, if C is defined and inherits
> +     constructors ([namespace.udecl]) from a direct base class denoted in the
> +     base-specifier-list by a class-or-decltype B, let A be an alias template
> +     whose template parameter list is that of C and whose defining-type-id is
> +     B.  If A is a deducible template ([dcl.type.simple]), the set contains the
> +     guides of A with the return type R of each guide replaced with typename
> +     CC::type given a class template
> +
> +     template <typename> class CC;
> +
> +     whose primary template is not defined and with a single partial
> +     specialization whose template parameter list is that of A and whose
> +     template argument list is a specialization of A with the template argument
> +     list of A ([temp.dep.type]) having a member typedef type designating a
> +     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 (!CLASS_TYPE_P (scope)
> +      || !CLASSTYPE_TEMPLATE_INFO (scope))
> +    return NULL_TREE;
> +
> +  tree t = build_tree_list (DECL_TEMPLATE_PARMS (tmpl), scope);
> +  bool any_dguides_p;
> +  tree uguides = deduction_guides_for (TYPE_TI_TEMPLATE (scope),
> +				       any_dguides_p, complain);
> +  return alias_ctad_tweaks (t, uguides);
> +}
> +
> +/* If template arguments for TMPL can be deduced from TYPE, return
> +   the deduced arguments, otherwise return NULL_TREE.
>      Used to implement CPTK_IS_DEDUCIBLE for alias CTAD according to
>      [over.match.class.deduct].
>   
>      This check is specified in terms of partial specialization, so the behavior
>      should be parallel to that of get_partial_spec_bindings.  */
>   
> -bool
> +tree
>   type_targs_deducible_from (tree tmpl, tree type)
>   {
> -  tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
> +  tree tparms, ttype;
> +  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
> +    {
> +      /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
> +	 specialization of TMPL.  */
> +      if (DECL_CLASS_TEMPLATE_P (tmpl))
> +	{
> +	  if (CLASS_TYPE_P (type)
> +	      && CLASSTYPE_TEMPLATE_INFO (type)
> +	      && CLASSTYPE_TI_TEMPLATE (type) == tmpl)
> +	    return INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
> +	  else
> +	    return NULL_TREE;
> +	}
> +
> +      /* Otherwise it's an alias template.  */
> +      tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
> +      ttype = TREE_TYPE (tmpl);
> +    }
> +  else
> +    {
> +      /* TMPL is a synthetic alias template represented as a TREE_LIST as
> +	 per alias_ctad_tweaks.  */
> +      tparms = INNERMOST_TEMPLATE_PARMS (TREE_PURPOSE (tmpl));
> +      ttype = TREE_VALUE (tmpl);
> +      tmpl = TI_TEMPLATE (TYPE_TEMPLATE_INFO_MAYBE_ALIAS (ttype));
> +    }
> +
>     int len = TREE_VEC_LENGTH (tparms);
>     tree targs = make_tree_vec (len);
>     bool tried_array_deduction = (cxx_dialect < cxx17);
>   
> -  /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
> -     specialization of TMPL.  */
> -  if (DECL_CLASS_TEMPLATE_P (tmpl))
> -    return (CLASS_TYPE_P (type)
> -	    && CLASSTYPE_TEMPLATE_INFO (type)
> -	    && CLASSTYPE_TI_TEMPLATE (type) == tmpl);
> -
> -  /* Otherwise it's an alias template.  */
>    again:
> -  if (unify (tparms, targs, TREE_TYPE (tmpl), type,
> +  if (unify (tparms, targs, ttype, type,
>   	     UNIFY_ALLOW_NONE, false))
> -    return false;
> +    return NULL_TREE;
>   
>     /* We don't fail on an undeduced targ the second time through (like
>        get_partial_spec_bindings) because we're going to try defaults.  */
> @@ -30074,7 +30188,7 @@ type_targs_deducible_from (tree tmpl, tree type)
>   	if (!tried_array_deduction
>   	    && TREE_CODE (tparm) == TYPE_DECL)
>   	  {
> -	    try_array_deduction (tparms, targs, TREE_TYPE (tmpl));
> +	    try_array_deduction (tparms, targs, ttype);
>   	    tried_array_deduction = true;
>   	    if (TREE_VEC_ELT (targs, i))
>   	      goto again;
> @@ -30103,12 +30217,15 @@ type_targs_deducible_from (tree tmpl, tree type)
>        partial specialization can't have default targs.  */
>     targs = coerce_template_parms (tparms, targs, tmpl, tf_none);
>     if (targs == error_mark_node)
> -    return false;
> +    return NULL_TREE;
>   
>     /* I believe we don't need the template_template_parm_bindings_ok_p call
>        because coerce_template_parms did coerce_template_template_parms.  */
>   
> -  return constraints_satisfied_p (tmpl, targs);
> +  if (!constraints_satisfied_p (tmpl, targs))
> +    return NULL_TREE;
> +
> +  return targs;
>   }
>   
>   /* Return artificial deduction guides built from the constructors of class
> @@ -30124,7 +30241,7 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
>   
>     for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter)
>       {
> -      /* Skip inherited constructors.  */
> +      /* We handle C++23 inherited CTAD below.  */
>         if (iter.using_p ())
>   	continue;
>   
> @@ -30132,6 +30249,15 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
>         cands = lookup_add (guide, cands);
>       }
>   
> +  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);
> +	  if (uguides)
> +	    cands = lookup_add (uguides, cands);
> +	}
> +
>     /* Add implicit default constructor deduction guide.  */
>     if (!TYPE_HAS_USER_CONSTRUCTOR (type))
>       {
> @@ -30181,7 +30307,10 @@ deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
>       }
>   
>     /* Cache the deduction guides for a template.  We also remember the result of
> -     lookup, and rebuild everything if it changes; should be very rare.  */
> +     lookup, and rebuild everything if it changes; should be very rare.
> +
> +     FIXME: Also rebuild if this is a class template that inherits guides from a
> +     base class, and lookup for the latter changed.  */
>     tree_pair_p cache = NULL;
>     if (tree_pair_p &r
>         = hash_map_safe_get_or_insert<hm_ggc> (dguide_cache, tmpl))
> diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
> index 4624794c4b7..74f92325d7a 100644
> --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
> +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
> @@ -1,5 +1,4 @@
> -// Deduction from inherited constructors isn't supported yet, but we shouldn't
> -// crash.  It may well be supported in C++23.
> +// Deduction from inherited constructors isn't supported before C++23.
>   
>   //{ dg-do compile { target c++17 } }
>   
> @@ -17,5 +16,5 @@ int main()
>   {
>     B b = 42;			// { dg-line init }
>     // { dg-prune-output "no matching function" }
> -  // { dg-error "class template argument deduction" "" { target *-*-* } init }
> +  // { dg-error "class template argument deduction" "" { target c++23_down } init }
>   }
> diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
> new file mode 100644
> index 00000000000..5fd1270e819
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
> @@ -0,0 +1,38 @@
> +// Modified example from P2582R1
> +// { dg-do compile { target c++23 } }
> +
> +template <typename T> struct B {
> +  B(T);
> +};
> +B(bool) -> B<char>;
> +template <typename T> struct C : public B<T> {
> +  using B<T>::B;
> +};
> +template <typename T> struct D : public B<T> {};
> +
> +C c(42);            // OK, deduces C<int>
> +using ty1 = decltype(c);
> +using ty1 = C<int>;
> +
> +D d(42);            // { dg-error "deduction|no match" }
> +
> +C c2(true);           // OK, deduces C<char>
> +using ty2 = decltype(c2);
> +using ty2 = C<char>;
> +
> +template <typename T> struct E : public B<int> {
> +  using B<int>::B;
> +};
> +
> +E e(42);            // { dg-error "deduction|no match" }
> +
> +template <typename T, typename U, typename V> struct F {
> +  F(T, U, V);
> +};
> +template <typename T, typename U> struct G : F<U, T, int> {
> +  using F<U, T, int>::F;
> +};
> +
> +G g(true, 'a', 1);  // OK, deduces G<char, bool>
> +using ty3 = decltype(g);
> +using ty3 = G<char, bool>;
> diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
> new file mode 100644
> index 00000000000..cb3c595f316
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
> @@ -0,0 +1,26 @@
> +// { dg-do compile { target c++23 } }
> +
> +template<class T, class U, class V> struct F {
> +  F(T, U, V);       // #1
> +  F(T*, U*, V*);    // #2
> +  template<class W>
> +  F(int, int, W);   // #3
> +};
> +
> +F(bool, bool, bool) -> F<bool*, void*, int>;
> +
> +template<class T, class U> struct G : F<U, T, int> {
> +  using F<U, T, int>::F;
> +};
> +
> +using ty1 = decltype(G(true, 'a', 1)); // uses #1
> +using ty1 = G<char, bool>;
> +
> +using ty2 = decltype(G((bool*)0, (char*)0, (int*)0)); // uses #2
> +using ty2 = G<char, bool>;
> +
> +using ty3 = decltype(G(0, 0, 0)); // uses #3
> +using ty3 = G<int, int>;
> +
> +using ty4 = decltype(G(true, true, true)); // uses #4
> +using ty4 = G<void*, bool*>;
> diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
> new file mode 100644
> index 00000000000..57e323b5124
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
> @@ -0,0 +1,16 @@
> +// { dg-do compile { target c++23 } }
> +
> +template<class T>
> +struct A {
> +  A(T);
> +  template<class U> A(T, U);
> +};
> +
> +template<class T>
> +struct B : A<const T> {
> +  using A<const T>::A;
> +};
> +
> +using type = decltype(B(0));
> +using type = decltype(B(0, 0));
> +using type = B<int>;
> diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> new file mode 100644
> index 00000000000..5e3a7f42919
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
> @@ -0,0 +1,32 @@
> +// { dg-do compile { target c++23 } }
> +
> +template<class T>
> +struct A { A(T); };
> +
> +template<class T>
> +struct B : A<T> {
> +  using B::A::A; // FIXME: we don't notice this inherited ctor
> +};
> +
> +using ty1 = decltype(B(0)); // { dg-bogus "" "" { xfail *-*-* } }
> +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 ty2 = decltype(C(0)); // { dg-bogus "" "" { xfail *-*-* } }
> +using ty2 = C<void>;
> +
> +template<class T>
> +struct D : A<T> {
> +  using A<T>::A;
> +};
> +
> +using ty3 = decltype(D(0));
> +using ty3 = D<int>;
> +
> +A(int) -> A<char>; // FIXME: we need to rebuild the guides of D
> +using ty4 = decltype(D(0));
> +using ty4 = D<char>; // { dg-bogus "conflicting" "" { xfail *-*-* } }
Marek Polacek Dec. 14, 2023, 5:54 p.m. UTC | #4
On Wed, Dec 13, 2023 at 08:48:49PM -0500, Jason Merrill wrote:
> On 11/27/23 10:58, Patrick Palka wrote:
> > gcc/cp/ChangeLog:
> > 
> > 	* cp-tree.h (type_targs_deducible_from): Adjust return type.
> > 	* pt.cc (alias_ctad_tweaks): Handle C++23 inherited CTAD.
> > 	(inherited_ctad_tweaks): Define.
> > 	(type_targs_deducible_from): Return the deduced arguments or
> > 	NULL_TREE instead of a bool.  Handle 'tmpl' being a TREE_LIST
> > 	representing a synthetic alias template.
> > 	(ctor_deduction_guides_for): Do inherited_ctad_tweaks for each
> > 	USING_DECL in C++23 mode.
> > 	(deduction_guides_for): Add FIXME for stale cache entries in
> > 	light of inherited CTAD.
> 
> check_GNU_style.py notices a few too-long lines in comments:
> 
> > === ERROR type #2: lines should not exceed 80 characters (3 error(s)) ===
> > gcc/cp/pt.cc:30076:80:                  /* FIXME this should mean they don't compare as equivalent.  */
> > gcc/cp/pt.cc:30138:80:   class template TMPL; adjust the base's guides be deduction guides for TMPL.  */
> > gcc/cp/pt.cc:30190:80:      /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
> 
> OK with those fixed.
 
> > index 4624794c4b7..74f92325d7a 100644
> > --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
> > +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
> > @@ -1,5 +1,4 @@
> > -// Deduction from inherited constructors isn't supported yet, but we shouldn't
> > -// crash.  It may well be supported in C++23.
> > +// Deduction from inherited constructors isn't supported before C++23.
> >   //{ dg-do compile { target c++17 } }
> > @@ -17,5 +16,5 @@ int main()
> >   {
> >     B b = 42;			// { dg-line init }
> >     // { dg-prune-output "no matching function" }
> > -  // { dg-error "class template argument deduction" "" { target *-*-* } init }
> > +  // { dg-error "class template argument deduction" "" { target c++23_down } init }
> >   }

I checked in this patch:

-- >8 --
The test says that CTAD from inherited constructors doesn't work
before C++23 so we should use c++20_down for the error.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1z/class-deduction67.C: Correct dg-error target.
---
 gcc/testsuite/g++.dg/cpp1z/class-deduction67.C | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
index 74f92325d7a..fa1523d99d5 100644
--- a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
@@ -16,5 +16,5 @@ int main()
 {
   B b = 42;			// { dg-line init }
   // { dg-prune-output "no matching function" }
-  // { dg-error "class template argument deduction" "" { target c++23_down } init }
+  // { dg-error "class template argument deduction" "" { target c++20_down } init }
 }

base-commit: e5e1999aa664333f766f3e6cc6996f769d50ae7a
diff mbox series

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1fa710d7154..633d58b1d12 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7434,7 +7434,7 @@  extern tree fn_type_unification			(tree, tree, tree,
 						 bool, bool);
 extern void mark_decl_instantiated		(tree, int);
 extern int more_specialized_fn			(tree, tree, int);
-extern bool type_targs_deducible_from		(tree, tree);
+extern bool type_targs_deducible_from		(tree, tree, tree * = nullptr);
 extern void do_decl_instantiation		(tree, tree);
 extern void do_type_instantiation		(tree, tree, tsubst_flags_t);
 extern bool always_instantiate_p		(tree);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 324f6f01555..75f5bc9bed5 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -223,6 +223,9 @@  static void instantiate_body (tree pattern, tree args, tree d, bool nested);
 static tree maybe_dependent_member_ref (tree, tree, tsubst_flags_t, tree);
 static void mark_template_arguments_used (tree, tree);
 static bool uses_outer_template_parms (tree);
+static tree alias_ctad_tweaks (tree, tree);
+static tree inherited_ctad_tweaks (tree, tree, tsubst_flags_t);
+static tree deduction_guides_for (tree, bool&, tsubst_flags_t);
 
 /* Make the current scope suitable for access checking when we are
    processing T.  T can be FUNCTION_DECL for instantiated function
@@ -29753,8 +29756,6 @@  is_spec_or_derived (tree etype, tree tmpl)
   return !err;
 }
 
-static tree alias_ctad_tweaks (tree, tree);
-
 /* Return a C++20 aggregate deduction candidate for TYPE initialized from
    INIT.  */
 
@@ -29859,7 +29860,13 @@  maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
 }
 
 /* UGUIDES are the deduction guides for the underlying template of alias
-   template TMPL; adjust them to be deduction guides for TMPL.  */
+   template TMPL; adjust them to be deduction guides for TMPL.
+
+   This routine also handles C++23 inherited CTAD, in which case TMPL is a
+   TREE_LIST representing a synthetic alias template whose TREE_PURPOSE is
+   the template parameter list of the alias template (equivalently, of the
+   derived class) and TREE_VALUE the defining-type-id (equivalently, the
+   base whose guides we're inheriting).  UGUIDES are the base's guides.  */
 
 static tree
 alias_ctad_tweaks (tree tmpl, tree uguides)
@@ -29903,13 +29910,30 @@  alias_ctad_tweaks (tree tmpl, tree uguides)
      * The explicit-specifier of f' is the explicit-specifier of g (if
      any).  */
 
+  enum { alias, inherited } ctad_kind;
+
+  tree atype;
+  tree fullatparms;
+  tree utype;
+  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
+    {
+      ctad_kind = alias;
+      atype = TREE_TYPE (tmpl);
+      fullatparms = DECL_TEMPLATE_PARMS (tmpl);
+      utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
+    }
+  else
+    {
+      ctad_kind = inherited;
+      atype = NULL_TREE;
+      fullatparms = TREE_PURPOSE (tmpl);
+      utype = TREE_VALUE (tmpl);
+    }
+
   tsubst_flags_t complain = tf_warning_or_error;
-  tree atype = TREE_TYPE (tmpl);
   tree aguides = NULL_TREE;
-  tree fullatparms = DECL_TEMPLATE_PARMS (tmpl);
   tree atparms = INNERMOST_TEMPLATE_PARMS (fullatparms);
   unsigned natparms = TREE_VEC_LENGTH (atparms);
-  tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl));
   for (ovl_iterator iter (uguides); iter; ++iter)
     {
       tree f = *iter;
@@ -29947,7 +29971,7 @@  alias_ctad_tweaks (tree tmpl, tree uguides)
 
 	  /* Set current_template_parms as in build_deduction_guide.  */
 	  auto ctp = make_temp_override (current_template_parms);
-	  current_template_parms = copy_node (DECL_TEMPLATE_PARMS (tmpl));
+	  current_template_parms = copy_node (fullatparms);
 	  TREE_VALUE (current_template_parms) = gtparms;
 
 	  j = 0;
@@ -30023,9 +30047,10 @@  alias_ctad_tweaks (tree tmpl, tree uguides)
 	  /* Add a constraint that the return type matches the instantiation of
 	     A with the same template arguments.  */
 	  ret = TREE_TYPE (TREE_TYPE (fprime));
-	  if (!same_type_p (atype, ret)
-	      /* FIXME this should mean they don't compare as equivalent.  */
-	      || dependent_alias_template_spec_p (atype, nt_opaque))
+	  if (ctad_kind == alias
+	      && (!same_type_p (atype, ret)
+		  /* FIXME this should mean they don't compare as equivalent.  */
+		  || dependent_alias_template_spec_p (atype, nt_opaque)))
 	    {
 	      tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
 	      ci = append_constraint (ci, same);
@@ -30042,8 +30067,37 @@  alias_ctad_tweaks (tree tmpl, tree uguides)
 	  /* For a non-template deduction guide, if the arguments of A aren't
 	     deducible from the return type, don't add the candidate.  */
 	non_template:
-	  if (!type_targs_deducible_from (tmpl, ret))
+	  if (ctad_kind == alias
+	      && !type_targs_deducible_from (tmpl, ret))
+	    continue;
+	}
+
+      /* Rewrite the return type of the inherited guide in terms of the
+	 derived class.  This is specified as replacing the return type R
+	 with typename CC<R>::type where the partially specialized CC maps a
+	 base class specialization to a specialization of the derived class
+	 having such a base (inducing substitution failure if no such derived
+	 class exists).
+
+	 As specified this mapping would be done at instantiation time using
+	 non-dependent template arguments, but we do it ahead of time using
+	 the generic arguments.  This seems to be good enough since generic
+	 deduction should succeed only if concrete deduction would.  */
+      if (ctad_kind == inherited)
+	{
+	  tree targs;
+	  if (!type_targs_deducible_from (tmpl, ret, &targs))
 	    continue;
+
+	  if (TREE_CODE (f) != TEMPLATE_DECL)
+	    fprime = copy_decl (fprime);
+	  tree fntype = TREE_TYPE (fprime);
+	  ret = lookup_template_class (TPARMS_PRIMARY_TEMPLATE (atparms), targs,
+				       in_decl, NULL_TREE, false, complain);
+	  fntype = build_function_type (ret, TYPE_ARG_TYPES (fntype));
+	  TREE_TYPE (fprime) = fntype;
+	  if (TREE_CODE (fprime) == TEMPLATE_DECL)
+	    TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype;
 	}
 
       aguides = lookup_add (fprime, aguides);
@@ -30052,6 +30106,45 @@  alias_ctad_tweaks (tree tmpl, tree uguides)
   return aguides;
 }
 
+/* CTOR is a using-declaration inheriting the constructors of some base of the
+   class template TMPL; adjust the base's guides be deduction guides for TMPL.  */
+
+static tree
+inherited_ctad_tweaks (tree tmpl, tree ctor, tsubst_flags_t complain)
+{
+  /* [over.match.class.deduct]: In addition, if C is defined and inherits
+     constructors ([namespace.udecl]) from a direct base class denoted in the
+     base-specifier-list by a class-or-decltype B, let A be an alias template
+     whose template parameter list is that of C and whose defining-type-id is
+     B.  If A is a deducible template ([dcl.type.simple]), the set contains the
+     guides of A with the return type R of each guide replaced with typename
+     CC::type given a class template
+
+     template <typename> class CC;
+
+     whose primary template is not defined and with a single partial
+     specialization whose template parameter list is that of A and whose
+     template argument list is a specialization of A with the template argument
+     list of A ([temp.dep.type]) having a member typedef type designating a
+     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 (!CLASS_TYPE_P (scope)
+      || !CLASSTYPE_TEMPLATE_INFO (scope))
+    return NULL_TREE;
+
+  tree t = build_tree_list (DECL_TEMPLATE_PARMS (tmpl), scope);
+  bool any_dguides_p;
+  tree uguides = deduction_guides_for (TYPE_TI_TEMPLATE (scope),
+				       any_dguides_p, complain);
+  return alias_ctad_tweaks (t, uguides);
+}
+
 /* True iff template arguments for TMPL can be deduced from TYPE.
    Used to implement CPTK_IS_DEDUCIBLE for alias CTAD according to
    [over.match.class.deduct].
@@ -30060,23 +30153,37 @@  alias_ctad_tweaks (tree tmpl, tree uguides)
    should be parallel to that of get_partial_spec_bindings.  */
 
 bool
-type_targs_deducible_from (tree tmpl, tree type)
+type_targs_deducible_from (tree tmpl, tree type, tree *targs_out /* = nullptr */)
 {
-  tree tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+  tree tparms, ttype;
+  if (TREE_CODE (tmpl) == TEMPLATE_DECL)
+    {
+      /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
+	 specialization of TMPL.  */
+      if (DECL_CLASS_TEMPLATE_P (tmpl))
+	return (CLASS_TYPE_P (type)
+		&& CLASSTYPE_TEMPLATE_INFO (type)
+		&& CLASSTYPE_TI_TEMPLATE (type) == tmpl);
+
+      /* Otherwise it's an alias template.  */
+      tparms = DECL_INNERMOST_TEMPLATE_PARMS (tmpl);
+      ttype = TREE_TYPE (tmpl);
+    }
+  else
+    {
+      /* TMPL is a synthetic alias template represented as a TREE_LIST as
+	 per alias_ctad_tweaks.  */
+      tparms = INNERMOST_TEMPLATE_PARMS (TREE_PURPOSE (tmpl));
+      ttype = TREE_VALUE (tmpl);
+      tmpl = TI_TEMPLATE (TYPE_TEMPLATE_INFO_MAYBE_ALIAS (ttype));
+    }
+
   int len = TREE_VEC_LENGTH (tparms);
   tree targs = make_tree_vec (len);
   bool tried_array_deduction = (cxx_dialect < cxx17);
 
-  /* If tmpl is a class template, this is trivial: it's deducible if TYPE is a
-     specialization of TMPL.  */
-  if (DECL_CLASS_TEMPLATE_P (tmpl))
-    return (CLASS_TYPE_P (type)
-	    && CLASSTYPE_TEMPLATE_INFO (type)
-	    && CLASSTYPE_TI_TEMPLATE (type) == tmpl);
-
-  /* Otherwise it's an alias template.  */
  again:
-  if (unify (tparms, targs, TREE_TYPE (tmpl), type,
+  if (unify (tparms, targs, ttype, type,
 	     UNIFY_ALLOW_NONE, false))
     return false;
 
@@ -30091,7 +30198,7 @@  type_targs_deducible_from (tree tmpl, tree type)
 	if (!tried_array_deduction
 	    && TREE_CODE (tparm) == TYPE_DECL)
 	  {
-	    try_array_deduction (tparms, targs, TREE_TYPE (tmpl));
+	    try_array_deduction (tparms, targs, ttype);
 	    tried_array_deduction = true;
 	    if (TREE_VEC_ELT (targs, i))
 	      goto again;
@@ -30125,7 +30232,12 @@  type_targs_deducible_from (tree tmpl, tree type)
   /* I believe we don't need the template_template_parm_bindings_ok_p call
      because coerce_template_parms did coerce_template_template_parms.  */
 
-  return constraints_satisfied_p (tmpl, targs);
+  if (!constraints_satisfied_p (tmpl, targs))
+    return false;
+
+  if (targs_out)
+    *targs_out = targs;
+  return true;
 }
 
 /* Return artificial deduction guides built from the constructors of class
@@ -30141,7 +30253,7 @@  ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
 
   for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter)
     {
-      /* Skip inherited constructors.  */
+      /* We handle C++23 inherited CTAD below.  */
       if (iter.using_p ())
 	continue;
 
@@ -30149,6 +30261,15 @@  ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
       cands = lookup_add (guide, cands);
     }
 
+  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);
+	  if (uguides)
+	    cands = lookup_add (uguides, cands);
+	}
+
   /* Add implicit default constructor deduction guide.  */
   if (!TYPE_HAS_USER_CONSTRUCTOR (type))
     {
@@ -30198,7 +30319,10 @@  deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
     }
 
   /* Cache the deduction guides for a template.  We also remember the result of
-     lookup, and rebuild everything if it changes; should be very rare.  */
+     lookup, and rebuild everything if it changes; should be very rare.
+
+     FIXME: Also rebuild if this is a class template that inherits guides from a
+     base class, and lookup for the latter changed.  */
   tree_pair_p cache = NULL;
   if (tree_pair_p &r
       = hash_map_safe_get_or_insert<hm_ggc> (dguide_cache, tmpl))
diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
index 4624794c4b7..74f92325d7a 100644
--- a/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
+++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction67.C
@@ -1,5 +1,4 @@ 
-// Deduction from inherited constructors isn't supported yet, but we shouldn't
-// crash.  It may well be supported in C++23.
+// Deduction from inherited constructors isn't supported before C++23.
 
 //{ dg-do compile { target c++17 } }
 
@@ -17,5 +16,5 @@  int main()
 {
   B b = 42;			// { dg-line init }
   // { dg-prune-output "no matching function" }
-  // { dg-error "class template argument deduction" "" { target *-*-* } init }
+  // { dg-error "class template argument deduction" "" { target c++23_down } init }
 }
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
new file mode 100644
index 00000000000..aa1948531e1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited1.C
@@ -0,0 +1,36 @@ 
+// Modified example from P2582R1
+// { dg-do compile { target c++23 } }
+
+template <typename T> struct B {
+  B(T);
+};
+B(bool) -> B<char>;
+template <typename T> struct C : public B<T> {
+  using B<T>::B;
+};
+template <typename T> struct D : public B<T> {};
+
+C c(42);            // OK, deduces C<int>
+using ty1 = decltype(C(42));
+using ty1 = C<int>;
+
+D d(42);            // { dg-error "deduction|no match" }
+
+C c2(true);           // OK, deduces C<char>
+using ty2 = decltype(C(true));
+using ty2 = C<char>;
+
+template <typename T> struct E : public B<int> {
+  using B<int>::B;
+};
+
+E e(42);            // { dg-error "deduction|no match" }
+
+template <typename T, typename U, typename V> struct F {
+  F(T, U, V);
+};
+template <typename T, typename U> struct G : F<U, T, int> {
+  using F<U, T, int>::F;
+};
+
+G g(true, 'a', 1);  // OK, deduces G<char, bool>
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
new file mode 100644
index 00000000000..cb3c595f316
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited2.C
@@ -0,0 +1,26 @@ 
+// { dg-do compile { target c++23 } }
+
+template<class T, class U, class V> struct F {
+  F(T, U, V);       // #1
+  F(T*, U*, V*);    // #2
+  template<class W>
+  F(int, int, W);   // #3
+};
+
+F(bool, bool, bool) -> F<bool*, void*, int>;
+
+template<class T, class U> struct G : F<U, T, int> {
+  using F<U, T, int>::F;
+};
+
+using ty1 = decltype(G(true, 'a', 1)); // uses #1
+using ty1 = G<char, bool>;
+
+using ty2 = decltype(G((bool*)0, (char*)0, (int*)0)); // uses #2
+using ty2 = G<char, bool>;
+
+using ty3 = decltype(G(0, 0, 0)); // uses #3
+using ty3 = G<int, int>;
+
+using ty4 = decltype(G(true, true, true)); // uses #4
+using ty4 = G<void*, bool*>;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
new file mode 100644
index 00000000000..57e323b5124
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited3.C
@@ -0,0 +1,16 @@ 
+// { dg-do compile { target c++23 } }
+
+template<class T>
+struct A {
+  A(T);
+  template<class U> A(T, U);
+};
+
+template<class T>
+struct B : A<const T> {
+  using A<const T>::A;
+};
+
+using type = decltype(B(0));
+using type = decltype(B(0, 0));
+using type = B<int>;