trait solver: Capture binder region constraints while relating#157984
trait solver: Capture binder region constraints while relating#157984Dnreikronos wants to merge 8 commits into
Conversation
|
r? @mejrs rustbot has assigned @mejrs. Use Why was this reviewer chosen?The reviewer was selected based on:
|
This comment has been minimized.
This comment has been minimized.
f411fec to
d78cef7
Compare
|
r? @lcnr |
|
r? BoxyUwU |
|
|
This comment has been minimized.
This comment has been minimized.
|
@BoxyUwU fwiw, i think the ci failure is still compatible with your suggestion. the direct registration path lgtm to me, but the current version is probably registering from too many relation contexts. the bad bit seems to be solverrelating::regions(): under -zassumptions-on-binders it always pushes regionoutlives into the solver constraint storage. some of those relation calls happen under enter_forall_with_empty_assumptions, so later placeholder handling has no assumptions to discharge the constraint and we hit the max_universe(...) < u assert. imo the fix is not to go back to manually bubbling a list everywhere. i'd rather keep direct registration, but only in contexts where the solver owns the binder assumptions, or keep the constraints local while relating a binder and pull them out before registering. idk which shape is cleaner yet, but unconditional registration in regions() seems too wide. |
|
follow-up after pushing 0d93b4c: i tried to keep the direct-registration shape, but idk, it still looked too broad in practice. buffering constraints around binder relation did not clear the reported failures for me. imo the safer fix is to keep the region constraints local while relating, then register the collected next-gen constraint only from the solver-owned relation entrypoints. ltm this still follows the direction boxy was pointing at, without letting solverrelating::regions() register constraints from the empty-assumption binder probes. i checked the same failure cases locally:
|
in theory that shouldn't cause us to hit an assert. if we have empty assumptions then we'll rewrite the constraints to i would like to fully understand what's going on with the ICEs when always registering the next gen region constraints :3 can you look more into what's going on there, if you could push the code that causes those ICEs to happen so I can see the CI failure too that would be useful :3 |
|
i looked into the unconditional direct-registration version locally. imo it confirms the failure mode from my earlier guess: the new repro tests pass, but the broader assumptions-on-binders set fails. specifically:
so idk, direct registration from i can push the failing experiment to a separate branch if seeing ci on that exact shape would help, but i'd rather not force-push it over this pr branch since the current version is passing. checked with:
|
|
like I said, I don't understand why it's hitting that assertion 😅 can you explain why that assertion is getting hit. i can't properly evaluate this alternate approach without understanding why the other one doesn't work |
This comment has been minimized.
This comment has been minimized.
|
yeah, my bad! i think i explained the empty-assumptions bit too confidently there. i pushed the direct registration version again so ci can show the actual failure: 31be28b idk yet why it ends up at the |
This comment has been minimized.
This comment has been minimized.
31be28b to
0837d0c
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. |
This comment has been minimized.
This comment has been minimized.
0837d0c to
83c5653
Compare
This comment has been minimized.
This comment has been minimized.
|
@rustbot author gonna do that while you figure out what's going on there, if you're having trouble we can chat about it over zulip and try figure it out together |
|
Reminder, once the PR becomes ready for a review, use |
This comment has been minimized.
This comment has been minimized.
|
@rustbot ready |
|
|
||
| // Do this before rewriting type outlives constraints: alias/env matching below needs to | ||
| // see placeholders equated with current-universe region variables in the same conjunction. | ||
| let constraint = normalize_equated_region_vars(infcx, constraint, u); |
There was a problem hiding this comment.
im having a hard time understanding what motivated this change 🤔 can you give an example with:
- an alias outlives assumption
- the alias being matched against it
- what the region constraints we get are
- why those region constraints are mishandled in some way if we dont do this line of code
- what region constraints we get instead if we do this line of code
There was a problem hiding this comment.
fyi I checked this with a local trace on tests/ui/assumptions_on_binders/alias_outlives.rs, and I think I have a better handle on what is happening now.
The passing case has this env assumption:
<T as AliasHaver>::Assoc: 'staticTo prove T::Assoc: for<'a> Trait<'a>, we enter the binder and prove T::Assoc: Trait<'!a_u1>. Selecting the blanket impl introduces an impl lifetime var, '?0 in my trace.
Before this normalization, the relevant constraint was:
RegionOutlives('!1_...ReqTrait::'a, '?0)
RegionOutlives('?0, '!1_...ReqTrait::'a)
AliasTyOutlivesViaEnv(<T as AliasHaver>::Assoc: '?0)
PlaceholderTyOutlives(!0, '?0)
So the alias being matched is <T as AliasHaver>::Assoc, but its outlives target is '?0, not the placeholder directly.
After normalization, the same constraint becomes:
RegionOutlives('!1_...ReqTrait::'a, '!1_...ReqTrait::'a)
RegionOutlives('!1_...ReqTrait::'a, '!1_...ReqTrait::'a)
AliasTyOutlivesViaEnv(<T as AliasHaver>::Assoc: '!1_...ReqTrait::'a)
PlaceholderTyOutlives(!0, '!1_...ReqTrait::'a)
Then matching against <T as AliasHaver>::Assoc: 'static gives:
RegionOutlives('static, '!1_...ReqTrait::'a)
For the failing case, where the env only has <T as AliasHaver>::Assoc: 'a, it gives:
RegionOutlives('a/#0, '!1_...ReqTrait::'a)
and that stays unsatisfied, which is what we want.
Without this normalization, the trace keeps the alias target as '?0, and REGIONCK_ENV_PASS fails with:
error[E0283]: type annotations needed: cannot satisfy `for<'a> <T as AliasHaver>::Assoc: Trait<'a>`
My read is that alias/env matching gets here before the later region closure pass. We already know '?0 == '!a_u1 in the same And branch, but unless we make that visible first, the alias outlives path sees a current-universe var instead of the placeholder shape it needs.
idk if this is the nicest layer for it, but the trace made the motivation a lot clearer to me. ltm if you want the raw trace bits and I can paste them too.
| let assumptions = infcx.get_placeholder_assumptions(u); | ||
|
|
||
| // Do this before rewriting type outlives constraints: alias/env matching below needs to | ||
| // see placeholders equated with current-universe region variables in the same conjunction. |
There was a problem hiding this comment.
also what's a conjunction :3
There was a problem hiding this comment.
By conjunction I meant one And([...]) branch, so the constraints that are all known to hold together.
btw yeah, that wording is not great. I can update it asap to say this instead:
// Do this before rewriting type outlives constraints: alias/env matching below needs to
// see placeholders equated with current-universe region variables in the same `And` branch.imo that is clearer than conjunction here.
View all comments
Fixes #157859
The issue repro is a pretty good example of how this slipped through: the principal upcast path looked fixed, but other relation paths could still drop binder-region constraints. Fwiw, I think centralizing this in the relation code is the cleaner move here, instead of chasing every object-upcast call site one by one.
This records NextGen region constraints from normal relation, structural alias equality, and the small goal-returning relation helper used by object candidate code. ReVars keep their concrete outlives edges too, so we don't flatten useful info into plain ambiguity. Added the original issue repro and a projection-bound variant, e.g. the kind of path I'd expect to regress later if this only lived in the principal upcast code.