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 (); +}