1 //! Code for projecting associated types out of trait references.
3 use super::PredicateObligation;
5 use crate::infer::InferCtxtUndoLogs;
7 use rustc_data_structures::{
8 snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage},
11 use rustc_middle::ty::{self, Ty};
13 pub use rustc_middle::traits::Reveal;
15 pub(crate) type UndoLog<'tcx> =
16 snapshot_map::UndoLog<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>;
19 pub struct MismatchedProjectionTypes<'tcx> {
20 pub err: ty::error::TypeError<'tcx>,
23 #[derive(Clone, TypeFoldable)]
24 pub struct Normalized<'tcx, T> {
26 pub obligations: Vec<PredicateObligation<'tcx>>,
29 pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>;
31 impl<'tcx, T> Normalized<'tcx, T> {
32 pub fn with<U>(self, value: U) -> Normalized<'tcx, U> {
33 Normalized { value, obligations: self.obligations }
39 /// The projection cache. Unlike the standard caches, this can include
40 /// infcx-dependent type variables, therefore we have to roll the
41 /// cache back each time we roll a snapshot back, to avoid assumptions
42 /// on yet-unresolved inference variables. Types with placeholder
43 /// regions also have to be removed when the respective snapshot ends.
45 /// Because of that, projection cache entries can be "stranded" and left
46 /// inaccessible when type variables inside the key are resolved. We make no
47 /// attempt to recover or remove "stranded" entries, but rather let them be
48 /// (for the lifetime of the infcx).
50 /// Entries in the projection cache might contain inference variables
51 /// that will be resolved by obligations on the projection cache entry (e.g.,
52 /// when a type parameter in the associated type is constrained through
53 /// an "RFC 447" projection on the impl).
55 /// When working with a fulfillment context, the derived obligations of each
56 /// projection cache entry will be registered on the fulfillcx, so any users
57 /// that can wait for a fulfillcx fixed point need not care about this. However,
58 /// users that don't wait for a fixed point (e.g., trait evaluation) have to
59 /// resolve the obligations themselves to make sure the projected result is
60 /// ok and avoid issues like #43132.
62 /// If that is done, after evaluation the obligations, it is a good idea to
63 /// call `ProjectionCache::complete` to make sure the obligations won't be
64 /// re-evaluated and avoid an exponential worst-case.
66 // FIXME: we probably also want some sort of cross-infcx cache here to
67 // reduce the amount of duplication. Let's see what we get with the Chalk reforms.
68 pub struct ProjectionCache<'a, 'tcx> {
69 map: &'a mut SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
70 undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
74 pub struct ProjectionCacheStorage<'tcx> {
75 map: SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
78 #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
79 pub struct ProjectionCacheKey<'tcx> {
80 ty: ty::ProjectionTy<'tcx>,
83 impl ProjectionCacheKey<'tcx> {
84 pub fn new(ty: ty::ProjectionTy<'tcx>) -> Self {
89 #[derive(Clone, Debug)]
90 pub enum ProjectionCacheEntry<'tcx> {
95 NormalizedTy(NormalizedTy<'tcx>),
98 impl<'tcx> ProjectionCacheStorage<'tcx> {
100 pub(crate) fn with_log<'a>(
102 undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
103 ) -> ProjectionCache<'a, 'tcx> {
104 ProjectionCache { map: &mut self.map, undo_log }
108 impl<'tcx> ProjectionCache<'_, 'tcx> {
114 ProjectionCacheKey<'tcx>,
115 ProjectionCacheEntry<'tcx>,
116 InferCtxtUndoLogs<'tcx>,
118 self.map.with_log(self.undo_log)
121 pub fn clear(&mut self) {
125 /// Try to start normalize `key`; returns an error if
126 /// normalization already occurred (this error corresponds to a
127 /// cache hit, so it's actually a good thing).
130 key: ProjectionCacheKey<'tcx>,
131 ) -> Result<(), ProjectionCacheEntry<'tcx>> {
132 let mut map = self.map();
133 if let Some(entry) = map.get(&key) {
134 return Err(entry.clone());
137 map.insert(key, ProjectionCacheEntry::InProgress);
141 /// Indicates that `key` was normalized to `value`.
142 pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) {
144 "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
147 let mut map = self.map();
148 if let Some(ProjectionCacheEntry::Recur) = map.get(&key) {
149 debug!("Not overwriting Recur");
152 let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy(value));
153 assert!(!fresh_key, "never started projecting `{:?}`", key);
156 /// Indicates that trying to normalize `key` resulted in
157 /// ambiguity. No point in trying it again then until we gain more
158 /// type information (in which case, the "fully resolved" key will
160 pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) {
161 let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous);
162 assert!(!fresh, "never started projecting `{:?}`", key);
165 /// Indicates that while trying to normalize `key`, `key` was required to
166 /// be normalized again. Selection or evaluation should eventually report
168 pub fn recur(&mut self, key: ProjectionCacheKey<'tcx>) {
169 let fresh = self.map().insert(key, ProjectionCacheEntry::Recur);
170 assert!(!fresh, "never started projecting `{:?}`", key);
173 /// Indicates that trying to normalize `key` resulted in
175 pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) {
176 let fresh = self.map().insert(key, ProjectionCacheEntry::Error);
177 assert!(!fresh, "never started projecting `{:?}`", key);
181 impl<'tcx> Rollback<UndoLog<'tcx>> for ProjectionCacheStorage<'tcx> {
182 fn reverse(&mut self, undo: UndoLog<'tcx>) {
183 self.map.reverse(undo);