On 11/22/25 1:48 PM, Jakub Jelinek wrote:
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.
I don't understand the "but"; there can only be one bit-field in a
COMPONENT_REF chain, and it should be fine to bind a reference to the
last struct. There are no bit-fields of class type.
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