Mode ProjectionCache to its own module.
This commit is contained in:
parent
796ca64e9a
commit
21d4e063bc
|
@ -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;
|
||||
|
|
|
@ -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<PredicateObligation<'tcx>>,
|
||||
}
|
||||
|
||||
pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>;
|
||||
|
||||
impl<'tcx, T> Normalized<'tcx, T> {
|
||||
pub fn with<U>(self, value: U) -> Normalized<'tcx, U> {
|
||||
Normalized { value, obligations: self.obligations }
|
||||
}
|
||||
}
|
||||
|
||||
/// The guts of `normalize`: normalize a specific projection like `<T
|
||||
/// as Trait>::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<ProjectionCacheKey<'tcx>, 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PredicateObligation<'tcx>>,
|
||||
}
|
||||
|
||||
pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>;
|
||||
|
||||
impl<'tcx, T> Normalized<'tcx, T> {
|
||||
pub fn with<U>(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<ProjectionCacheKey<'tcx>, 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
Loading…
Reference in New Issue