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