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<traits::ProjectionCache<'tcx>>,
+
// We instantiate UnificationTable with bounds<Ty> because the
// types that might instantiate a general type variable have an
// order, represented by its upper and lower bounds.
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,
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()),
#[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<ty::IntVid>,
float_snapshot: unify::Snapshot<ty::FloatVid>,
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(),
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,
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);
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,
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);
F: FnOnce() -> Result<T, E>
{
debug!("commit_regions_if_ok()");
- let CombinedSnapshot { type_snapshot,
+ let CombinedSnapshot { projection_cache_snapshot,
+ type_snapshot,
int_snapshot,
float_snapshot,
region_vars_snapshot,
// 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);
// 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::*;
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};
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
.next()
}
}
+
+// # Cache
+
+pub struct ProjectionCache<'tcx> {
+ map: SnapshotMap<ty::ProjectionTy<'tcx>, 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);
+ }
+}