1 //! Code for projecting associated types out of trait references.
3 use super::PredicateObligation;
5 use rustc_data_structures::snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage};
6 use rustc_middle::ty::fold::TypeFoldable;
7 use rustc_middle::ty::{self, Ty};
9 pub use rustc_middle::traits::Reveal;
11 pub(crate) type UndoLog<'tcx> =
12 snapshot_map::UndoLog<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>;
15 pub struct MismatchedProjectionTypes<'tcx> {
16 pub err: ty::error::TypeError<'tcx>,
19 #[derive(Clone, TypeFoldable)]
20 pub struct Normalized<'tcx, T> {
22 pub obligations: Vec<PredicateObligation<'tcx>>,
25 pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>;
27 impl<'tcx, T> Normalized<'tcx, T> {
28 pub fn with<U>(self, value: U) -> Normalized<'tcx, U> {
29 Normalized { value, obligations: self.obligations }
35 /// The projection cache. Unlike the standard caches, this can include
36 /// infcx-dependent type variables, therefore we have to roll the
37 /// cache back each time we roll a snapshot back, to avoid assumptions
38 /// on yet-unresolved inference variables. Types with placeholder
39 /// regions also have to be removed when the respective snapshot ends.
41 /// Because of that, projection cache entries can be "stranded" and left
42 /// inaccessible when type variables inside the key are resolved. We make no
43 /// attempt to recover or remove "stranded" entries, but rather let them be
44 /// (for the lifetime of the infcx).
46 /// Entries in the projection cache might contain inference variables
47 /// that will be resolved by obligations on the projection cache entry (e.g.,
48 /// when a type parameter in the associated type is constrained through
49 /// an "RFC 447" projection on the impl).
51 /// When working with a fulfillment context, the derived obligations of each
52 /// projection cache entry will be registered on the fulfillcx, so any users
53 /// that can wait for a fulfillcx fixed point need not care about this. However,
54 /// users that don't wait for a fixed point (e.g., trait evaluation) have to
55 /// resolve the obligations themselves to make sure the projected result is
56 /// ok and avoid issues like #43132.
58 /// If that is done, after evaluation the obligations, it is a good idea to
59 /// call `ProjectionCache::complete` to make sure the obligations won't be
60 /// re-evaluated and avoid an exponential worst-case.
62 // FIXME: we probably also want some sort of cross-infcx cache here to
63 // reduce the amount of duplication. Let's see what we get with the Chalk reforms.
64 pub struct ProjectionCache<'tcx, 'a> {
65 map: &'a mut SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
66 undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
70 pub struct ProjectionCacheStorage<'tcx> {
71 map: SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
74 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
75 pub struct ProjectionCacheKey<'tcx> {
76 ty: ty::ProjectionTy<'tcx>,
79 impl ProjectionCacheKey<'tcx> {
80 pub fn new(ty: ty::ProjectionTy<'tcx>) -> Self {
85 #[derive(Clone, Debug)]
86 pub enum ProjectionCacheEntry<'tcx> {
90 NormalizedTy(NormalizedTy<'tcx>),
93 impl<'tcx> ProjectionCacheStorage<'tcx> {
94 pub(crate) fn with_log<'a>(
96 undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
97 ) -> ProjectionCache<'tcx, 'a> {
98 ProjectionCache { map: &mut self.map, undo_log }
102 impl<'tcx> ProjectionCache<'tcx, '_> {
107 ProjectionCacheKey<'tcx>,
108 ProjectionCacheEntry<'tcx>,
109 InferCtxtUndoLogs<'tcx>,
111 self.map.with_log(self.undo_log)
114 pub fn clear(&mut self) {
118 /// Try to start normalize `key`; returns an error if
119 /// normalization already occurred (this error corresponds to a
120 /// cache hit, so it's actually a good thing).
123 key: ProjectionCacheKey<'tcx>,
124 ) -> Result<(), ProjectionCacheEntry<'tcx>> {
125 let mut map = self.map();
126 if let Some(entry) = map.get(&key) {
127 return Err(entry.clone());
130 map.insert(key, ProjectionCacheEntry::InProgress);
134 /// Indicates that `key` was normalized to `value`.
135 pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) {
137 "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
140 let fresh_key = self.map().insert(key, ProjectionCacheEntry::NormalizedTy(value));
141 assert!(!fresh_key, "never started projecting `{:?}`", key);
144 /// Mark the relevant projection cache key as having its derived obligations
145 /// complete, so they won't have to be re-computed (this is OK to do in a
146 /// snapshot - if the snapshot is rolled back, the obligations will be
147 /// marked as incomplete again).
148 pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) {
149 let mut map = self.map();
150 let ty = match map.get(&key) {
151 Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => {
152 debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
156 // Type inference could "strand behind" old cache entries. Leave
157 // them alone for now.
158 debug!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key, value);
165 ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }),
169 /// A specialized version of `complete` for when the key's value is known
170 /// to be a NormalizedTy.
171 pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) {
172 // We want to insert `ty` with no obligations. If the existing value
173 // already has no obligations (as is common) we don't insert anything.
174 if !ty.obligations.is_empty() {
177 ProjectionCacheEntry::NormalizedTy(Normalized {
185 /// Indicates that trying to normalize `key` resulted in
186 /// ambiguity. No point in trying it again then until we gain more
187 /// type information (in which case, the "fully resolved" key will
189 pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) {
190 let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous);
191 assert!(!fresh, "never started projecting `{:?}`", key);
194 /// Indicates that trying to normalize `key` resulted in
196 pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) {
197 let fresh = self.map().insert(key, ProjectionCacheEntry::Error);
198 assert!(!fresh, "never started projecting `{:?}`", key);
202 impl<'tcx> Rollback<UndoLog<'tcx>> for ProjectionCacheStorage<'tcx> {
203 fn reverse(&mut self, undo: UndoLog<'tcx>) {
204 self.map.reverse(undo);