From d211a298ce7c5ff8b34323e0575480fc9f80ada9 Mon Sep 17 00:00:00 2001 From: Roger Sayle Date: Tue, 21 Sep 2004 16:20:12 +0000 Subject: [PATCH] re PR c++/7503 ((x < Y ? X : Y) used for assignment causes wrong code) PR c++/7503 * tree.c (lvalue_p_1): Disallow MIN_EXPR and MAX_EXPR as lvalues if either operand has side-effects. * typeck.c (rationalize_conditional_expr): Assert that neither operand of MIN_EXPR or MAX_EXPR has side-effects. (build_modify_expr): Add support for MIN_EXPR and MAX_EXPR. Check that the "lhs" is a valid lvalue, i.e. that neither operand of a MIN_EXPR or MAX_EXPR has a side-effect. * g++.dg/opt/pr7503-1.C: New testcase for COND_EXPR lvalues. * g++.dg/opt/pr7503-2.C: New testcase for ? lvalues. * g++.dg/opt/pr7503-3.C: New testcase for invalid ?= assignments. * g++.dg/opt/pr7503-5.C: New testcase for side-effects with + + PR c++/7503 + * tree.c (lvalue_p_1): Disallow MIN_EXPR and MAX_EXPR as lvalues + if either operand has side-effects. + * typeck.c (rationalize_conditional_expr): Assert that neither + operand of MIN_EXPR or MAX_EXPR has side-effects. + (build_modify_expr): Add support for MIN_EXPR and MAX_EXPR. + Check that the "lhs" is a valid lvalue, i.e. that neither operand + of a MIN_EXPR or MAX_EXPR has a side-effect. + 2004-09-21 Nathan Sidwell * cp-tree.h (struct lang_type_header): Remove diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 28bb512da75..fc3516d8e8a 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -129,6 +129,10 @@ lvalue_p_1 (tree ref, gcc_unreachable (); case MAX_EXPR: case MIN_EXPR: + /* Disallow ? as lvalues if either argument side-effects. */ + if (TREE_SIDE_EFFECTS (TREE_OPERAND (ref, 0)) + || TREE_SIDE_EFFECTS (TREE_OPERAND (ref, 1))) + return clk_none; op1_lvalue_kind = lvalue_p_1 (TREE_OPERAND (ref, 0), treat_class_rvalues_as_lvalues); op2_lvalue_kind = lvalue_p_1 (TREE_OPERAND (ref, 1), diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 990352d492d..164f7a34a71 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -1524,6 +1524,9 @@ rationalize_conditional_expr (enum tree_code code, tree t) are equal, so we know what conditional expression this used to be. */ if (TREE_CODE (t) == MIN_EXPR || TREE_CODE (t) == MAX_EXPR) { + /* The following code is incorrect if either operand side-effects. */ + gcc_assert (!TREE_SIDE_EFFECTS (TREE_OPERAND (t, 0)) + && !TREE_SIDE_EFFECTS (TREE_OPERAND (t, 1))); return build_conditional_expr (build_x_binary_op ((TREE_CODE (t) == MIN_EXPR ? LE_EXPR : GE_EXPR), @@ -5038,6 +5041,25 @@ build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs) return error_mark_node; return build2 (COMPOUND_EXPR, lhstype, lhs, newrhs); + case MIN_EXPR: + case MAX_EXPR: + /* MIN_EXPR and MAX_EXPR are currently only permitted as lvalues, + when neither operand has side-effects. */ + if (!lvalue_or_else (lhs, "assignment")) + return error_mark_node; + + gcc_assert (!TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 0)) + && !TREE_SIDE_EFFECTS (TREE_OPERAND (lhs, 1))); + + lhs = build3 (COND_EXPR, TREE_TYPE (lhs), + build2 (TREE_CODE (lhs) == MIN_EXPR ? LE_EXPR : GE_EXPR, + boolean_type_node, + TREE_OPERAND (lhs, 0), + TREE_OPERAND (lhs, 1)), + TREE_OPERAND (lhs, 0), + TREE_OPERAND (lhs, 1)); + /* Fall through. */ + /* Handle (a ? b : c) used as an "lvalue". */ case COND_EXPR: { diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e64aafb22f5..c52f7ba68dd 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2004-09-21 Roger Sayle + + PR c++/7503 + * g++.dg/opt/pr7503-1.C: New testcase for COND_EXPR lvalues. + * g++.dg/opt/pr7503-2.C: New testcase for ? lvalues. + * g++.dg/opt/pr7503-3.C: New testcase for invalid ?= assignments. + * g++.dg/opt/pr7503-5.C: New testcase for side-effects with PR fortran/17286 diff --git a/gcc/testsuite/g++.dg/opt/pr7503-1.C b/gcc/testsuite/g++.dg/opt/pr7503-1.C new file mode 100644 index 00000000000..d366a618030 --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/pr7503-1.C @@ -0,0 +1,148 @@ +// PR c++/7503 +// { dg-do run } +// { dg-options "-O2" } + +extern "C" void abort(); + +void test1a() +{ + int A = 4; + int B = 4; + + (A > B ? A : B) = 1; + if (A != 4 || B != 1) + abort (); +} + +void test1b() +{ + int A = 3; + int B = 5; + + (A > B ? A : B) = 1; + if (A != 3 || B != 1) + abort (); +} + +void test1c() +{ + int A = 5; + int B = 3; + + (A > B ? A : B) = 1; + if (A != 1 || B != 3) + abort (); +} + +void test2a() +{ + int A = 4; + int B = 4; + + (A >= B ? A : B) = 1; + if (A != 1 || B != 4) + abort (); +} + +void test2b() +{ + int A = 3; + int B = 5; + + (A >= B ? A : B) = 1; + if (A != 3 || B != 1) + abort (); +} + +void test2c() +{ + int A = 5; + int B = 3; + + (A >= B ? A : B) = 1; + if (A != 1 || B != 3) + abort (); +} + +void test3a() +{ + int A = 4; + int B = 4; + + (A < B ? A : B) = 1; + if (A != 4 || B != 1) + abort (); +} + +void test3b() +{ + int A = 3; + int B = 5; + + (A < B ? A : B) = 1; + if (A != 1 || B != 5) + abort (); +} + +void test3c() +{ + int A = 5; + int B = 3; + + (A < B ? A : B) = 1; + if (A != 5 || B != 1) + abort (); +} + +void test4a() +{ + int A = 4; + int B = 4; + + (A <= B ? A : B) = 1; + if (A != 1 || B != 4) + abort (); +} + +void test4b() +{ + int A = 3; + int B = 5; + + (A <= B ? A : B) = 1; + if (A != 1 || B != 5) + abort (); +} + +void test4c() +{ + int A = 5; + int B = 3; + + (A <= B ? A : B) = 1; + if (A != 5 || B != 1) + abort (); +} + + +int main() +{ + test1a(); + test1b(); + test1c(); + + test2a(); + test2b(); + test2c(); + + test3a(); + test3b(); + test3c(); + + test4a(); + test4b(); + test4c(); + + return 0; +} + diff --git a/gcc/testsuite/g++.dg/opt/pr7503-2.C b/gcc/testsuite/g++.dg/opt/pr7503-2.C new file mode 100644 index 00000000000..68bb143e45e --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/pr7503-2.C @@ -0,0 +1,79 @@ +// PR c++/7503 +// { dg-do run } +// { dg-options "-O2" } + +extern "C" void abort(); + +void test1a() +{ + int A = 4; + int B = 4; + + (A >? B) = 1; + if (A != 1 || B != 4) + abort (); +} + +void test1b() +{ + int A = 3; + int B = 5; + + (A >? B) = 1; + if (A != 3 || B != 1) + abort (); +} + +void test1c() +{ + int A = 5; + int B = 3; + + (A >? B) = 1; + if (A != 1 || B != 3) + abort (); +} + + +void test2a() +{ + int A = 4; + int B = 4; + + (A ? B) = 0; // { dg-error "non-lvalue in assignment" } +} + +void test4() +{ + (A >? B++) = 0; // { dg-error "non-lvalue in assignment" } +} + diff --git a/gcc/testsuite/g++.dg/opt/pr7503-4.C b/gcc/testsuite/g++.dg/opt/pr7503-4.C new file mode 100644 index 00000000000..06ac901229f --- /dev/null +++ b/gcc/testsuite/g++.dg/opt/pr7503-4.C @@ -0,0 +1,81 @@ +// PR c++/7503 +// { dg-do run } +// { dg-options "-O2" } + +extern "C" void abort(); + +void test1a() +{ + int A = 4; + int B = 4; + + A >?= B; + if (A != 4 || B != 4) + abort (); +} + +void test1b() +{ + int A = 3; + int B = 5; + + A >?= B; + if (A != 5 || B != 5) + abort (); +} + +void test1c() +{ + int A = 5; + int B = 3; + + A >?= B; + if (A != 5 || B != 3) + abort (); +} + + +void test2a() +{ + int A = 4; + int B = 4; + + A ?= B++; + if (A != 4 || B != 5) + abort (); +} + +void test1b() +{ + int A = 3; + int B = 5; + + A >?= B++; + if (A != 5 || B != 6) + abort (); +} + +void test1c() +{ + int A = 5; + int B = 3; + + A >?= B++; + if (A != 5 || B != 4) + abort (); +} + + +void test2a() +{ + int A = 4; + int B = 4; + + A