mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-22 02:00:00 +03:00
29b7590130
-Zassumptions-on-binders r? lcnr cc https://rust-lang.github.io/rust-project-goals/2026/assumptions_on_binders.html I would cc a tracking issue but the project goals haven't been finalized yet ^^' Implements `-Zassumptions-on-binders`. This has a few main components: 1. We introduce a new form of region constraints for use by the trait solver which supports ORs 2. When entering binders universally inside of the trait solver we walk the bound thing and compute a list of region assumptions. We then track in the `InferCtxt` all the region outlives and type outlives mentioning a placeholder from the binder for use when handling constraints involving placeholders 3. Ideally when exiting a binder, but currently actually when computing a response inside the solver, we look through all of region constraints involving placeholders and eagerly handle these region constraints instead of returning them to the caller This is very much a first-draft impl (though it is vaguely functional), there's a lot we need to change going forwards: - We should really be using this new form of region constraint everywhere/more generally we shouldnt have two kinds of region constraints - We shouldn't be computing implied bounds when entering binders, instead they should be explicit everywhere and actually checked when instantiating binders. As-is `-Zassumptions-on-binders` probably widens existing soundness holes around implied bounds due to having significantly more implied bounds - We should be eagerly handling placeholders *everywhere* not just inside of the trait solver. Right now there will still be missing assumptions for placeholders when we do higher ranked type relations during type checking outside of the trait solver. - I'm not normalizing our assumptions or our constraints and we should be doing both ✨ - Handling of alias outlives' involving placeholders is incomplete in a number of ways - We should handle placeholders when leaving binders not when computing responses IMO - Actually support OR region constraints in borrow checking and region checking ✨ right now all our OR constraints are converted into normal ANDed region constraints in root contexts. - Right now diagnostics just point to the whole item which the unsatisfied constraints came from. this is suboptimal! fix this! - Move universe information into InferCtxtInner so it can be rolled back by probes - we should make some kind of test suite helper so we can directly write universal/existential quantifiers and assumptions rather than having to go through rust syntax :') How do we actually eagerly handle constraints? The general idea is that we have some function (`eagerly_handle_placeholders_in_universe`) which takes: - A set of region constraints - The universe which we want to eagerly handle constraints in - A set of outlives assumptions associated with that universe/binder This function will rewrite all of the region constraints which involve placeholders from the passed in universe to be in terms of variables from smaller universes (or drop the constraints if we know them to be satisfied). For example: ```rust for<'a> where('a: 'b) { prove ('a: 'c) } ``` when exiting `for<'a>` we want to handle the `'!a: 'c` constraint *somehow* and we can do that by requiring that *any* of the lifetimes which `'!a` outlives, themselves outlive `'c`. In this case we can require `Or('b: 'c)` and instead of `'!a: 'c` which gives us a constraint that makes sense after exiting the forall. some more examples: ```rust for<'a> where('a: 'b, 'a: 'c) { prove('a: 'd) } // rewritten to Or('b: 'd, 'c: 'd) for<'a> where('b: 'a) { prove(T: 'a) } // rewritten to Or(T: 'b) ``` The tricky thing here is that we want/need to avoid the trait solver knowing about *all* type outlives/region outlives assumptions. So this algorithm is implemented with only knowing about the assumptions coming from the binder that is being exited. We want to avoid passing all outlives assumptions through the trait solver for two main reasons. The first is just perf, augmenting the `ParamEnv` with significant amounts of outlives assumptions could easily mess up caching. The second is that it's Not Possible™️ to implement. Type checking requires trait solving inside of a closure which still has an uninferred signature, this means that there are some set of implied bounds that we just don't know about yet because we only know some inference variable is well formed and nothing else. --- Long term we should be able to wholly rip out placeholder handling from borrow checking and we won't ever encounter placeholders outside of the binders they were produced from.