Skip to content

Adding a trait bound can change evaluation order of compound assignments. #146430

@theemathas

Description

@theemathas

I'm not sure if this is a bug or not.

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:

left
right
right
left

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-trait-systemArea: Trait systemC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language teamT-typesRelevant to the types team, which will review and decide on the PR/issue.needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions