Description
Here's a contrived example:
use std::fmt::Debug;
trait Left<T> {}
trait Right<T> {}
trait Join<U> {
fn test();
}
// With the reordering,
// impl<T, U> Join<U> for T where T: Right<U>, T: Left<U>, U: Default + Debug {
// you'll get a different output
impl<T, U> Join<U> for T where T: Left<U>, T: Right<U>, U: Default + Debug {
fn test() {
println!("{:?}", U::default())
}
}
impl<T, U: Default + Debug> Left<U> for T {}
impl<T, U: Default + Debug> Right<U> for T {}
fn try_it<T: Default + Debug>() where T: Left<bool>, T: Right<()> {
<T as Join<_>>::test()
}
fn main() {
try_it::<u8>() // the type here is irrelevant
}
In the order given, the output is false
. If you swap the order as suggested in the comment, the output is ()
.
What's happening here is a combination of a few things. The Join
impl creates obligations in order of its where clauses, and the solution to each obligation informs inference, which affects future obligations. At the same time, when it comes time to show T: Left<U>
, for example, it can be proved either by virtue of the blanket impl or the where clause on try_it
. We currently give preference to the where clause, which then drives inference.
The same mechanisms also lead to cases where:
- Adding a valid where clause can cause code to stop compiling.
- Adding a where clause can change program behavior.
These issues seem fairly fundamental to the where clause preference approach, which is something we likely cannot change backwards compatibly (and there were good reasons for it in the first place). So it's possible that we will just have to live with these consequences. But I wanted to file a bug for posterity.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status