diff mbox

RFC: Make diagnostics for C++ reference binding more consistent

Message ID 20160727120502.GV4264@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely July 27, 2016, 12:05 p.m. UTC
Consider:
 
template<typename T> T declval();
 
int& r1 = declval<int&&>();
int&& r2 = declval<int&>();
int& r3 = declval<const int&>();
 
 
This produces three quite different errors:
 
 
refs.cc:3:25: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
 int& r1 = declval<int&&>();
           ~~~~~~~~~~~~~~^~
refs.cc:4:25: error: cannot bind 'int' lvalue to 'int&&'
 int&& r2 = declval<int&>();
            ~~~~~~~~~~~~~^~
refs.cc:5:30: error: binding 'const int' to reference of type 'int&' discards qualifiers
 int& r3 = declval<const int&>();
           ~~~~~~~~~~~~~~~~~~~^~
 
 
The first one uses the order to/from, but the other two are from/to.
 
The first and second are saying the same thing (wrong value category)
but very differently.
 
The first and third mention references but the second doesn't.
 
The standard talks about binding a reference to something, but the
second and third diagnostics talk about binding something to a
reference (and the first doesn't mention binding at all).
 
The first diagnostic is specific to lvalue references (it wouldn't be
invalid if it was binding a non-const _rvalue_ reference to an
rvalue), but doesn't use the word "lvalue".
 
(FWIW Clang and EDG both have their own inconsistencies for the same
example, but that shouldn't stop us trying to improve.)
 
 
IMHO the first should use the words "bind" and "lvalue reference":
 
refs.cc:3:25: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
 int& r1 = declval<int&&>();
           ~~~~~~~~~~~~~~^~
 
The second should use the phrasing "bind ref to value" instead of
"bind value to ref", and mention "rvalue reference":
 
refs.cc:4:25: error: cannot bind rvalue reference of type 'int&&' to an lvalue of type 'int'
 int&& r2 = declval<int&>();
            ~~~~~~~~~~~~~^~
 
The third should use the same phrasing (but doesn't need to mention
lvalue/rvalue because the problem is related to cv-qualifiers not
value categories):
 
refs.cc:5:30: error: binding reference of type 'int&' to 'const int' discards qualifiers
 int& r3 = declval<const int&>();
           ~~~~~~~~~~~~~~~~~~~^~
 

I've only considered trivial examples here, is there some reason to
prefer to current diagnostics for more complex cases?
 
The attached patch makes that change, but there are probably lots of
tests that would need updating which I haven't done until I know if
the change is worthwhile.

Comments

Jason Merrill July 27, 2016, 10:06 p.m. UTC | #1
On Wed, Jul 27, 2016 at 8:05 AM, Jonathan Wakely <jwakely@redhat.com> wrote:
> Consider:
>
> template<typename T> T declval();
>
> int& r1 = declval<int&&>();
> int&& r2 = declval<int&>();
> int& r3 = declval<const int&>();
>
>
> This produces three quite different errors:
>
>
> refs.cc:3:25: error: invalid initialization of non-const reference of type
> 'int&' from an rvalue of type 'int'
> int& r1 = declval<int&&>();
>           ~~~~~~~~~~~~~~^~
> refs.cc:4:25: error: cannot bind 'int' lvalue to 'int&&'
> int&& r2 = declval<int&>();
>            ~~~~~~~~~~~~~^~
> refs.cc:5:30: error: binding 'const int' to reference of type 'int&'
> discards qualifiers
> int& r3 = declval<const int&>();
>           ~~~~~~~~~~~~~~~~~~~^~
>
>
> The first one uses the order to/from, but the other two are from/to.
>
> The first and second are saying the same thing (wrong value category)
> but very differently.
>
> The first and third mention references but the second doesn't.
>
> The standard talks about binding a reference to something, but the
> second and third diagnostics talk about binding something to a
> reference (and the first doesn't mention binding at all).
>
> The first diagnostic is specific to lvalue references (it wouldn't be
> invalid if it was binding a non-const _rvalue_ reference to an
> rvalue), but doesn't use the word "lvalue".
>
> (FWIW Clang and EDG both have their own inconsistencies for the same
> example, but that shouldn't stop us trying to improve.)
>
>
> IMHO the first should use the words "bind" and "lvalue reference":
>
> refs.cc:3:25: error: cannot bind non-const lvalue reference of type 'int&'
> to an rvalue of type 'int'
> int& r1 = declval<int&&>();
>           ~~~~~~~~~~~~~~^~
>
> The second should use the phrasing "bind ref to value" instead of
> "bind value to ref", and mention "rvalue reference":
>
> refs.cc:4:25: error: cannot bind rvalue reference of type 'int&&' to an
> lvalue of type 'int'
> int&& r2 = declval<int&>();
>            ~~~~~~~~~~~~~^~
>
> The third should use the same phrasing (but doesn't need to mention
> lvalue/rvalue because the problem is related to cv-qualifiers not
> value categories):
>
> refs.cc:5:30: error: binding reference of type 'int&' to 'const int'
> discards qualifiers
> int& r3 = declval<const int&>();
>           ~~~~~~~~~~~~~~~~~~~^~
>
>
> I've only considered trivial examples here, is there some reason to
> prefer to current diagnostics for more complex cases?
>
> The attached patch makes that change, but there are probably lots of
> tests that would need updating which I haven't done until I know if
> the change is worthwhile.

Sounds good to me.

Jason
diff mbox

Patch

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 802c325..918661a 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -6710,15 +6710,15 @@  convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	    tree extype = TREE_TYPE (expr);
 	    if (TYPE_REF_IS_RVALUE (ref_type)
 		&& lvalue_p (expr))
-	      error_at (loc, "cannot bind %qT lvalue to %qT",
-			extype, totype);
+	      error_at (loc, "cannot bind rvalue reference of type %qT to "
+                        "lvalue of type %qT", totype, extype);
 	    else if (!TYPE_REF_IS_RVALUE (ref_type) && !lvalue_p (expr)
 		     && !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (ref_type)))
-	      error_at (loc, "invalid initialization of non-const reference of "
-			"type %qT from an rvalue of type %qT", totype, extype);
+	      error_at (loc, "cannot bind non-const lvalue reference of "
+			"type %qT to an rvalue of type %qT", totype, extype);
 	    else if (!reference_compatible_p (TREE_TYPE (totype), extype))
-	      error_at (loc, "binding %qT to reference of type %qT "
-			"discards qualifiers", extype, totype);
+	      error_at (loc, "binding reference of type %qT to %qT "
+			"discards qualifiers", totype, extype);
 	    else
 	      gcc_unreachable ();
 	    maybe_print_user_conv_context (convs);