This is an overlooked case in C++17 mandatory copy elision: We want overload
resolution to reflect that initializing an object from a prvalue does not
involve a copy or move constructor even when [over.match.ctor] says that
only constructors are candidates.  Here I implement that by looking through
the copy/move constructor in joust.

Clang does something similar in this situation.

Tested x86_64-pc-linux-gnu, applying to trunk.

        * call.c (joust_maybe_elide_copy): New.
        (joust): Call it.
---
 gcc/cp/call.c                                | 53 ++++++++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/overload-conv-3.C |  2 +-
 gcc/testsuite/g++.dg/overload/conv-op2.C     |  6 +--
 gcc/cp/ChangeLog                             |  6 +++
 4 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index bf48ae2c27a..d1f50551cd3 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -10508,6 +10508,33 @@ add_warning (struct z_candidate *winner, struct 
z_candidate *loser)
   winner->warnings = cw;
 }
 
+/* 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.  */
+
+static bool
+joust_maybe_elide_copy (z_candidate *&cand)
+{
+  tree fn = cand->fn;
+  if (!DECL_COPY_CONSTRUCTOR_P (fn) && !DECL_MOVE_CONSTRUCTOR_P (fn))
+    return false;
+  conversion *conv = cand->convs[0];
+  gcc_checking_assert (conv->kind == ck_ref_bind);
+  conv = next_conversion (conv);
+  if (conv->kind == ck_user && !TYPE_REF_P (conv->type))
+    {
+      gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
+                          (conv->type, DECL_CONTEXT (fn)));
+      z_candidate *uc = conv->cand;
+      if (DECL_CONV_FN_P (uc->fn))
+       {
+         cand = uc;
+         return true;
+       }
+    }
+  return false;
+}
+
 /* Compare two candidates for overloading as described in
    [over.match.best].  Return values:
 
@@ -10588,6 +10615,27 @@ 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];
@@ -10697,6 +10745,11 @@ 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
diff --git a/gcc/testsuite/g++.dg/cpp0x/overload-conv-3.C 
b/gcc/testsuite/g++.dg/cpp0x/overload-conv-3.C
index 42a135dbf44..ae587f9673b 100644
--- a/gcc/testsuite/g++.dg/cpp0x/overload-conv-3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/overload-conv-3.C
@@ -17,5 +17,5 @@ struct Source {
 
 int main() {
   Source x;
-  Dest d(move(x));             // { dg-error "ambiguous" }
+  Dest d(move(x));        // { dg-error "ambiguous" "" { target c++14_down } }
 }
diff --git a/gcc/testsuite/g++.dg/overload/conv-op2.C 
b/gcc/testsuite/g++.dg/overload/conv-op2.C
index e8e533b1dc5..cc5ebe78619 100644
--- a/gcc/testsuite/g++.dg/overload/conv-op2.C
+++ b/gcc/testsuite/g++.dg/overload/conv-op2.C
@@ -1,5 +1,5 @@
 // PR c++/81311
-// { dg-do link }
+// { dg-do compile { target c++11 } }
 
 struct function
 {
@@ -8,12 +8,12 @@ struct function
 
 struct ref
 {
-  operator function&() const;
+  operator function&() const = delete;
 } r;
 
 struct val
 {
-  operator function() const;
+  operator function() const = delete;
 } v;
 
 int main()
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index ca8bc03b406..1164652268f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,9 @@
+2019-03-13  Jason Merrill  <ja...@redhat.com>
+
+       PR c++/86521 - C++17 copy elision in initialization by constructor.
+       * call.c (joust_maybe_elide_copy): New.
+       (joust): Call it.
+
 2019-03-13  Marek Polacek  <pola...@redhat.com>
 
        PR c++/88979 - further P0634 fix for constructors.

base-commit: c69047a18c10a2cc2824a9fdb184768c86e2a26a
-- 
2.20.1

Reply via email to