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" }

Reply via email to