Skip to content

Commit 21eb366

Browse files
committed
create but do not use a projection cache
1 parent d042ce2 commit 21eb366

File tree

3 files changed

+118
-6
lines changed

3 files changed

+118
-6
lines changed

src/librustc/infer/mod.rs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
106106

107107
pub tables: InferTables<'a, 'gcx, 'tcx>,
108108

109+
// Cache for projections. This cache is snapshotted along with the
110+
// infcx.
111+
//
112+
// Public so that `traits::project` can use it.
113+
pub projection_cache: RefCell<traits::ProjectionCache<'tcx>>,
114+
109115
// We instantiate UnificationTable with bounds<Ty> because the
110116
// types that might instantiate a general type variable have an
111117
// order, represented by its upper and lower bounds.
@@ -479,6 +485,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'gcx> {
479485
parameter_environment: param_env,
480486
selection_cache: traits::SelectionCache::new(),
481487
evaluation_cache: traits::EvaluationCache::new(),
488+
projection_cache: RefCell::new(traits::ProjectionCache::new()),
482489
reported_trait_errors: RefCell::new(FnvHashSet()),
483490
normalize: false,
484491
projection_mode: ProjectionMode::AnyFinal,
@@ -512,6 +519,7 @@ impl<'a, 'gcx, 'tcx> InferCtxtBuilder<'a, 'gcx, 'tcx> {
512519
global_tcx.enter_local(arenas, |tcx| f(InferCtxt {
513520
tcx: tcx,
514521
tables: tables,
522+
projection_cache: RefCell::new(traits::ProjectionCache::new()),
515523
type_variables: RefCell::new(type_variable::TypeVariableTable::new()),
516524
int_unification_table: RefCell::new(UnificationTable::new()),
517525
float_unification_table: RefCell::new(UnificationTable::new()),
@@ -547,6 +555,7 @@ impl<'tcx, T> InferOk<'tcx, T> {
547555

548556
#[must_use = "once you start a snapshot, you should always consume it"]
549557
pub struct CombinedSnapshot {
558+
projection_cache_snapshot: traits::ProjectionCacheSnapshot,
550559
type_snapshot: type_variable::Snapshot,
551560
int_snapshot: unify::Snapshot<ty::IntVid>,
552561
float_snapshot: unify::Snapshot<ty::FloatVid>,
@@ -827,6 +836,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
827836
self.obligations_in_snapshot.set(false);
828837

829838
CombinedSnapshot {
839+
projection_cache_snapshot: self.projection_cache.borrow_mut().snapshot(),
830840
type_snapshot: self.type_variables.borrow_mut().snapshot(),
831841
int_snapshot: self.int_unification_table.borrow_mut().snapshot(),
832842
float_snapshot: self.float_unification_table.borrow_mut().snapshot(),
@@ -837,7 +847,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
837847

838848
fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot) {
839849
debug!("rollback_to(cause={})", cause);
840-
let CombinedSnapshot { type_snapshot,
850+
let CombinedSnapshot { projection_cache_snapshot,
851+
type_snapshot,
841852
int_snapshot,
842853
float_snapshot,
843854
region_vars_snapshot,
@@ -846,6 +857,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
846857
assert!(!self.obligations_in_snapshot.get());
847858
self.obligations_in_snapshot.set(obligations_in_snapshot);
848859

860+
self.projection_cache
861+
.borrow_mut()
862+
.rollback_to(projection_cache_snapshot);
849863
self.type_variables
850864
.borrow_mut()
851865
.rollback_to(type_snapshot);
@@ -861,14 +875,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
861875

862876
fn commit_from(&self, snapshot: CombinedSnapshot) {
863877
debug!("commit_from()");
864-
let CombinedSnapshot { type_snapshot,
878+
let CombinedSnapshot { projection_cache_snapshot,
879+
type_snapshot,
865880
int_snapshot,
866881
float_snapshot,
867882
region_vars_snapshot,
868883
obligations_in_snapshot } = snapshot;
869884

870885
self.obligations_in_snapshot.set(obligations_in_snapshot);
871886

887+
self.projection_cache
888+
.borrow_mut()
889+
.commit(projection_cache_snapshot);
872890
self.type_variables
873891
.borrow_mut()
874892
.commit(type_snapshot);
@@ -926,7 +944,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
926944
F: FnOnce() -> Result<T, E>
927945
{
928946
debug!("commit_regions_if_ok()");
929-
let CombinedSnapshot { type_snapshot,
947+
let CombinedSnapshot { projection_cache_snapshot,
948+
type_snapshot,
930949
int_snapshot,
931950
float_snapshot,
932951
region_vars_snapshot,
@@ -941,6 +960,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
941960

942961
// Roll back any non-region bindings - they should be resolved
943962
// inside `f`, with, e.g. `resolve_type_vars_if_possible`.
963+
self.projection_cache
964+
.borrow_mut()
965+
.rollback_to(projection_cache_snapshot);
944966
self.type_variables
945967
.borrow_mut()
946968
.rollback_to(type_snapshot);

src/librustc/traits/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
//! Trait Resolution. See the Book for more.
11+
//! Trait Resolution. See README.md for an overview of how this works.
1212
1313
pub use self::SelectionError::*;
1414
pub use self::FulfillmentErrorCode::*;
@@ -30,8 +30,9 @@ pub use self::coherence::orphan_check;
3030
pub use self::coherence::overlapping_impls;
3131
pub use self::coherence::OrphanCheckErr;
3232
pub use self::fulfill::{FulfillmentContext, GlobalFulfilledPredicates, RegionObligation};
33-
pub use self::project::{MismatchedProjectionTypes, ProjectionMode};
33+
pub use self::project::MismatchedProjectionTypes;
3434
pub use self::project::{normalize, normalize_projection_type, Normalized};
35+
pub use self::project::{ProjectionCache, ProjectionCacheSnapshot, ProjectionMode};
3536
pub use self::object_safety::ObjectSafetyViolation;
3637
pub use self::object_safety::MethodViolationCode;
3738
pub use self::select::{EvaluationCache, SelectionContext, SelectionCache};

src/librustc/traits/project.rs

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,8 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a,
340340
data.clone(),
341341
self.cause.clone(),
342342
self.depth);
343-
debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} with {} add'l obligations",
343+
debug!("AssociatedTypeNormalizer: depth={} normalized {:?} to {:?} \
344+
with {} add'l obligations",
344345
self.depth, ty, normalized_ty, obligations.len());
345346
self.obligations.extend(obligations);
346347
normalized_ty
@@ -1237,3 +1238,91 @@ fn assoc_ty_def<'cx, 'gcx, 'tcx>(
12371238
.next()
12381239
}
12391240
}
1241+
1242+
// # Cache
1243+
1244+
pub struct ProjectionCache<'tcx> {
1245+
map: SnapshotMap<ty::ProjectionTy<'tcx>, ProjectionCacheEntry<'tcx>>,
1246+
}
1247+
1248+
#[derive(Clone, Debug)]
1249+
enum ProjectionCacheEntry<'tcx> {
1250+
InProgress,
1251+
Ambiguous,
1252+
Error,
1253+
NormalizedTy(Ty<'tcx>),
1254+
}
1255+
1256+
// NB: intentionally not Clone
1257+
pub struct ProjectionCacheSnapshot {
1258+
snapshot: Snapshot
1259+
}
1260+
1261+
impl<'tcx> ProjectionCache<'tcx> {
1262+
pub fn new() -> Self {
1263+
ProjectionCache {
1264+
map: SnapshotMap::new()
1265+
}
1266+
}
1267+
1268+
pub fn snapshot(&mut self) -> ProjectionCacheSnapshot {
1269+
ProjectionCacheSnapshot { snapshot: self.map.snapshot() }
1270+
}
1271+
1272+
pub fn rollback_to(&mut self, snapshot: ProjectionCacheSnapshot) {
1273+
self.map.rollback_to(snapshot.snapshot);
1274+
}
1275+
1276+
pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) {
1277+
self.map.commit(snapshot.snapshot);
1278+
}
1279+
1280+
/// Try to start normalize `key`; returns an error if
1281+
/// normalization already occured (this error corresponds to a
1282+
/// cache hit, so it's actually a good thing).
1283+
fn try_start(&mut self, key: ty::ProjectionTy<'tcx>)
1284+
-> Result<(), ProjectionCacheEntry<'tcx>> {
1285+
match self.map.get(&key) {
1286+
Some(entry) => return Err(entry.clone()),
1287+
None => { }
1288+
}
1289+
1290+
self.map.insert(key, ProjectionCacheEntry::InProgress);
1291+
Ok(())
1292+
}
1293+
1294+
/// Indicates that `key` was normalized to `value`. If `cacheable` is false,
1295+
/// then this result is sadly not cacheable.
1296+
fn complete(&mut self,
1297+
key: ty::ProjectionTy<'tcx>,
1298+
value: &NormalizedTy<'tcx>,
1299+
cacheable: bool) {
1300+
let fresh_key = if cacheable {
1301+
debug!("ProjectionCacheEntry::complete: adding cache entry: key={:?}, value={:?}",
1302+
key, value);
1303+
self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.value))
1304+
} else {
1305+
debug!("ProjectionCacheEntry::complete: cannot cache: key={:?}, value={:?}",
1306+
key, value);
1307+
!self.map.remove(key)
1308+
};
1309+
1310+
assert!(!fresh_key, "never started projecting `{:?}`", key);
1311+
}
1312+
1313+
/// Indicates that trying to normalize `key` resulted in
1314+
/// ambiguity. No point in trying it again then until we gain more
1315+
/// type information (in which case, the "fully resolved" key will
1316+
/// be different).
1317+
fn ambiguous(&mut self, key: ty::ProjectionTy<'tcx>) {
1318+
let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous);
1319+
assert!(!fresh, "never started projecting `{:?}`", key);
1320+
}
1321+
1322+
/// Indicates that trying to normalize `key` resulted in
1323+
/// error.
1324+
fn error(&mut self, key: ty::ProjectionTy<'tcx>) {
1325+
let fresh = self.map.insert(key, ProjectionCacheEntry::Error);
1326+
assert!(!fresh, "never started projecting `{:?}`", key);
1327+
}
1328+
}

0 commit comments

Comments
 (0)