Skip to main content

rustc_infer/traits/
project.rs

1//! Code for projecting associated types out of trait references.
2
3use rustc_data_structures::snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage};
4use rustc_data_structures::undo_log::Rollback;
5use rustc_macros::TypeVisitable;
6use rustc_middle::traits::EvaluationResult;
7use rustc_middle::ty;
8use tracing::{debug, info};
9
10use super::PredicateObligations;
11use crate::infer::snapshot::undo_log::InferCtxtUndoLogs;
12
13pub(crate) type UndoLog<'tcx> =
14    snapshot_map::UndoLog<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>;
15
16#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for MismatchedProjectionTypes<'tcx> {
    #[inline]
    fn clone(&self) -> MismatchedProjectionTypes<'tcx> {
        MismatchedProjectionTypes {
            err: ::core::clone::Clone::clone(&self.err),
        }
    }
}Clone, const _: () =
    {
        impl<'tcx>
            ::rustc_middle::ty::TypeVisitable<::rustc_middle::ty::TyCtxt<'tcx>>
            for MismatchedProjectionTypes<'tcx> {
            fn visit_with<__V: ::rustc_middle::ty::TypeVisitor<::rustc_middle::ty::TyCtxt<'tcx>>>(&self,
                __visitor: &mut __V) -> __V::Result {
                match *self {
                    MismatchedProjectionTypes { err: ref __binding_0 } => {
                        {
                            match ::rustc_middle::ty::VisitorResult::branch(::rustc_middle::ty::TypeVisitable::visit_with(__binding_0,
                                        __visitor)) {
                                ::core::ops::ControlFlow::Continue(()) => {}
                                ::core::ops::ControlFlow::Break(r) => {
                                    return ::rustc_middle::ty::VisitorResult::from_residual(r);
                                }
                            }
                        }
                    }
                }
                <__V::Result as ::rustc_middle::ty::VisitorResult>::output()
            }
        }
    };TypeVisitable)]
17pub struct MismatchedProjectionTypes<'tcx> {
18    pub err: ty::error::TypeError<'tcx>,
19}
20
21#[derive(#[automatically_derived]
impl<'tcx, T: ::core::clone::Clone> ::core::clone::Clone for
    Normalized<'tcx, T> {
    #[inline]
    fn clone(&self) -> Normalized<'tcx, T> {
        Normalized {
            value: ::core::clone::Clone::clone(&self.value),
            obligations: ::core::clone::Clone::clone(&self.obligations),
        }
    }
}Clone)]
22pub struct Normalized<'tcx, T> {
23    pub value: T,
24    pub obligations: PredicateObligations<'tcx>,
25}
26
27pub type NormalizedTerm<'tcx> = Normalized<'tcx, ty::Term<'tcx>>;
28
29impl<'tcx, T> Normalized<'tcx, T> {
30    pub fn with<U>(self, value: U) -> Normalized<'tcx, U> {
31        Normalized { value, obligations: self.obligations }
32    }
33}
34
35// # Cache
36
37/// The projection cache. Unlike the standard caches, this can include
38/// infcx-dependent type variables, therefore we have to roll the
39/// cache back each time we roll a snapshot back, to avoid assumptions
40/// on yet-unresolved inference variables. Types with placeholder
41/// regions also have to be removed when the respective snapshot ends.
42///
43/// Because of that, projection cache entries can be "stranded" and left
44/// inaccessible when type variables inside the key are resolved. We make no
45/// attempt to recover or remove "stranded" entries, but rather let them be
46/// (for the lifetime of the infcx).
47///
48/// Entries in the projection cache might contain inference variables
49/// that will be resolved by obligations on the projection cache entry (e.g.,
50/// when a type parameter in the associated type is constrained through
51/// an "RFC 447" projection on the impl).
52///
53/// When working with a fulfillment context, the derived obligations of each
54/// projection cache entry will be registered on the fulfillcx, so any users
55/// that can wait for a fulfillcx fixed point need not care about this. However,
56/// users that don't wait for a fixed point (e.g., trait evaluation) have to
57/// resolve the obligations themselves to make sure the projected result is
58/// ok and avoid issues like #43132.
59///
60/// If that is done, after evaluation the obligations, it is a good idea to
61/// call `ProjectionCache::complete` to make sure the obligations won't be
62/// re-evaluated and avoid an exponential worst-case.
63//
64// FIXME: we probably also want some sort of cross-infcx cache here to
65// reduce the amount of duplication. Let's see what we get with the Chalk reforms.
66pub struct ProjectionCache<'a, 'tcx> {
67    map: &'a mut SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
68    undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
69}
70
71#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for ProjectionCacheStorage<'tcx> {
    #[inline]
    fn clone(&self) -> ProjectionCacheStorage<'tcx> {
        ProjectionCacheStorage { map: ::core::clone::Clone::clone(&self.map) }
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::default::Default for ProjectionCacheStorage<'tcx> {
    #[inline]
    fn default() -> ProjectionCacheStorage<'tcx> {
        ProjectionCacheStorage { map: ::core::default::Default::default() }
    }
}Default)]
72pub struct ProjectionCacheStorage<'tcx> {
73    map: SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
74}
75
76#[derive(#[automatically_derived]
impl<'tcx> ::core::marker::Copy for ProjectionCacheKey<'tcx> { }Copy, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for ProjectionCacheKey<'tcx> {
    #[inline]
    fn clone(&self) -> ProjectionCacheKey<'tcx> {
        let _: ::core::clone::AssertParamIsClone<ty::AliasTerm<'tcx>>;
        let _: ::core::clone::AssertParamIsClone<ty::ParamEnv<'tcx>>;
        *self
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for ProjectionCacheKey<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "ProjectionCacheKey", "term", &self.term, "param_env",
            &&self.param_env)
    }
}Debug, #[automatically_derived]
impl<'tcx> ::core::hash::Hash for ProjectionCacheKey<'tcx> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.term, state);
        ::core::hash::Hash::hash(&self.param_env, state)
    }
}Hash, #[automatically_derived]
impl<'tcx> ::core::cmp::PartialEq for ProjectionCacheKey<'tcx> {
    #[inline]
    fn eq(&self, other: &ProjectionCacheKey<'tcx>) -> bool {
        self.term == other.term && self.param_env == other.param_env
    }
}PartialEq, #[automatically_derived]
impl<'tcx> ::core::cmp::Eq for ProjectionCacheKey<'tcx> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<ty::AliasTerm<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<ty::ParamEnv<'tcx>>;
    }
}Eq)]
77pub struct ProjectionCacheKey<'tcx> {
78    term: ty::AliasTerm<'tcx>,
79    param_env: ty::ParamEnv<'tcx>,
80}
81
82impl<'tcx> ProjectionCacheKey<'tcx> {
83    pub fn new(term: ty::AliasTerm<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
84        Self { term, param_env }
85    }
86}
87
88#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for ProjectionCacheEntry<'tcx> {
    #[inline]
    fn clone(&self) -> ProjectionCacheEntry<'tcx> {
        match self {
            ProjectionCacheEntry::InProgress =>
                ProjectionCacheEntry::InProgress,
            ProjectionCacheEntry::Ambiguous =>
                ProjectionCacheEntry::Ambiguous,
            ProjectionCacheEntry::Recur => ProjectionCacheEntry::Recur,
            ProjectionCacheEntry::Error => ProjectionCacheEntry::Error,
            ProjectionCacheEntry::NormalizedTerm {
                ty: __self_0, complete: __self_1 } =>
                ProjectionCacheEntry::NormalizedTerm {
                    ty: ::core::clone::Clone::clone(__self_0),
                    complete: ::core::clone::Clone::clone(__self_1),
                },
        }
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for ProjectionCacheEntry<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            ProjectionCacheEntry::InProgress =>
                ::core::fmt::Formatter::write_str(f, "InProgress"),
            ProjectionCacheEntry::Ambiguous =>
                ::core::fmt::Formatter::write_str(f, "Ambiguous"),
            ProjectionCacheEntry::Recur =>
                ::core::fmt::Formatter::write_str(f, "Recur"),
            ProjectionCacheEntry::Error =>
                ::core::fmt::Formatter::write_str(f, "Error"),
            ProjectionCacheEntry::NormalizedTerm {
                ty: __self_0, complete: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "NormalizedTerm", "ty", __self_0, "complete", &__self_1),
        }
    }
}Debug)]
89pub enum ProjectionCacheEntry<'tcx> {
90    InProgress,
91    Ambiguous,
92    Recur,
93    Error,
94    NormalizedTerm {
95        ty: NormalizedTerm<'tcx>,
96        /// If we were able to successfully evaluate the corresponding cache
97        /// entry key during predicate evaluation, then this field stores the
98        /// final result obtained from evaluating all of the projection
99        /// sub-obligations. During evaluation, we will skip evaluating the
100        /// cached sub-obligations in `ty` if this field is set. Evaluation
101        /// only cares about the final result, so we don't care about any
102        /// region constraint side-effects produced by evaluating the
103        /// sub-obligations.
104        ///
105        /// Additionally, we will clear out the sub-obligations entirely if we
106        /// ever evaluate the cache entry (along with all its sub obligations)
107        /// to `EvaluatedToOk`. This affects all users of the cache, not just
108        /// evaluation. Since a result of `EvaluatedToOk` means that there were
109        /// no region obligations that need to be tracked, it's fine to forget
110        /// about the sub-obligations - they don't provide any additional
111        /// information. However, we do *not* discard any obligations when we
112        /// see `EvaluatedToOkModuloRegions` - we don't know which
113        /// sub-obligations may introduce region constraints, so we keep them
114        /// all to be safe.
115        ///
116        /// When we are not performing evaluation (e.g. in
117        /// `FulfillmentContext`), we ignore this field, and always re-process
118        /// the cached sub-obligations (which may have been cleared out - see
119        /// the above paragraph). This ensures that we do not lose any regions
120        /// constraints that arise from processing the sub-obligations.
121        complete: Option<EvaluationResult>,
122    },
123}
124
125impl<'tcx> ProjectionCacheStorage<'tcx> {
126    #[inline]
127    pub(crate) fn with_log<'a>(
128        &'a mut self,
129        undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
130    ) -> ProjectionCache<'a, 'tcx> {
131        ProjectionCache { map: &mut self.map, undo_log }
132    }
133}
134
135impl<'tcx> ProjectionCache<'_, 'tcx> {
136    #[inline]
137    fn map(
138        &mut self,
139    ) -> SnapshotMapRef<
140        '_,
141        ProjectionCacheKey<'tcx>,
142        ProjectionCacheEntry<'tcx>,
143        InferCtxtUndoLogs<'tcx>,
144    > {
145        self.map.with_log(self.undo_log)
146    }
147
148    pub fn clear(&mut self) {
149        self.map().clear();
150    }
151
152    /// Try to start normalize `key`; returns an error if
153    /// normalization already occurred (this error corresponds to a
154    /// cache hit, so it's actually a good thing).
155    pub fn try_start(
156        &mut self,
157        key: ProjectionCacheKey<'tcx>,
158    ) -> Result<(), ProjectionCacheEntry<'tcx>> {
159        let mut map = self.map();
160        if let Some(entry) = map.get(&key) {
161            return Err(entry.clone());
162        }
163
164        map.insert(key, ProjectionCacheEntry::InProgress);
165        Ok(())
166    }
167
168    /// Indicates that `key` was normalized to `value`.
169    pub fn insert_term(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTerm<'tcx>) {
170        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/traits/project.rs:170",
                        "rustc_infer::traits::project", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/traits/project.rs"),
                        ::tracing_core::__macro_support::Option::Some(170u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_infer::traits::project"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("ProjectionCacheEntry::insert_ty: adding cache entry: key={0:?}, value={1:?}",
                                                    key, value) as &dyn Value))])
            });
    } else { ; }
};debug!(
171            "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}",
172            key, value
173        );
174        let mut map = self.map();
175        if let Some(ProjectionCacheEntry::Recur) = map.get(&key) {
176            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/traits/project.rs:176",
                        "rustc_infer::traits::project", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/traits/project.rs"),
                        ::tracing_core::__macro_support::Option::Some(176u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_infer::traits::project"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("Not overwriting Recur")
                                            as &dyn Value))])
            });
    } else { ; }
};debug!("Not overwriting Recur");
177            return;
178        }
179        let fresh_key =
180            map.insert(key, ProjectionCacheEntry::NormalizedTerm { ty: value, complete: None });
181        if !!fresh_key {
    {
        ::core::panicking::panic_fmt(format_args!("never started projecting `{0:?}`",
                key));
    }
};assert!(!fresh_key, "never started projecting `{key:?}`");
182    }
183
184    /// Mark the relevant projection cache key as having its derived obligations
185    /// complete, so they won't have to be re-computed (this is OK to do in a
186    /// snapshot - if the snapshot is rolled back, the obligations will be
187    /// marked as incomplete again).
188    pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>, result: EvaluationResult) {
189        let mut map = self.map();
190        match map.get(&key) {
191            Some(ProjectionCacheEntry::NormalizedTerm { ty, complete: _ }) => {
192                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/traits/project.rs:192",
                        "rustc_infer::traits::project", ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/traits/project.rs"),
                        ::tracing_core::__macro_support::Option::Some(192u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_infer::traits::project"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("ProjectionCacheEntry::complete({0:?}) - completing {1:?}",
                                                    key, ty) as &dyn Value))])
            });
    } else { ; }
};info!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
193                let mut ty = ty.clone();
194                if result.must_apply_considering_regions() {
195                    ty.obligations = PredicateObligations::new();
196                }
197                map.insert(
198                    key,
199                    ProjectionCacheEntry::NormalizedTerm { ty, complete: Some(result) },
200                );
201            }
202            ref value => {
203                // Type inference could "strand behind" old cache entries. Leave
204                // them alone for now.
205                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_infer/src/traits/project.rs:205",
                        "rustc_infer::traits::project", ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_infer/src/traits/project.rs"),
                        ::tracing_core::__macro_support::Option::Some(205u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_infer::traits::project"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("ProjectionCacheEntry::complete({0:?}) - ignoring {1:?}",
                                                    key, value) as &dyn Value))])
            });
    } else { ; }
};info!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key, value);
206            }
207        };
208    }
209
210    pub fn is_complete(&mut self, key: ProjectionCacheKey<'tcx>) -> Option<EvaluationResult> {
211        self.map().get(&key).and_then(|res| match res {
212            ProjectionCacheEntry::NormalizedTerm { ty: _, complete } => *complete,
213            _ => None,
214        })
215    }
216
217    /// Indicates that trying to normalize `key` resulted in
218    /// ambiguity. No point in trying it again then until we gain more
219    /// type information (in which case, the "fully resolved" key will
220    /// be different).
221    pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) {
222        let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous);
223        if !!fresh {
    {
        ::core::panicking::panic_fmt(format_args!("never started projecting `{0:?}`",
                key));
    }
};assert!(!fresh, "never started projecting `{key:?}`");
224    }
225
226    /// Indicates that while trying to normalize `key`, `key` was required to
227    /// be normalized again. Selection or evaluation should eventually report
228    /// an error here.
229    pub fn recur(&mut self, key: ProjectionCacheKey<'tcx>) {
230        let fresh = self.map().insert(key, ProjectionCacheEntry::Recur);
231        if !!fresh {
    {
        ::core::panicking::panic_fmt(format_args!("never started projecting `{0:?}`",
                key));
    }
};assert!(!fresh, "never started projecting `{key:?}`");
232    }
233
234    /// Indicates that trying to normalize `key` resulted in
235    /// error.
236    pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) {
237        let fresh = self.map().insert(key, ProjectionCacheEntry::Error);
238        if !!fresh {
    {
        ::core::panicking::panic_fmt(format_args!("never started projecting `{0:?}`",
                key));
    }
};assert!(!fresh, "never started projecting `{key:?}`");
239    }
240}
241
242impl<'tcx> Rollback<UndoLog<'tcx>> for ProjectionCacheStorage<'tcx> {
243    fn reverse(&mut self, undo: UndoLog<'tcx>) {
244        self.map.reverse(undo);
245    }
246}