On Thu, Mar 21, 2019 at 07:41:37PM -0400, Jason Merrill wrote:
> On 3/21/19 4:55 PM, Marek Polacek wrote:
> > On Thu, Mar 21, 2019 at 04:13:29PM -0400, Jason Merrill wrote:
> > > On 3/16/19 4:53 PM, Marek Polacek wrote:
> > > > 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.
> > > 
> > > I don't think it is.  g++ and clang++ reject the first test if you change
> > > int to a class type, but prvalues of scalar type have no cv-qualifiers, so
> > > the result of the conversion is a prvalue of type int, which is a 
> > > perfectly
> > > good initializer for int&&.
> > > 
> > > This is OK for the same reason:
> > > 
> > >    int&& r = (const int)42;
> > 
> > Oop, this is embarassing, sorry.  So I guess we're not handling the (5.3.2)
> > case in [dcl.init.ref] properly: "If the converted initializer is a prvalue,
> > its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the temporary
> > materialization conversion ([conv.rval]) is applied.
> 
> Sure, though I would think it's an issue of the bullet just above:
> 
> * has a class type (i.e., T2 is a class type), where T1 is not
> reference-related to T2, and can be converted to an rvalue or function
> lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3
> T3” (see 11.3.1.6),
> 
> where X can be converted to an rvalue of type int, which is
> reference-compatible with int.

Thanks.  I wonder if you think that the following makes sense.

The gist of the patch is that if a conversion function returns a prvalue of
non-class type, get rid of the cv-quals.  Then reference_binding won't mark it
as bad here:
 1890   if (related_p && !at_least_as_qualified_p (to, from))
 1891     conv->bad_p = true;
so we accept the code.

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

2019-03-22  Marek Polacek  <pola...@redhat.com>

        PR c++/89705 - ICE with reference binding with conversion function.
        * call.c (reference_binding): If the result of the conversion function
        is a prvalue of non-class type, use the cv-unqualified type.

        * g++.dg/cpp0x/rv-conv2.C: New test.

diff --git gcc/cp/call.c gcc/cp/call.c
index a4adab20d41..9efca735b16 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -1853,6 +1853,9 @@ reference_binding (tree rto, tree rfrom, tree expr, bool 
c_cast_p, int flags,
            && DECL_CONV_FN_P (t->cand->fn))
          {
            tree ftype = TREE_TYPE (TREE_TYPE (t->cand->fn));
+           /* A prvalue of non-class type is cv-unqualified.  */
+           if (!TYPE_REF_P (ftype) && !CLASS_TYPE_P (ftype))
+             ftype = cv_unqualified (ftype);
            int sflags = (flags|LOOKUP_NO_CONVERSION)&~LOOKUP_NO_TEMP_BIND;
            conversion *new_second
              = reference_binding (rto, ftype, NULL_TREE, c_cast_p,
diff --git gcc/testsuite/g++.dg/cpp0x/rv-conv2.C 
gcc/testsuite/g++.dg/cpp0x/rv-conv2.C
new file mode 100644
index 00000000000..9b9b154995b
--- /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();
+
+struct X { operator const int(); };
+int&& rri = X();
+
+struct Y { operator volatile int(); };
+int&& rri2 = Y();
+
+struct Z { operator const volatile int(); };
+volatile int&& rri3 = Z();
+
+enum E { A };
+struct S { operator const E(); };
+E&& rre = S();

Reply via email to