Skip to content

Commit 80447c3

Browse files
committedSep 2, 2017
limit and clear cache obligations opportunistically
Keep **all** the obligations for every projection is wasteful of memory and compilation time. We only really care about those subobligations that may inform the result of the projection (i.e., may help to resolve any inference variables that appear within). Therefore, we can clear the subobligations from the cache that don't potentially affect the result of the projection. On every cache hit, we also take the opportunity to check if the type variables have been resolved *yet* and, if so, clear out the pending obligations. Fixes rust-lang#43613
1 parent ed532c0 commit 80447c3

File tree

4 files changed

+111
-8
lines changed

4 files changed

+111
-8
lines changed
 

‎src/librustc/infer/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
11601160
value.fold_with(&mut r)
11611161
}
11621162

1163+
/// Returns true if `T` contains unresolved type variables. In the
1164+
/// process of visiting `T`, this will resolve (where possible)
1165+
/// type variables in `T`, but it never constructs the final,
1166+
/// resolved type, so it's more efficient than
1167+
/// `resolve_type_vars_if_possible()`.
1168+
pub fn any_unresolved_type_vars<T>(&self, value: &T) -> bool
1169+
where T: TypeFoldable<'tcx>
1170+
{
1171+
let mut r = resolve::UnresolvedTypeFinder::new(self);
1172+
value.visit_with(&mut r)
1173+
}
1174+
11631175
pub fn resolve_type_and_region_vars_if_possible<T>(&self, value: &T) -> T
11641176
where T: TypeFoldable<'tcx>
11651177
{

‎src/librustc/infer/resolve.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use super::{InferCtxt, FixupError, FixupResult};
1212
use ty::{self, Ty, TyCtxt, TypeFoldable};
13-
use ty::fold::TypeFolder;
13+
use ty::fold::{TypeFolder, TypeVisitor};
1414

1515
///////////////////////////////////////////////////////////////////////////
1616
// OPPORTUNISTIC TYPE RESOLVER
@@ -80,6 +80,43 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv
8080
}
8181
}
8282

83+
///////////////////////////////////////////////////////////////////////////
84+
// UNRESOLVED TYPE FINDER
85+
86+
/// The unresolved type **finder** walks your type and searches for
87+
/// type variables that don't yet have a value. They get pushed into a
88+
/// vector. It does not construct the fully resolved type (which might
89+
/// involve some hashing and so forth).
90+
pub struct UnresolvedTypeFinder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
91+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
92+
}
93+
94+
impl<'a, 'gcx, 'tcx> UnresolvedTypeFinder<'a, 'gcx, 'tcx> {
95+
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
96+
UnresolvedTypeFinder { infcx }
97+
}
98+
}
99+
100+
impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'gcx, 'tcx> {
101+
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
102+
let t = self.infcx.shallow_resolve(t);
103+
if t.has_infer_types() {
104+
if let ty::TyInfer(_) = t.sty {
105+
// Since we called `shallow_resolve` above, this must
106+
// be an (as yet...) unresolved inference variable.
107+
true
108+
} else {
109+
// Otherwise, visit its contents.
110+
t.super_visit_with(self)
111+
}
112+
} else {
113+
// Micro-optimize: no inference types at all Can't have unresolved type
114+
// variables, no need to visit the contents.
115+
false
116+
}
117+
}
118+
}
119+
83120
///////////////////////////////////////////////////////////////////////////
84121
// FULL TYPE RESOLUTION
85122

‎src/librustc/traits/project.rs

+57-7
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use super::VtableImplData;
2525
use super::util;
2626

2727
use hir::def_id::DefId;
28-
use infer::InferOk;
28+
use infer::{InferCtxt, InferOk};
2929
use infer::type_variable::TypeVariableOrigin;
3030
use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
3131
use syntax::ast;
@@ -416,7 +416,8 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
416416
// bounds. It might be the case that we want two distinct caches,
417417
// or else another kind of cache entry.
418418

419-
match infcx.projection_cache.borrow_mut().try_start(cache_key) {
419+
let cache_result = infcx.projection_cache.borrow_mut().try_start(cache_key);
420+
match cache_result {
420421
Ok(()) => { }
421422
Err(ProjectionCacheEntry::Ambiguous) => {
422423
// If we found ambiguity the last time, that generally
@@ -466,7 +467,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
466467
projection_ty);
467468
selcx.infcx().report_overflow_error(&obligation, false);
468469
}
469-
Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
470+
Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => {
470471
// If we find the value in the cache, then return it along
471472
// with the obligations that went along with it. Note
472473
// that, when using a fulfillment context, these
@@ -479,6 +480,14 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
479480
debug!("opt_normalize_projection_type: \
480481
found normalized ty `{:?}`",
481482
ty);
483+
484+
// Once we have inferred everything we need to know, we
485+
// can ignore the `obligations` from that point on.
486+
if !infcx.any_unresolved_type_vars(&ty.value) {
487+
infcx.projection_cache.borrow_mut().complete(cache_key);
488+
ty.obligations = vec![];
489+
}
490+
482491
return Some(ty);
483492
}
484493
Err(ProjectionCacheEntry::Error) => {
@@ -527,7 +536,10 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
527536
obligations,
528537
}
529538
};
530-
infcx.projection_cache.borrow_mut().insert_ty(cache_key, &result);
539+
540+
let cache_value = prune_cache_value_obligations(infcx, &result);
541+
infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
542+
531543
Some(result)
532544
}
533545
Ok(ProjectedTy::NoProgress(projected_ty)) => {
@@ -538,7 +550,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
538550
value: projected_ty,
539551
obligations: vec![]
540552
};
541-
infcx.projection_cache.borrow_mut().insert_ty(cache_key, &result);
553+
infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
542554
Some(result)
543555
}
544556
Err(ProjectionTyError::TooManyCandidates) => {
@@ -562,6 +574,44 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
562574
}
563575
}
564576

577+
/// If there are unresolved type variables, then we need to include
578+
/// any subobligations that bind them, at least until those type
579+
/// variables are fully resolved.
580+
fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
581+
result: &NormalizedTy<'tcx>)
582+
-> NormalizedTy<'tcx> {
583+
if !infcx.any_unresolved_type_vars(&result.value) {
584+
return NormalizedTy { value: result.value, obligations: vec![] };
585+
}
586+
587+
let mut obligations: Vec<_> =
588+
result.obligations
589+
.iter()
590+
.filter(|obligation| match obligation.predicate {
591+
// We found a `T: Foo<X = U>` predicate, let's check
592+
// if `U` references any unresolved type
593+
// variables. In principle, we only care if this
594+
// projection can help resolve any of the type
595+
// variables found in `result.value` -- but we just
596+
// check for any type variables here, for fear of
597+
// indirect obligations (e.g., we project to `?0`,
598+
// but we have `T: Foo<X = ?1>` and `?1: Bar<X =
599+
// ?0>`).
600+
ty::Predicate::Projection(ref data) =>
601+
!infcx.any_unresolved_type_vars(&data.ty()),
602+
603+
// We are only interested in `T: Foo<X = U>` predicates, whre
604+
// `U` references one of `unresolved_type_vars`. =)
605+
_ => false,
606+
})
607+
.cloned()
608+
.collect();
609+
610+
obligations.shrink_to_fit();
611+
612+
NormalizedTy { value: result.value, obligations }
613+
}
614+
565615
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
566616
/// hold. In various error cases, we cannot generate a valid
567617
/// normalized projection. Therefore, we create an inference variable
@@ -1493,10 +1543,10 @@ impl<'tcx> ProjectionCache<'tcx> {
14931543
}
14941544

14951545
/// Indicates that `key` was normalized to `value`.
1496-
fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: &NormalizedTy<'tcx>) {
1546+
fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) {
14971547
debug!("ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
14981548
key, value);
1499-
let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.clone()));
1549+
let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value));
15001550
assert!(!fresh_key, "never started projecting `{:?}`", key);
15011551
}
15021552

‎src/librustc/ty/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,10 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
10171017
// levels.
10181018
ty::Binder(self.0.projection_ty.trait_ref(tcx))
10191019
}
1020+
1021+
pub fn ty(&self) -> Binder<Ty<'tcx>> {
1022+
Binder(self.skip_binder().ty) // preserves binding levels
1023+
}
10201024
}
10211025

10221026
pub trait ToPolyTraitRef<'tcx> {

0 commit comments

Comments
 (0)
Please sign in to comment.