98a12cb
2017-01-30  Jakub Jelinek  <jakub@redhat.com>
98a12cb
98a12cb
	PR c++/79232
98a12cb
	* typeck.c (cp_build_modify_expr): Handle properly COMPOUND_EXPRs
98a12cb
	on lhs that have {PRE{DEC,INC}REMENT,MODIFY,MIN,MAX,COND}_EXPR
98a12cb
	in the rightmost operand.
98a12cb
98a12cb
	* g++.dg/cpp1z/eval-order4.C: New test.
98a12cb
	* g++.dg/other/pr79232.C: New test.
98a12cb
98a12cb
--- gcc/cp/typeck.c.jj	2017-01-30 09:31:43.076595640 +0100
98a12cb
+++ gcc/cp/typeck.c	2017-01-30 15:56:33.601002577 +0100
98a12cb
@@ -7568,16 +7568,26 @@ tree
98a12cb
 cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
98a12cb
 		      tree rhs, tsubst_flags_t complain)
98a12cb
 {
98a12cb
-  tree result;
98a12cb
+  tree result = NULL_TREE;
98a12cb
   tree newrhs = rhs;
98a12cb
   tree lhstype = TREE_TYPE (lhs);
98a12cb
+  tree olhs = lhs;
98a12cb
   tree olhstype = lhstype;
98a12cb
   bool plain_assign = (modifycode == NOP_EXPR);
98a12cb
+  bool compound_side_effects_p = false;
98a12cb
+  tree preeval = NULL_TREE;
98a12cb
 
98a12cb
   /* Avoid duplicate error messages from operands that had errors.  */
98a12cb
   if (error_operand_p (lhs) || error_operand_p (rhs))
98a12cb
     return error_mark_node;
98a12cb
 
98a12cb
+  while (TREE_CODE (lhs) == COMPOUND_EXPR)
98a12cb
+    {
98a12cb
+      if (TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0)))
98a12cb
+	compound_side_effects_p = true;
98a12cb
+      lhs = TREE_OPERAND (lhs, 1);
98a12cb
+    }
98a12cb
+
98a12cb
   /* Handle control structure constructs used as "lvalues".  Note that we
98a12cb
      leave COMPOUND_EXPR on the LHS because it is sequenced after the RHS.  */
98a12cb
   switch (TREE_CODE (lhs))
98a12cb
@@ -7585,20 +7595,57 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
       /* Handle --foo = 5; as these are valid constructs in C++.  */
98a12cb
     case PREDECREMENT_EXPR:
98a12cb
     case PREINCREMENT_EXPR:
98a12cb
+      if (compound_side_effects_p)
98a12cb
+	{
98a12cb
+	  if (VOID_TYPE_P (TREE_TYPE (rhs)))
98a12cb
+	    {
98a12cb
+	      if (complain & tf_error)
98a12cb
+		error ("void value not ignored as it ought to be");
98a12cb
+	      return error_mark_node;
98a12cb
+	    }
98a12cb
+	  newrhs = rhs = stabilize_expr (rhs, &preeval);
98a12cb
+	}
98a12cb
       if (TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0)))
98a12cb
 	lhs = build2 (TREE_CODE (lhs), TREE_TYPE (lhs),
98a12cb
 		      cp_stabilize_reference (TREE_OPERAND (lhs, 0)),
98a12cb
 		      TREE_OPERAND (lhs, 1));
98a12cb
       lhs = build2 (COMPOUND_EXPR, lhstype, lhs, TREE_OPERAND (lhs, 0));
98a12cb
+    maybe_add_compound:
98a12cb
+      /* If we had (bar, --foo) = 5; or (bar, (baz, --foo)) = 5;
98a12cb
+	 and looked through the COMPOUND_EXPRs, readd them now around
98a12cb
+	 the resulting lhs.  */
98a12cb
+      if (TREE_CODE (olhs) == COMPOUND_EXPR)
98a12cb
+	{
98a12cb
+	  lhs = build2 (COMPOUND_EXPR, lhstype, TREE_OPERAND (olhs, 0), lhs);
98a12cb
+	  tree *ptr = &TREE_OPERAND (lhs, 1);
98a12cb
+	  for (olhs = TREE_OPERAND (olhs, 1);
98a12cb
+	       TREE_CODE (olhs) == COMPOUND_EXPR;
98a12cb
+	       olhs = TREE_OPERAND (olhs, 1))
98a12cb
+	    {
98a12cb
+	      *ptr = build2 (COMPOUND_EXPR, lhstype,
98a12cb
+			     TREE_OPERAND (olhs, 0), *ptr);
98a12cb
+	      ptr = &TREE_OPERAND (*ptr, 1);
98a12cb
+	    }
98a12cb
+	}
98a12cb
       break;
98a12cb
 
98a12cb
     case MODIFY_EXPR:
98a12cb
+      if (compound_side_effects_p)
98a12cb
+	{
98a12cb
+	  if (VOID_TYPE_P (TREE_TYPE (rhs)))
98a12cb
+	    {
98a12cb
+	      if (complain & tf_error)
98a12cb
+		error ("void value not ignored as it ought to be");
98a12cb
+	      return error_mark_node;
98a12cb
+	    }
98a12cb
+	  newrhs = rhs = stabilize_expr (rhs, &preeval);
98a12cb
+	}
98a12cb
       if (TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0)))
98a12cb
 	lhs = build2 (TREE_CODE (lhs), TREE_TYPE (lhs),
98a12cb
 		      cp_stabilize_reference (TREE_OPERAND (lhs, 0)),
98a12cb
 		      TREE_OPERAND (lhs, 1));
98a12cb
       lhs = build2 (COMPOUND_EXPR, lhstype, lhs, TREE_OPERAND (lhs, 0));
98a12cb
-      break;
98a12cb
+      goto maybe_add_compound;
98a12cb
 
98a12cb
     case MIN_EXPR:
98a12cb
     case MAX_EXPR:
98a12cb
@@ -7626,7 +7673,6 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 	   except that the RHS goes through a save-expr
98a12cb
 	   so the code to compute it is only emitted once.  */
98a12cb
 	tree cond;
98a12cb
-	tree preeval = NULL_TREE;
98a12cb
 
98a12cb
 	if (VOID_TYPE_P (TREE_TYPE (rhs)))
98a12cb
 	  {
98a12cb
@@ -7652,14 +7698,31 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 
98a12cb
 	if (cond == error_mark_node)
98a12cb
 	  return cond;
98a12cb
+	/* If we had (e, (a ? b : c)) = d; or (e, (f, (a ? b : c))) = d;
98a12cb
+	   and looked through the COMPOUND_EXPRs, readd them now around
98a12cb
+	   the resulting cond before adding the preevaluated rhs.  */
98a12cb
+	if (TREE_CODE (olhs) == COMPOUND_EXPR)
98a12cb
+	  {
98a12cb
+	    cond = build2 (COMPOUND_EXPR, TREE_TYPE (cond),
98a12cb
+			   TREE_OPERAND (olhs, 0), cond);
98a12cb
+	    tree *ptr = &TREE_OPERAND (cond, 1);
98a12cb
+	    for (olhs = TREE_OPERAND (olhs, 1);
98a12cb
+		 TREE_CODE (olhs) == COMPOUND_EXPR;
98a12cb
+		 olhs = TREE_OPERAND (olhs, 1))
98a12cb
+	      {
98a12cb
+		*ptr = build2 (COMPOUND_EXPR, TREE_TYPE (cond),
98a12cb
+			       TREE_OPERAND (olhs, 0), *ptr);
98a12cb
+		ptr = &TREE_OPERAND (*ptr, 1);
98a12cb
+	      }
98a12cb
+	  }
98a12cb
 	/* Make sure the code to compute the rhs comes out
98a12cb
 	   before the split.  */
98a12cb
-	if (preeval)
98a12cb
-	  cond = build2 (COMPOUND_EXPR, TREE_TYPE (lhs), preeval, cond);
98a12cb
-	return cond;
98a12cb
+	result = cond;
98a12cb
+	goto ret;
98a12cb
       }
98a12cb
 
98a12cb
     default:
98a12cb
+      lhs = olhs;
98a12cb
       break;
98a12cb
     }
98a12cb
 
98a12cb
@@ -7675,7 +7738,7 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 	    rhs = convert (lhstype, rhs);
98a12cb
 	  result = build2 (INIT_EXPR, lhstype, lhs, rhs);
98a12cb
 	  TREE_SIDE_EFFECTS (result) = 1;
98a12cb
-	  return result;
98a12cb
+	  goto ret;
98a12cb
 	}
98a12cb
       else if (! MAYBE_CLASS_TYPE_P (lhstype))
98a12cb
 	/* Do the default thing.  */;
98a12cb
@@ -7688,7 +7751,7 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 	  release_tree_vector (rhs_vec);
98a12cb
 	  if (result == NULL_TREE)
98a12cb
 	    return error_mark_node;
98a12cb
-	  return result;
98a12cb
+	  goto ret;
98a12cb
 	}
98a12cb
     }
98a12cb
   else
98a12cb
@@ -7703,7 +7766,7 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 	    {
98a12cb
 	      result = objc_maybe_build_modify_expr (lhs, rhs);
98a12cb
 	      if (result)
98a12cb
-		return result;
98a12cb
+		goto ret;
98a12cb
 	    }
98a12cb
 
98a12cb
 	  /* `operator=' is not an inheritable operator.  */
98a12cb
@@ -7717,7 +7780,7 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 				     complain);
98a12cb
 	      if (result == NULL_TREE)
98a12cb
 		return error_mark_node;
98a12cb
-	      return result;
98a12cb
+	      goto ret;
98a12cb
 	    }
98a12cb
 	  lhstype = olhstype;
98a12cb
 	}
98a12cb
@@ -7762,7 +7825,7 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 	    {
98a12cb
 	      result = objc_maybe_build_modify_expr (lhs, newrhs);
98a12cb
 	      if (result)
98a12cb
-		return result;
98a12cb
+		goto ret;
98a12cb
 	    }
98a12cb
 	}
98a12cb
       gcc_assert (TREE_CODE (lhstype) != REFERENCE_TYPE);
98a12cb
@@ -7858,9 +7921,10 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
 
98a12cb
       from_array = TREE_CODE (TREE_TYPE (newrhs)) == ARRAY_TYPE
98a12cb
 		   ? 1 + (modifycode != INIT_EXPR): 0;
98a12cb
-      return build_vec_init (lhs, NULL_TREE, newrhs,
98a12cb
-			     /*explicit_value_init_p=*/false,
98a12cb
-			     from_array, complain);
98a12cb
+      result = build_vec_init (lhs, NULL_TREE, newrhs,
98a12cb
+			       /*explicit_value_init_p=*/false,
98a12cb
+			       from_array, complain);
98a12cb
+      goto ret;
98a12cb
     }
98a12cb
 
98a12cb
   if (modifycode == INIT_EXPR)
98a12cb
@@ -7899,7 +7963,7 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
       result = objc_generate_write_barrier (lhs, modifycode, newrhs);
98a12cb
 
98a12cb
       if (result)
98a12cb
-	return result;
98a12cb
+	goto ret;
98a12cb
     }
98a12cb
 
98a12cb
   result = build2 (modifycode == NOP_EXPR ? MODIFY_EXPR : INIT_EXPR,
98a12cb
@@ -7909,6 +7973,9 @@ cp_build_modify_expr (location_t loc, tr
98a12cb
   if (!plain_assign)
98a12cb
     TREE_NO_WARNING (result) = 1;
98a12cb
 
98a12cb
+ ret:
98a12cb
+  if (preeval)
98a12cb
+    result = build2 (COMPOUND_EXPR, TREE_TYPE (result), preeval, result);
98a12cb
   return result;
98a12cb
 }
98a12cb
 
98a12cb
--- gcc/testsuite/g++.dg/cpp1z/eval-order4.C.jj	2017-01-30 16:08:22.195641383 +0100
98a12cb
+++ gcc/testsuite/g++.dg/cpp1z/eval-order4.C	2017-01-30 16:08:09.000000000 +0100
98a12cb
@@ -0,0 +1,80 @@
98a12cb
+// PR c++/79232
98a12cb
+// { dg-do run }
98a12cb
+// { dg-options "-fstrong-eval-order" }
98a12cb
+
98a12cb
+int last = 0;
98a12cb
+
98a12cb
+int
98a12cb
+foo (int i)
98a12cb
+{
98a12cb
+  if (i != last + 1)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = i;
98a12cb
+  return i;
98a12cb
+}
98a12cb
+
98a12cb
+char a, b;
98a12cb
+int c;
98a12cb
+
98a12cb
+char &
98a12cb
+bar (int i, int j)
98a12cb
+{
98a12cb
+  foo (i);
98a12cb
+  return j ? a : b;
98a12cb
+}
98a12cb
+
98a12cb
+int
98a12cb
+main ()
98a12cb
+{
98a12cb
+  (foo (2) ? bar (3, 0) : bar (3, 1)) = foo (1);
98a12cb
+  if (last != 3)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), foo (3) ? bar (4, 0) : bar (4, 1)) = foo (1);
98a12cb
+  if (last != 4)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), (foo (3) ? bar (4, 0) : bar (4, 1))) = foo (1);
98a12cb
+  if (last != 4)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), foo (3), foo (4) ? bar (5, 0) : bar (5, 1)) = foo (1);
98a12cb
+  if (last != 5)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), (foo (3), (foo (4) ? bar (5, 0) : bar (5, 1)))) = foo (1);
98a12cb
+  if (last != 5)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  --c = foo (1);
98a12cb
+  if (c != 1)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), --c) = foo (1);
98a12cb
+  if (last != 2 || c != 1)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), foo (3), --c) = foo (1);
98a12cb
+  if (last != 3 || c != 1)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), (foo (3), --c)) = foo (1);
98a12cb
+  if (last != 3 || c != 1)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  bar (2, 0) = foo (1);
98a12cb
+  if (last != 2)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), bar (3, 0)) = foo (1);
98a12cb
+  if (last != 3)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), foo (3), bar (4, 0)) = foo (1);
98a12cb
+  if (last != 4)
98a12cb
+    __builtin_abort ();
98a12cb
+  last = 0;
98a12cb
+  (foo (2), (foo (3), bar (4, 0))) = foo (1);
98a12cb
+  if (last != 4)
98a12cb
+    __builtin_abort ();
98a12cb
+}
98a12cb
--- gcc/testsuite/g++.dg/other/pr79232.C.jj	2017-01-30 13:37:32.095090643 +0100
98a12cb
+++ gcc/testsuite/g++.dg/other/pr79232.C	2017-01-30 13:35:17.000000000 +0100
98a12cb
@@ -0,0 +1,12 @@
98a12cb
+// PR c++/79232
98a12cb
+// { dg-do compile }
98a12cb
+
98a12cb
+extern char a[];
98a12cb
+int b;
98a12cb
+char c, e;
98a12cb
+
98a12cb
+void
98a12cb
+foo (long d)
98a12cb
+{
98a12cb
+  (0, b ? &c : a)[d] = e;
98a12cb
+}