I'm not sure if this is a bug or not.
This is a consequence of #24066, and uses the same setup as #149234
trait ToI32 {
type I32: Default + std::ops::AddAssign;
}
impl<T> ToI32 for T {
type I32 = i32;
}
fn make<U: Default>(msg: &str) -> U {
println!("{msg}");
U::default()
}
fn foo<T: ToI32>() {
*&mut make::<<T as ToI32>::I32>("left") += make::<<T as ToI32>::I32>("right");
}
fn bar<T>() {
*&mut make::<<T as ToI32>::I32>("left") += make::<<T as ToI32>::I32>("right");
}
fn main() {
foo::<()>();
bar::<()>();
}
I expected foo and bar to behave the same way. Instead, I got the following output:
It is documented in the reference that the evaluation order of compound assignment expressions depends on whether the types involved are primitives or not. That is, for primitives, the right operand is evaluated first, but for other types, the left operand is evaluated first.
It seems that adding a ToI32 trait bound causes the compiler to no longer see that the type involved is i32, causing the evaluation order to change.
Meta
Reproducible on the playground with version 1.91.0-nightly (2025-09-10 565a9ca63e9df4b223fe)
I'm not sure if this is a bug or not.
This is a consequence of #24066, and uses the same setup as #149234
I expected
fooandbarto behave the same way. Instead, I got the following output:It is documented in the reference that the evaluation order of compound assignment expressions depends on whether the types involved are primitives or not. That is, for primitives, the right operand is evaluated first, but for other types, the left operand is evaluated first.
It seems that adding a
ToI32trait bound causes the compiler to no longer see that the type involved isi32, causing the evaluation order to change.Meta
Reproducible on the playground with version
1.91.0-nightly (2025-09-10 565a9ca63e9df4b223fe)