diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index b3534991d41d..a27643eef91e 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,9 @@ 2014-02-25 Jason Merrill + DR 1571 + * call.c (reference_binding): Recurse on user-defined conversion. + (convert_like_real) [ck_ref_bind]: Explain cv-qual mismatch. + * call.c (print_conversion_rejection): Handle n_arg of -2. (build_user_type_conversion_1): Pass it. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 700099d99ddf..32767ec7c062 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -1677,20 +1677,37 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags, if (!conv) return NULL; - conv = build_conv (ck_ref_bind, rto, conv); + /* Limit this to C++11 mode for GCC 4.9, to be safe. */ + if (cxx_dialect >= cxx11 && conv->user_conv_p) + { + /* If initializing the temporary used a conversion function, + recalculate the second conversion sequence. */ + for (conversion *t = conv; t; t = next_conversion (t)) + if (t->kind == ck_user + && DECL_CONV_FN_P (t->cand->fn)) + { + tree ftype = TREE_TYPE (TREE_TYPE (t->cand->fn)); + if (TREE_CODE (ftype) != REFERENCE_TYPE) + /* Pretend we start from an xvalue to avoid trouble from + LOOKUP_NO_TEMP_BIND. */ + ftype = cp_build_reference_type (ftype, true); + conversion *new_second + = reference_binding (rto, ftype, NULL_TREE, c_cast_p, + flags|LOOKUP_NO_CONVERSION, complain); + if (!new_second) + return NULL; + conv = merge_conversion_sequences (t, new_second); + break; + } + } + + if (conv->kind != ck_ref_bind) + conv = build_conv (ck_ref_bind, rto, conv); + /* This reference binding, unlike those above, requires the creation of a temporary. */ conv->need_temporary_p = true; - if (TYPE_REF_IS_RVALUE (rto)) - { - conv->rvaluedness_matches_p = 1; - /* In the second case, if the reference is an rvalue reference and - the second standard conversion sequence of the user-defined - conversion sequence includes an lvalue-to-rvalue conversion, the - program is ill-formed. */ - if (conv->user_conv_p && next_conversion (conv)->kind == ck_rvalue) - conv->bad_p = 1; - } + conv->rvaluedness_matches_p = TYPE_REF_IS_RVALUE (rto); return conv; } @@ -6213,12 +6230,25 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, if (convs->bad_p && !next_conversion (convs)->bad_p) { - gcc_assert (TYPE_REF_IS_RVALUE (ref_type) - && (real_lvalue_p (expr) - || next_conversion(convs)->kind == ck_rvalue)); + gcc_assert (TYPE_REF_IS_RVALUE (ref_type)); - error_at (loc, "cannot bind %qT lvalue to %qT", - TREE_TYPE (expr), totype); + if (real_lvalue_p (expr) + || next_conversion(convs)->kind == ck_rvalue) + error_at (loc, "cannot bind %qT lvalue to %qT", + TREE_TYPE (expr), totype); + else if (!reference_compatible_p (totype, TREE_TYPE (expr))) + error_at (loc, "binding %qT to reference of type %qT " + "discards qualifiers", TREE_TYPE (expr),totype); + else + gcc_unreachable (); + if (convs->user_conv_p) + for (conversion *t = convs; t; t = next_conversion (t)) + if (t->kind == ck_user) + { + print_z_candidate (loc, "after user-defined conversion:", + t->cand); + break; + } if (fn) inform (input_location, "initializing argument %P of %q+D", argnum, fn); diff --git a/gcc/testsuite/g++.dg/cpp0x/overload3.C b/gcc/testsuite/g++.dg/cpp0x/overload3.C index e521b35bd0db..b8f781ad3f7f 100644 --- a/gcc/testsuite/g++.dg/cpp0x/overload3.C +++ b/gcc/testsuite/g++.dg/cpp0x/overload3.C @@ -13,5 +13,5 @@ struct wrap int main() { wrap w; - f(w); // { dg-error "lvalue" } + f(w); // { dg-error "" } } diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-init1.C b/gcc/testsuite/g++.dg/cpp0x/rv-init1.C new file mode 100644 index 000000000000..2e8d4f748efe --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/rv-init1.C @@ -0,0 +1,26 @@ +// Core DR 1604/1571/1572 +// { dg-require-effective-target c++11 } + +struct Banana { }; +struct Enigma { operator const Banana(); }; +struct Doof { operator Banana&(); }; +void enigmatic() { + typedef const Banana ConstBanana; + Banana &&banana1 = ConstBanana(); // { dg-error "" } + Banana &&banana2 = Enigma(); // { dg-error "" } + Banana &&banana3 = Doof(); // { dg-error "" } +} + +class A { +public: + operator volatile int &(); +}; +A a; + +const int & ir1a = a.operator volatile int&(); // { dg-error "" } +const int & ir2a = a; // { dg-error "" } + +struct X { + operator int&(); +} x; +int&& rri2 = X(); // { dg-error "" }