You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Assemble impl candidate first, which requires dyn Trait: Trait and dyn Trait: Impossible
We try to prove dyn Trait: Trait
Assemble object-bound candidate, which requires dyn Trait: Supertrait to hold
We see this is a stack-dependent overflow, so it gets EvaluatedToErrStackDependent
Assemble impl candidate, which requires dyn Trait: impossible, so it gets EvaluatedToErr
During winnowing, neither candiate holds, so we filter both of them, and return SelectionError::UnimplementedTHIS IS THE IMPORTANT PART
(at this point, we will assemble the object bound candidate, however we've already cached dyn Trait: Trait as ErrorGuaranteed, which causes the later goal to fail.)
Specifically, winnowing does not take into account stack dependence:
And so therefore it may inadvertently "downgrade" a result that may be stack dependent to one that isn't stack dependent by removing it from the list of candidates, meaning we return SelectionError::Unimplemented (since we now have 0 candidates).
Unfortunately, this is only hit when we actually do winnowing -- if there's only one candidate, then we won't winnow:
If #122791 weren't so close to landing, then I would probably instead fix this by changing winnowing:
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 1894fbba302..09d1df586c3 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs@@ -492,10 +492,16 @@ fn candidate_from_obligation_no_cache<'o>(
let mut candidates = candidates
.into_iter()
.map(|c| match self.evaluate_candidate(stack, &c) {
- Ok(eval) if eval.may_apply() => {+ // We CANNOT winnow `EvaluateToErrStackDependent`, since if we do,+ // then we may end up with NO candidates in this list. If that's+ // the case, then we will return `SelectionError::Unimplemented`,+ // which results in `EvaluatedToErr` which is NOT stack dependent.+ // This will cause us to incompletely cache a stack-dependent trait+ // result as `EvaluatedToErr`.+ Ok(EvaluatedToErr) => Ok(None),+ Ok(eval) => {
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
}
- Ok(_) => Ok(None),
Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)),
Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))),
})
This has probably failed since forever -- this example only fails since 1.49 which is when we started to enforce supertrait bounds hold (#73905) for object built-in candidates, but I wouldn't be surprised if you could construct an example which fails for even longer.
To clarify this a bit and to make sure I understand it correctly, the underlying issue here is the following afaict:
GoalA
CandA1
GoalB
CandB1
GoalA (inductive cycle -> error)
CandB1
Impossible (necessary to evaluate nested goals of CandB1 during selection)
CandA2 (trivially true)
when first evaluating GoalA, GoalB gets moved to the global cache as an error, evne though it only errors when used as a nested goal of GoalA. Later proving GoalB therefore fails.
Activity
compiler-errors commentedon Mar 31, 2024
cc @lcnr who may know if this issue already is tracked
compiler-errors commentedon Mar 31, 2024
Actually this is just because
EvaluatedToErrStackDependentis super scuffed. This will be fixed by #122791.compiler-errors commentedon Mar 31, 2024
So what happens here:
dyn Trait: Supertraitdyn Trait: Traitanddyn Trait: Impossibledyn Trait: Traitdyn Trait: Supertraitto holdEvaluatedToErrStackDependentdyn Trait: impossible, so it getsEvaluatedToErrSelectionError::UnimplementedTHIS IS THE IMPORTANT PARTdyn Trait: TraitasErrorGuaranteed, which causes the later goal to fail.)Specifically, winnowing does not take into account stack dependence:
rust/compiler/rustc_trait_selection/src/traits/select/mod.rs
Lines 495 to 497 in 8058136
And so therefore it may inadvertently "downgrade" a result that may be stack dependent to one that isn't stack dependent by removing it from the list of candidates, meaning we return
SelectionError::Unimplemented(since we now have 0 candidates).Unfortunately, this is only hit when we actually do winnowing -- if there's only one candidate, then we won't winnow:
rust/compiler/rustc_trait_selection/src/traits/select/mod.rs
Lines 472 to 488 in 8058136
So that's why we don't see this more often.
compiler-errors commentedon Mar 31, 2024
If #122791 weren't so close to landing, then I would probably instead fix this by changing winnowing:
compiler-errors commentedon Apr 1, 2024
This has probably failed since forever -- this example only fails since 1.49 which is when we started to enforce supertrait bounds hold (#73905) for object built-in candidates, but I wouldn't be surprised if you could construct an example which fails for even longer.
lcnr commentedon Apr 2, 2024
To clarify this a bit and to make sure I understand it correctly, the underlying issue here is the following afaict:
CandB1during selection)when first evaluating
GoalA,GoalBgets moved to the global cache as an error, evne though it only errors when used as a nested goal ofGoalA. Later provingGoalBtherefore fails.lcnr commentedon Apr 2, 2024
I expect this to also affect
EvaluatedToAmbigStackDependentwith the following setup:CandA2have not yet been applied)CandB1during selection)Rollup merge of rust-lang#123302 - compiler-errors:sized-bound-first,…
Rollup merge of rust-lang#123302 - compiler-errors:sized-bound-first,…
Unrolled build for rust-lang#123302