diff mbox

C++ PATCHes for conditional operator issues

Message ID 536D1A6A.3020306@redhat.com
State New
Headers show

Commit Message

Jason Merrill May 9, 2014, 6:11 p.m. UTC
While I was working on reference binding diagnostics, I noticed a latent 
bug with conversion10.C that I tracked down to a problem with the fix 
for c++/22434: we don't want to do a bad conversion early if we will 
find a better one later through overload resolution.  So the first patch 
fixes that PR better.  And after that I decided to see if there were any 
other ?: issues to be fixed.

The next patch fixes a couple of complaints about the diagnostic we give 
when we can't figure out how to handle different types; if the problem 
is ambiguous conversions, we should say that.  And if the problem is 
unrelated types, we should say that instead of talking about overload 
resolution.

The third patch fixes an issue that isn't specific to conditional 
operators, but was reported in that context; stabilize_expr was 
inappropriately turning a prvalue into an lvalue, causing wrong overload 
resolution.

The last patch implements DR 587, which allows glvalues of the same type 
and value category but with different cv-qualifiers to produce a glvalue 
result.

Tested x86_64-pc-linux-gnu, applying to trunk.
diff mbox

Patch

commit 500305e38a79e4977bdfbda12d79c5401a149e9c
Author: Jason Merrill <jason@redhat.com>
Date:   Fri May 9 00:00:32 2014 -0400

    	DR 587
    	PR c++/51317
    	* call.c (build_conditional_expr_1, conditional_conversion): Handle
    	non-class lvalues and xvalues that differ only in cv-qualifiers.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 187fc77..9e83c4a 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4382,20 +4382,31 @@  conditional_conversion (tree e1, tree e2, tsubst_flags_t complain)
      If E2 is an lvalue: E1 can be converted to match E2 if E1 can be
      implicitly converted (clause _conv_) to the type "lvalue reference to
      T2", subject to the constraint that in the conversion the
-     reference must bind directly (_dcl.init.ref_) to an lvalue.  */
-  if (real_lvalue_p (e2))
+     reference must bind directly (_dcl.init.ref_) to an lvalue.
+
+     If E2 is an xvalue: E1 can be converted to match E2 if E1 can be
+     implicitly converted to the type "rvalue reference to T2", subject to
+     the constraint that the reference must bind directly.  */
+  if (lvalue_or_rvalue_with_address_p (e2))
     {
-      conv = implicit_conversion (build_reference_type (t2),
+      tree rtype = cp_build_reference_type (t2, !real_lvalue_p (e2));
+      conv = implicit_conversion (rtype,
 				  t1,
 				  e1,
 				  /*c_cast_p=*/false,
 				  LOOKUP_NO_TEMP_BIND|LOOKUP_NO_RVAL_BIND
 				  |LOOKUP_ONLYCONVERTING,
 				  complain);
-      if (conv)
+      if (conv && !conv->bad_p)
 	return conv;
     }
 
+  /* If E2 is a prvalue or if neither of the conversions above can be done
+     and at least one of the operands has (possibly cv-qualified) class
+     type: */
+  if (!CLASS_TYPE_P (t1) && !CLASS_TYPE_P (t2))
+    return NULL;
+
   /* [expr.cond]
 
      If E1 and E2 have class type, and the underlying class types are
@@ -4690,10 +4701,17 @@  build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
   /* [expr.cond]
 
      Otherwise, if the second and third operand have different types,
-     and either has (possibly cv-qualified) class type, an attempt is
-     made to convert each of those operands to the type of the other.  */
+     and either has (possibly cv-qualified) class type, or if both are
+     glvalues of the same value category and the same type except for
+     cv-qualification, an attempt is made to convert each of those operands
+     to the type of the other.  */
   else if (!same_type_p (arg2_type, arg3_type)
-	   && (CLASS_TYPE_P (arg2_type) || CLASS_TYPE_P (arg3_type)))
+	    && (CLASS_TYPE_P (arg2_type) || CLASS_TYPE_P (arg3_type)
+		|| (same_type_ignoring_top_level_qualifiers_p (arg2_type,
+							       arg3_type)
+		    && lvalue_or_rvalue_with_address_p (arg2)
+		    && lvalue_or_rvalue_with_address_p (arg3)
+		    && real_lvalue_p (arg2) == real_lvalue_p (arg3))))
     {
       conversion *conv2;
       conversion *conv3;
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-cond2.C b/gcc/testsuite/g++.dg/cpp0x/rv-cond2.C
new file mode 100644
index 0000000..e231b11
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-cond2.C
@@ -0,0 +1,11 @@ 
+// { dg-do compile { target c++11 } }
+
+template <class T, class U> struct ST;
+template <class T> struct ST<T,T> {};
+
+int&& f();
+const int&& g();
+
+void h(bool b) {
+  ST<decltype(b ? f() : g()),const int&&>();
+}
diff --git a/gcc/testsuite/g++.dg/expr/cond14.C b/gcc/testsuite/g++.dg/expr/cond14.C
new file mode 100644
index 0000000..5276287
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond14.C
@@ -0,0 +1,6 @@ 
+// DR 587
+// PR c++/51317
+
+int x = 1;
+int const y = 2;
+int const *p = &(1 ? x : y); // error: lvalue required as unary '&' operand
diff --git a/gcc/testsuite/g++.dg/warn/return-reference.C b/gcc/testsuite/g++.dg/warn/return-reference.C
index 8302190..710e87c 100644
--- a/gcc/testsuite/g++.dg/warn/return-reference.C
+++ b/gcc/testsuite/g++.dg/warn/return-reference.C
@@ -7,7 +7,7 @@  foo1()
 {
   static int empty;
   const int* x = bar();
-  return (x ? *x : empty);      // { dg-bogus ".*" "" { xfail *-*-* } }
+  return (x ? *x : empty);      // { dg-bogus ".*" }
 }
 
 const int&