c++: Implement P2582R1, CTAD from inherited constructors
Checks
Commit Message
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
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 *-*-* } }
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 *-*-* } }
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 *-*-* } }
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
@@ -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);
@@ -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))
@@ -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 }
}
new file mode 100644
@@ -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>
new file mode 100644
@@ -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*>;
new file mode 100644
@@ -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>;