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 <[email protected]>
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();