struct A<T: ?Sized>(*const T);
struct B<T: ?Sized>(*const T);
struct C<T: ?Sized>(*const T);
impl<T: ?Sized> Unpin for A<T>
where
B<T>: Unpin,
B<T>: Inductive,
{}
impl<T: ?Sized> Unpin for B<T>
where
A<T>: Unpin,
{}
trait Inductive {}
impl<T: ?Sized + Unpin> Inductive for T {}
fn is_unpin<T: Unpin>() {}
fn main() {
is_unpin::<A<()>>();
}
This test should fail as A: Unpin -> B: Inductive -> B: Unpin -> A: Unpin is an inductive cycle. However, the old solver reuses the provisional cache entry of the coinductive A: Unpin -> B: Unpin -> A: Unpin cycle for B: Unpin. Changing the order of where clauses on the A<T>: Unpin impl causes this test to correctly The only place we remove provisional entries is fn on_failure or when completely done with this cycle. Idk if and how you'd get an unsoundness from that, but this explains why the old solver is fast when handling complex auto trait cycles.
This test should fail as
A: Unpin -> B: Inductive -> B: Unpin -> A: Unpin
is an inductive cycle. However, the old solver reuses the provisional cache entry of the coinductiveA: Unpin -> B: Unpin -> A: Unpin
cycle forB: Unpin
. Changing the order of where clauses on theA<T>: Unpin
impl causes this test to correctly The only place we remove provisional entries isfn on_failure
or when completely done with this cycle. Idk if and how you'd get an unsoundness from that, but this explains why the old solver is fast when handling complex auto trait cycles.It does hang during coherence when computing intercrate ambiguity causes. It recursively evaluates nested goals, but still returns
Ok(None)
, clearing the provisional cache.