trait_selection: Keep type-op region constraints in borrowck - #161423
trait_selection: Keep type-op region constraints in borrowck#161423Dnreikronos wants to merge 6 commits into
Conversation
Keep rust-lang#158588 focused on reporting solver region constraints. The type-op behavior and its borrowck coverage now live in rust-lang#161423.
|
r? @davidtwco rustbot has assigned @davidtwco. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
r? BoxyUwU |
|
I think this is a partial revert of #160982 which we don't want. I think that teaching @rustbot author |
|
Reminder, once the PR becomes ready for a review, use |
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
…tions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes rust-lang#157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the rust-lang#157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into rust-lang#161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
Rollup merge of #158588 - Dnreikronos:trait_selection/assumptions_binders_diagnostics, r=BoxyUwU trait_selection: fix assumptions-on-binders diagnostics fixes #157732 `-Zassumptions-on-binders` was losing the origin of solver region constraints. By the time regionck or borrowck reported them, every constraint received the containing item span, so diagnostics pointed at an entire function or const. Some paths were also still emitting placeholder text (`:3` and `meoow :c`). This keeps canonical solver responses and `ExternalConstraintsData` span-free, so source locations do not participate in candidate equality or caching. When a response is applied, `EvalCtxt::origin_span` is attached to each atomic solver `RegionConstraint` stored in `InferCtxt`. Late region conversion then uses the span attached to each constraint, including for ambiguity diagnostics. The affected paths now report `higher-ranked lifetime bound could not be satisfied` at the type use that introduced the failing constraint. UI coverage checks the #157732 call-site diagnostic and the existing regionck alias-outlives case. Unit coverage checks that the spanned evaluator stays in semantic parity with the type-ir evaluator and preserves the first ambiguity origin span. The type-op behavior I ran into while working on this is split into #161423. Future work: carry the failed outlives predicate or originating binder far enough through this path to name the exact bound that failed, rather than only pointing at its origin.
65f9dd2 to
2959d13
Compare
This comment has been minimized.
This comment has been minimized.
|
Sup,@BoxyUwU :) Yeah, The next solver was storing the tree in the temporary query I also found another place dropping the same tree during implied-bound normalization, before lexical regionck. I fixed that too and added regressions for that path and the borrowck case. I like this version a lot more. It fixes the point where the data was lost and keeps the cache behavior from #160982. The local version worked, but mostly by going around the problem, and that felt wrong once I understood the full path. @rustbot ready |
This comment has been minimized.
This comment has been minimized.
2959d13 to
dc2e6c6
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
|
||
| /// Runs `op` with an empty solver-region-constraint store, restores the | ||
| /// caller's constraints, and returns the constraints produced by `op`. | ||
| pub fn with_fresh_solver_region_constraints<R>( |
There was a problem hiding this comment.
why is this necessary for new style constraints but not old style?
There was a problem hiding this comment.
The difference is that the old obligations and assumptions are taken before the op and checked to be empty, so everything collected afterward belongs to that op. We cannot make that same check for the solver tree. Borrowck may already have constraints from an earlier type op in the same InferCtxt, and those stay there until the end of typeck. If we took the full tree afterward, this response would also contain the caller's older constraints. Clearing it first would lose them. The helper parks the old tree, captures what this op made, then restores the old one.
I do not love the extra swap, but I think this part is a real difference in how the constraints are stored and when they are consumed.
| /// discharge a requirement from another query, which is a potential problem if we did throw | ||
| /// away these assumptions because there were no constraints. | ||
| pub fn is_empty(&self) -> bool { | ||
| let QueryRegionConstraints { constraints, assumptions } = self; |
There was a problem hiding this comment.
the destructuring here is intentional. it means that adding new fields doesn't silently keep compiling and doing the wrong thing. please keep the let QueryRegionConstraints :3
There was a problem hiding this comment.
Yep, put the full destructuring back in is_empty and extend, and removed the .. from the implied-bound path too. This PR adding solver_constraints is exactly the kind of change that match was meant to catch, so keeping it exhaustive makes sense.
| ®ion_constraint_data, | ||
| region_assumptions, | ||
| ); | ||
| region_constraints.solver_constraints = solver_constraints; |
There was a problem hiding this comment.
this generally feels kinda off, why doesn't this just work the same way as or region_obligations/region_assumptions?
There was a problem hiding this comment.
Yeah, agreed. I changed make_query_region_constraints to take the solver tree together with region_obligations and region_assumptions. The normal query path and scrape_region_constraints now build QueryRegionConstraints through the same function, and callers that cannot produce a solver tree pass the empty one.
The temporary swap is still needed so we do not mix a solver tree already registered by the caller into this op's response, but the query response side no longer bolts the field on afterward. I think this reads much better.
| // outlives bound required proving some higher-ranked coroutine obl. | ||
| let QueryRegionConstraints { constraints, assumptions: _ } = constraints; | ||
| let QueryRegionConstraints { constraints, solver_constraints, .. } = constraints; | ||
| if !solver_constraints.is_true() { |
There was a problem hiding this comment.
what's with all of these checks for !constraint.is_true() is this necessary or just a perf opt? if it's a perf opt I'd prefer we drop it.
There was a problem hiding this comment.
The checks do matter with the current representation, but putting them at every caller was the wrong place. true is And([]). Pushing that into the store turns it into And([And([])]); it still means true, but is_true() only recognizes the empty outer And, so an empty query starts looking nonempty.
I moved that check into the store instead. Registering a trivial tree now does nothing and does not add an undo-log entry, so the callers can register unconditionally. There is a unit test for the empty-query case too.
|
@rustbot author |
|
Some changes occurred to the core trait solver cc @rust-lang/initiative-trait-system-refactor |
|
@rustbot ready |
I ran into this while working on #158588.
borrowck_env_failstill had a FIXME because the function body wasn't reporting the outlives error. The next solver creates the region constraint, but the canonical type-op path doesn't put it inQueryResponse. Borrowck never sees it, so the type op looks fine and the error is lost.With
-Zassumptions-on-binders, these type ops now run locally on borrowck'sInferCtxt. The fast path still runs first. That leaves the constraint in the same inference context borrowck reads later.I prefer this over adding more data to the old canonical response. The next solver already caches its work, and teaching the old query path about these constraints felt like extra machinery for something we can avoid. Running locally is pretty boring, but I think that's a good thing here. The old FIXME is now the regression test.