Aaaand this addresses <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87150#c11>,
as I promised earlier.  I hope I got it right.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2018-09-19  Marek Polacek  <pola...@redhat.com>

        PR c++/87109 - wrong ctor with maybe-rvalue semantics.
        * call.c (build_user_type_conversion_1): Refine the maybe-rvalue
        check to only return if we're converting from a base class.

        * g++.dg/cpp0x/ref-qual19.C: Adjust the expected results.
        * g++.dg/cpp0x/ref-qual20.C: New test.

diff --git gcc/cp/call.c gcc/cp/call.c
index ddf0ed044a0..4bbd77b9cef 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -4034,9 +4034,13 @@ build_user_type_conversion_1 (tree totype, tree expr, 
int flags,
     conv->bad_p = true;
 
   /* We're performing the maybe-rvalue overload resolution and
-     a conversion function is in play.  This isn't going to work
-     because we would not end up with a suitable constructor.  */
-  if ((flags & LOOKUP_PREFER_RVALUE) && !DECL_CONSTRUCTOR_P (cand->fn))
+     a conversion function is in play.  If we're converting from
+     a base class to a derived class, reject the conversion.  */
+  if ((flags & LOOKUP_PREFER_RVALUE)
+      && !DECL_CONSTRUCTOR_P (cand->fn)
+      && CLASS_TYPE_P (fromtype)
+      && CLASS_TYPE_P (totype)
+      && DERIVED_FROM_P (fromtype, totype))
     return NULL;
 
   /* Remember that this was a list-initialization.  */
diff --git gcc/testsuite/g++.dg/cpp0x/ref-qual19.C 
gcc/testsuite/g++.dg/cpp0x/ref-qual19.C
index 8494b83e5b0..50f92977c49 100644
--- gcc/testsuite/g++.dg/cpp0x/ref-qual19.C
+++ gcc/testsuite/g++.dg/cpp0x/ref-qual19.C
@@ -85,13 +85,13 @@ int
 main ()
 {
   C c1 = f (A());
-  if (c1.i != 1)
+  if (c1.i != 2)
     __builtin_abort ();
   C c2 = f2 (A());
   if (c2.i != 2)
     __builtin_abort ();
   C c3 = f3 ();
-  if (c3.i != 1)
+  if (c3.i != 2)
     __builtin_abort ();
   C c4 = f4 ();
   if (c4.i != 2)
@@ -100,13 +100,13 @@ main ()
   if (c5.i != 2)
     __builtin_abort ();
   D c6 = f6 (B());
-  if (c6.i != 3)
+  if (c6.i != 4)
     __builtin_abort ();
   D c7 = f7 (B());
   if (c7.i != 4)
     __builtin_abort ();
   D c8 = f8 ();
-  if (c8.i != 3)
+  if (c8.i != 4)
     __builtin_abort ();
   D c9 = f9 ();
   if (c9.i != 4)
diff --git gcc/testsuite/g++.dg/cpp0x/ref-qual20.C 
gcc/testsuite/g++.dg/cpp0x/ref-qual20.C
index e69de29bb2d..49c10564c11 100644
--- gcc/testsuite/g++.dg/cpp0x/ref-qual20.C
+++ gcc/testsuite/g++.dg/cpp0x/ref-qual20.C
@@ -0,0 +1,73 @@
+// PR c++/87109
+// { dg-do run { target c++11 } }
+
+#include <utility>
+
+struct A;
+
+struct B {
+  int i;
+  B() : i(0) { }
+  B(int i) : i(i) { }
+  operator A() &;
+  operator A() &&;
+};
+
+struct A : public B {
+  A(int i) : B(i) { }
+};
+
+B::operator A() & { return { 1 }; }
+B::operator A() && { return { 2 }; }
+
+A
+f (B b)
+{
+  return b;
+}
+
+A
+f2 (B b)
+{
+  return std::move (b);
+}
+
+A
+f3 ()
+{
+  B b;
+  return b;
+}
+
+A
+f4 ()
+{
+  B b;
+  return std::move (b);
+}
+
+A
+f5 ()
+{
+  return B();
+}
+
+int
+main ()
+{
+  A a1 = f (B());
+  if (a1.i != 1)
+    __builtin_abort ();
+  A a2 = f2 (B());
+  if (a2.i != 2)
+    __builtin_abort ();
+  A a3 = f3 ();
+  if (a3.i != 1)
+    __builtin_abort ();
+  A a4 = f4 ();
+  if (a4.i != 2)
+    __builtin_abort ();
+  A a5 = f5 ();
+  if (a5.i != 2)
+    __builtin_abort ();
+}

Reply via email to