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&
