PR c++/86521 - C++17 copy elision in initialization by constructor.
authorJason Merrill <jason@redhat.com>
Wed, 13 Mar 2019 23:34:51 +0000 (19:34 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 13 Mar 2019 23:34:51 +0000 (19:34 -0400)
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.

* call.c (joust_maybe_elide_copy): New.
(joust): Call it.

From-SVN: r269667

gcc/cp/ChangeLog
gcc/cp/call.c
gcc/testsuite/g++.dg/cpp0x/overload-conv-3.C
gcc/testsuite/g++.dg/overload/conv-op2.C

index ca8bc03b4065805a6d98443beb5fabd615b8c8f8..1164652268f36d81a107b500ab5adc8b3d6b6b2c 100644 (file)
@@ -1,3 +1,9 @@
+2019-03-13  Jason Merrill  <jason@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  <polacek@redhat.com>
 
        PR c++/88979 - further P0634 fix for constructors.
index bf48ae2c27a7e12a145874cee88349084588634c..d1f50551cd3623693c9d45237f54772922778b6f 100644 (file)
@@ -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
index 42a135dbf4449aadfb6497c3c7bb4696d99eb301..ae587f9673b1398cf692349f21c800aaf47a9f74 100644 (file)
@@ -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 } }
 }
index e8e533b1dc5b4913c885ac1fc11afdd0771a96a0..cc5ebe786194c5d879fd2c51b6ffc7397a99d7d1 100644 (file)
@@ -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()