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();