C++ PATCH for c++/64372/DR 1560, value category of ?: with one throw

Message ID CADzB+2n8WxJLq=LXxcbgrSn7UbHBi6fiJOQp9eCGUVpRE2eSjA@mail.gmail.com
State New
Headers show
Series
  • C++ PATCH for c++/64372/DR 1560, value category of ?: with one throw
Related show

Commit Message

Jason Merrill May 15, 2018, 9:54 p.m.
DR 1560 in C++14 fixed this rule to not do a gratuitous lvalue-rvalue
conversion in this case.

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

Patch

commit c7b8b79f72865740ead84aa602a6bc8651a60a93
Author: Jason Merrill <jason@redhat.com>
Date:   Tue May 15 15:46:51 2018 -0400

            PR c++/64372 - CWG 1560, gratuitous lvalue-rvalue conversion in ?:
    
            * call.c (build_conditional_expr_1): Don't force_rvalue when one arm
            is a throw-expression.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f620c0d86e8..09a3618b007 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4969,56 +4969,33 @@  build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
   arg3_type = unlowered_expr_type (arg3);
   if (VOID_TYPE_P (arg2_type) || VOID_TYPE_P (arg3_type))
     {
-      /* Do the conversions.  We don't these for `void' type arguments
-	 since it can't have any effect and since decay_conversion
-	 does not handle that case gracefully.  */
-      if (!VOID_TYPE_P (arg2_type))
-	arg2 = decay_conversion (arg2, complain);
-      if (!VOID_TYPE_P (arg3_type))
-	arg3 = decay_conversion (arg3, complain);
-      arg2_type = TREE_TYPE (arg2);
-      arg3_type = TREE_TYPE (arg3);
-
       /* [expr.cond]
 
 	 One of the following shall hold:
 
 	 --The second or the third operand (but not both) is a
-	   throw-expression (_except.throw_); the result is of the
-	   type of the other and is an rvalue.
+	   throw-expression (_except.throw_); the result is of the type
+	   and value category of the other.
 
 	 --Both the second and the third operands have type void; the
-	   result is of type void and is an rvalue.
-
-	 We must avoid calling force_rvalue for expressions of type
-	 "void" because it will complain that their value is being
-	 used.  */
+	   result is of type void and is a prvalue.  */
       if (TREE_CODE (arg2) == THROW_EXPR
 	  && TREE_CODE (arg3) != THROW_EXPR)
 	{
-	  if (!VOID_TYPE_P (arg3_type))
-	    {
-	      arg3 = force_rvalue (arg3, complain);
-	      if (arg3 == error_mark_node)
-		return error_mark_node;
-	    }
-	  arg3_type = TREE_TYPE (arg3);
 	  result_type = arg3_type;
+	  is_glvalue = glvalue_p (arg3);
 	}
       else if (TREE_CODE (arg2) != THROW_EXPR
 	       && TREE_CODE (arg3) == THROW_EXPR)
 	{
-	  if (!VOID_TYPE_P (arg2_type))
-	    {
-	      arg2 = force_rvalue (arg2, complain);
-	      if (arg2 == error_mark_node)
-		return error_mark_node;
-	    }
-	  arg2_type = TREE_TYPE (arg2);
 	  result_type = arg2_type;
+	  is_glvalue = glvalue_p (arg2);
 	}
       else if (VOID_TYPE_P (arg2_type) && VOID_TYPE_P (arg3_type))
-	result_type = void_type_node;
+	{
+	  result_type = void_type_node;
+	  is_glvalue = false;
+	}
       else
 	{
           if (complain & tf_error)
@@ -5037,7 +5014,6 @@  build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 	  return error_mark_node;
 	}
 
-      is_glvalue = false;
       goto valid_operands;
     }
   /* [expr.cond]
@@ -5155,10 +5131,6 @@  build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
       && same_type_p (arg2_type, arg3_type))
     {
       result_type = arg2_type;
-      if (processing_template_decl)
-	/* Let lvalue_kind know this was a glvalue.  */
-	result_type = cp_build_reference_type (result_type, xvalue_p (arg2));
-
       arg2 = mark_lvalue_use (arg2);
       arg3 = mark_lvalue_use (arg3);
       goto valid_operands;
@@ -5352,6 +5324,13 @@  build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
     return error_mark_node;
 
  valid_operands:
+  if (processing_template_decl && is_glvalue)
+    {
+      /* Let lvalue_kind know this was a glvalue.  */
+      tree arg = (result_type == arg2_type ? arg2 : arg3);
+      result_type = cp_build_reference_type (result_type, xvalue_p (arg));
+    }
+
   result = build3_loc (loc, COND_EXPR, result_type, arg1, arg2, arg3);
 
   /* If the ARG2 and ARG3 are the same and don't have side-effects,
diff --git a/gcc/testsuite/g++.dg/cpp1y/dr1560.C b/gcc/testsuite/g++.dg/cpp1y/dr1560.C
new file mode 100644
index 00000000000..b21ca98e279
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/dr1560.C
@@ -0,0 +1,14 @@ 
+// Core 1560
+// { dg-do compile { target c++14 } }
+
+struct A
+{
+  A();
+  A(const A&) = delete;
+};
+
+void f(bool b)
+{
+  A a;
+  b ? a : throw 42;
+}