diff mbox

C++ PATCH for c++/70744 (wrong-code with x ?: y extension)

Message ID 20160422115033.GX28445@redhat.com
State New
Headers show

Commit Message

Marek Polacek April 22, 2016, 11:50 a.m. UTC
This PR shows that we generate wrong code with the x ?: y extension in case the
first operand contains either predecrement or preincrement.  The problem is
that we don't emit SAVE_EXPR, thus the operand is evaluated twice, which it
should not be.

While ++i or --i can be lvalues in C++, i++ or i-- can not.  The code in
build_conditional_expr_1 has:
 4635       /* Make sure that lvalues remain lvalues.  See g++.oliva/ext1.C.  */
 4636       if (real_lvalue_p (arg1))
 4637         arg2 = arg1 = stabilize_reference (arg1);
 4638       else
 4639         arg2 = arg1 = save_expr (arg1);
so for ++i/--i we call stabilize_reference, but that doesn't know anything
about PREINCREMENT_EXPR or PREDECREMENT_EXPR and just returns the same
expression, so SAVE_EXPR isn't created.

I think one fix would be to teach stabilize_reference what to do with those,
similarly to how we handle COMPOUND_EXPR there.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2016-04-22  Marek Polacek  <polacek@redhat.com>

	PR c++/70744
	* tree.c (stabilize_reference): Handle PREINCREMENT_EXPR and
	PREDECREMENT_EXPR.

	* g++.dg/ext/cond2.C: New test.


	Marek

Comments

Jason Merrill April 22, 2016, 1:43 p.m. UTC | #1
On 04/22/2016 07:50 AM, Marek Polacek wrote:
> This PR shows that we generate wrong code with the x ?: y extension in case the
> first operand contains either predecrement or preincrement.  The problem is
> that we don't emit SAVE_EXPR, thus the operand is evaluated twice, which it
> should not be.
>
> While ++i or --i can be lvalues in C++, i++ or i-- can not.  The code in
> build_conditional_expr_1 has:
>   4635       /* Make sure that lvalues remain lvalues.  See g++.oliva/ext1.C.  */
>   4636       if (real_lvalue_p (arg1))
>   4637         arg2 = arg1 = stabilize_reference (arg1);
>   4638       else
>   4639         arg2 = arg1 = save_expr (arg1);
> so for ++i/--i we call stabilize_reference, but that doesn't know anything
> about PREINCREMENT_EXPR or PREDECREMENT_EXPR and just returns the same
> expression, so SAVE_EXPR isn't created.
>
> I think one fix would be to teach stabilize_reference what to do with those,
> similarly to how we handle COMPOUND_EXPR there.

Your change will turn the expression into an rvalue, so that isn't 
enough.  We need to turn the expression into some sort of _REF before 
passing it to stabilize_reference, perhaps by factoring out the 
cast-to-reference code from force_paren_expr.  This should probably be 
part of a cp_stabilize_reference function that replaces all uses of 
stabilize_reference in the front end.

Jason
diff mbox

Patch

diff --git gcc/testsuite/g++.dg/ext/cond2.C gcc/testsuite/g++.dg/ext/cond2.C
index e69de29..d9f1d59 100644
--- gcc/testsuite/g++.dg/ext/cond2.C
+++ gcc/testsuite/g++.dg/ext/cond2.C
@@ -0,0 +1,28 @@ 
+// PR c++/70744
+// { dg-do run }
+// { dg-options "" }
+
+static void
+fn1 (void)
+{
+  int x = 2;
+  ++x ? : 42;
+  if (x != 3)
+    __builtin_abort ();
+  --x ? : 42;
+  if (x != 2)
+    __builtin_abort ();
+  x++ ? : 42;
+  if (x != 3)
+    __builtin_abort ();
+  x-- ? : 42;
+  if (x != 2)
+    __builtin_abort ();
+}
+
+int
+main ()
+{
+  fn1 ();
+  return 0;
+}
diff --git gcc/tree.c gcc/tree.c
index 6de46a8..8fd4e81 100644
--- gcc/tree.c
+++ gcc/tree.c
@@ -4255,6 +4255,13 @@  stabilize_reference (tree ref)
 	 volatiles.  */
       return stabilize_reference_1 (ref);
 
+    case PREINCREMENT_EXPR:
+    case PREDECREMENT_EXPR:
+      /* While in C++ postincrement and postdecrement expressions are not
+	 considered lvalues, preincrement and predecrement expressions can
+	 be lvalues.  */
+      return stabilize_reference_1 (ref);
+
       /* If arg isn't a kind of lvalue we recognize, make no change.
 	 Caller should recognize the error for an invalid lvalue.  */
     default: