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  <pola...@redhat.com>

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

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

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:

        Marek

Reply via email to