Skip to main content

rustc_infer/infer/snapshot/
undo_log.rs

1use std::assert_matches;
2use std::marker::PhantomData;
3
4use rustc_data_structures::undo_log::{Rollback, UndoLogs};
5use rustc_data_structures::{snapshot_vec as sv, unify as ut};
6use rustc_middle::ty::{self, OpaqueTypeKey, ProvisionalHiddenType};
7use tracing::debug;
8
9use crate::infer::unify_key::{ConstVidKey, RegionVidKey};
10use crate::infer::{InferCtxtInner, SolverRegionConstraint, region_constraints, type_variable};
11use crate::traits;
12
13pub struct Snapshot<'tcx> {
14    pub(crate) undo_len: usize,
15    _marker: PhantomData<&'tcx ()>,
16}
17
18/// Records the "undo" data for a single operation that affects some form of inference variable.
19#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for UndoLog<'tcx> {
    #[inline]
    fn clone(&self) -> Self {
        match self {
            UndoLog::DuplicateOpaqueType => UndoLog::DuplicateOpaqueType,
            UndoLog::OpaqueTypes(__self_0, __self_1) =>
                UndoLog::OpaqueTypes(::core::clone::Clone::clone(__self_0),
                    ::core::clone::Clone::clone(__self_1)),
            UndoLog::TypeVariables(__self_0) =>
                UndoLog::TypeVariables(::core::clone::Clone::clone(__self_0)),
            UndoLog::ConstUnificationTable(__self_0) =>
                UndoLog::ConstUnificationTable(::core::clone::Clone::clone(__self_0)),
            UndoLog::IntUnificationTable(__self_0) =>
                UndoLog::IntUnificationTable(::core::clone::Clone::clone(__self_0)),
            UndoLog::FloatUnificationTable(__self_0) =>
                UndoLog::FloatUnificationTable(::core::clone::Clone::clone(__self_0)),
            UndoLog::RegionConstraintCollector(__self_0) =>
                UndoLog::RegionConstraintCollector(::core::clone::Clone::clone(__self_0)),
            UndoLog::RegionUnificationTable(__self_0) =>
                UndoLog::RegionUnificationTable(::core::clone::Clone::clone(__self_0)),
            UndoLog::ProjectionCache(__self_0) =>
                UndoLog::ProjectionCache(::core::clone::Clone::clone(__self_0)),
            UndoLog::PushTypeOutlivesConstraint =>
                UndoLog::PushTypeOutlivesConstraint,
            UndoLog::OverwriteSolverRegionConstraint {
                old_constraint: __self_0 } =>
                UndoLog::OverwriteSolverRegionConstraint {
                    old_constraint: ::core::clone::Clone::clone(__self_0),
                },
            UndoLog::PushRegionAssumption => UndoLog::PushRegionAssumption,
            UndoLog::PushHirTypeckPotentiallyRegionDependentGoal =>
                UndoLog::PushHirTypeckPotentiallyRegionDependentGoal,
        }
    }
}Clone)]
20pub(crate) enum UndoLog<'tcx> {
21    DuplicateOpaqueType,
22    OpaqueTypes(OpaqueTypeKey<'tcx>, Option<ProvisionalHiddenType<'tcx>>),
23    TypeVariables(type_variable::UndoLog<'tcx>),
24    ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
25    IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
26    FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
27    RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
28    RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
29    ProjectionCache(traits::UndoLog<'tcx>),
30    PushTypeOutlivesConstraint,
31    OverwriteSolverRegionConstraint { old_constraint: SolverRegionConstraint<'tcx> },
32    PushRegionAssumption,
33    PushHirTypeckPotentiallyRegionDependentGoal,
34}
35
36macro_rules! impl_from {
37    ($($ctor:ident ($ty:ty),)*) => {
38        $(
39        impl<'tcx> From<$ty> for UndoLog<'tcx> {
40            fn from(x: $ty) -> Self {
41                UndoLog::$ctor(x.into())
42            }
43        }
44        )*
45    }
46}
47
48// Upcast from a single kind of "undoable action" to the general enum
49impl<'tcx> From<region_constraints::UndoLog<'tcx>> for UndoLog<'tcx> {
    fn from(x: region_constraints::UndoLog<'tcx>) -> Self {
        UndoLog::RegionConstraintCollector(x.into())
    }
}
impl<'tcx> From<sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>>
    for UndoLog<'tcx> {
    fn from(x: sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>)
        -> Self {
        UndoLog::TypeVariables(x.into())
    }
}
impl<'tcx> From<sv::UndoLog<ut::Delegate<type_variable::TyVidSubKey>>> for
    UndoLog<'tcx> {
    fn from(x: sv::UndoLog<ut::Delegate<type_variable::TyVidSubKey>>)
        -> Self {
        UndoLog::TypeVariables(x.into())
    }
}
impl<'tcx> From<type_variable::UndoLog<'tcx>> for UndoLog<'tcx> {
    fn from(x: type_variable::UndoLog<'tcx>) -> Self {
        UndoLog::TypeVariables(x.into())
    }
}
impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::IntVid>>> for UndoLog<'tcx> {
    fn from(x: sv::UndoLog<ut::Delegate<ty::IntVid>>) -> Self {
        UndoLog::IntUnificationTable(x.into())
    }
}
impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::FloatVid>>> for UndoLog<'tcx> {
    fn from(x: sv::UndoLog<ut::Delegate<ty::FloatVid>>) -> Self {
        UndoLog::FloatUnificationTable(x.into())
    }
}
impl<'tcx> From<sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>> for
    UndoLog<'tcx> {
    fn from(x: sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>) -> Self {
        UndoLog::ConstUnificationTable(x.into())
    }
}
impl<'tcx> From<sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>> for
    UndoLog<'tcx> {
    fn from(x: sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>) -> Self {
        UndoLog::RegionUnificationTable(x.into())
    }
}
impl<'tcx> From<traits::UndoLog<'tcx>> for UndoLog<'tcx> {
    fn from(x: traits::UndoLog<'tcx>) -> Self {
        UndoLog::ProjectionCache(x.into())
    }
}impl_from! {
50    RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
51
52    TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
53    TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidSubKey>>),
54    TypeVariables(type_variable::UndoLog<'tcx>),
55    IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
56    FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
57
58    ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
59
60    RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
61    ProjectionCache(traits::UndoLog<'tcx>),
62}
63
64/// The Rollback trait defines how to rollback a particular action.
65impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
66    fn reverse(&mut self, undo: UndoLog<'tcx>) {
67        match undo {
68            UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(),
69            UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx),
70            UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
71            UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
72            UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
73            UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
74            UndoLog::RegionConstraintCollector(undo) => {
75                self.region_constraint_storage.as_mut().unwrap().reverse(undo)
76            }
77            UndoLog::RegionUnificationTable(undo) => {
78                self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
79            }
80            UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo),
81            UndoLog::OverwriteSolverRegionConstraint { old_constraint } => {
82                self.solver_region_constraint_storage.overwrite(old_constraint);
83            }
84            UndoLog::PushTypeOutlivesConstraint => {
85                let popped = self.region_obligations.pop();
86                {
    match popped {
        Some(_) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val, "Some(_)",
                ::core::option::Option::Some(format_args!("pushed region constraint but could not pop it")));
        }
    }
};assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
87            }
88            UndoLog::PushRegionAssumption => {
89                let popped = self.region_assumptions.pop();
90                {
    match popped {
        Some(_) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val, "Some(_)",
                ::core::option::Option::Some(format_args!("pushed region assumption but could not pop it")));
        }
    }
};assert_matches!(popped, Some(_), "pushed region assumption but could not pop it");
91            }
92            UndoLog::PushHirTypeckPotentiallyRegionDependentGoal => {
93                let popped = self.hir_typeck_potentially_region_dependent_goals.pop();
94                {
    match popped {
        Some(_) => {}
        ref left_val => {
            ::core::panicking::assert_matches_failed(left_val, "Some(_)",
                ::core::option::Option::Some(format_args!("pushed goal but could not pop it")));
        }
    }
};assert_matches!(popped, Some(_), "pushed goal but could not pop it");
95            }
96        }
97    }
98}
99
100/// The combined undo log for all the various unification tables. For each change to the storage
101/// for any kind of inference variable, we record an UndoLog entry in the vector here.
102#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for InferCtxtUndoLogs<'tcx> {
    #[inline]
    fn clone(&self) -> Self {
        InferCtxtUndoLogs {
            logs: ::core::clone::Clone::clone(&self.logs),
            num_open_snapshots: ::core::clone::Clone::clone(&self.num_open_snapshots),
        }
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::default::Default for InferCtxtUndoLogs<'tcx> {
    #[inline]
    fn default() -> Self {
        InferCtxtUndoLogs {
            logs: ::core::default::Default::default(),
            num_open_snapshots: ::core::default::Default::default(),
        }
    }
}Default)]
103pub(crate) struct InferCtxtUndoLogs<'tcx> {
104    logs: Vec<UndoLog<'tcx>>,
105    num_open_snapshots: usize,
106}
107
108/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
109/// action that is convertible into an UndoLog (per the From impls above).
110impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx>
111where
112    UndoLog<'tcx>: From<T>,
113{
114    #[inline]
115    fn num_open_snapshots(&self) -> usize {
116        self.num_open_snapshots
117    }
118
119    #[inline]
120    fn push(&mut self, undo: T) {
121        if self.in_snapshot() {
122            self.logs.push(undo.into())
123        }
124    }
125
126    fn clear(&mut self) {
127        self.logs.clear();
128        self.num_open_snapshots = 0;
129    }
130
131    fn extend<J>(&mut self, undos: J)
132    where
133        J: IntoIterator<Item = T>,
134    {
135        if self.in_snapshot() {
136            self.logs.extend(undos.into_iter().map(UndoLog::from))
137        }
138    }
139}
140
141impl<'tcx> InferCtxtInner<'tcx> {
142    pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) {
143        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/1303417c416e1595173d9689e7394c31e136ae95/compiler/rustc_infer/src/infer/snapshot/undo_log.rs:143",
                        "rustc_infer::infer::snapshot::undo_log",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/1303417c416e1595173d9689e7394c31e136ae95/compiler/rustc_infer/src/infer/snapshot/undo_log.rs"),
                        ::tracing_core::__macro_support::Option::Some(143u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_infer::infer::snapshot::undo_log"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("rollback_to({0})",
                                                    snapshot.undo_len) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("rollback_to({})", snapshot.undo_len);
144        self.undo_log.assert_open_snapshot(&snapshot);
145
146        while self.undo_log.logs.len() > snapshot.undo_len {
147            let undo = self.undo_log.logs.pop().unwrap();
148            self.reverse(undo);
149        }
150
151        self.type_variable_storage.finalize_rollback();
152
153        if self.undo_log.num_open_snapshots == 1 {
154            // After the root snapshot the undo log should be empty.
155            if !(snapshot.undo_len == 0) {
    ::core::panicking::panic("assertion failed: snapshot.undo_len == 0")
};assert!(snapshot.undo_len == 0);
156            if !self.undo_log.logs.is_empty() {
    ::core::panicking::panic("assertion failed: self.undo_log.logs.is_empty()")
};assert!(self.undo_log.logs.is_empty());
157        }
158
159        self.undo_log.num_open_snapshots -= 1;
160    }
161
162    pub fn commit(&mut self, snapshot: Snapshot<'tcx>) {
163        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/1303417c416e1595173d9689e7394c31e136ae95/compiler/rustc_infer/src/infer/snapshot/undo_log.rs:163",
                        "rustc_infer::infer::snapshot::undo_log",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/1303417c416e1595173d9689e7394c31e136ae95/compiler/rustc_infer/src/infer/snapshot/undo_log.rs"),
                        ::tracing_core::__macro_support::Option::Some(163u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_infer::infer::snapshot::undo_log"),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("commit({0})",
                                                    snapshot.undo_len) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("commit({})", snapshot.undo_len);
164
165        if self.undo_log.num_open_snapshots == 1 {
166            // The root snapshot. It's safe to clear the undo log because
167            // there's no snapshot further out that we might need to roll back
168            // to.
169            if !(snapshot.undo_len == 0) {
    ::core::panicking::panic("assertion failed: snapshot.undo_len == 0")
};assert!(snapshot.undo_len == 0);
170            self.undo_log.logs.clear();
171        }
172
173        self.undo_log.num_open_snapshots -= 1;
174    }
175}
176
177impl<'tcx> InferCtxtUndoLogs<'tcx> {
178    pub(crate) fn start_snapshot(&mut self) -> Snapshot<'tcx> {
179        self.num_open_snapshots += 1;
180        Snapshot { undo_len: self.logs.len(), _marker: PhantomData }
181    }
182
183    pub(crate) fn region_constraints_in_snapshot(
184        &self,
185        s: &Snapshot<'tcx>,
186    ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
187        self.logs[s.undo_len..].iter().filter_map(|log| match log {
188            UndoLog::RegionConstraintCollector(log) => Some(log),
189            _ => None,
190        })
191    }
192
193    pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot<'tcx>) -> bool {
194        self.logs[s.undo_len..].iter().any(|log| #[allow(non_exhaustive_omitted_patterns)] match log {
    UndoLog::OpaqueTypes(..) => true,
    _ => false,
}matches!(log, UndoLog::OpaqueTypes(..)))
195    }
196
197    fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
198        // Failures here may indicate a failure to follow a stack discipline.
199        if !(self.logs.len() >= snapshot.undo_len) {
    ::core::panicking::panic("assertion failed: self.logs.len() >= snapshot.undo_len")
};assert!(self.logs.len() >= snapshot.undo_len);
200        if !(self.num_open_snapshots > 0) {
    ::core::panicking::panic("assertion failed: self.num_open_snapshots > 0")
};assert!(self.num_open_snapshots > 0);
201    }
202}
203
204impl<'tcx> std::ops::Index<usize> for InferCtxtUndoLogs<'tcx> {
205    type Output = UndoLog<'tcx>;
206
207    fn index(&self, key: usize) -> &Self::Output {
208        &self.logs[key]
209    }
210}
211
212impl<'tcx> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'tcx> {
213    fn index_mut(&mut self, key: usize) -> &mut Self::Output {
214        &mut self.logs[key]
215    }
216}