From 21eb36608e5ff53a250afe5b7443606ab96c429a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 21 May 2016 08:18:52 -0400 Subject: [PATCH] create but do not use a projection cache --- src/librustc/infer/mod.rs | 28 +++++++++-- src/librustc/traits/mod.rs | 5 +- src/librustc/traits/project.rs | 91 +++++++++++++++++++++++++++++++++- 3 files changed, 118 insertions(+), 6 deletions(-) diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index ab9c056644571..8e3d63508bfc5 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -106,6 +106,12 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { pub tables: InferTables<'a, 'gcx, 'tcx>, + // Cache for projections. This cache is snapshotted along with the + // infcx. + // + // Public so that `traits::project` can use it. + pub projection_cache: RefCell>, + // We instantiate UnificationTable with bounds because the // types that might instantiate a general type variable have an // order, represented by its upper and lower bounds. @@ -479,6 +485,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> { parameter_environment: param_env, selection_cache: traits::SelectionCache::new(), evaluation_cache: traits::EvaluationCache::new(), + projection_cache: RefCell::new(traits::ProjectionCache::new()), reported_trait_errors: RefCell::new(FnvHashSet()), normalize: false, projection_mode: ProjectionMode::AnyFinal, @@ -512,6 +519,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> { global_tcx.enter_local(arenas, |tcx| f(InferCtxt { tcx: tcx, tables: tables, + projection_cache: RefCell::new(traits::ProjectionCache::new()), type_variables: RefCell::new(type_variable::TypeVariableTable::new()), int_unification_table: RefCell::new(UnificationTable::new()), float_unification_table: RefCell::new(UnificationTable::new()), @@ -547,6 +555,7 @@ impl<'tcx, T> InferOk<'tcx, T> { #[must_use = "once you start a snapshot, you should always consume it"] pub struct CombinedSnapshot { + projection_cache_snapshot: traits::ProjectionCacheSnapshot, type_snapshot: type_variable::Snapshot, int_snapshot: unify::Snapshot, float_snapshot: unify::Snapshot, @@ -827,6 +836,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.obligations_in_snapshot.set(false); CombinedSnapshot { + projection_cache_snapshot: self.projection_cache.borrow_mut().snapshot(), type_snapshot: self.type_variables.borrow_mut().snapshot(), int_snapshot: self.int_unification_table.borrow_mut().snapshot(), float_snapshot: self.float_unification_table.borrow_mut().snapshot(), @@ -837,7 +847,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot) { debug!("rollback_to(cause={})", cause); - let CombinedSnapshot { type_snapshot, + let CombinedSnapshot { projection_cache_snapshot, + type_snapshot, int_snapshot, float_snapshot, region_vars_snapshot, @@ -846,6 +857,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { assert!(!self.obligations_in_snapshot.get()); self.obligations_in_snapshot.set(obligations_in_snapshot); + self.projection_cache + .borrow_mut() + .rollback_to(projection_cache_snapshot); self.type_variables .borrow_mut() .rollback_to(type_snapshot); @@ -861,7 +875,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { fn commit_from(&self, snapshot: CombinedSnapshot) { debug!("commit_from()"); - let CombinedSnapshot { type_snapshot, + let CombinedSnapshot { projection_cache_snapshot, + type_snapshot, int_snapshot, float_snapshot, region_vars_snapshot, @@ -869,6 +884,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { self.obligations_in_snapshot.set(obligations_in_snapshot); + self.projection_cache + .borrow_mut() + .commit(projection_cache_snapshot); self.type_variables .borrow_mut() .commit(type_snapshot); @@ -926,7 +944,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { F: FnOnce() -> Result { debug!("commit_regions_if_ok()"); - let CombinedSnapshot { type_snapshot, + let CombinedSnapshot { projection_cache_snapshot, + type_snapshot, int_snapshot, float_snapshot, region_vars_snapshot, @@ -941,6 +960,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { // Roll back any non-region bindings - they should be resolved // inside `f`, with, e.g. `resolve_type_vars_if_possible`. + self.projection_cache + .borrow_mut() + .rollback_to(projection_cache_snapshot); self.type_variables .borrow_mut() .rollback_to(type_snapshot); diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index c177ec4dbede7..5b363d90578b1 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Trait Resolution. See the Book for more. +//! Trait Resolution. See README.md for an overview of how this works. pub use self::SelectionError::*; pub use self::FulfillmentErrorCode::*; @@ -30,8 +30,9 @@ pub use self::coherence::orphan_check; pub use self::coherence::overlapping_impls; pub use self::coherence::OrphanCheckErr; pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation}; -pub use self::project::{MismatchedProjectionTypes, ProjectionMode}; +pub use self::project::MismatchedProjectionTypes; pub use self::project::{normalize, normalize_projection_type, Normalized}; +pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, ProjectionMode}; pub use self::object_safety::ObjectSafetyViolation; pub use self::object_safety::MethodViolationCode; pub use self::select::{EvaluationCache, SelectionContext, SelectionCache}; diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 011662fe8c2de..7eb44d0e79e8e 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -340,7 +340,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, data.clone(), self.cause.clone(), self.depth); - debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} with {} add'l obligations", + debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \ + with {} add'l obligations", self.depth, ty, normalized_ty, obligations.len()); self.obligations.extend(obligations); normalized_ty @@ -1237,3 +1238,91 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>( .next() } } + +// # Cache + +pub struct ProjectionCache<'tcx> { + map: SnapshotMap, ProjectionCacheEntry<'tcx>>, +} + +#[derive(Clone, Debug)] +enum ProjectionCacheEntry<'tcx> { + InProgress, + Ambiguous, + Error, + NormalizedTy(Ty<'tcx>), +} + +// NB: intentionally not Clone +pub struct ProjectionCacheSnapshot { + snapshot: Snapshot +} + +impl<'tcx> ProjectionCache<'tcx> { + pub fn new() -> Self { + ProjectionCache { + map: SnapshotMap::new() + } + } + + pub fn snapshot(&mut self) -> ProjectionCacheSnapshot { + ProjectionCacheSnapshot { snapshot: self.map.snapshot() } + } + + pub fn rollback_to(&mut self, snapshot: ProjectionCacheSnapshot) { + self.map.rollback_to(snapshot.snapshot); + } + + pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { + self.map.commit(snapshot.snapshot); + } + + /// Try to start normalize `key`; returns an error if + /// normalization already occured (this error corresponds to a + /// cache hit, so it's actually a good thing). + fn try_start(&mut self, key: ty::ProjectionTy<'tcx>) + -> Result<(), ProjectionCacheEntry<'tcx>> { + match self.map.get(&key) { + Some(entry) => return Err(entry.clone()), + None => { } + } + + self.map.insert(key, ProjectionCacheEntry::InProgress); + Ok(()) + } + + /// Indicates that `key` was normalized to `value`. If `cacheable` is false, + /// then this result is sadly not cacheable. + fn complete(&mut self, + key: ty::ProjectionTy<'tcx>, + value: &NormalizedTy<'tcx>, + cacheable: bool) { + let fresh_key = if cacheable { + debug!("ProjectionCacheEntry::complete: adding cache entry: key={:?}, value={:?}", + key, value); + self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.value)) + } else { + debug!("ProjectionCacheEntry::complete: cannot cache: key={:?}, value={:?}", + key, value); + !self.map.remove(key) + }; + + assert!(!fresh_key, "never started projecting `{:?}`", key); + } + + /// Indicates that trying to normalize `key` resulted in + /// ambiguity. No point in trying it again then until we gain more + /// type information (in which case, the "fully resolved" key will + /// be different). + fn ambiguous(&mut self, key: ty::ProjectionTy<'tcx>) { + let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous); + assert!(!fresh, "never started projecting `{:?}`", key); + } + + /// Indicates that trying to normalize `key` resulted in + /// error. + fn error(&mut self, key: ty::ProjectionTy<'tcx>) { + let fresh = self.map.insert(key, ProjectionCacheEntry::Error); + assert!(!fresh, "never started projecting `{:?}`", key); + } +}