Change:
Split out from the discussion in See for the original comments. The issue is caused by the way that we handle regions in the evaluation cache. When we insert a result into the evaluation cache (either or , we use a 'freshened' version of the original as the key. The 'freshened' has all non-late-bound regions erased. Unfortunately, this can lead to issues if we try to evaluate the following two predicates: When we evaluate , we'll get , since the region parameter in our trait ref is known to match the impl. We will then cache the result as . If we later try to evaluate , we will end up matching the evaluation cache entry, giving us a result of . However, we should have gotten a result of , since we don't know that holds. This is really difficult to observe in practice, for a number of reasons: The relevant trait predicates need to get evaluated, not just registered in a . Trait evaluation usually goes through the query, which canonicalizes the regions in the input trait ref. To end up trying to evaluate , we need to end up calling on it, with a different original trait ref used in the original query. Using instead of only seems to cause an error when it results in the incremental hash changing (I don't know if it's possible to weaponize this into extending a lifetime). As a result, I haven't been able to minimize this.
With landing, this is going to start causing ICEs (since the query result is actually changing across compilatioon sessions). I'm still not sure if this can actually cause unsoundness, but it should be fixed anyway, since it's a bug in a pretty subtle part of the trait system. cc - I'm not familiar enough with the relevant code to be sure of how to fix this.
This can be reproduced by following the instructions in after lands - it turns out that that reproducer also hits this issue. Unfortunately, that reproducer is quite large, and I'm not sure how to minimize it.
I wouldn't know where to look (or rather, I only know the vague shape of all of this, but not enough details) and would defer to or myself. The only thing I can think of is we should blanket use any time freshening erased a lifetime.
The same issue happens on
managed to create a minimized reproducer in

Discussion:
stack: &TraitObligationStack<'o, 'tcx>, candidate: &SelectionCandidate<'tcx>, ) -> Result<EvaluationResult, OverflowError> { <del> let result = self.evaluation_probe(|this| { </del> <ins> let mut result = self.evaluation_probe(|this| { </ins> let candidate = (*candidate).clone(); match this.confirm_candidate(stack.obligation, candidate) { Ok(selection) => {

Err(..) => Ok(EvaluatedToErr), } })?; <ins> // If we erased any lifetimes, then we want to use // `EvaluatedToOkModuloRegions` instead of `EvaluatedToOk` // as your final result. The result will be cached using // the freshened trait predicate as a key, so we need // our result to be correct by *any* choice of original lifetimes, // not just the lifetime choice for this particular (non-erased) // predicate. // See issue #80691 if stack.fresh_trait_ref.has_erased_regions() { result = result.max(EvaluatedToOkModuloRegions); } </ins> debug!(?result); Ok(result) }

<ins> // revisions: rfail1 rfail2 // failure-status: 101 // error-pattern: not implemented pub trait Interner { type InternedVariableKinds; } trait RustIrDatabase<I: Interner> { fn associated_ty_data(&self) -> AssociatedTyDatum<I>; fn impl_datum(&self) -> ImplDatum<I>; } trait Fold<I: Interner> { type Result; } impl<T, I: Interner> Fold<I> for Binders<T> where T: HasInterner<Interner = I> + Fold<I>, <T as Fold<I>>::Result: HasInterner<Interner = I>, I: Interner, { type Result = Binders<T::Result>; } impl<I: Interner> Fold<I> for WhereClause<I> { type Result = Binders<WhereClause<I>>; } trait HasInterner { type Interner: Interner; } impl<T: HasInterner> HasInterner for Vec<T> { type Interner = T::Interner; } impl<T: HasInterner + ?Sized> HasInterner for &T { type Interner = T::Interner; } pub struct VariableKind<I: Interner> { _marker: std::marker::PhantomData<I>, } struct VariableKinds<I: Interner> { _interned: I::InternedVariableKinds, } struct WhereClause<I: Interner> { _marker: std::marker::PhantomData<I>, } impl<I: Interner> HasInterner for WhereClause<I> { type Interner = I; } struct Binders<T> { _marker: std::marker::PhantomData<T>, } impl<T: HasInterner> HasInterner for Binders<T> { type Interner = T::Interner; } impl<T> Binders<&T> { fn cloned(self) -> Binders<T> { unimplemented!() } } impl<T: HasInterner> Binders<T> { fn map_ref<'a, U, OP>(&'a self, _op: OP) -> Binders<U> where OP: FnOnce(&'a T) -> U, U: HasInterner<Interner = T::Interner>, { unimplemented!() } } impl<T, I: Interner> Binders<T> where T: Fold<I> + HasInterner<Interner = I>, I: Interner, { fn substitute(self) -> T::Result { unimplemented!() } } impl<V, U> IntoIterator for Binders<V> where V: HasInterner + IntoIterator<Item = U>, U: HasInterner<Interner = V::Interner>, { type Item = Binders<U>; type IntoIter = BindersIntoIterator<V>; fn into_iter(self) -> Self::IntoIter { unimplemented!() } } struct BindersIntoIterator<V: HasInterner> { _binders: VariableKinds<V::Interner>, } impl<V> Iterator for BindersIntoIterator<V> where V: HasInterner + IntoIterator, <V as IntoIterator>::Item: HasInterner<Interner = V::Interner>, { type Item = Binders<<V as IntoIterator>::Item>; fn next(&mut self) -> Option<Self::Item> { unimplemented!() } } struct ImplDatum<I: Interner> { binders: Binders<ImplDatumBound<I>>, } struct ImplDatumBound<I: Interner> { where_clauses: Vec<Binders<WhereClause<I>>>, } impl<I: Interner> HasInterner for ImplDatumBound<I> { type Interner = I; } struct AssociatedTyDatum<I: Interner> { binders: Binders<AssociatedTyDatumBound<I>>, } struct AssociatedTyDatumBound<I: Interner> { where_clauses: Vec<Binders<WhereClause<I>>>, } impl<I: Interner> HasInterner for AssociatedTyDatumBound<I> { type Interner = I; } struct ClauseBuilder<'me, I: Interner> { db: &'me dyn RustIrDatabase<I>, } impl<'me, I: Interner> ClauseBuilder<'me, I> { fn new() -> Self { unimplemented!() } fn push_clause(&mut self, _conditions: impl Iterator<Item = Binders<Binders<WhereClause<I>>>>) { unimplemented!() } } pub(crate) struct Forest<I: Interner> { _marker: std::marker::PhantomData<I>, } impl<I: Interner> Forest<I> { fn iter_answers<'f>(&'f self) { let builder = &mut ClauseBuilder::<I>::new(); let impl_datum = builder.db.impl_datum(); let impl_where_clauses = impl_datum .binders .map_ref(|b| &b.where_clauses) .into_iter() .map(|wc| wc.cloned().substitute()); let associated_ty = builder.db.associated_ty_data(); let assoc_ty_where_clauses = associated_ty .binders .map_ref(|b| &b.where_clauses) .into_iter() .map(|wc| wc.cloned().substitute()); builder.push_clause(impl_where_clauses.chain(assoc_ty_where_clauses)); } } pub struct SLGSolver { pub(crate) forest: Forest<ChalkIr>, } impl SLGSolver { fn new() -> Self { unimplemented!() } fn solve_multiple(&self) { let _answers = self.forest.iter_answers(); } } pub struct ChalkIr; impl Interner for ChalkIr { type InternedVariableKinds = Vec<VariableKind<ChalkIr>>; } fn main() { let solver = SLGSolver::new(); solver.solve_multiple(); } </ins>