Understanding a Trait Selection Bug
2024-Apr-21
Recently, I ran into rust-lang/issues/#24066 and spent some time analysing the underlying trait selection mechanism. In this blog post, I’ll briefly explain why this bug prevents the following snippet from compiling.
fn add<T>() where u32: std::ops::Add<T, Output = T> { let _ = 1_u32 + 1_u32; // error[E0308]: mismatched types }
Inside the compiler, the rustc_hir_typeck
crate handles lookup of methods for binary operations. When it encounters the expression 1_u32 + 1_u32
, it does something peculiar: instead of directly looking up for a method that adds an u32
to another u32
, it searches for a method that can add an u32
to a type inference variable $0
. This allows the compiler to either borrow or take ownership of the value on the RHS of the binary expression based on the available methods1.
The request “find a method to add an u32
with the type inference variable $0
” is handled by the rustc_trait_selection
crate, which selects an appropriate method from a collection of candidates. Here, traits specified in the where clause are given a higher precedence over other candidates2. I believe this heuristic is followed as the programmer is expected to specify only the necessary traits in the where clause.
Thus, for the expression 1_u32 + 1_u32
, a method which adds an u32
and a type variable T
is chosen because it’s mentioned in the where clause and T
unifies with the type inference variable $0
. This attempts to unify the u32
on the RHS with type variable T
, resulting in a type mismatch error.
Guess, this is an other instance where a compiler leans towards soundness over completeness.