Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk? I'm not sure this is worth backporting without the previous CWG 2273 tweak since it'll mean inconsistent behavior between implict vs explicit object members in GCC 14: the call to S<>{}.f() in concepts-memfun4.C would now return 10 (due to the CWG 2273 tiebreaker incorrectly triggering), while the g() and h() calls would be ambiguous (since that tiebreaker doesn't consider object correspondence).
Also I'm not 100% sure if I'm interpreting "both are direct members of the same class" correctly here to mean ruling out using'd vs non-using'd members, since https://eel.is/c++draft/namespace.udecl#note-5 says using'd members are "treated as though they were direct members of the derived class"...? -- >8 -- After CWG 2789, the "more constrained" tiebreaker for non-template functions should exclude members that come from different classes via using. This patch implements this missing refinement. In turn we can get rid of four-parameter overload of object_parms_correspond and call the main overload directly since we know correspondence is now only checked for members from the same class. PR c++/116492 DR 2789 gcc/cp/ChangeLog: * call.cc (object_parms_correspond): Remove. (cand_parms_match): Return false for member functions that come from different classes. Adjust call to object_parms_correspond. (joust): Update comment for the non-template "more constrained" case. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-memfun4.C: Expect ambiguity when candidates come from different classes. * g++.dg/cpp2a/concepts-inherit-ctor12.C: New test. --- gcc/cp/call.cc | 54 +++++++------------ .../g++.dg/cpp2a/concepts-inherit-ctor12.C | 16 ++++++ gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C | 24 +++++---- 3 files changed, 49 insertions(+), 45 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 87b54291b51..de742da6927 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -12808,27 +12808,6 @@ class_of_implicit_object (z_candidate *cand) return BINFO_TYPE (cand->conversion_path); } -/* True if candidates C1 and C2 have corresponding object parameters per - [basic.scope.scope]. */ - -static bool -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); - if (!ctx2) - /* Leave context as is. */; - else if (!context) - context = ctx2; - else if (context != ctx2) - /* This can't happen for normal function calls, since it means finding - functions in multiple bases which would fail with an ambiguous lookup, - but it can occur with reversed operators. */ - return false; - - return object_parms_correspond (fn1, fn2, context); -} - /* Return whether the first parameter of C1 matches the second parameter of C2. */ @@ -12893,16 +12872,19 @@ cand_parms_match (z_candidate *c1, z_candidate *c2, pmatch match_kind) 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, fn1, c2, fn2)) - return false; - else + if (DECL_FUNCTION_MEMBER_P (fn1) + && DECL_FUNCTION_MEMBER_P (fn2)) { + tree base1 = DECL_CONTEXT (strip_inheriting_ctors (fn1)); + tree base2 = DECL_CONTEXT (strip_inheriting_ctors (fn2)); + if (base1 != base2) + return false; + + /* CWG2789 is not adequate, it should specify corresponding object + parameters, not same typed object parameters. */ + if (!object_parms_correspond (fn1, fn2, base1)) + return false; + /* We just compared the object parameters, if they don't correspond we already returned false. */ auto skip_parms = [] (tree fn, tree parms) @@ -13269,10 +13251,14 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, return winner; } - /* Concepts: F1 and F2 are non-template functions with the same - parameter-type-lists, and F1 is more constrained than F2 according to the - partial ordering of constraints described in 13.5.4. */ - + /* F1 and F2 are non-template functions and + - they have the same non-object-parameter-type-lists ([dcl.fct]), and + - if they are member functions, both are direct members of the same + class, and + - if both are non-static member functions, they have the same types for + their object parameters, and + - F1 is more constrained than F2 according to the partial ordering of + constraints described in [temp.constr.order]. */ if (flag_concepts && DECL_P (cand1->fn) && DECL_P (cand2->fn) && !cand1->template_decl && !cand2->template_decl && cand_parms_match (cand1, cand2, pmatch::current)) diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C new file mode 100644 index 00000000000..3e5dbfc37ad --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C @@ -0,0 +1,16 @@ +// PR c++/116492 +// CWG 2789 +// { dg-do compile { target c++20 } } + +template<class T> +struct A { + A() requires true = delete; +}; + +struct B : A<int> { + B(); + using A<int>::A; +}; + +B b; // OK, selects the non-inherited constructor over the more constrained + // inherited constructor. diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C index 91e34f1cd7a..62e304b5322 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C @@ -1,5 +1,7 @@ // PR c++/113191 -// { dg-do compile { target c++23 } } +// CWG 2789 +// { dg-do compile { target c++20 } } +// { dg-additional-options "-Wno-error=c++23-extensions" { target c++20_only } } template<typename> struct S; @@ -8,6 +10,7 @@ struct B { constexpr int f() const requires true { return 5; } constexpr operator int () const requires true { return 5; } constexpr int g(this S<T>&&) requires true { return 5; } + // { dg-warning "explicit object" "" { target c++20_only } .-1 } constexpr int h() requires true { return 5; } }; @@ -20,12 +23,14 @@ struct S : B<> { constexpr operator int () const { return 10; } constexpr int g() { return 10; } constexpr int h(this S&&) { return 10; } + // { dg-warning "explicit object" "" { target c++20_only } .-1 } }; -// implicit object parms match, B::f is more constrained -static_assert(S<>{}.f() == 5); -static_assert(S<>{}.g() == 5); -static_assert(S<>{}.h() == 5); +// ambiguous, constraints aren't considered since the candidates +// are defined from different classes +static_assert(S<>{}.f() == 5); // { dg-error "ambiguous" } +static_assert(S<>{}.g() == 5); // { dg-error "ambiguous" } +static_assert(S<>{}.h() == 5); // { dg-error "ambiguous" } template <typename = void> struct C { @@ -36,9 +41,8 @@ struct C { template <typename = void> struct S2: B<>, C<> { }; -// implicit object parms for conversion functions are all considered to be from -// the class of the object argument -static_assert(S2<>{} == 5); +// ambiguous as above +static_assert(S2<>{} == 5); // { dg-error "ambiguous" } // ambiguous lookup, so we never actually compare the candidates // if we did, implicit object parms don't match due to different classes @@ -51,7 +55,6 @@ struct S3 : B<> { constexpr int f() volatile { return 10; } }; -// implicit object parms don't match due to different cv-quals static_assert(S3<>{}.f() == 5); // { dg-error "ambiguous" } template <typename = void> @@ -60,8 +63,7 @@ struct S4 : B<> { constexpr int f() const & { return 10; } }; -// no ref-qual matches any ref-qual -static_assert(S4<>{}.f() == 5); +static_assert(S4<>{}.f() == 5); // { dg-error "ambiguous" } template <typename = void> struct C2 { -- 2.46.1.544.g3fb745257b