trait_selection: Preserve eager normalization failures - #162618
trait_selection: Preserve eager normalization failures#162618Dnreikronos wants to merge 3 commits into
Conversation
The fallback normalization path can build a value from fresh inference variables and return their obligations separately. Fulfillment may later constrain those variables without rebuilding the folded value. Resolve the value after fulfillment so deep normalization does not return inference nodes that the context has already constrained.
The failure is only observable through a debug assertion because the caller resolves the value before returning. Run the test only with compiler debug assertions enabled. Use a const function and an intentionally invalid body to reach the MIR normalization path in metadata-only UI tests.
|
The job Click to see the possible cause of the failure (guessed by this bot) |
|
sgtm, this is now 2 unrelated changes, is it?
I think we should do both. Generally try to only do changes in the same PR if they build on each other |
|
ah, with the |
Yeah, I can split them. My thinking was that once we kept the eager failure, we already had an error to report, so I dropped the extra obligations for higher-ranked aliases in the fallback. That's why I bundled the two changes. I'd prefer to keep this one focused on normalize_with_universes returning Err and work through the visitor behavior separately.
yep, I removed too much there. I figured keeping the eager failure gave us the error we needed, so I dropped the higher-ranked projection obligations during recovery ;/. But that first failure doesn't cover every alias the visitor walks. I'll keep emitting the Projection obligation while leaving the alias in the returned type. I think handling that separately will make it easier to reason about, so I'll keep this PR focused on normalize_with_universes returning Err. |
Draft experiment following the discussion on #161407 and Zulip about preserving eager normalization failures.
The nested inherent associated type case fails during eager normalization, but the fallback can accept it. Eager normalization reduces the inner alias to
&'b (), then tries to equateFoo<for<'b> fn(&'b ())>with the impl'sFoo<fn(&'a ())>. The impl lifetime sits outside thefor<'b>binder, so that relation fails the leak check. The fallback retries the original nested aliases. That turns the lifetime equalities into constraints returned by nested goals, whichinstantiate_and_apply_query_responsemarksVisibleForLeakCheck::No. The parent leak check misses them and the fallback succeeds. Resolving the resulting inference variable at the end hides the rejected relation.I changed
normalize_with_universesto return aResultcarrying the failed obligation. Deep normalization returns that error immediately. The finalresolve_vars_if_possibleworkaround proposed in #161407 is absent here. The general policy for nested constraints stays as it is.For the infallible
normalizeentry point, I kept recovery inside the function for now. It catches the error, puts the failed obligation in the list, then runsReplaceAliasWithInferover the original value. The visitor still creates inference variables and obligations for aliases without escaping bound vars. For aliases with escaping bound vars, it leaves the alias in place and no longer creates placeholder obligations. Keeping the original failure means a successful fallback can't silently discard it.My thinking was to get deep normalization to report the failure while keeping the existing
normalizecallers working. I think making normalization fallible throughout is the better direction, with HIR typeck reporting the failure and replacing aliases withTyKind::Error. I haven't made those caller changes here. That's the part I'd like to settle before taking this further.