use super::{InferCtxt, FixupError, FixupResult};
use ty::{self, Ty, TyCtxt, TypeFoldable};
-use ty::fold::TypeFolder;
+use ty::fold::{TypeFolder, TypeVisitor};
///////////////////////////////////////////////////////////////////////////
// OPPORTUNISTIC TYPE RESOLVER
}
}
+///////////////////////////////////////////////////////////////////////////
+// UNRESOLVED TYPE FINDER
+
+/// The unresolved type **finder** walks your type and searches for
+/// type variables that don't yet have a value. They get pushed into a
+/// vector. It does not construct the fully resolved type (which might
+/// involve some hashing and so forth).
+pub struct UnresolvedTypeFinder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+ infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+}
+
+impl<'a, 'gcx, 'tcx> UnresolvedTypeFinder<'a, 'gcx, 'tcx> {
+ pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
+ UnresolvedTypeFinder { infcx }
+ }
+}
+
+impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'gcx, 'tcx> {
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
+ let t = self.infcx.shallow_resolve(t);
+ if t.has_infer_types() {
+ if let ty::TyInfer(_) = t.sty {
+ // Since we called `shallow_resolve` above, this must
+ // be an (as yet...) unresolved inference variable.
+ true
+ } else {
+ // Otherwise, visit its contents.
+ t.super_visit_with(self)
+ }
+ } else {
+ // Micro-optimize: no inference types at all Can't have unresolved type
+ // variables, no need to visit the contents.
+ false
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// FULL TYPE RESOLUTION
use super::util;
use hir::def_id::DefId;
-use infer::InferOk;
+use infer::{InferCtxt, InferOk};
use infer::type_variable::TypeVariableOrigin;
use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap};
use syntax::ast;
// bounds. It might be the case that we want two distinct caches,
// or else another kind of cache entry.
- match infcx.projection_cache.borrow_mut().try_start(cache_key) {
+ let cache_result = infcx.projection_cache.borrow_mut().try_start(cache_key);
+ match cache_result {
Ok(()) => { }
Err(ProjectionCacheEntry::Ambiguous) => {
// If we found ambiguity the last time, that generally
projection_ty);
selcx.infcx().report_overflow_error(&obligation, false);
}
- Err(ProjectionCacheEntry::NormalizedTy(ty)) => {
+ Err(ProjectionCacheEntry::NormalizedTy(mut ty)) => {
// If we find the value in the cache, then return it along
// with the obligations that went along with it. Note
// that, when using a fulfillment context, these
debug!("opt_normalize_projection_type: \
found normalized ty `{:?}`",
ty);
+
+ // Once we have inferred everything we need to know, we
+ // can ignore the `obligations` from that point on.
+ if !infcx.any_unresolved_type_vars(&ty.value) {
+ infcx.projection_cache.borrow_mut().complete(cache_key);
+ ty.obligations = vec![];
+ }
+
return Some(ty);
}
Err(ProjectionCacheEntry::Error) => {
obligations,
}
};
- infcx.projection_cache.borrow_mut().insert_ty(cache_key, &result);
+
+ let cache_value = prune_cache_value_obligations(infcx, &result);
+ infcx.projection_cache.borrow_mut().insert_ty(cache_key, cache_value);
+
Some(result)
}
Ok(ProjectedTy::NoProgress(projected_ty)) => {
value: projected_ty,
obligations: vec![]
};
- infcx.projection_cache.borrow_mut().insert_ty(cache_key, &result);
+ infcx.projection_cache.borrow_mut().insert_ty(cache_key, result.clone());
Some(result)
}
Err(ProjectionTyError::TooManyCandidates) => {
}
}
+/// If there are unresolved type variables, then we need to include
+/// any subobligations that bind them, at least until those type
+/// variables are fully resolved.
+fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+ result: &NormalizedTy<'tcx>)
+ -> NormalizedTy<'tcx> {
+ if !infcx.any_unresolved_type_vars(&result.value) {
+ return NormalizedTy { value: result.value, obligations: vec![] };
+ }
+
+ let mut obligations: Vec<_> =
+ result.obligations
+ .iter()
+ .filter(|obligation| match obligation.predicate {
+ // We found a `T: Foo<X = U>` predicate, let's check
+ // if `U` references any unresolved type
+ // variables. In principle, we only care if this
+ // projection can help resolve any of the type
+ // variables found in `result.value` -- but we just
+ // check for any type variables here, for fear of
+ // indirect obligations (e.g., we project to `?0`,
+ // but we have `T: Foo<X = ?1>` and `?1: Bar<X =
+ // ?0>`).
+ ty::Predicate::Projection(ref data) =>
+ !infcx.any_unresolved_type_vars(&data.ty()),
+
+ // We are only interested in `T: Foo<X = U>` predicates, whre
+ // `U` references one of `unresolved_type_vars`. =)
+ _ => false,
+ })
+ .cloned()
+ .collect();
+
+ obligations.shrink_to_fit();
+
+ NormalizedTy { value: result.value, obligations }
+}
+
/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not
/// hold. In various error cases, we cannot generate a valid
/// normalized projection. Therefore, we create an inference variable
}
/// Indicates that `key` was normalized to `value`.
- fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: &NormalizedTy<'tcx>) {
+ fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) {
debug!("ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
key, value);
- let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value.clone()));
+ let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value));
assert!(!fresh_key, "never started projecting `{:?}`", key);
}