Folding of $subject is currently broken (noticed that when playing with types in pointer_int_sum). We happily ignore the fact that the negate operates on an unsigned type and change it to operate on a signed one - which may cause new undefined overflow. Seen with the testcase below which aborts with current trunk.
The fix is to not strip sign-changing conversions as already done for ABS_EXPR. Bootstrapped on x86_64-unknown-linux-gnu, testing in progress. Richard. 2011-07-07 Richard Guenther <rguent...@suse.de> * fold-const.c (fold_unary_loc): Do not strip sign-changes for NEGATE_EXPR. * gcc.dg/ftrapv-3.c: New testcase. Index: gcc/fold-const.c =================================================================== --- gcc/fold-const.c (revision 175962) +++ gcc/fold-const.c (working copy) @@ -7561,7 +7561,7 @@ fold_unary_loc (location_t loc, enum tre if (arg0) { if (CONVERT_EXPR_CODE_P (code) - || code == FLOAT_EXPR || code == ABS_EXPR) + || code == FLOAT_EXPR || code == ABS_EXPR || code == NEGATE_EXPR) { /* Don't use STRIP_NOPS, because signedness of argument type matters. */ Index: gcc/testsuite/gcc.dg/ftrapv-3.c =================================================================== --- gcc/testsuite/gcc.dg/ftrapv-3.c (revision 0) +++ gcc/testsuite/gcc.dg/ftrapv-3.c (revision 0) @@ -0,0 +1,16 @@ +/* { dg-do run } */ +/* { dg-options "-ftrapv" } */ + +extern void abort (void); +unsigned long +foo (long i, long j) +{ + /* We may not fold this to (unsigned long)(i * j). */ + return -(unsigned long)(i * -j); +} +int main() +{ + if (foo (-__LONG_MAX__ - 1, -1) != -(unsigned long)(-__LONG_MAX__ - 1)) + abort (); + return 0; +}