On 1/11/24 17:01, Jason Merrill wrote:
Tested x86_64-pc-linux-gnu, applying to trunk.-- 8< -- As discussed, our handling of corresponding object parameters needed to handle the using-declaration case better. And I took the opportunity to share code between the add_method and cand_parms_match uses. This patch specifically doesn't compare reversed parameters, but a follow-up patch will.
Thus.
From 8182dc2cc293009d0bc95dd667bb872246f2ca04 Mon Sep 17 00:00:00 2001 From: Jason Merrill <ja...@redhat.com> Date: Wed, 10 Jan 2024 23:18:23 -0500 Subject: [PATCH] c++: cand_parms_match and reversed candidates To: gcc-patches@gcc.gnu.org When considering whether the candidate parameters match, according to the language we're considering the synthesized reversed candidate, so we should compare the parameters in swapped order. In this situation it doesn't make sense to consider whether object parameters correspond, since we're comparing an object parameter to a non-object parameter, so I generalized xobj_iobj_parameters_correspond accordingly. As I refine cand_parms_match, more behaviors need to differ between its original use to compare the original templates for two candidates, and the later use to decide whether to compare constraints. So now there's a parameter to select between the semantics. gcc/cp/ChangeLog: * call.cc (reversed_match): New. (enum class pmatch): New enum. (cand_parms_match): Add match_kind parm. (object_parms_correspond): Add fn parms. (joust): Adjust. * class.cc (xobj_iobj_parameters_correspond): Rename to... (iobj_parm_corresponds_to): ...this. Take the other type instead of a second function. (object_parms_correspond): Adjust. * cp-tree.h (iobj_parm_corresponds_to): Declare. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-memfun4.C: Change expected reversed handling. --- gcc/cp/cp-tree.h | 1 + gcc/cp/call.cc | 76 ++++++++++++++----- gcc/cp/class.cc | 32 +++----- gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C | 10 +-- 4 files changed, 73 insertions(+), 46 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 83009fc837c..d9b14d7c4f5 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6854,6 +6854,7 @@ extern tree build_vtbl_ref (tree, tree); extern tree build_vfn_ref (tree, tree); extern tree get_vtable_decl (tree, int); extern bool object_parms_correspond (tree, tree, tree); +extern bool iobj_parm_corresponds_to (tree, tree, tree); extern bool add_method (tree, tree, bool); extern tree declared_access (tree); extern bool maybe_push_used_methods (tree); diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 6f024b8abc3..1f5ff417c81 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -12713,7 +12713,7 @@ class_of_implicit_object (z_candidate *cand) [basic.scope.scope]. */ static bool -object_parms_correspond (z_candidate *c1, z_candidate *c2) +object_parms_correspond (z_candidate *c1, tree fn1, z_candidate *c2, tree fn2) { tree context = class_of_implicit_object (c1); tree ctx2 = class_of_implicit_object (c2); @@ -12727,43 +12727,80 @@ object_parms_correspond (z_candidate *c1, z_candidate *c2) but it can occur with reversed operators. */ return false; - return object_parms_correspond (c1->fn, c2->fn, context); + return object_parms_correspond (fn1, fn2, context); +} + +/* Return whether the first parameter of C1 matches the second parameter + of C2. */ + +static bool +reversed_match (z_candidate *c1, z_candidate *c2) +{ + tree fn1 = c1->fn; + tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (c2->fn)); + tree parm2 = TREE_VALUE (TREE_CHAIN (parms2)); + if (DECL_IOBJ_MEMBER_FUNCTION_P (fn1)) + { + tree ctx = class_of_implicit_object (c1); + return iobj_parm_corresponds_to (fn1, parm2, ctx); + } + else + { + tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1)); + tree parm1 = TREE_VALUE (parms1); + return same_type_p (parm1, parm2); + } } /* True if the defining declarations of the two candidates have equivalent - parameters. */ + parameters. MATCH_KIND controls whether we're trying to compare the + original declarations (for a warning) or the actual candidates. */ + +enum class pmatch { original, current }; static bool -cand_parms_match (z_candidate *c1, z_candidate *c2) +cand_parms_match (z_candidate *c1, z_candidate *c2, pmatch match_kind) { tree fn1 = c1->fn; tree fn2 = c2->fn; - if (fn1 == fn2) + bool reversed = (match_kind == pmatch::current + && c1->reversed () != c2->reversed ()); + if (fn1 == fn2 && !reversed) return true; if (identifier_p (fn1) || identifier_p (fn2)) return false; - /* We don't look at c1->template_decl because that's only set for primary - templates, not e.g. non-template member functions of class templates. */ - tree t1 = most_general_template (fn1); - tree t2 = most_general_template (fn2); - if (t1 || t2) + if (match_kind == pmatch::original) { - if (!t1 || !t2) - return false; - if (t1 == t2) - return true; - fn1 = DECL_TEMPLATE_RESULT (t1); - fn2 = DECL_TEMPLATE_RESULT (t2); + /* We don't look at c1->template_decl because that's only set for + primary templates, not e.g. non-template member functions of + class templates. */ + tree t1 = most_general_template (fn1); + tree t2 = most_general_template (fn2); + if (t1 || t2) + { + if (!t1 || !t2) + return false; + if (t1 == t2) + return true; + fn1 = DECL_TEMPLATE_RESULT (t1); + fn2 = DECL_TEMPLATE_RESULT (t2); + } } + + else if (reversed) + return (reversed_match (c1, c2) + && reversed_match (c2, c1)); + tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1)); tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2)); + if (!(DECL_FUNCTION_MEMBER_P (fn1) && DECL_FUNCTION_MEMBER_P (fn2))) /* Early escape. */; /* CWG2789 is not adequate, it should specify corresponding object parameters, not same typed object parameters. */ - else if (!object_parms_correspond (c1, c2)) + else if (!object_parms_correspond (c1, fn1, c2, fn2)) return false; else { @@ -12938,7 +12975,7 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, this approach to resolving the ambiguity, so pedwarn. */ if ((complain & tf_warning_or_error) && (cand1->reversed () != cand2->reversed ()) - && cand_parms_match (cand1, cand2)) + && cand_parms_match (cand1, cand2, pmatch::original)) { struct z_candidate *w, *l; if (cand2->reversed ()) @@ -13139,8 +13176,7 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, if (flag_concepts && DECL_P (cand1->fn) && DECL_P (cand2->fn) && !cand1->template_decl && !cand2->template_decl - && cand1->reversed () == cand2->reversed () - && cand_parms_match (cand1, cand2)) + && cand_parms_match (cand1, cand2, pmatch::current)) { winner = more_constrained (cand1->fn, cand2->fn); if (winner) diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 3374756fb9a..556943c3e55 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -1019,24 +1019,13 @@ modify_vtable_entry (tree t, } -/* Check if the object parameters of an xobj and iobj member function - correspond. CONTEXT is the class that an implicit object parameter - refers to. */ +/* Check if the object parameter of an iobj member function corresponds to + another parameter type. CONTEXT is the class that the implicit object + parameter is considered to refer to. */ -static bool -xobj_iobj_parameters_correspond (tree fn1, tree fn2, tree context) +bool +iobj_parm_corresponds_to (tree iobj_fn, tree xobj_param, tree context) { - gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn1) - || DECL_IOBJ_MEMBER_FUNCTION_P (fn2)); - gcc_assert (DECL_XOBJ_MEMBER_FUNCTION_P (fn1) - || DECL_XOBJ_MEMBER_FUNCTION_P (fn2)); - gcc_assert (fn1 != fn2); - - tree xobj_fn = DECL_XOBJ_MEMBER_FUNCTION_P (fn1) ? fn1 : fn2; - /* A reference, pointer, or something else. */ - tree xobj_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (xobj_fn))); - - tree iobj_fn = DECL_IOBJ_MEMBER_FUNCTION_P (fn1) ? fn1 : fn2; tree iobj_fn_type = TREE_TYPE (iobj_fn); /* If the iobj member function was introduced with a using declaration, the @@ -1253,11 +1242,14 @@ object_parms_correspond (tree fn, tree method, tree context) if (!same_type_p (fn_param, method_param)) return false; } - else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn) - || DECL_XOBJ_MEMBER_FUNCTION_P (method)) - return xobj_iobj_parameters_correspond (fn, method, context); else - gcc_unreachable (); + { + tree xobj_fn = DECL_XOBJ_MEMBER_FUNCTION_P (fn) ? fn : method; + tree iobj_fn = xobj_fn != fn ? fn : method; + tree xobj_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (xobj_fn))); + + return iobj_parm_corresponds_to (iobj_fn, xobj_param, context); + } return true; } diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C index 2fa661dbe96..91e34f1cd7a 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C @@ -79,19 +79,17 @@ namespace N1 { template <class = void> struct A { - constexpr bool operator==(B<>&) { return true; } + constexpr bool operator==(B<>&) { return false; } }; template <class> struct B { - constexpr bool operator==(A<>&) requires true { return false; } + constexpr bool operator==(A<>&) requires true { return true; } }; A<> a; B<> b; - // when comparing the A op== to the reversed B op==, we don't compare - // constraints and so fall through to the tiebreaker that chooses the - // non-reversed candidate. - // ??? shouldn't we compare constraints? + // when comparing the A op== to the reversed B op==, we compare them in + // reverse order, so they match, and we choose the more constrained. static_assert (a == b); } -- 2.39.3