aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2024-08-09 21:15:25 -0400
committerPatrick Palka <ppalka@redhat.com>2024-08-09 21:15:25 -0400
commit8cc67b520968ca9a13fd96896522aa66e39a99e2 (patch)
tree9e37bbf8d78bc0b57f6cd2cbb14dbc122ae8fdd9
parent70da0ca1239faefa6dec0494a85e998eae34beff (diff)
downloadgcc-8cc67b520968ca9a13fd96896522aa66e39a99e2.zip
gcc-8cc67b520968ca9a13fd96896522aa66e39a99e2.tar.gz
gcc-8cc67b520968ca9a13fd96896522aa66e39a99e2.tar.bz2
c++: inherited CTAD fixes [PR116276]
This implements the overlooked inherited vs non-inherited guide tiebreaker from P2582R1. This requires tracking inherited-ness of a guide, for which it seems natural to reuse the lang_decl_fn::context field which for a constructor tracks its inherited-ness. This patch also works around CLASSTYPE_CONSTRUCTORS not reliably returning all inherited constructors (due to some using-decl handling quirks in in push_class_level_binding) 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, iterate over TYPE_FIELDS instead of CLASSTYPE_CONSTRUCTORS to recognize all inherited constructors. gcc/testsuite/ChangeLog: * g++.dg/cpp23/class-deduction-inherited4.C: Remove an xfail. * g++.dg/cpp23/class-deduction-inherited5.C: New test. * g++.dg/cpp23/class-deduction-inherited6.C: New test. Reviewed-by: Jason Merrill <jason@redhat.com>
-rw-r--r--gcc/cp/call.cc27
-rw-r--r--gcc/cp/cp-tree.h8
-rw-r--r--gcc/cp/pt.cc43
-rw-r--r--gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C4
-rw-r--r--gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C25
-rw-r--r--gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C46
6 files changed, 139 insertions, 14 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 67d38e2..94015db 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -13262,10 +13262,35 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
else if (cand2->rewritten ())
return 1;
- /* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
if (deduction_guide_p (cand1->fn))
{
gcc_assert (deduction_guide_p (cand2->fn));
+
+ /* F1 and F2 are generated from class template argument deduction for a
+ class D, and F2 is generated from inheriting constructors from a base
+ class of D while F1 is not, and for each explicit function argument,
+ the corresponding parameters of F1 and F2 are either both ellipses or
+ have the same type. */
+ bool inherited1 = inherited_guide_p (cand1->fn);
+ bool inherited2 = inherited_guide_p (cand2->fn);
+ if (int diff = inherited2 - inherited1)
+ {
+ for (i = 0; i < len; ++i)
+ {
+ conversion *t1 = cand1->convs[i + off1];
+ conversion *t2 = cand2->convs[i + off2];
+ /* ??? It seems the ellipses part of this tiebreaker isn't
+ needed since a mismatch should have broken the tie earlier
+ during ICS comparison. */
+ gcc_checking_assert (t1->ellipsis_p == t2->ellipsis_p);
+ if (!same_type_p (t1->type, t2->type))
+ break;
+ }
+ if (i == len)
+ return diff;
+ }
+
+ /* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
/* We distinguish between candidates from an explicit deduction guide and
candidates built from a constructor based on DECL_ARTIFICIAL. */
int art1 = DECL_ARTIFICIAL (cand1->fn);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 5807f4e..a53fbcb 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2972,9 +2972,10 @@ struct GTY(()) lang_decl_fn {
this pointer and result pointer adjusting thunks are
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). */
+ For a DECL_CONSTRUCTOR_P or deduction_guide_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). */
tree context;
union lang_decl_u5
@@ -7662,6 +7663,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 c1d4cdc..8725a5e 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -29716,6 +29716,25 @@ builtin_guide_p (const_tree fn)
return true;
}
+/* True if FN is a C++23 inherited guide. */
+
+bool
+inherited_guide_p (const_tree fn)
+{
+ gcc_assert (deduction_guide_p (fn));
+ return LANG_DECL_FN_CHECK (fn)->context != NULL_TREE;
+}
+
+/* Set the base class BASE from which the transformed guide FN
+ was inherited as part of C++23 inherited CTAD. */
+
+static void
+set_inherited_guide_context (const_tree fn, tree base)
+{
+ gcc_assert (deduction_guide_p (fn));
+ LANG_DECL_FN_CHECK (fn)->context = base;
+}
+
/* OLDDECL is a _DECL for a template parameter. Return a similar parameter at
LEVEL:INDEX, using tsubst_args and complain for substitution into non-type
template parameter types. Note that the handling of template template
@@ -30486,6 +30505,7 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
TREE_TYPE (fprime) = fntype;
if (TREE_CODE (fprime) == TEMPLATE_DECL)
TREE_TYPE (DECL_TEMPLATE_RESULT (fprime)) = fntype;
+ set_inherited_guide_context (fprime, utype);
}
aguides = lookup_add (fprime, aguides);
@@ -30517,11 +30537,14 @@ inherited_ctad_tweaks (tree tmpl, tree ctor, tsubst_flags_t complain)
template specialization with the template argument list of A but with C as
the template. */
- /* FIXME: Also recognize inherited constructors of the form 'using C::B::B',
- which seem to be represented with TYPENAME_TYPE C::B as USING_DECL_SCOPE?
- And recognize constructors inherited from a non-dependent base class, which
- seem to be missing from the overload set entirely? */
tree scope = USING_DECL_SCOPE (ctor);
+ if (TREE_CODE (scope) == TYPENAME_TYPE
+ && (TYPE_IDENTIFIER (TYPE_CONTEXT (scope))
+ == TYPENAME_TYPE_FULLNAME (scope)))
+ /* Recognize using B<T>::B::B as an inherited constructor. */
+ /* FIXME: Also recognize using C::B::B? We might have to call
+ resolve_typename_type for that. */
+ scope = TYPE_CONTEXT (scope);
if (!CLASS_TYPE_P (scope)
|| !CLASSTYPE_TEMPLATE_INFO (scope)
|| !PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (scope)))
@@ -30655,10 +30678,14 @@ ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain)
}
if (cxx_dialect >= cxx23)
- for (tree ctor : ovl_range (CLASSTYPE_CONSTRUCTORS (type)))
- if (TREE_CODE (ctor) == USING_DECL)
- {
- tree uguides = inherited_ctad_tweaks (tmpl, ctor, complain);
+ /* FIXME: CLASSTYPE_CONSTRUCTORS doesn't contain inherited constructors if
+ e.g. the class also has 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 5e3a7f4..806f016 100644
--- a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited4.C
@@ -13,10 +13,10 @@ using ty1 = B<int>;
template<class T=void>
struct C : A<int> {
- using A<int>::A; // FIXME: we don't notice this one either
+ using A<int>::A;
};
-using ty2 = decltype(C(0)); // { dg-bogus "" "" { xfail *-*-* } }
+using ty2 = decltype(C(0));
using ty2 = C<void>;
template<class T>
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C
new file mode 100644
index 0000000..d835acb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited5.C
@@ -0,0 +1,25 @@
+// PR c++/116276
+// { dg-do compile { target c++20 } }
+
+template<class T>
+struct Base1 { };
+
+template<class T>
+struct Base2 { };
+
+template<class T = int>
+struct Derived : public Base1<T>, Base2<T> {
+ using Base1<T>::Base1;
+ using Base2<T>::Base2;
+};
+
+Derived d;
+
+template<class T = int>
+struct Derived2 : public Base1<T>, Base2<T> {
+ using Base1<T>::Base1::Base1;
+ using Base2<T>::Base2::Base2;
+ Derived2();
+};
+
+Derived2 d2;
diff --git a/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C
new file mode 100644
index 0000000..df8199c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/class-deduction-inherited6.C
@@ -0,0 +1,46 @@
+// PR c++/116276
+// { dg-do compile { target c++23 } }
+
+template<class T>
+struct Base1 {
+ Base1();
+ Base1(T);
+};
+
+template<class T>
+struct Base2 {
+ Base2();
+ Base2(T*);
+};
+
+template<class T = int>
+struct Derived : public Base1<T>, Base2<T> {
+ using Base1<T>::Base1;
+ using Base2<T>::Base2;
+};
+
+using ty1 = decltype(Derived{});
+using ty1 = Derived<int>;
+
+using ty2 = decltype(Derived{true});
+using ty2 = Derived<bool>;
+
+using ty3 = decltype(Derived{(char*)nullptr});
+using ty3 = Derived<char>;
+
+template<class T = int>
+struct Derived2 : public Base1<T>, Base2<T> {
+ using Base1<T>::Base1;
+ using Base2<T>::Base2;
+ Derived2();
+ Derived2(T);
+};
+
+using ty4 = decltype(Derived2{});
+using ty4 = Derived2<int>;
+
+using ty5 = decltype(Derived2{true});
+using ty5 = Derived2<bool>;
+
+using ty6 = decltype(Derived2{(char*)nullptr});
+using ty6 = Derived2<char>;