In discussion of this issue CWG decided that the change of behavior on well-formed code like overload-conv-4.C is undesirable. In further discussion of possible resolutions, we discovered that we can avoid that change while still getting the desired behavior on overload-conv-3.C by making this a tiebreaker after comparing conversions, rather than before. This also simplifies the implementation.
The issue resolution has not yet been finalized, but this seems like a clear improvement. DR 2327 gcc/cp/ChangeLog: * call.cc (joust_maybe_elide_copy): Don't change cand. (joust): Move the elided tiebreaker later. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/overload-conv-4.C: Remove warnings. * g++.dg/cpp1z/elide7.C: New test. --- gcc/cp/call.cc | 56 ++++++++------------ gcc/testsuite/g++.dg/cpp0x/overload-conv-4.C | 5 +- gcc/testsuite/g++.dg/cpp1z/elide7.C | 14 +++++ 3 files changed, 39 insertions(+), 36 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/elide7.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 68cf878308e..15a3d6f2a1f 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -12560,11 +12560,11 @@ add_warning (struct z_candidate *winner, struct z_candidate *loser) } /* CAND is a constructor candidate in joust in C++17 and up. If it copies a - prvalue returned from a conversion function, replace CAND with the candidate - for the conversion and return true. Otherwise, return false. */ + prvalue returned from a conversion function, return true. Otherwise, return + false. */ static bool -joust_maybe_elide_copy (z_candidate *&cand) +joust_maybe_elide_copy (z_candidate *cand) { tree fn = cand->fn; if (!DECL_COPY_CONSTRUCTOR_P (fn) && !DECL_MOVE_CONSTRUCTOR_P (fn)) @@ -12580,10 +12580,7 @@ joust_maybe_elide_copy (z_candidate *&cand) (conv->type, DECL_CONTEXT (fn))); z_candidate *uc = conv->cand; if (DECL_CONV_FN_P (uc->fn)) - { - cand = uc; - return true; - } + return true; } return false; } @@ -12735,27 +12732,6 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, } } - /* Handle C++17 copy elision in [over.match.ctor] (direct-init) context. The - standard currently says that only constructors are candidates, but if one - copies a prvalue returned by a conversion function we want to treat the - conversion as the candidate instead. - - Clang does something similar, as discussed at - http://lists.isocpp.org/core/2017/10/3166.php - http://lists.isocpp.org/core/2019/03/5721.php */ - int elided_tiebreaker = 0; - if (len == 1 && cxx_dialect >= cxx17 - && DECL_P (cand1->fn) - && DECL_COMPLETE_CONSTRUCTOR_P (cand1->fn) - && !(cand1->flags & LOOKUP_ONLYCONVERTING)) - { - bool elided1 = joust_maybe_elide_copy (cand1); - bool elided2 = joust_maybe_elide_copy (cand2); - /* As a tiebreaker below we will prefer a constructor to a conversion - operator exposed this way. */ - elided_tiebreaker = elided2 - elided1; - } - for (i = 0; i < len; ++i) { conversion *t1 = cand1->convs[i + off1]; @@ -12917,11 +12893,6 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, if (winner) return winner; - /* Put this tiebreaker first, so that we don't try to look at second_conv of - a constructor candidate that doesn't have one. */ - if (elided_tiebreaker) - return elided_tiebreaker; - /* DR 495 moved this tiebreaker above the template ones. */ /* or, if not that, the context is an initialization by user-defined conversion (see @@ -12958,6 +12929,25 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, } } + /* DR2327: C++17 copy elision in [over.match.ctor] (direct-init) context. + The standard currently says that only constructors are candidates, but if + one copies a prvalue returned by a conversion function we prefer that. + + Clang does something similar, as discussed at + http://lists.isocpp.org/core/2017/10/3166.php + http://lists.isocpp.org/core/2019/03/5721.php */ + if (len == 1 && cxx_dialect >= cxx17 + && DECL_P (cand1->fn) + && DECL_COMPLETE_CONSTRUCTOR_P (cand1->fn) + && !(cand1->flags & LOOKUP_ONLYCONVERTING)) + { + bool elided1 = joust_maybe_elide_copy (cand1); + bool elided2 = joust_maybe_elide_copy (cand2); + winner = elided1 - elided2; + if (winner) + return winner; + } + /* or, if not that, F1 is a non-template function and F2 is a template function specialization. */ diff --git a/gcc/testsuite/g++.dg/cpp0x/overload-conv-4.C b/gcc/testsuite/g++.dg/cpp0x/overload-conv-4.C index 6fcdbbaa6a4..d2663e6cb20 100644 --- a/gcc/testsuite/g++.dg/cpp0x/overload-conv-4.C +++ b/gcc/testsuite/g++.dg/cpp0x/overload-conv-4.C @@ -17,7 +17,6 @@ B f (A x) { // C++14: we call B::B(A const &) - // C++17: we call A::operator B() - return B(x); // { dg-warning "choosing .A::operator B\\(\\). over .B::B\\(const A&\\)" "" { target c++17 } } - // { dg-warning "for conversion from .A. to .B." "" { target c++17 } .-1 } + // C++17: now the same + return B(x); } diff --git a/gcc/testsuite/g++.dg/cpp1z/elide7.C b/gcc/testsuite/g++.dg/cpp1z/elide7.C new file mode 100644 index 00000000000..2d8cc65bca8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/elide7.C @@ -0,0 +1,14 @@ +// DR2327: In direct-initialization, prefer a constructor even if it requires a +// qualification conversion. + +// { dg-do compile { target c++11 } } + +struct Dog; +struct Cat { + Cat(const Dog&); +}; +struct Dog { + operator Cat() = delete; +}; + +Cat cat(Dog{}); base-commit: b7e42b85212e03eb59193a712eb523f26911a581 -- 2.31.1