Skip to content

Commit b6f9705

Browse files
committed
Add test for eval order for a+=b
Yes, the order of evaluation *does* change depending on the types of the operands. Cursed, I know. I've elected to place this test into `expr/compound-assignment` creating both the `expr` directory and the `compound-assignment` directory. I plan in a future PR to also move the `if` directory and the loose `if` tests into `expr/if` and other similar cleanups of the `test/ui` directory. Future work: Test more than just `+=`, but all operators. I don't know if using a macro to generate these tests cases would be okay or not, but it'd be boilerplatey without it. I'm also confident you cannot change the evaluation order of one operator without changing all of them. Future work: Additionally, test more than just `i32 += i32` for the primitive version. I don't actually know the full set of primitive implementations, but I imagine there's enough to cause a combinatorial explosion with the previous future work item. Somewhere on the order of one to two hundred individual functions.
1 parent 7009011 commit b6f9705

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Test evaluation order of operands of the compound assignment operators
2+
3+
// run-pass
4+
5+
use std::ops::AddAssign;
6+
7+
enum Side {
8+
Lhs,
9+
Rhs,
10+
}
11+
12+
// In the following tests, we place our value into a wrapper type so that we
13+
// can do an element access as the outer place expression. If we just had the
14+
// block expression, it'd be a value expression and not compile.
15+
struct Wrapper<T>(T);
16+
17+
// Evaluation order for `a op= b` where typeof(a) and typeof(b) are primitives
18+
// is first `b` then `a`.
19+
fn primitive_compound() {
20+
let mut side_order = vec![];
21+
let mut int = Wrapper(0);
22+
23+
{
24+
side_order.push(Side::Lhs);
25+
int
26+
}.0 += {
27+
side_order.push(Side::Rhs);
28+
0
29+
};
30+
31+
assert!(matches!(side_order[..], [Side::Rhs, Side::Lhs]));
32+
}
33+
34+
// Evaluation order for `a op=b` otherwise is first `a` then `b`.
35+
fn generic_compound<T: AddAssign<T> + Default>() {
36+
let mut side_order = vec![];
37+
let mut add_assignable: Wrapper<T> = Wrapper(Default::default());
38+
39+
{
40+
side_order.push(Side::Lhs);
41+
add_assignable
42+
}.0 += {
43+
side_order.push(Side::Rhs);
44+
Default::default()
45+
};
46+
47+
assert!(matches!(side_order[..], [Side::Lhs, Side::Rhs]));
48+
}
49+
50+
fn custom_compound() {
51+
struct Custom;
52+
53+
impl AddAssign<()> for Custom {
54+
fn add_assign(&mut self, _: ()) {
55+
// this block purposely left blank
56+
}
57+
}
58+
59+
let mut side_order = vec![];
60+
let mut custom = Wrapper(Custom);
61+
62+
{
63+
side_order.push(Side::Lhs);
64+
custom
65+
}.0 += {
66+
side_order.push(Side::Rhs);
67+
};
68+
69+
assert!(matches!(side_order[..], [Side::Lhs, Side::Rhs]));
70+
}
71+
72+
fn main() {
73+
primitive_compound();
74+
generic_compound::<i32>();
75+
custom_compound();
76+
}

0 commit comments

Comments
 (0)