generalize: handle occurs check failure in aliases by lcnr · Pull Request #117088 · rust-lang/rust (original) (raw)

This PR mostly fixes generalization for aliases, fixing #105787. It is therefore a (theoretical) breaking change.

Equating ?x == Alias<?x> previously failed. This is incomplete (and therefore allows overlaps via the implicit negative overlap check in coherence). This PR instead delays equality in this case, emitting a Projection goal. This projection goal then stalls until the projection can be normalized to a rigid type, in which case the occurs check is correct, or the projection cannot be normalized and stays rigid (due to a ParamEnv candidate).

It is non-trivial whether we can get trait solver overflow (in the old solver) for rigid projections, e.g.

trait Unnormalizable { type Assoc; }

struct Inv(*mut T);

fn unconstrained() -> T { todo!() }

fn create<T, U: Unnormalizable>( x: &U, ) -> (Inv, Inv<<U as Unnormalizable>::Assoc>) { todo!() }

fn foo<T: Unnormalizable + Unnormalizable, U, V>() { let q = unconstrained(); let (mut x, y) = create::<_, _>(&q); x = y; // equate ?x with <T as Unnormalizable<?x>>::Assoc drop::(q); }

However, projection goals only constrain the term to the projection itself if we were able to select a ParamEnv candidate. As we do not have non-lifetime binders, this only happens if the projection does not contain type inference variables.

I believe that you can get solver overflow with specialization and default associated items, but idc 😁

This only fixes problem case 2 of rust-lang/trait-system-refactor-initiative#8. However, this is by far the easiest way to get an incorrect occurs check failure (and the only one I bothered to write a coherence unsoundness test for). This only impacts the old solver for projections containing bound variables (as we otherwise eagerly normalize and replace the projection with an inference variable).

I consider this to 1) be a step in the right direction and 2) necessary for future work on the new trait solver.