Getting the reference binding rules for C++11 right (in the standard)
has taken quite a few iterations. I'm pretty happy with the latest
wording, which deals with user-defined conversions by recursing on the
result of the conversion. This patch implements those rules. I'm a
little uncertain about applying this so late in the 4.9 cycle, but I
think it's a significant improvement to C++11 support.
The second patch fixes a diagnostic issue I noticed while working on
this: when explaining that a conversion from the result of the
conversion function failed, the compiler was talking about the 'this'
parameter.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit acf9584aa8d20a2ae1e4b4505f224fc9f937e836
Author: Jason Merrill <ja...@redhat.com>
Date: Tue Feb 11 11:29:18 2014 -0800
DR 1571
* call.c (reference_binding): Recurse on user-defined conversion.
(convert_like_real) [ck_ref_bind]: Explain cv-qual mismatch.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 700099d..32767ec 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -1677,20 +1677,37 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
if (!conv)
return NULL;
- conv = build_conv (ck_ref_bind, rto, conv);
+ /* Limit this to C++11 mode for GCC 4.9, to be safe. */
+ if (cxx_dialect >= cxx11 && conv->user_conv_p)
+ {
+ /* If initializing the temporary used a conversion function,
+ recalculate the second conversion sequence. */
+ for (conversion *t = conv; t; t = next_conversion (t))
+ if (t->kind == ck_user
+ && DECL_CONV_FN_P (t->cand->fn))
+ {
+ tree ftype = TREE_TYPE (TREE_TYPE (t->cand->fn));
+ if (TREE_CODE (ftype) != REFERENCE_TYPE)
+ /* Pretend we start from an xvalue to avoid trouble from
+ LOOKUP_NO_TEMP_BIND. */
+ ftype = cp_build_reference_type (ftype, true);
+ conversion *new_second
+ = reference_binding (rto, ftype, NULL_TREE, c_cast_p,
+ flags|LOOKUP_NO_CONVERSION, complain);
+ if (!new_second)
+ return NULL;
+ conv = merge_conversion_sequences (t, new_second);
+ break;
+ }
+ }
+
+ if (conv->kind != ck_ref_bind)
+ conv = build_conv (ck_ref_bind, rto, conv);
+
/* This reference binding, unlike those above, requires the
creation of a temporary. */
conv->need_temporary_p = true;
- if (TYPE_REF_IS_RVALUE (rto))
- {
- conv->rvaluedness_matches_p = 1;
- /* In the second case, if the reference is an rvalue reference and
- the second standard conversion sequence of the user-defined
- conversion sequence includes an lvalue-to-rvalue conversion, the
- program is ill-formed. */
- if (conv->user_conv_p && next_conversion (conv)->kind == ck_rvalue)
- conv->bad_p = 1;
- }
+ conv->rvaluedness_matches_p = TYPE_REF_IS_RVALUE (rto);
return conv;
}
@@ -6213,12 +6230,25 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
if (convs->bad_p && !next_conversion (convs)->bad_p)
{
- gcc_assert (TYPE_REF_IS_RVALUE (ref_type)
- && (real_lvalue_p (expr)
- || next_conversion(convs)->kind == ck_rvalue));
+ gcc_assert (TYPE_REF_IS_RVALUE (ref_type));
- error_at (loc, "cannot bind %qT lvalue to %qT",
- TREE_TYPE (expr), totype);
+ if (real_lvalue_p (expr)
+ || next_conversion(convs)->kind == ck_rvalue)
+ error_at (loc, "cannot bind %qT lvalue to %qT",
+ TREE_TYPE (expr), totype);
+ else if (!reference_compatible_p (totype, TREE_TYPE (expr)))
+ error_at (loc, "binding %qT to reference of type %qT "
+ "discards qualifiers", TREE_TYPE (expr),totype);
+ else
+ gcc_unreachable ();
+ if (convs->user_conv_p)
+ for (conversion *t = convs; t; t = next_conversion (t))
+ if (t->kind == ck_user)
+ {
+ print_z_candidate (loc, "after user-defined conversion:",
+ t->cand);
+ break;
+ }
if (fn)
inform (input_location,
"initializing argument %P of %q+D", argnum, fn);
diff --git a/gcc/testsuite/g++.dg/cpp0x/overload3.C b/gcc/testsuite/g++.dg/cpp0x/overload3.C
index e521b35..b8f781a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/overload3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/overload3.C
@@ -13,5 +13,5 @@ struct wrap
int main()
{
wrap w;
- f(w); // { dg-error "lvalue" }
+ f(w); // { dg-error "" }
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-init1.C b/gcc/testsuite/g++.dg/cpp0x/rv-init1.C
new file mode 100644
index 0000000..2e8d4f7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-init1.C
@@ -0,0 +1,26 @@
+// Core DR 1604/1571/1572
+// { dg-require-effective-target c++11 }
+
+struct Banana { };
+struct Enigma { operator const Banana(); };
+struct Doof { operator Banana&(); };
+void enigmatic() {
+ typedef const Banana ConstBanana;
+ Banana &&banana1 = ConstBanana(); // { dg-error "" }
+ Banana &&banana2 = Enigma(); // { dg-error "" }
+ Banana &&banana3 = Doof(); // { dg-error "" }
+}
+
+class A {
+public:
+ operator volatile int &();
+};
+A a;
+
+const int & ir1a = a.operator volatile int&(); // { dg-error "" }
+const int & ir2a = a; // { dg-error "" }
+
+struct X {
+ operator int&();
+} x;
+int&& rri2 = X(); // { dg-error "" }
commit f84d42ca81f6c327a0356a4dd426a68925db582b
Author: Jason Merrill <ja...@redhat.com>
Date: Tue Feb 11 11:10:04 2014 -0800
* call.c (print_conversion_rejection): Handle n_arg of -2.
(build_user_type_conversion_1): Pass it.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index d3db585..700099d 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -3180,6 +3180,10 @@ print_conversion_rejection (location_t loc, struct conversion_info *info)
inform (loc, " no known conversion for implicit "
"%<this%> parameter from %qT to %qT",
info->from_type, info->to_type);
+ else if (info->n_arg == -2)
+ /* Conversion of conversion function return value failed. */
+ inform (loc, " no known conversion from %qT to %qT",
+ info->from_type, info->to_type);
else
inform (loc, " no known conversion for argument %d from %qT to %qT",
info->n_arg+1, info->from_type, info->to_type);
@@ -3604,7 +3608,7 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
if (!ics)
{
cand->viable = 0;
- cand->reason = arg_conversion_rejection (NULL_TREE, -1,
+ cand->reason = arg_conversion_rejection (NULL_TREE, -2,
rettype, totype);
}
else if (DECL_NONCONVERTING_P (cand->fn)
@@ -3624,7 +3628,7 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
{
cand->viable = -1;
cand->reason
- = bad_arg_conversion_rejection (NULL_TREE, -1,
+ = bad_arg_conversion_rejection (NULL_TREE, -2,
rettype, totype);
}
else if (primary_template_instantiation_p (cand->fn)