------- Additional Comments From aoliva at gcc dot gnu dot org 2005-03-09 04:11 ------- Subject: Re: [3.3/3.4/4.0/4.1 Regression] Wrong warning about returning a reference to a temporary
On Mar 8, 2005, Roger Sayle <[EMAIL PROTECTED]> wrote: > On 8 Mar 2005, Alexandre Oliva wrote: >> >> * fold-const.c (non_lvalue): Split tests into... >> (maybe_lvalue_p): New function. >> (fold_ternary): Use it to avoid turning a COND_EXPR lvalue into >> a MIN_EXPR rvalue. > This version is Ok for mainline, and currently open release branches. Unfortunately, the problem in g++.oliva/expr2.C resurfaced, because, as it turns out, the transformation (I != J ? I : J) => I yields an lvalue as required, but not the *correct* lvalue in all cases. I tried what you'd suggested on IRC (testing whether the thing is an lvalue after the fact), but that obviously failed in this case as well. So I figured a better approach would be to perform lvalue tests to fold_cond_expr_with_comparison(), where we can avoid specific inappropriate transformations while still trying other alternatives. While at that, I figured we could use pedantic_lvalues to avoid refraining from applying optimizations just because it looks like the cond-expr might be an lvalue, even though the language requires it not to be. This is currently bootstrapping on x86_64-linux-gnu. Ok to install if bootstrap completes and regtesting passes? Index: gcc/ChangeLog from Alexandre Oliva <[EMAIL PROTECTED]> PR c++/19199 * fold-const.c (non_lvalue): Split tests into... (maybe_lvalue_p): New function. (fold_cond_expr_with_comparison): Preserve lvalue-ness. Index: gcc/fold-const.c =================================================================== RCS file: /cvs/gcc/gcc/gcc/fold-const.c,v retrieving revision 1.535 diff -u -p -r1.535 fold-const.c --- gcc/fold-const.c 7 Mar 2005 21:24:21 -0000 1.535 +++ gcc/fold-const.c 9 Mar 2005 03:59:18 -0000 @@ -2005,16 +2005,12 @@ fold_convert (tree type, tree arg) } } -/* Return an expr equal to X but certainly not valid as an lvalue. */ +/* Return false if expr can be assumed to not be an lvalue, true + otherwise. */ -tree -non_lvalue (tree x) +static bool +maybe_lvalue_p (tree x) { - /* While we are in GIMPLE, NON_LVALUE_EXPR doesn't mean anything to - us. */ - if (in_gimple_form) - return x; - /* We only need to wrap lvalue tree codes. */ switch (TREE_CODE (x)) { @@ -2054,8 +2050,24 @@ non_lvalue (tree x) /* Assume the worst for front-end tree codes. */ if ((int)TREE_CODE (x) >= NUM_TREE_CODES) break; - return x; + return false; } + + return true; +} + +/* Return an expr equal to X but certainly not valid as an lvalue. */ + +tree +non_lvalue (tree x) +{ + /* While we are in GIMPLE, NON_LVALUE_EXPR doesn't mean anything to + us. */ + if (in_gimple_form) + return x; + + if (! maybe_lvalue_p (x)) + return x; return build1 (NON_LVALUE_EXPR, TREE_TYPE (x), x); } @@ -4162,7 +4174,15 @@ fold_cond_expr_with_comparison (tree typ tree arg00 = TREE_OPERAND (arg0, 0); tree arg01 = TREE_OPERAND (arg0, 1); tree arg1_type = TREE_TYPE (arg1); - tree tem; + tree tem = NULL; + /* If the COND_EXPR can possibly be an lvalue, we don't want to + perform transformations that return a simplified result that will + be recognized as lvalue, but that will not match the expected + result. We may still return other expressions that would be + incorrect, but those are going to be rvalues, and the caller is + supposed to discard them. */ + bool lvalue = !pedantic_lvalues + && maybe_lvalue_p (arg1) && maybe_lvalue_p (arg2); STRIP_NOPS (arg1); STRIP_NOPS (arg2); @@ -4196,10 +4216,12 @@ fold_cond_expr_with_comparison (tree typ case EQ_EXPR: case UNEQ_EXPR: tem = fold_convert (arg1_type, arg1); - return pedantic_non_lvalue (fold_convert (type, negate_expr (tem))); + tem = pedantic_non_lvalue (fold_convert (type, negate_expr (tem))); + break; case NE_EXPR: case LTGT_EXPR: - return pedantic_non_lvalue (fold_convert (type, arg1)); + tem = pedantic_non_lvalue (fold_convert (type, arg1)); + break; case UNGE_EXPR: case UNGT_EXPR: if (flag_trapping_math) @@ -4211,7 +4233,8 @@ fold_cond_expr_with_comparison (tree typ arg1 = fold_convert (lang_hooks.types.signed_type (TREE_TYPE (arg1)), arg1); tem = fold (build1 (ABS_EXPR, TREE_TYPE (arg1), arg1)); - return pedantic_non_lvalue (fold_convert (type, tem)); + tem = pedantic_non_lvalue (fold_convert (type, tem)); + break; case UNLE_EXPR: case UNLT_EXPR: if (flag_trapping_math) @@ -4222,12 +4245,18 @@ fold_cond_expr_with_comparison (tree typ arg1 = fold_convert (lang_hooks.types.signed_type (TREE_TYPE (arg1)), arg1); tem = fold (build1 (ABS_EXPR, TREE_TYPE (arg1), arg1)); - return negate_expr (fold_convert (type, tem)); + tem = negate_expr (fold_convert (type, tem)); + break; default: gcc_assert (TREE_CODE_CLASS (comp_code) == tcc_comparison); break; } + if (tem && (! lvalue || maybe_lvalue_p (tem))) + return tem; + else + tem = NULL; + /* A != 0 ? A : 0 is simply A, unless A is -0. Likewise A == 0 ? A : 0 is always 0 unless A is -0. Note that both transformations are correct when A is NaN: A != 0 @@ -4236,11 +4265,16 @@ fold_cond_expr_with_comparison (tree typ if (integer_zerop (arg01) && integer_zerop (arg2)) { if (comp_code == NE_EXPR) - return pedantic_non_lvalue (fold_convert (type, arg1)); + tem = pedantic_non_lvalue (fold_convert (type, arg1)); else if (comp_code == EQ_EXPR) - return fold_convert (type, integer_zero_node); + tem = fold_convert (type, integer_zero_node); } + if (tem && (! lvalue || maybe_lvalue_p (tem))) + return tem; + else + tem = NULL; + /* Try some transformations of A op B ? A : B. A == B? A : B same as B @@ -4284,9 +4318,15 @@ fold_cond_expr_with_comparison (tree typ switch (comp_code) { case EQ_EXPR: - return pedantic_non_lvalue (fold_convert (type, arg2)); + if (lvalue) + break; + tem = pedantic_non_lvalue (fold_convert (type, arg2)); + break; case NE_EXPR: - return pedantic_non_lvalue (fold_convert (type, arg1)); + if (lvalue) + break; + tem = pedantic_non_lvalue (fold_convert (type, arg1)); + break; case LE_EXPR: case LT_EXPR: case UNLE_EXPR: @@ -4302,7 +4342,7 @@ fold_cond_expr_with_comparison (tree typ tem = (comp_code == LE_EXPR || comp_code == UNLE_EXPR) ? fold (build2 (MIN_EXPR, comp_type, comp_op0, comp_op1)) : fold (build2 (MIN_EXPR, comp_type, comp_op1, comp_op0)); - return pedantic_non_lvalue (fold_convert (type, tem)); + tem = pedantic_non_lvalue (fold_convert (type, tem)); } break; case GE_EXPR: @@ -4316,16 +4356,16 @@ fold_cond_expr_with_comparison (tree typ tem = (comp_code == GE_EXPR || comp_code == UNGE_EXPR) ? fold (build2 (MAX_EXPR, comp_type, comp_op0, comp_op1)) : fold (build2 (MAX_EXPR, comp_type, comp_op1, comp_op0)); - return pedantic_non_lvalue (fold_convert (type, tem)); + tem = pedantic_non_lvalue (fold_convert (type, tem)); } break; case UNEQ_EXPR: - if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1)))) - return pedantic_non_lvalue (fold_convert (type, arg2)); + if (! lvalue && !HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1)))) + tem = pedantic_non_lvalue (fold_convert (type, arg2)); break; case LTGT_EXPR: - if (!HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1)))) - return pedantic_non_lvalue (fold_convert (type, arg1)); + if (! lvalue && !HONOR_NANS (TYPE_MODE (TREE_TYPE (arg1)))) + tem = pedantic_non_lvalue (fold_convert (type, arg1)); break; default: gcc_assert (TREE_CODE_CLASS (comp_code) == tcc_comparison); @@ -4333,6 +4373,11 @@ fold_cond_expr_with_comparison (tree typ } } + if (tem && (! lvalue || maybe_lvalue_p (tem))) + return tem; + else + tem = NULL; + /* If this is A op C1 ? A : C2 with C1 and C2 constant integers, we might still be able to simplify this. For example, if C1 is one less or one more than C2, this might have started @@ -4347,7 +4392,7 @@ fold_cond_expr_with_comparison (tree typ case EQ_EXPR: /* We can replace A with C1 in this case. */ arg1 = fold_convert (type, arg01); - return fold (build3 (COND_EXPR, type, arg0, arg1, arg2)); + tem = fold (build3 (COND_EXPR, type, arg0, arg1, arg2)); case LT_EXPR: /* If C1 is C2 + 1, this is min(A, C2). */ @@ -4357,8 +4402,8 @@ fold_cond_expr_with_comparison (tree typ const_binop (PLUS_EXPR, arg2, integer_one_node, 0), OEP_ONLY_CONST)) - return pedantic_non_lvalue (fold (build2 (MIN_EXPR, - type, arg1, arg2))); + tem = pedantic_non_lvalue (fold (build2 (MIN_EXPR, + type, arg1, arg2))); break; case LE_EXPR: @@ -4369,8 +4414,8 @@ fold_cond_expr_with_comparison (tree typ const_binop (MINUS_EXPR, arg2, integer_one_node, 0), OEP_ONLY_CONST)) - return pedantic_non_lvalue (fold (build2 (MIN_EXPR, - type, arg1, arg2))); + tem = pedantic_non_lvalue (fold (build2 (MIN_EXPR, + type, arg1, arg2))); break; case GT_EXPR: @@ -4381,8 +4426,8 @@ fold_cond_expr_with_comparison (tree typ const_binop (MINUS_EXPR, arg2, integer_one_node, 0), OEP_ONLY_CONST)) - return pedantic_non_lvalue (fold (build2 (MAX_EXPR, - type, arg1, arg2))); + tem = pedantic_non_lvalue (fold (build2 (MAX_EXPR, + type, arg1, arg2))); break; case GE_EXPR: @@ -4393,8 +4438,8 @@ fold_cond_expr_with_comparison (tree typ const_binop (PLUS_EXPR, arg2, integer_one_node, 0), OEP_ONLY_CONST)) - return pedantic_non_lvalue (fold (build2 (MAX_EXPR, - type, arg1, arg2))); + tem = pedantic_non_lvalue (fold (build2 (MAX_EXPR, + type, arg1, arg2))); break; case NE_EXPR: break; @@ -4402,6 +4447,11 @@ fold_cond_expr_with_comparison (tree typ gcc_unreachable (); } + if (tem && (! lvalue || maybe_lvalue_p (tem))) + return tem; + else + tem = NULL; + return NULL_TREE; } Index: gcc/testsuite/ChangeLog from Alexandre Oliva <[EMAIL PROTECTED]> PR c++/19199 * g++.dg/expr/lval2.C: New. Index: gcc/testsuite/g++.dg/expr/lval2.C =================================================================== RCS file: gcc/testsuite/g++.dg/expr/lval2.C diff -N gcc/testsuite/g++.dg/expr/lval2.C --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ gcc/testsuite/g++.dg/expr/lval2.C 9 Mar 2005 03:59:32 -0000 @@ -0,0 +1,26 @@ +// PR c++/19199 + +// { dg-do run } + +// We used to turn the COND_EXPR lvalue into a MIN_EXPR rvalue, and +// then return a reference to a temporary in qMin. + +#include <assert.h> + +enum Foo { A, B }; + +template<typename T> T &qMin(T &a, T &b) +{ + return a < b ? a : b; +} + +int main (int, char **) +{ + Foo f = A; + Foo g = B; + Foo &h = qMin(f, g); + assert (&h == &f || &h == &g); + const Foo &i = qMin((const Foo&)f, (const Foo&)g); + assert (&i == &f || &i == &g); + return 0; +} Index: gcc/testsuite/g++.old-deja/g++.oliva/ChangeLog from Alexandre Oliva <[EMAIL PROTECTED]> PR c++/19199 * expr2.C: Fixed. Index: gcc/testsuite/g++.old-deja/g++.oliva/expr2.C =================================================================== RCS file: /cvs/gcc/gcc/gcc/testsuite/g++.old-deja/g++.oliva/expr2.C,v retrieving revision 1.5 diff -u -p -r1.5 expr2.C --- gcc/testsuite/g++.old-deja/g++.oliva/expr2.C 1 May 2003 02:02:47 -0000 1.5 +++ gcc/testsuite/g++.old-deja/g++.oliva/expr2.C 9 Mar 2005 03:59:32 -0000 @@ -1,4 +1,4 @@ -// { dg-do run { xfail *-*-* } } +// { dg-do run } // Copyright (C) 2000 Free Software Foundation -- Alexandre Oliva http://www.ic.unicamp.br/~oliva/ Red Hat Compiler Engineer [EMAIL PROTECTED], gcc.gnu.org} Free Software Evangelist [EMAIL PROTECTED], gnu.org} -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=19199