Split out from the discussion in #80336. See #80336 (comment) 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 infcx.evaluation_cache or tcx.evaluation_cache, we use a 'freshened' version of the original TraitPredicate as the key. The 'freshened' TraitPredicate has all non-late-bound regions erased.
Unfortunately, this can lead to issues if we try to evaluate the following two predicates:
impl SomeTrait for SomeType<'static>
SomeType<'static> as SomeTrait
SomeType<'#_r> as SomeTrait
When we evaluate SomeType<'static> as SomeTrait, we'll get EvaluatedToOk, since the region parameter in our trait ref is known to match the impl. We will then cache the result as <SomeType<'erased> as SomeTrait> -> EvaluatedToOk.
If we later try to evaluate SomeType<'#_r> as SomeTrait, we will end up matching the evaluation cache entry, giving us a result of EvaluatedToOk. However, we should have gotten a result of EvaluatedToOkModuloRegions, since we don't know that '#_r == 'static 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
FulfillmentContext.
- Trait evaluation usually goes through the
evaluate_obligation query, which canonicalizes the regions in the input trait ref. To end up trying to evaluate SomeType<'static> as SomeTrait, we need to end up calling evaluate_predicate_recursively on it, with a different original trait ref used in the original query.
- Using
EvaluatedToOkModuloRegions instead of EvaluatedToOk 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.
Split out from the discussion in #80336. See #80336 (comment) 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
infcx.evaluation_cacheortcx.evaluation_cache, we use a 'freshened' version of the originalTraitPredicateas the key. The 'freshened'TraitPredicatehas all non-late-bound regions erased.Unfortunately, this can lead to issues if we try to evaluate the following two predicates:
When we evaluate
SomeType<'static> as SomeTrait, we'll getEvaluatedToOk, since the region parameter in our trait ref is known to match the impl. We will then cache the result as<SomeType<'erased> as SomeTrait> -> EvaluatedToOk.If we later try to evaluate
SomeType<'#_r> as SomeTrait, we will end up matching the evaluation cache entry, giving us a result ofEvaluatedToOk. However, we should have gotten a result ofEvaluatedToOkModuloRegions, since we don't know that'#_r == 'staticholds.This is really difficult to observe in practice, for a number of reasons:
FulfillmentContext.evaluate_obligationquery, which canonicalizes the regions in the input trait ref. To end up trying to evaluateSomeType<'static> as SomeTrait, we need to end up callingevaluate_predicate_recursivelyon it, with a different original trait ref used in the original query.EvaluatedToOkModuloRegionsinstead ofEvaluatedToOkonly 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.