Here we have code like struct X { operator const int(); }; int&& rri = X();
which I think is invalid, because [dcl.init.ref] says that if types T1 and T2 are reference-related, no qualifiers can be dropped, and if the reference is an rvalue reference, the initializer expression can't be an lvalue. And here the result of the conversion is "const int", so the "const" would be dropped. A similar ill-formed test from the standard is struct X { operator int&(); }; int&& rri = X(); where the result of the conversion is an lvalue of related type. All the compilers I've tried actually accept the first test, but I think that's wrong. We actually mark the conversion as bad_p, but we were crashing in convert_like. The problem is that if EXPR is a conversion function, we can't just take its TREE_TYPE, we need to dig a little bit deeper than that. Then the call to reference_compatible_p will return false and we don't hit gcc_unreachable(). Bootstrapped/regtested on x86_64-linux, ok for trunk? 2019-03-16 Marek Polacek <pola...@redhat.com> PR c++/89705 - ICE with reference binding with conversion function. * call.c (convert_like_real): If EXPR is a conversion function, get the correct type. * g++.dg/cpp0x/rv-conv2.C: New test. diff --git gcc/cp/call.c gcc/cp/call.c index d1f50551cd3..281b204f0f8 100644 --- gcc/cp/call.c +++ gcc/cp/call.c @@ -7326,9 +7326,15 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, if (convs->bad_p && !next_conversion (convs)->bad_p) { tree extype = TREE_TYPE (expr); + /* If EXPR is a conversion function, get the correct type. */ + if (TREE_CODE (expr) == CALL_EXPR) + { + tree fndecl = cp_get_callee_fndecl_nofold (expr); + if (fndecl && DECL_CONV_FN_P (fndecl)) + extype = DECL_CONV_FN_TYPE (fndecl); + } auto_diagnostic_group d; - if (TYPE_REF_IS_RVALUE (ref_type) - && lvalue_p (expr)) + if (TYPE_REF_IS_RVALUE (ref_type) && lvalue_p (expr)) error_at (loc, "cannot bind rvalue reference of type %qH to " "lvalue of type %qI", totype, extype); else if (!TYPE_REF_IS_RVALUE (ref_type) && !lvalue_p (expr) diff --git gcc/testsuite/g++.dg/cpp0x/rv-conv2.C gcc/testsuite/g++.dg/cpp0x/rv-conv2.C new file mode 100644 index 00000000000..df0af475e62 --- /dev/null +++ gcc/testsuite/g++.dg/cpp0x/rv-conv2.C @@ -0,0 +1,18 @@ +// PR c++/89705 +// { dg-do compile { target c++11 } } + +struct W { operator const volatile int(); }; +const int& rci = W(); // { dg-error "binding reference" } + +struct X { operator const int(); }; +int&& rri = X(); // { dg-error "binding reference" } + +struct Y { operator volatile int(); }; +int&& rri2 = Y(); // { dg-error "binding reference" } + +struct Z { operator const volatile int(); }; +volatile int&& rri3 = Z(); // { dg-error "binding reference" } + +enum E { A }; +struct S { operator const E(); }; +E&& rre = S(); // { dg-error "binding reference" }