diff --git a/src/librustc_infer/traits/mod.rs b/src/librustc_infer/traits/mod.rs index aa0cfedff9e8..9f7d019e8fd6 100644 --- a/src/librustc_infer/traits/mod.rs +++ b/src/librustc_infer/traits/mod.rs @@ -13,6 +13,7 @@ pub mod misc; mod object_safety; mod on_unimplemented; mod project; +mod projection_cache; pub mod query; mod select; mod specialize; @@ -49,11 +50,14 @@ pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::ObjectSafetyViolation; pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote}; -pub use self::project::MismatchedProjectionTypes; pub use self::project::{ normalize, normalize_projection_type, normalize_to, poly_project_and_unify_type, }; -pub use self::project::{Normalized, ProjectionCache, ProjectionCacheSnapshot, Reveal}; +pub use self::projection_cache::MismatchedProjectionTypes; +pub use self::projection_cache::{ + Normalized, ProjectionCache, ProjectionCacheEntry, ProjectionCacheKey, ProjectionCacheSnapshot, + Reveal, +}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::find_associated_item; diff --git a/src/librustc_infer/traits/project.rs b/src/librustc_infer/traits/project.rs index 78483cf6577d..551b8618af1f 100644 --- a/src/librustc_infer/traits/project.rs +++ b/src/librustc_infer/traits/project.rs @@ -1,15 +1,18 @@ //! Code for projecting associated types out of trait references. use super::elaborate_predicates; +use super::projection_cache::NormalizedTy; use super::specialization_graph; use super::translate_substs; use super::util; +use super::MismatchedProjectionTypes; use super::Obligation; use super::ObligationCause; use super::PredicateObligation; use super::Selection; use super::SelectionContext; use super::SelectionError; +use super::{Normalized, ProjectionCacheEntry, ProjectionCacheKey}; use super::{VtableClosureData, VtableFnPointerData, VtableGeneratorData, VtableImplData}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -18,7 +21,6 @@ use rustc::ty::fold::{TypeFoldable, TypeFolder}; use rustc::ty::subst::{InternalSubsts, Subst}; use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_ast::ast::Ident; -use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; use rustc_hir::def_id::DefId; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; @@ -41,11 +43,6 @@ pub enum ProjectionTyError<'tcx> { TraitSelectionError(SelectionError<'tcx>), } -#[derive(Clone)] -pub struct MismatchedProjectionTypes<'tcx> { - pub err: ty::error::TypeError<'tcx>, -} - #[derive(PartialEq, Eq, Debug)] enum ProjectionTyCandidate<'tcx> { // from a where-clause in the env or object type @@ -393,20 +390,6 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { } } -#[derive(Clone, TypeFoldable)] -pub struct Normalized<'tcx, T> { - pub value: T, - pub obligations: Vec>, -} - -pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>; - -impl<'tcx, T> Normalized<'tcx, T> { - pub fn with(self, value: U) -> Normalized<'tcx, U> { - Normalized { value, obligations: self.obligations } - } -} - /// The guts of `normalize`: normalize a specific projection like `::Item`. The result is always a type (and possibly /// additional obligations). If ambiguity arises, which implies that @@ -1500,47 +1483,6 @@ fn assoc_ty_def( } } -// # Cache - -/// The projection cache. Unlike the standard caches, this can include -/// infcx-dependent type variables, therefore we have to roll the -/// cache back each time we roll a snapshot back, to avoid assumptions -/// on yet-unresolved inference variables. Types with placeholder -/// regions also have to be removed when the respective snapshot ends. -/// -/// Because of that, projection cache entries can be "stranded" and left -/// inaccessible when type variables inside the key are resolved. We make no -/// attempt to recover or remove "stranded" entries, but rather let them be -/// (for the lifetime of the infcx). -/// -/// Entries in the projection cache might contain inference variables -/// that will be resolved by obligations on the projection cache entry (e.g., -/// when a type parameter in the associated type is constrained through -/// an "RFC 447" projection on the impl). -/// -/// When working with a fulfillment context, the derived obligations of each -/// projection cache entry will be registered on the fulfillcx, so any users -/// that can wait for a fulfillcx fixed point need not care about this. However, -/// users that don't wait for a fixed point (e.g., trait evaluation) have to -/// resolve the obligations themselves to make sure the projected result is -/// ok and avoid issues like #43132. -/// -/// If that is done, after evaluation the obligations, it is a good idea to -/// call `ProjectionCache::complete` to make sure the obligations won't be -/// re-evaluated and avoid an exponential worst-case. -// -// FIXME: we probably also want some sort of cross-infcx cache here to -// reduce the amount of duplication. Let's see what we get with the Chalk reforms. -#[derive(Default)] -pub struct ProjectionCache<'tcx> { - map: SnapshotMap, ProjectionCacheEntry<'tcx>>, -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct ProjectionCacheKey<'tcx> { - ty: ty::ProjectionTy<'tcx>, -} - impl<'cx, 'tcx> ProjectionCacheKey<'tcx> { pub fn from_poly_projection_predicate( selcx: &mut SelectionContext<'cx, 'tcx>, @@ -1558,119 +1500,3 @@ impl<'cx, 'tcx> ProjectionCacheKey<'tcx> { }) } } - -#[derive(Clone, Debug)] -enum ProjectionCacheEntry<'tcx> { - InProgress, - Ambiguous, - Error, - NormalizedTy(NormalizedTy<'tcx>), -} - -// N.B., intentionally not Clone -pub struct ProjectionCacheSnapshot { - snapshot: Snapshot, -} - -impl<'tcx> ProjectionCache<'tcx> { - pub fn clear(&mut self) { - self.map.clear(); - } - - 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 rollback_placeholder(&mut self, snapshot: &ProjectionCacheSnapshot) { - self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_placeholders()); - } - - pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { - self.map.commit(snapshot.snapshot); - } - - /// Try to start normalize `key`; returns an error if - /// normalization already occurred (this error corresponds to a - /// cache hit, so it's actually a good thing). - fn try_start( - &mut self, - key: ProjectionCacheKey<'tcx>, - ) -> Result<(), ProjectionCacheEntry<'tcx>> { - if let Some(entry) = self.map.get(&key) { - return Err(entry.clone()); - } - - self.map.insert(key, ProjectionCacheEntry::InProgress); - Ok(()) - } - - /// Indicates that `key` was normalized to `value`. - 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)); - assert!(!fresh_key, "never started projecting `{:?}`", key); - } - - /// Mark the relevant projection cache key as having its derived obligations - /// complete, so they won't have to be re-computed (this is OK to do in a - /// snapshot - if the snapshot is rolled back, the obligations will be - /// marked as incomplete again). - pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) { - let ty = match self.map.get(&key) { - Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => { - debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty); - ty.value - } - ref value => { - // Type inference could "strand behind" old cache entries. Leave - // them alone for now. - debug!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key, value); - return; - } - }; - - self.map.insert( - key, - ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }), - ); - } - - /// A specialized version of `complete` for when the key's value is known - /// to be a NormalizedTy. - pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) { - // We want to insert `ty` with no obligations. If the existing value - // already has no obligations (as is common) we don't insert anything. - if !ty.obligations.is_empty() { - self.map.insert( - key, - ProjectionCacheEntry::NormalizedTy(Normalized { - value: ty.value, - obligations: vec![], - }), - ); - } - } - - /// 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: ProjectionCacheKey<'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: ProjectionCacheKey<'tcx>) { - let fresh = self.map.insert(key, ProjectionCacheEntry::Error); - assert!(!fresh, "never started projecting `{:?}`", key); - } -} diff --git a/src/librustc_infer/traits/projection_cache.rs b/src/librustc_infer/traits/projection_cache.rs new file mode 100644 index 000000000000..fb7b5fdb8eac --- /dev/null +++ b/src/librustc_infer/traits/projection_cache.rs @@ -0,0 +1,185 @@ +//! Code for projecting associated types out of trait references. + +use super::PredicateObligation; + +use rustc::ty::fold::TypeFoldable; +use rustc::ty::{self, Ty}; +use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; + +pub use rustc::traits::Reveal; + +#[derive(Clone)] +pub struct MismatchedProjectionTypes<'tcx> { + pub err: ty::error::TypeError<'tcx>, +} + +#[derive(Clone, TypeFoldable)] +pub struct Normalized<'tcx, T> { + pub value: T, + pub obligations: Vec>, +} + +pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>; + +impl<'tcx, T> Normalized<'tcx, T> { + pub fn with(self, value: U) -> Normalized<'tcx, U> { + Normalized { value: value, obligations: self.obligations } + } +} + +// # Cache + +/// The projection cache. Unlike the standard caches, this can include +/// infcx-dependent type variables, therefore we have to roll the +/// cache back each time we roll a snapshot back, to avoid assumptions +/// on yet-unresolved inference variables. Types with placeholder +/// regions also have to be removed when the respective snapshot ends. +/// +/// Because of that, projection cache entries can be "stranded" and left +/// inaccessible when type variables inside the key are resolved. We make no +/// attempt to recover or remove "stranded" entries, but rather let them be +/// (for the lifetime of the infcx). +/// +/// Entries in the projection cache might contain inference variables +/// that will be resolved by obligations on the projection cache entry (e.g., +/// when a type parameter in the associated type is constrained through +/// an "RFC 447" projection on the impl). +/// +/// When working with a fulfillment context, the derived obligations of each +/// projection cache entry will be registered on the fulfillcx, so any users +/// that can wait for a fulfillcx fixed point need not care about this. However, +/// users that don't wait for a fixed point (e.g., trait evaluation) have to +/// resolve the obligations themselves to make sure the projected result is +/// ok and avoid issues like #43132. +/// +/// If that is done, after evaluation the obligations, it is a good idea to +/// call `ProjectionCache::complete` to make sure the obligations won't be +/// re-evaluated and avoid an exponential worst-case. +// +// FIXME: we probably also want some sort of cross-infcx cache here to +// reduce the amount of duplication. Let's see what we get with the Chalk reforms. +#[derive(Default)] +pub struct ProjectionCache<'tcx> { + map: SnapshotMap, ProjectionCacheEntry<'tcx>>, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ProjectionCacheKey<'tcx> { + pub ty: ty::ProjectionTy<'tcx>, +} + +#[derive(Clone, Debug)] +pub enum ProjectionCacheEntry<'tcx> { + InProgress, + Ambiguous, + Error, + NormalizedTy(NormalizedTy<'tcx>), +} + +// N.B., intentionally not Clone +pub struct ProjectionCacheSnapshot { + snapshot: Snapshot, +} + +impl<'tcx> ProjectionCache<'tcx> { + pub fn clear(&mut self) { + self.map.clear(); + } + + 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 rollback_placeholder(&mut self, snapshot: &ProjectionCacheSnapshot) { + self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_placeholders()); + } + + pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { + self.map.commit(snapshot.snapshot); + } + + /// Try to start normalize `key`; returns an error if + /// normalization already occurred (this error corresponds to a + /// cache hit, so it's actually a good thing). + pub fn try_start( + &mut self, + key: ProjectionCacheKey<'tcx>, + ) -> Result<(), ProjectionCacheEntry<'tcx>> { + if let Some(entry) = self.map.get(&key) { + return Err(entry.clone()); + } + + self.map.insert(key, ProjectionCacheEntry::InProgress); + Ok(()) + } + + /// Indicates that `key` was normalized to `value`. + pub 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)); + assert!(!fresh_key, "never started projecting `{:?}`", key); + } + + /// Mark the relevant projection cache key as having its derived obligations + /// complete, so they won't have to be re-computed (this is OK to do in a + /// snapshot - if the snapshot is rolled back, the obligations will be + /// marked as incomplete again). + pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) { + let ty = match self.map.get(&key) { + Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => { + debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty); + ty.value + } + ref value => { + // Type inference could "strand behind" old cache entries. Leave + // them alone for now. + debug!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key, value); + return; + } + }; + + self.map.insert( + key, + ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }), + ); + } + + /// A specialized version of `complete` for when the key's value is known + /// to be a NormalizedTy. + pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) { + // We want to insert `ty` with no obligations. If the existing value + // already has no obligations (as is common) we don't insert anything. + if !ty.obligations.is_empty() { + self.map.insert( + key, + ProjectionCacheEntry::NormalizedTy(Normalized { + value: ty.value, + obligations: vec![], + }), + ); + } + } + + /// 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). + pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { + let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous); + assert!(!fresh, "never started projecting `{:?}`", key); + } + + /// Indicates that trying to normalize `key` resulted in + /// error. + pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) { + let fresh = self.map.insert(key, ProjectionCacheEntry::Error); + assert!(!fresh, "never started projecting `{:?}`", key); + } +} diff --git a/src/librustc_infer/traits/query/normalize.rs b/src/librustc_infer/traits/query/normalize.rs index 4577e3d2e1cf..365bf9e295b5 100644 --- a/src/librustc_infer/traits/query/normalize.rs +++ b/src/librustc_infer/traits/query/normalize.rs @@ -5,7 +5,7 @@ use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::project::Normalized; +use crate::traits::Normalized; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; use rustc::ty::fold::{TypeFoldable, TypeFolder}; use rustc::ty::subst::Subst; diff --git a/src/librustc_infer/traits/select.rs b/src/librustc_infer/traits/select.rs index 5c805731f256..12f39b12c726 100644 --- a/src/librustc_infer/traits/select.rs +++ b/src/librustc_infer/traits/select.rs @@ -9,9 +9,7 @@ use self::SelectionCandidate::*; use super::coherence::{self, Conflict}; use super::project; -use super::project::{ - normalize_with_depth, normalize_with_depth_to, Normalized, ProjectionCacheKey, -}; +use super::project::{normalize_with_depth, normalize_with_depth_to}; use super::util; use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; use super::wf; @@ -21,6 +19,7 @@ use super::SelectionResult; use super::TraitNotObjectSafe; use super::TraitQueryMode; use super::{BuiltinDerivedObligation, ImplDerivedObligation, ObligationCauseCode}; +use super::{Normalized, ProjectionCacheKey}; use super::{ObjectCastObligation, Obligation}; use super::{ObligationCause, PredicateObligation, TraitObligation}; use super::{OutputTypeParameterMismatch, Overflow, SelectionError, Unimplemented}; diff --git a/src/librustc_infer/traits/structural_impls.rs b/src/librustc_infer/traits/structural_impls.rs index 6630f664f96e..a164995255a9 100644 --- a/src/librustc_infer/traits/structural_impls.rs +++ b/src/librustc_infer/traits/structural_impls.rs @@ -1,5 +1,5 @@ use crate::traits; -use crate::traits::project::Normalized; +use crate::traits::Normalized; use rustc::ty; use rustc::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};