Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk/14?
-- >8 -- This implements the inherited vs non-inherited guide tiebreaker specified by P2582R1. In order to track inherited-ness of a guide it seems natural to reuse the lang_decl_fn::context field that already tracks inherited-ness of a constructor. This patch also works around CLASSTYPE_CONSTRUCTORS apparently not always containing all inherited constructors, by iterating over TYPE_FIELDS instead. This patch also makes us recognize another written form of inherited constructor, 'using Base<T>::Base::Base' whose USING_DECL_SCOPE is a TYPENAME_TYPE. PR c++/116276 gcc/cp/ChangeLog: * call.cc (joust): Implement P2582R1 inherited vs non-inherited guide tiebreaker. * cp-tree.h (lang_decl_fn::context): Document usage in deduction_guide_p FUNCTION_DECLs. (inherited_guide_p): Declare. * pt.cc (inherited_guide_p): Define. (set_inherited_guide_context): Define. (alias_ctad_tweaks): Use set_inherited_guide_context. (inherited_ctad_tweaks): Recognize some inherited constructors whose scope is a TYPENAME_TYPE. (ctor_deduction_guides_for): For C++23 inherited CTAD, loop over TYPE_FIELDS instead of using CLASSTYPE_CONSTRUCTORS to recognize all relevant using-decls. gcc/testsuite/ChangeLog: * g++.dg/cpp23/class-deduction-inherited4.C: Extend test. * g++.dg/cpp23/class-deduction-inherited5.C: New test. --- gcc/cp/call.cc | 22 +++++++++ gcc/cp/cp-tree.h | 8 +++- gcc/cp/pt.cc | 45 +++++++++++++++---- .../g++.dg/cpp23/class-deduction-inherited4.C | 15 ++++++- .../g++.dg/cpp23/class-deduction-inherited5.C | 25 +++++++++++ 5 files changed, 103 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index a75e2e5e3af..3287f77b59b 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -13261,6 +13261,28 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, else if (cand2->rewritten ()) return 1; + /* F1 and F2 are generated from class template argument deduction for a class + D, and F2 is generated from inheriting constructors from a base class of D + while F1 is not, and for each explicit function argument, the corresponding + parameters of F1 and F2 are either both ellipses or have the same type */ + if (deduction_guide_p (cand1->fn)) + { + bool inherited1 = inherited_guide_p (cand1->fn); + bool inherited2 = inherited_guide_p (cand2->fn); + if (int diff = inherited2 - inherited1) + { + for (i = 0; i < len; ++i) + { + conversion *t1 = cand1->convs[i + off1]; + conversion *t2 = cand2->convs[i + off2]; + if (!same_type_p (t1->type, t2->type)) + break; + } + if (i == len) + return diff; + } + } + /* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */ if (deduction_guide_p (cand1->fn)) { diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 0c25ec5a04e..0b76fef0df4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2973,8 +2973,11 @@ struct GTY(()) lang_decl_fn { chained here. This pointer thunks to return pointer thunks will be chained on the return pointer thunk. For a DECL_CONSTUCTOR_P FUNCTION_DECL, this is the base from - whence we inherit. Otherwise, it is the class in which a - (namespace-scope) friend is defined (if any). */ + whence we inherit. + For a deduction_guide_p FUNCTION_DECL, this is the base class + from whence we inherited the guide (if any). + Otherwise, it is the class in which a (namespace-scope) friend + is defined (if any). */ tree context; union lang_decl_u5 @@ -7655,6 +7658,7 @@ extern bool deduction_guide_p (const_tree); extern bool copy_guide_p (const_tree); extern bool template_guide_p (const_tree); extern bool builtin_guide_p (const_tree); +extern bool inherited_guide_p (const_tree); extern void store_explicit_specifier (tree, tree); extern tree lookup_explicit_specifier (tree); extern tree lookup_imported_hidden_friend (tree); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index d468a3037b6..b518e6d5185 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -29678,6 +29678,26 @@ builtin_guide_p (const_tree fn) return true; } +/* True if FN is a C++23 inherited guide. */ + +bool +inherited_guide_p (const_tree fn) +{ + gcc_assert (deduction_guide_p (fn)); + return LANG_DECL_FN_CHECK (fn)->context != NULL_TREE; +} + +/* Set the base class from which this transformed guide was inherited + as part of C++23 inherited CTAD. */ + +static void +set_inherited_guide_context (const_tree fn, tree context) +{ + gcc_assert (deduction_guide_p (fn)); + LANG_DECL_FN_CHECK (fn)->context = context; +} + + /* OLDDECL is a _DECL for a template parameter. Return a similar parameter at LEVEL:INDEX, using tsubst_args and complain for substitution into non-type template parameter types. Note that the handling of template template @@ -30448,6 +30468,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides) TREE_TYPE (fprime) = fntype; if (TREE_CODE (fprime) == TEMPLATE_DECL) TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype; + set_inherited_guide_context (fprime, utype); } aguides = lookup_add (fprime, aguides); @@ -30479,11 +30500,14 @@ inherited_ctad_tweaks (tree tmpl, tree ctor, tsubst_flags_t complain) template specialization with the template argument list of A but with C as the template. */ - /* FIXME: Also recognize inherited constructors of the form 'using C::B::B', - which seem to be represented with TYPENAME_TYPE C::B as USING_DECL_SCOPE? - And recognize constructors inherited from a non-dependent base class, which - seem to be missing from the overload set entirely? */ tree scope = USING_DECL_SCOPE (ctor); + if (TREE_CODE (scope) == TYPENAME_TYPE + && (TYPE_IDENTIFIER (TYPE_CONTEXT (scope)) + == TYPENAME_TYPE_FULLNAME (scope))) + /* Recognize using B<T>::B::B; as an inheriting constructor. */ + /* FIXME: Also recognize using C::B::B; ? We might have to call + resolve_typename_type for that. */ + scope = TYPE_CONTEXT (scope); if (!CLASS_TYPE_P (scope) || !CLASSTYPE_TEMPLATE_INFO (scope) || !PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (scope))) @@ -30617,10 +30641,15 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain) } if (cxx_dialect >= cxx23) - for (tree ctor : ovl_range (CLASSTYPE_CONSTRUCTORS (type))) - if (TREE_CODE (ctor) == USING_DECL) - { - tree uguides = inherited_ctad_tweaks (tmpl, ctor, complain); + /* FIXME: CLASSTYPE_CONSTRUCTORS seems to omit (dependent) inherited + constructors if e.g. there's a user-defined constructor. So instead + iterate over TYPE_FIELDS manually to robustly find all relevant using + decls. */ + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == USING_DECL + && DECL_NAME (field) == ctor_identifier) + { + tree uguides = inherited_ctad_tweaks (tmpl, field, complain); if (uguides) cands = lookup_add (uguides, cands); } diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C index 5e3a7f42919..bc0d5cc53d4 100644 --- a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C @@ -13,10 +13,10 @@ using ty1 = B<int>; template<class T=void> struct C : A<int> { - using A<int>::A; // FIXME: we don't notice this one either + using A<int>::A; }; -using ty2 = decltype(C(0)); // { dg-bogus "" "" { xfail *-*-* } } +using ty2 = decltype(C(0)); using ty2 = C<void>; template<class T> @@ -30,3 +30,14 @@ 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 *-*-* } } + +template<class T> +struct E { E(T); }; + +template<class T> +struct F : E<T> { + using E<T>::E::E; +}; + +using ty5 = decltype(F(0)); +using ty5 = F<int>; diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C new file mode 100644 index 00000000000..7eee8664b10 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C @@ -0,0 +1,25 @@ +// PR c++/116276 +// { dg-do compile { target c++20 } } + +template<class T> +struct Base1 { }; + +template<class T> +struct Base2 { }; + +template<class T = int> +struct Derived : public Base1<T>, Base2<T> { + using Base1<T>::Base1; + using Base2<T>::Base2; +}; + +Derived d; + +template<class T = int> +struct Derived2 : public Base1<T>, Base2<T> { + using Base1<T>::Base1; + using Base2<T>::Base2; + Derived2(); +}; + +Derived2 d2; -- 2.46.0.39.g891ee3b9db