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
> {
94 NormalizedTy(NormalizedTy
<'tcx
>),
97 impl<'tcx
> ProjectionCacheStorage
<'tcx
> {
99 pub(crate) fn with_log
<'a
>(
101 undo_log
: &'a
mut InferCtxtUndoLogs
<'tcx
>,
102 ) -> ProjectionCache
<'a
, 'tcx
> {
103 ProjectionCache { map: &mut self.map, undo_log }
107 impl<'tcx
> ProjectionCache
<'_
, 'tcx
> {
113 ProjectionCacheKey
<'tcx
>,
114 ProjectionCacheEntry
<'tcx
>,
115 InferCtxtUndoLogs
<'tcx
>,
117 self.map
.with_log(self.undo_log
)
120 pub fn clear(&mut self) {
124 /// Try to start normalize `key`; returns an error if
125 /// normalization already occurred (this error corresponds to a
126 /// cache hit, so it's actually a good thing).
129 key
: ProjectionCacheKey
<'tcx
>,
130 ) -> Result
<(), ProjectionCacheEntry
<'tcx
>> {
131 let mut map
= self.map();
132 if let Some(entry
) = map
.get(&key
) {
133 return Err(entry
.clone());
136 map
.insert(key
, ProjectionCacheEntry
::InProgress
);
140 /// Indicates that `key` was normalized to `value`.
141 pub fn insert_ty(&mut self, key
: ProjectionCacheKey
<'tcx
>, value
: NormalizedTy
<'tcx
>) {
143 "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
146 let fresh_key
= self.map().insert(key
, ProjectionCacheEntry
::NormalizedTy(value
));
147 assert
!(!fresh_key
, "never started projecting `{:?}`", key
);
150 /// Mark the relevant projection cache key as having its derived obligations
151 /// complete, so they won't have to be re-computed (this is OK to do in a
152 /// snapshot - if the snapshot is rolled back, the obligations will be
153 /// marked as incomplete again).
154 pub fn complete(&mut self, key
: ProjectionCacheKey
<'tcx
>) {
155 let mut map
= self.map();
156 let ty
= match map
.get(&key
) {
157 Some(&ProjectionCacheEntry
::NormalizedTy(ref ty
)) => {
158 debug
!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key
, ty
);
162 // Type inference could "strand behind" old cache entries. Leave
163 // them alone for now.
164 debug
!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key
, value
);
171 ProjectionCacheEntry
::NormalizedTy(Normalized { value: ty, obligations: vec![] }
),
175 /// A specialized version of `complete` for when the key's value is known
176 /// to be a NormalizedTy.
177 pub fn complete_normalized(&mut self, key
: ProjectionCacheKey
<'tcx
>, ty
: &NormalizedTy
<'tcx
>) {
178 // We want to insert `ty` with no obligations. If the existing value
179 // already has no obligations (as is common) we don't insert anything.
180 if !ty
.obligations
.is_empty() {
183 ProjectionCacheEntry
::NormalizedTy(Normalized
{
191 /// Indicates that trying to normalize `key` resulted in
192 /// ambiguity. No point in trying it again then until we gain more
193 /// type information (in which case, the "fully resolved" key will
195 pub fn ambiguous(&mut self, key
: ProjectionCacheKey
<'tcx
>) {
196 let fresh
= self.map().insert(key
, ProjectionCacheEntry
::Ambiguous
);
197 assert
!(!fresh
, "never started projecting `{:?}`", key
);
200 /// Indicates that trying to normalize `key` resulted in
202 pub fn error(&mut self, key
: ProjectionCacheKey
<'tcx
>) {
203 let fresh
= self.map().insert(key
, ProjectionCacheEntry
::Error
);
204 assert
!(!fresh
, "never started projecting `{:?}`", key
);
208 impl<'tcx
> Rollback
<UndoLog
<'tcx
>> for ProjectionCacheStorage
<'tcx
> {
209 fn reverse(&mut self, undo
: UndoLog
<'tcx
>) {
210 self.map
.reverse(undo
);