From 6dbb9d4eee0a2789a74bb2d4b8228626f7cd741c Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 20 Sep 2021 14:23:21 -0500 Subject: [PATCH] Don't use projection cache or candidate cache in intercrate mode Fixes #88969 It appears that *just* disabling the evaluation cache (in #88994) leads to other issues involving intercrate mode caching. I suspect that since we now always end up performing the full evaluation in intercrate mode, we end up 'polluting' the candidate and projection caches with results that depend on being in intercrate mode in some way. Previously, we might have hit a cached evaluation (stored during non-intercrate mode), and skipped doing this extra work in intercrate mode. The whole situation with intercrate mode caching is turning into a mess. Ideally, we would remove intercrate mode entirely - however, this might require waiting on Chalk. --- .../src/traits/project.rs | 30 +++++++++++++++---- .../src/traits/select/mod.rs | 18 +++++++++++ ...oherence-projection-conflict-orphan.stderr | 1 + 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 4f22543950c..3b4d175a9a3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -844,6 +844,10 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( obligations: &mut Vec>, ) -> Result>, InProgress> { let infcx = selcx.infcx(); + // Don't use the projection cache in intercrate mode - + // the `infcx` may be re-used between intercrate in non-intercrate + // mode, which could lead to using incorrect cache results. + let use_cache = !selcx.is_intercrate(); let projection_ty = infcx.resolve_vars_if_possible(projection_ty); let cache_key = ProjectionCacheKey::new(projection_ty); @@ -856,7 +860,11 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // bounds. It might be the case that we want two distinct caches, // or else another kind of cache entry. - let cache_result = infcx.inner.borrow_mut().projection_cache().try_start(cache_key); + let cache_result = if use_cache { + infcx.inner.borrow_mut().projection_cache().try_start(cache_key) + } else { + Ok(()) + }; match cache_result { Ok(()) => debug!("no cache"), Err(ProjectionCacheEntry::Ambiguous) => { @@ -881,7 +889,9 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // should ensure that, unless this happens within a snapshot that's // rolled back, fulfillment or evaluation will notice the cycle. - infcx.inner.borrow_mut().projection_cache().recur(cache_key); + if use_cache { + infcx.inner.borrow_mut().projection_cache().recur(cache_key); + } return Err(InProgress); } Err(ProjectionCacheEntry::Recur) => { @@ -963,20 +973,26 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( .map_or(false, |res| res.must_apply_considering_regions()) }); - infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); + if use_cache { + infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); + } obligations.extend(result.obligations); Ok(Some(result.value)) } Ok(ProjectedTy::NoProgress(projected_ty)) => { debug!(?projected_ty, "opt_normalize_projection_type: no progress"); let result = Normalized { value: projected_ty, obligations: vec![] }; - infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); + if use_cache { + infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); + } // No need to extend `obligations`. Ok(Some(result.value)) } Err(ProjectionTyError::TooManyCandidates) => { debug!("opt_normalize_projection_type: too many candidates"); - infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); + if use_cache { + infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); + } Ok(None) } Err(ProjectionTyError::TraitSelectionError(_)) => { @@ -986,7 +1002,9 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // Trait`, which when processed will cause the error to be // reported later - infcx.inner.borrow_mut().projection_cache().error(cache_key); + if use_cache { + infcx.inner.borrow_mut().projection_cache().error(cache_key); + } let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); Ok(Some(result.value)) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index b966779500b..50d6f82ae18 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -314,6 +314,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.tcx } + pub fn is_intercrate(&self) -> bool { + self.intercrate + } + /// Returns `true` if the trait predicate is considerd `const` to this selection context. pub fn is_trait_predicate_const(&self, pred: ty::TraitPredicate<'_>) -> bool { match pred.constness { @@ -1197,6 +1201,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option>> { + // Neither the global nor local cache is aware of intercrate + // mode, so don't do any caching. In particular, we might + // re-use the same `InferCtxt` with both an intercrate + // and non-intercrate `SelectionContext` + if self.intercrate { + return None; + } let tcx = self.tcx(); let pred = &cache_fresh_trait_pred.skip_binder(); let trait_ref = pred.trait_ref; @@ -1233,6 +1244,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &self, result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, ) -> bool { + // Neither the global nor local cache is aware of intercrate + // mode, so don't do any caching. In particular, we might + // re-use the same `InferCtxt` with both an intercrate + // and non-intercrate `SelectionContext` + if self.intercrate { + return false; + } match result { Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.needs_infer(), _ => true, diff --git a/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr b/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr index 51f6faab3c7..9efb5dc75f4 100644 --- a/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr +++ b/src/test/ui/coherence/coherence-projection-conflict-orphan.stderr @@ -8,6 +8,7 @@ LL | impl Foo for A { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `i32` | = note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `i32` in future versions + = note: upstream crates may add a new impl of trait `std::iter::Iterator` for type `i32` in future versions error: aborting due to previous error