Skip to content

Commit

Permalink
create but do not use a projection cache
Browse files Browse the repository at this point in the history
  • Loading branch information
nikomatsakis committed May 31, 2016
1 parent d042ce2 commit 21eb366
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 6 deletions.
28 changes: 25 additions & 3 deletions src/librustc/infer/mod.rs
Expand Up @@ -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<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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()),
Expand Down Expand Up @@ -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<ty::IntVid>,
float_snapshot: unify::Snapshot<ty::FloatVid>,
Expand Down Expand Up @@ -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(),
Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -861,14 +875,18 @@ 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,
obligations_in_snapshot } = 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);
Expand Down Expand Up @@ -926,7 +944,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
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,
Expand All @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/librustc/traits/mod.rs
Expand Up @@ -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::*;
Expand All @@ -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};
Expand Down
91 changes: 90 additions & 1 deletion src/librustc/traits/project.rs
Expand Up @@ -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
Expand Down Expand Up @@ -1237,3 +1238,91 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
.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);
}
}

0 comments on commit 21eb366

Please sign in to comment.