Description
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 evaluateSomeType<'static> as SomeTrait
, we need to end up callingevaluate_predicate_recursively
on it, with a different original trait ref used in the original query. - Using
EvaluatedToOkModuloRegions
instead ofEvaluatedToOk
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.