This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

C++ PATCHes for conditional operator issues


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.
commit 96d47cc3e844df44c13baa75d951792367487b5a
Author: Jason Merrill <jason@redhat.com>
Date:   Sun Apr 20 20:14:57 2014 -0400

    	PR c++/22434
    	* call.c (build_conditional_expr_1): Don't try to pool cv-quals
    	if we didn't find a conversion.
    	Don't accept a bad conversion too early.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 857df57..691e9e3 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4714,7 +4714,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 		      arg2_type, arg3_type);
 	  result = error_mark_node;
 	}
-      else if (conv2 && (!conv2->bad_p || !conv3))
+      else if (conv2 && !conv2->bad_p)
 	{
 	  arg2 = convert_like (conv2, arg2, complain);
 	  arg2 = convert_from_reference (arg2);
@@ -4727,7 +4727,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 	  if (error_operand_p (arg2))
 	    result = error_mark_node;
 	}
-      else if (conv3 && (!conv3->bad_p || !conv2))
+      else if (conv3 && !conv3->bad_p)
 	{
 	  arg3 = convert_like (conv3, arg3, complain);
 	  arg3 = convert_from_reference (arg3);
@@ -4757,7 +4757,8 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 	 conditional expression failing altogether, even though,
 	 according to this step, the one operand could be converted to
 	 the type of the other.  */
-      if ((conv2 || conv3)
+      if (((conv2 && !conv2->bad_p)
+	   || (conv3 && !conv3->bad_p))
 	  && CLASS_TYPE_P (arg2_type)
 	  && cp_type_quals (arg2_type) != cp_type_quals (arg3_type))
 	arg2_type = arg3_type =
diff --git a/gcc/testsuite/g++.dg/expr/cond8.C b/gcc/testsuite/g++.dg/expr/cond8.C
index 3e08953..11b090d 100644
--- a/gcc/testsuite/g++.dg/expr/cond8.C
+++ b/gcc/testsuite/g++.dg/expr/cond8.C
@@ -1,13 +1,12 @@
 // PR c++/22434
-// { dg-options "" }
 
 struct A
 {
-  A(void*);			// { dg-message "initializing" }
+  A(void*);
   ~A();
 };
 
 void foo(const int i, bool b)
 {
-  b ? A(0) : i; // { dg-error "conversion" }
+  b ? A(0) : i; // { dg-error "" }
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.jason/conversion10.C b/gcc/testsuite/g++.old-deja/g++.jason/conversion10.C
index 8cbba41..0eeceeb 100644
--- a/gcc/testsuite/g++.old-deja/g++.jason/conversion10.C
+++ b/gcc/testsuite/g++.old-deja/g++.jason/conversion10.C
@@ -1,4 +1,5 @@
 // { dg-do assemble  }
+// { dg-options "" }
 // PRMS Id: 9019
 // Bug: g++ doesn't find conversion to const char *.
 
commit 847081842b3a2caf096cfd579257f36bf7c5631d
Author: Jason Merrill <jason@redhat.com>
Date:   Thu May 8 13:45:18 2014 -0400

    	PR c++/32019
    	* call.c (build_conditional_expr_1): Improve ambiguity diagnostic.
    
    	PR c++/54348
    	* call.c (build_conditional_expr_1): If overload resolution finds
    	no match, just say "different types".

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 691e9e3..cff7ef3 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4710,8 +4710,16 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 	  || (conv3 && conv3->kind == ck_ambig))
 	{
 	  if (complain & tf_error)
-	    error_at (loc, "operands to ?: have different types %qT and %qT",
-		      arg2_type, arg3_type);
+	    {
+	      error_at (loc, "operands to ?: have different types %qT and %qT",
+			arg2_type, arg3_type);
+	      if (conv2 && !conv2->bad_p && conv3 && !conv3->bad_p)
+		inform (loc, "  and each type can be converted to the other");
+	      else if (conv2 && conv2->kind == ck_ambig)
+		convert_like (conv2, arg2, complain);
+	      else
+		convert_like (conv3, arg3, complain);
+	    }
 	  result = error_mark_node;
 	}
       else if (conv2 && !conv2->bad_p)
@@ -4818,10 +4826,8 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
       if (!any_viable_p)
 	{
           if (complain & tf_error)
-            {
-              op_error (loc, COND_EXPR, NOP_EXPR, arg1, arg2, arg3, FALSE);
-              print_z_candidates (loc, candidates);
-            }
+	    error_at (loc, "operands to ?: have different types %qT and %qT",
+		      arg2_type, arg3_type);
 	  return error_mark_node;
 	}
       cand = tourney (candidates, complain);
diff --git a/gcc/testsuite/g++.dg/cpp0x/explicit3.C b/gcc/testsuite/g++.dg/cpp0x/explicit3.C
index 678076c..6318ade 100644
--- a/gcc/testsuite/g++.dg/cpp0x/explicit3.C
+++ b/gcc/testsuite/g++.dg/cpp0x/explicit3.C
@@ -45,6 +45,6 @@ int main()
   f(a);				// { dg-error "" }
   B b2 = { a };			// { dg-error "" }
   a + true;			// { dg-error "5:no match" }
-  b ? a : true;			// { dg-error "5:no match" }
-  a ? a : true;			// { dg-error "5:no match" }
+  b ? a : true;			// { dg-error "5:?:" }
+  a ? a : true;			// { dg-error "5:?:" }
 }
diff --git a/gcc/testsuite/g++.dg/expr/cond10.C b/gcc/testsuite/g++.dg/expr/cond10.C
new file mode 100644
index 0000000..892576f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond10.C
@@ -0,0 +1,17 @@
+// PR c++/32019
+
+struct C
+{
+  C(const char *);
+  operator const char *();
+};
+
+extern C c;
+extern const char * s;
+
+void
+foo (bool b)
+{
+  b ? c : s;			// { dg-error "?:" }
+  // { dg-message "convert" "" { target *-*-* } 15 }
+}
diff --git a/gcc/testsuite/g++.dg/expr/cond11.C b/gcc/testsuite/g++.dg/expr/cond11.C
new file mode 100644
index 0000000..7bd122d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond11.C
@@ -0,0 +1,20 @@
+struct A;
+struct C
+{
+  operator A();
+};
+
+struct A
+{
+  A(C);
+};
+
+extern A a;
+extern C c;
+
+void
+foo (bool b)
+{
+  b ? c : a;			// { dg-error "?:" }
+  // { dg-message "ambiguous" "" { target *-*-* } 18 }
+}
diff --git a/gcc/testsuite/g++.dg/expr/cond13.C b/gcc/testsuite/g++.dg/expr/cond13.C
new file mode 100644
index 0000000..90ae904
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond13.C
@@ -0,0 +1,9 @@
+// PR c++/54348
+
+struct A {} a;
+struct B {} b;
+
+void f()
+{
+    false ? a : b;		// { dg-error "different types" }
+}
diff --git a/gcc/testsuite/g++.dg/parse/crash41.C b/gcc/testsuite/g++.dg/parse/crash41.C
index 746dbbc..c14fbad 100644
--- a/gcc/testsuite/g++.dg/parse/crash41.C
+++ b/gcc/testsuite/g++.dg/parse/crash41.C
@@ -5,4 +5,4 @@ struct A
   A(int)(); // { dg-error "declared" }
 };
 
-template<int> void foo(bool b, A a) { b ? a : 0; } // { dg-error "no match" }
+template<int> void foo(bool b, A a) { b ? a : 0; } // { dg-error "?:" }
commit 9a22cbc52ee25e7bc12b72291003ca5653f5a097
Author: Jason Merrill <jason@redhat.com>
Date:   Thu May 8 16:45:04 2014 -0400

    	PR c++/58714
    	* tree.c (stabilize_expr): A stabilized prvalue is an xvalue.

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index e140024..229d476 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -3785,6 +3785,10 @@ stabilize_expr (tree exp, tree* initp)
     {
       init_expr = get_target_expr (exp);
       exp = TARGET_EXPR_SLOT (init_expr);
+      if (CLASS_TYPE_P (TREE_TYPE (exp)))
+	exp = move (exp);
+      else
+	exp = rvalue (exp);
     }
   else
     {
diff --git a/gcc/testsuite/g++.dg/cpp0x/rv-cond1.C b/gcc/testsuite/g++.dg/cpp0x/rv-cond1.C
new file mode 100644
index 0000000..a8f598f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/rv-cond1.C
@@ -0,0 +1,13 @@
+// PR c++/58714
+// { dg-do compile { target c++11 } }
+
+struct X {
+  X& operator=(const X&) = delete;
+  X& operator=(X&& ) = default;
+};
+
+void f(bool t) {
+  X a, b;
+  *(t ? &a : &b) = X();
+  (t ? a : b) = X();
+}
diff --git a/gcc/testsuite/g++.dg/expr/cond12.C b/gcc/testsuite/g++.dg/expr/cond12.C
new file mode 100644
index 0000000..9134f81
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cond12.C
@@ -0,0 +1,12 @@
+// PR c++/58714
+// { dg-do run }
+
+struct X {
+    X& operator=(const X&){}
+    X& operator=(X&){__builtin_abort();}
+};
+
+int main(int argv,char**) {
+  X a, b;
+  ((argv > 2) ? a : b) = X();
+}
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&

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]