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

Reply via email to