Hi! The following testcase is rejected, because due to the C++17 b @= a ordering of side-effects cp_stabilize_reference is called on the lhs of the compound assignment. For some cases cp_stabilize_reference just uses stabilize_reference, but for other cases it attempts to bind a reference to the expression. This doesn't work for bit-fields and DECL_PACKED fields though, we can't take address of a bit-field (not DECL_PACKED field) and error on that. First I was thinking about just adjusting the expression so that it is a reference to the structure which contains the bit-field and make the whole thing COMPONENT_REF of that, but there doesn't have to be just one such struct, there can be many and each one having different bit-field. So, this patch introduces for this another wrapper around stabilize_reference, which for clk_bitfield | clk_packed handles some trees stabilize_reference doesn't handle correctly for C++, and for the rest defers to stabilize_reference. This way, we can introduce multiple SAVE_EXPRs (like stabilize_expr itself already can as well), but can handle even the weirdest lhs expressions for which lvalue_kind returns clk_bitfield or clk_packed set.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk and later on release branches? 2025-11-22 Jakub Jelinek <[email protected]> PR c++/122772 * tree.cc (cp_stabilize_bitfield_reference): New function. (cp_stabilize_reference): Use it for stabilization of clk_bitfield or clk_packed lvalues. * g++.dg/cpp1z/eval-order14.C: New test. --- gcc/cp/tree.cc.jj 2025-11-21 07:11:58.801048029 +0100 +++ gcc/cp/tree.cc 2025-11-21 08:50:11.379813157 +0100 @@ -424,6 +424,83 @@ non_mergeable_glvalue_p (const_tree ref) && !(kind & (clk_class|clk_mergeable))); } +/* C++-specific version of stabilize_reference for bit-fields and + DECL_PACKED fields. We can't bind a reference to those. */ + +static tree +cp_stabilize_bitfield_reference (tree ref) +{ + tree op1, op2, op3; + STRIP_ANY_LOCATION_WRAPPER (ref); + switch (TREE_CODE (ref)) + { + case VAR_DECL: + case PARM_DECL: + case RESULT_DECL: + CASE_CONVERT: + case FLOAT_EXPR: + case FIX_TRUNC_EXPR: + case INDIRECT_REF: + case COMPONENT_REF: + case BIT_FIELD_REF: + case ARRAY_REF: + case ARRAY_RANGE_REF: + case ERROR_MARK: + case REALPART_EXPR: + case IMAGPART_EXPR: + default: + break; + case COMPOUND_EXPR: + op2 = cp_stabilize_bitfield_reference (TREE_OPERAND (ref, 1)); + if (op2 == TREE_OPERAND (ref, 1)) + return ref; + op1 = TREE_OPERAND (ref, 0); + if (TREE_SIDE_EFFECTS (op1)) + { + if (VOID_TYPE_P (TREE_TYPE (op1))) + op1 = save_expr (op1); + else + { + op1 = build2 (COMPOUND_EXPR, void_type_node, op1, + void_node); + op1 = save_expr (op1); + } + } + return build2 (COMPOUND_EXPR, TREE_TYPE (op2), op1, op2); + case COND_EXPR: + op1 = TREE_OPERAND (ref, 0); + op2 = TREE_OPERAND (ref, 1); + op3 = TREE_OPERAND (ref, 2); + if (op2 && TREE_CODE (op2) != THROW_EXPR) + op2 = cp_stabilize_bitfield_reference (op2); + if (TREE_CODE (op3) != THROW_EXPR) + op3 = cp_stabilize_bitfield_reference (op3); + if (op2 == NULL_TREE) + op1 = cp_stabilize_bitfield_reference (op1); + if (op1 == TREE_OPERAND (ref, 0) + && op2 == TREE_OPERAND (ref, 1) + && op3 == TREE_OPERAND (ref, 2)) + return ref; + if (op2 != NULL_TREE && TREE_SIDE_EFFECTS (op1)) + op1 = save_expr (op1); + return build3 (COND_EXPR, TREE_TYPE (ref), op1, op2, op3); + case PREINCREMENT_EXPR: + case PREDECREMENT_EXPR: + op1 = cp_stabilize_bitfield_reference (TREE_OPERAND (ref, 0)); + if (op1 == TREE_OPERAND (ref, 0)) + return ref; + return build2 (COMPOUND_EXPR, TREE_TYPE (ref), + build2 (TREE_CODE (ref), TREE_TYPE (ref), op1, + TREE_OPERAND (ref, 0)), op1); + case PAREN_EXPR: + op1 = cp_stabilize_bitfield_reference (TREE_OPERAND (ref, 0)); + if (op1 == TREE_OPERAND (ref, 0)) + return ref; + return build1 (PAREN_EXPR, TREE_TYPE (ref), op1); + } + return stabilize_reference (ref); +} + /* C++-specific version of stabilize_reference. */ tree @@ -455,6 +532,8 @@ cp_stabilize_reference (tree ref) cp_lvalue_kind kind = lvalue_kind (ref); if ((kind & ~clk_class) != clk_none) { + if (kind & (clk_bitfield | clk_packed)) + return cp_stabilize_bitfield_reference (ref); tree type = unlowered_expr_type (ref); bool rval = !!(kind & clk_rvalueref); type = cp_build_reference_type (type, rval); --- gcc/testsuite/g++.dg/cpp1z/eval-order14.C.jj 2025-11-21 09:28:32.520256080 +0100 +++ gcc/testsuite/g++.dg/cpp1z/eval-order14.C 2025-11-21 09:18:54.011356446 +0100 @@ -0,0 +1,114 @@ +// PR c++/122772 +// { dg-do run } + +struct S { int y : 7; } s; +struct T { int x; int z : 7; } t; +int cnt, mode; + +int +foo () +{ +#if __cplusplus >= 201703L + if (cnt != 1 + (mode & 1)) + __builtin_abort (); +#endif + ++cnt; + return 2; +} + +int +bar () +{ +#if __cplusplus >= 201703L + if (cnt != 0) + __builtin_abort (); +#endif + ++cnt; + return 40; +} + +S & +baz () +{ +#if __cplusplus >= 201703L + if (cnt != 2 + (mode & 1)) + __builtin_abort (); +#endif + if (mode & 2) + __builtin_abort (); + ++cnt; + return s; +} + +T & +qux () +{ +#if __cplusplus >= 201703L + if (cnt != 2 + (mode & 1)) + __builtin_abort (); +#endif + if ((mode & 2) == 0) + __builtin_abort (); + ++cnt; + return t; +} + +bool +fred (bool x) +{ +#if __cplusplus >= 201703L + if (cnt != 1) + __builtin_abort (); +#endif + ++cnt; + return x; +} + +void +plugh (bool b) +{ + S x; + x.y = 5; + mode = 0; + cnt = 0; + (x.y = foo ()) += bar (); + if (cnt != 2 || x.y != 42) + __builtin_abort (); + x.y = 5; + cnt = 0; + (baz ().y = foo ()) += bar (); + if (cnt != 3 || s.y != 42) + __builtin_abort (); + s.y = 5; + mode = (b ? 0 : 2); + cnt = 0; + (b ? (baz ().y = foo ()) : (qux ().z = foo ())) += bar (); + if (cnt != 3 || (b ? (s.y != 42) : (t.z != 42))) + __builtin_abort (); + s.y = 5; + t.z = 5; + mode |= 1; + cnt = 0; + (fred (b) ? (baz ().y = foo ()) : (qux ().z = foo ())) += bar (); + if (cnt != 4 || (b ? (s.y != 42) : (t.z != 42))) + __builtin_abort (); + s.y = 5; + t.z = 5; + mode = 0; + cnt = 0; + ++x.y += bar (); + if (cnt != 1 || x.y != 46) + __builtin_abort (); + cnt = 0; + x.y = 9; + --x.y += bar (); + if (cnt != 1 || x.y != 48) + __builtin_abort (); +} + +int +main () +{ + plugh (false); + plugh (true); +} Jakub
