diff --git a/Cargo.lock b/Cargo.lock index 16bb501d312..83d7df889f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -993,6 +993,15 @@ dependencies = [ "log", ] +[[package]] +name = "ena" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" +dependencies = [ + "log", +] + [[package]] name = "encoding_rs" version = "0.8.17" @@ -3234,7 +3243,7 @@ dependencies = [ "bitflags", "cfg-if", "crossbeam-utils 0.7.2", - "ena", + "ena 0.13.1", "indexmap", "jobserver", "lazy_static 1.4.0", @@ -3683,7 +3692,7 @@ dependencies = [ "bitflags", "cfg-if", "crossbeam-utils 0.7.2", - "ena", + "ena 0.14.0", "graphviz", "indexmap", "jobserver", diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml index 6d7022acc78..e257ada0629 100644 --- a/src/librustc_data_structures/Cargo.toml +++ b/src/librustc_data_structures/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" doctest = false [dependencies] -ena = "0.13.1" +ena = "0.14" indexmap = "1" log = "0.4" jobserver_crate = { version = "0.1.13", package = "jobserver" } diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index bc2da535fd3..a7bee8a067c 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -84,6 +84,7 @@ pub mod sync; pub mod thin_vec; pub mod tiny_list; pub mod transitive_relation; +pub use ena::undo_log; pub use ena::unify; mod atomic_ref; pub mod fingerprint; diff --git a/src/librustc_data_structures/snapshot_map/mod.rs b/src/librustc_data_structures/snapshot_map/mod.rs index b71163a8f94..52865f55f78 100644 --- a/src/librustc_data_structures/snapshot_map/mod.rs +++ b/src/librustc_data_structures/snapshot_map/mod.rs @@ -1,77 +1,75 @@ use crate::fx::FxHashMap; +use crate::undo_log::{Rollback, Snapshots, UndoLogs, VecLog}; +use std::borrow::{Borrow, BorrowMut}; use std::hash::Hash; -use std::mem; +use std::marker::PhantomData; use std::ops; +pub use crate::undo_log::Snapshot; + #[cfg(test)] mod tests; -pub struct SnapshotMap -where - K: Clone + Eq, -{ - map: FxHashMap, - undo_log: Vec>, - num_open_snapshots: usize, +pub type SnapshotMapStorage = SnapshotMap, ()>; +pub type SnapshotMapRef<'a, K, V, L> = SnapshotMap, &'a mut L>; + +pub struct SnapshotMap, L = VecLog>> { + map: M, + undo_log: L, + _marker: PhantomData<(K, V)>, } // HACK(eddyb) manual impl avoids `Default` bounds on `K` and `V`. -impl Default for SnapshotMap +impl Default for SnapshotMap where - K: Hash + Clone + Eq, + M: Default, + L: Default, { fn default() -> Self { - SnapshotMap { map: Default::default(), undo_log: Default::default(), num_open_snapshots: 0 } + SnapshotMap { map: Default::default(), undo_log: Default::default(), _marker: PhantomData } } } -pub struct Snapshot { - len: usize, -} - -enum UndoLog { +pub enum UndoLog { Inserted(K), Overwrite(K, V), Purged, } -impl SnapshotMap +impl SnapshotMap { + pub fn with_log(&mut self, undo_log: L2) -> SnapshotMap { + SnapshotMap { map: &mut self.map, undo_log, _marker: PhantomData } + } +} + +impl SnapshotMap where K: Hash + Clone + Eq, + M: BorrowMut> + Borrow>, + L: UndoLogs>, { pub fn clear(&mut self) { - self.map.clear(); + self.map.borrow_mut().clear(); self.undo_log.clear(); - self.num_open_snapshots = 0; - } - - fn in_snapshot(&self) -> bool { - self.num_open_snapshots > 0 } pub fn insert(&mut self, key: K, value: V) -> bool { - match self.map.insert(key.clone(), value) { + match self.map.borrow_mut().insert(key.clone(), value) { None => { - if self.in_snapshot() { - self.undo_log.push(UndoLog::Inserted(key)); - } + self.undo_log.push(UndoLog::Inserted(key)); true } Some(old_value) => { - if self.in_snapshot() { - self.undo_log.push(UndoLog::Overwrite(key, old_value)); - } + self.undo_log.push(UndoLog::Overwrite(key, old_value)); false } } } pub fn remove(&mut self, key: K) -> bool { - match self.map.remove(&key) { + match self.map.borrow_mut().remove(&key) { Some(old_value) => { - if self.in_snapshot() { - self.undo_log.push(UndoLog::Overwrite(key, old_value)); - } + self.undo_log.push(UndoLog::Overwrite(key, old_value)); true } None => false, @@ -79,83 +77,64 @@ where } pub fn get(&self, key: &K) -> Option<&V> { - self.map.get(key) + self.map.borrow().get(key) } +} +impl SnapshotMap +where + K: Hash + Clone + Eq, +{ pub fn snapshot(&mut self) -> Snapshot { - let len = self.undo_log.len(); - self.num_open_snapshots += 1; - Snapshot { len } - } - - fn assert_open_snapshot(&self, snapshot: &Snapshot) { - assert!(self.undo_log.len() >= snapshot.len); - assert!(self.num_open_snapshots > 0); + self.undo_log.start_snapshot() } pub fn commit(&mut self, snapshot: Snapshot) { - self.assert_open_snapshot(&snapshot); - if self.num_open_snapshots == 1 { - // The root snapshot. It's safe to clear the undo log because - // there's no snapshot further out that we might need to roll back - // to. - assert!(snapshot.len == 0); - self.undo_log.clear(); - } - - self.num_open_snapshots -= 1; - } - - pub fn partial_rollback(&mut self, snapshot: &Snapshot, should_revert_key: &F) - where - F: Fn(&K) -> bool, - { - self.assert_open_snapshot(snapshot); - for i in (snapshot.len..self.undo_log.len()).rev() { - let reverse = match self.undo_log[i] { - UndoLog::Purged => false, - UndoLog::Inserted(ref k) => should_revert_key(k), - UndoLog::Overwrite(ref k, _) => should_revert_key(k), - }; - - if reverse { - let entry = mem::replace(&mut self.undo_log[i], UndoLog::Purged); - self.reverse(entry); - } - } + self.undo_log.commit(snapshot) } pub fn rollback_to(&mut self, snapshot: Snapshot) { - self.assert_open_snapshot(&snapshot); - while self.undo_log.len() > snapshot.len { - let entry = self.undo_log.pop().unwrap(); - self.reverse(entry); - } - - self.num_open_snapshots -= 1; + let map = &mut self.map; + self.undo_log.rollback_to(|| map, snapshot) } +} - fn reverse(&mut self, entry: UndoLog) { - match entry { +impl<'k, K, V, M, L> ops::Index<&'k K> for SnapshotMap +where + K: Hash + Clone + Eq, + M: Borrow>, +{ + type Output = V; + fn index(&self, key: &'k K) -> &V { + &self.map.borrow()[key] + } +} + +impl Rollback> for SnapshotMap +where + K: Eq + Hash, + M: Rollback>, +{ + fn reverse(&mut self, undo: UndoLog) { + self.map.reverse(undo) + } +} + +impl Rollback> for FxHashMap +where + K: Eq + Hash, +{ + fn reverse(&mut self, undo: UndoLog) { + match undo { UndoLog::Inserted(key) => { - self.map.remove(&key); + self.remove(&key); } UndoLog::Overwrite(key, old_value) => { - self.map.insert(key, old_value); + self.insert(key, old_value); } UndoLog::Purged => {} } } } - -impl<'k, K, V> ops::Index<&'k K> for SnapshotMap -where - K: Hash + Clone + Eq, -{ - type Output = V; - fn index(&self, key: &'k K) -> &V { - &self.map[key] - } -} diff --git a/src/librustc_infer/infer/combine.rs b/src/librustc_infer/infer/combine.rs index b03044b72da..d4af4704996 100644 --- a/src/librustc_infer/infer/combine.rs +++ b/src/librustc_infer/infer/combine.rs @@ -76,7 +76,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { self.inner .borrow_mut() - .int_unification_table + .int_unification_table() .unify_var_var(a_id, b_id) .map_err(|e| int_unification_error(a_is_expected, e))?; Ok(a) @@ -98,7 +98,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => { self.inner .borrow_mut() - .float_unification_table + .float_unification_table() .unify_var_var(a_id, b_id) .map_err(|e| float_unification_error(relation.a_is_expected(), e))?; Ok(a) @@ -133,8 +133,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { return Ok(a); } - let a = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table, a); - let b = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table, b); + let a = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), a); + let b = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), b); let a_is_expected = relation.a_is_expected(); @@ -145,7 +145,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) => { self.inner .borrow_mut() - .const_unification_table + .const_unification_table() .unify_var_var(a_vid, b_vid) .map_err(|e| const_unification_error(a_is_expected, e))?; return Ok(a); @@ -179,7 +179,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { self.inner .borrow_mut() - .const_unification_table + .const_unification_table() .unify_var_value( vid, ConstVarValue { @@ -202,7 +202,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) -> RelateResult<'tcx, Ty<'tcx>> { self.inner .borrow_mut() - .int_unification_table + .int_unification_table() .unify_var_value(vid, Some(val)) .map_err(|e| int_unification_error(vid_is_expected, e))?; match val { @@ -219,7 +219,7 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { ) -> RelateResult<'tcx, Ty<'tcx>> { self.inner .borrow_mut() - .float_unification_table + .float_unification_table() .unify_var_value(vid, Some(ty::FloatVarValue(val))) .map_err(|e| float_unification_error(vid_is_expected, e))?; Ok(self.tcx.mk_mach_float(val)) @@ -266,7 +266,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { use self::RelationDir::*; // Get the actual variable that b_vid has been inferred to - debug_assert!(self.infcx.inner.borrow_mut().type_variables.probe(b_vid).is_unknown()); + debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown()); debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid); @@ -286,7 +286,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { "instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})", a_ty, dir, b_vid, b_ty ); - self.infcx.inner.borrow_mut().type_variables.instantiate(b_vid, b_ty); + self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty); if needs_wf { self.obligations.push(Obligation::new( @@ -344,7 +344,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { debug!("generalize: ambient_variance = {:?}", ambient_variance); - let for_universe = match self.infcx.inner.borrow_mut().type_variables.probe(for_vid) { + let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) { v @ TypeVariableValue::Known { .. } => { panic!("instantiating {:?} which has a known value {:?}", for_vid, v,) } @@ -356,7 +356,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { let mut generalize = Generalizer { infcx: self.infcx, span: self.trace.cause.span, - for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables.sub_root_var(for_vid), + for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), for_universe, ambient_variance, needs_wf: false, @@ -508,14 +508,14 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { // us from creating infinitely sized types. match t.kind { ty::Infer(ty::TyVar(vid)) => { - let vid = self.infcx.inner.borrow_mut().type_variables.root_var(vid); - let sub_vid = self.infcx.inner.borrow_mut().type_variables.sub_root_var(vid); + let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid); + let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid); if sub_vid == self.for_vid_sub_root { // If sub-roots are equal, then `for_vid` and // `vid` are related via subtyping. Err(TypeError::CyclicTy(self.root_ty)) } else { - let probe = self.infcx.inner.borrow_mut().type_variables.probe(vid); + let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid); match probe { TypeVariableValue::Known { value: u } => { debug!("generalize: known value {:?}", u); @@ -542,12 +542,13 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { } let origin = - *self.infcx.inner.borrow_mut().type_variables.var_origin(vid); - let new_var_id = self.infcx.inner.borrow_mut().type_variables.new_var( - self.for_universe, - false, - origin, - ); + *self.infcx.inner.borrow_mut().type_variables().var_origin(vid); + let new_var_id = self + .infcx + .inner + .borrow_mut() + .type_variables() + .new_var(self.for_universe, false, origin); let u = self.tcx().mk_ty_var(new_var_id); debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); Ok(u) @@ -618,7 +619,8 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> { match c.val { ty::ConstKind::Infer(InferConst::Var(vid)) => { - let variable_table = &mut self.infcx.inner.borrow_mut().const_unification_table; + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); let var_value = variable_table.probe_value(vid); match var_value.val { ConstVariableValue::Known { value: u } => self.relate(&u, &u), diff --git a/src/librustc_infer/infer/equate.rs b/src/librustc_infer/infer/equate.rs index e05094cda27..d054070e292 100644 --- a/src/librustc_infer/infer/equate.rs +++ b/src/librustc_infer/infer/equate.rs @@ -72,14 +72,14 @@ impl TypeRelation<'tcx> for Equate<'combine, 'infcx, 'tcx> { } let infcx = self.fields.infcx; - let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b); + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b); match (&a.kind, &b.kind) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { - infcx.inner.borrow_mut().type_variables.equate(a_id, b_id); + infcx.inner.borrow_mut().type_variables().equate(a_id, b_id); } (&ty::Infer(TyVar(a_id)), _) => { diff --git a/src/librustc_infer/infer/error_reporting/need_type_info.rs b/src/librustc_infer/infer/error_reporting/need_type_info.rs index 93c8e505697..d8133c58df7 100644 --- a/src/librustc_infer/infer/error_reporting/need_type_info.rs +++ b/src/librustc_infer/infer/error_reporting/need_type_info.rs @@ -59,7 +59,7 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { .infcx .inner .borrow_mut() - .type_variables + .type_variables() .sub_unified(a_vid, b_vid), _ => false, } @@ -194,7 +194,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { highlight: Option, ) -> (String, Option, Cow<'static, str>, Option, Option<&'static str>) { if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind { - let ty_vars = &self.inner.borrow().type_variables; + let mut inner = self.inner.borrow_mut(); + let ty_vars = &inner.type_variables(); let var_origin = ty_vars.var_origin(ty_vid); if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = var_origin.kind { let parent_def_id = def_id.and_then(|def_id| self.tcx.parent(def_id)); @@ -248,7 +249,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let ty_to_string = |ty: Ty<'tcx>| -> String { let mut s = String::new(); let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); - let ty_vars = &self.inner.borrow().type_variables; + let mut inner = self.inner.borrow_mut(); + let ty_vars = inner.type_variables(); let getter = move |ty_vid| { let var_origin = ty_vars.var_origin(ty_vid); if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind { diff --git a/src/librustc_infer/infer/freshen.rs b/src/librustc_infer/infer/freshen.rs index 636cf42198b..47346c3a856 100644 --- a/src/librustc_infer/infer/freshen.rs +++ b/src/librustc_infer/infer/freshen.rs @@ -147,7 +147,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { match t.kind { ty::Infer(ty::TyVar(v)) => { - let opt_ty = self.infcx.inner.borrow_mut().type_variables.probe(v).known(); + let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy) } @@ -155,7 +155,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { self.infcx .inner .borrow_mut() - .int_unification_table + .int_unification_table() .probe_value(v) .map(|v| v.to_type(tcx)), ty::IntVar(v), @@ -166,7 +166,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { self.infcx .inner .borrow_mut() - .float_unification_table + .float_unification_table() .probe_value(v) .map(|v| v.to_type(tcx)), ty::FloatVar(v), @@ -222,7 +222,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { .infcx .inner .borrow_mut() - .const_unification_table + .const_unification_table() .probe_value(v) .val .known(); diff --git a/src/librustc_infer/infer/fudge.rs b/src/librustc_infer/infer/fudge.rs index 1a58e100fb3..c6651108df5 100644 --- a/src/librustc_infer/infer/fudge.rs +++ b/src/librustc_infer/infer/fudge.rs @@ -3,18 +3,30 @@ use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, use super::type_variable::TypeVariableOrigin; use super::InferCtxt; -use super::{ConstVariableOrigin, RegionVariableOrigin}; +use super::{ConstVariableOrigin, RegionVariableOrigin, UnificationTable}; +use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::unify as ut; use ut::UnifyKey; use std::ops::Range; +fn vars_since_snapshot<'tcx, T>( + table: &mut UnificationTable<'_, 'tcx, T>, + snapshot_var_len: usize, +) -> Range +where + T: UnifyKey, + super::UndoLog<'tcx>: From>>, +{ + T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32) +} + fn const_vars_since_snapshot<'tcx>( - table: &mut ut::UnificationTable>>, - snapshot: &ut::Snapshot>>, + table: &mut UnificationTable<'_, 'tcx, ConstVid<'tcx>>, + snapshot_var_len: usize, ) -> (Range>, Vec) { - let range = table.vars_since_snapshot(snapshot); + let range = vars_since_snapshot(table, snapshot_var_len); ( range.start..range.end, (range.start.index..range.end.index) @@ -23,7 +35,26 @@ fn const_vars_since_snapshot<'tcx>( ) } +struct VariableLengths { + type_var_len: usize, + const_var_len: usize, + int_var_len: usize, + float_var_len: usize, + region_constraints_len: usize, +} + impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + type_var_len: inner.type_variables().num_vars(), + const_var_len: inner.const_unification_table().len(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + } + } + /// This rather funky routine is used while processing expected /// types. What happens here is that we want to propagate a /// coercion through the return type of a fn to its @@ -70,7 +101,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { debug!("fudge_inference_if_ok()"); - let (mut fudger, value) = self.probe(|snapshot| { + let variable_lengths = self.variable_lengths(); + let (mut fudger, value) = self.probe(|_| { match f() { Ok(value) => { let value = self.resolve_vars_if_possible(&value); @@ -83,17 +115,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let mut inner = self.inner.borrow_mut(); let type_vars = - inner.type_variables.vars_since_snapshot(&snapshot.type_snapshot); - let int_vars = - inner.int_unification_table.vars_since_snapshot(&snapshot.int_snapshot); - let float_vars = - inner.float_unification_table.vars_since_snapshot(&snapshot.float_snapshot); + inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); + let int_vars = vars_since_snapshot( + &mut inner.int_unification_table(), + variable_lengths.int_var_len, + ); + let float_vars = vars_since_snapshot( + &mut inner.float_unification_table(), + variable_lengths.float_var_len, + ); let region_vars = inner .unwrap_region_constraints() - .vars_since_snapshot(&snapshot.region_constraints_snapshot); + .vars_since_snapshot(variable_lengths.region_constraints_len); let const_vars = const_vars_since_snapshot( - &mut inner.const_unification_table, - &snapshot.const_snapshot, + &mut inner.const_unification_table(), + variable_lengths.const_var_len, ); let fudger = InferenceFudger { @@ -161,7 +197,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> { // that it is unbound, so we can just return // it. debug_assert!( - self.infcx.inner.borrow_mut().type_variables.probe(vid).is_unknown() + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() ); ty } diff --git a/src/librustc_infer/infer/lattice.rs b/src/librustc_infer/infer/lattice.rs index c29614b8556..1bf43e74dcd 100644 --- a/src/librustc_infer/infer/lattice.rs +++ b/src/librustc_infer/infer/lattice.rs @@ -56,8 +56,8 @@ where } let infcx = this.infcx(); - let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b); + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); match (&a.kind, &b.kind) { // If one side is known to be a variable and one is not, // create a variable (`v`) to represent the LUB. Make sure to diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs index 54f80e8f388..2cf9dd882e4 100644 --- a/src/librustc_infer/infer/mod.rs +++ b/src/librustc_infer/infer/mod.rs @@ -6,11 +6,14 @@ pub use self::RegionVariableOrigin::*; pub use self::SubregionOrigin::*; pub use self::ValuePairs::*; +pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; + use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::unify as ut; use rustc_errors::DiagnosticBuilder; use rustc_hir as hir; @@ -42,7 +45,9 @@ use self::free_regions::RegionRelations; use self::lexical_region_resolve::LexicalRegionResolutions; use self::outlives::env::OutlivesEnvironment; use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound}; -use self::region_constraints::{RegionConstraintCollector, RegionSnapshot}; +use self::region_constraints::{ + RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, +}; use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; pub mod at; @@ -64,6 +69,7 @@ pub mod region_constraints; pub mod resolve; mod sub; pub mod type_variable; +mod undo_log; use crate::infer::canonical::OriginalQueryValues; pub use rustc_middle::infer::unify_key; @@ -80,6 +86,10 @@ pub type Bound = Option; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult<'tcx, T> = Result>; // "fixup result" +pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable< + ut::InPlace, &'a mut InferCtxtUndoLogs<'tcx>>, +>; + /// How we should handle region solving. /// /// This is used so that the region values inferred by HIR region solving are @@ -136,28 +146,28 @@ pub struct InferCtxtInner<'tcx> { /// Cache for projections. This cache is snapshotted along with the infcx. /// /// Public so that `traits::project` can use it. - pub projection_cache: traits::ProjectionCache<'tcx>, + pub projection_cache: traits::ProjectionCacheStorage<'tcx>, /// We instantiate `UnificationTable` with `bounds` because the types /// that might instantiate a general type variable have an order, /// represented by its upper and lower bounds. - type_variables: type_variable::TypeVariableTable<'tcx>, + type_variable_storage: type_variable::TypeVariableStorage<'tcx>, /// Map from const parameter variable to the kind of const it represents. - const_unification_table: ut::UnificationTable>>, + const_unification_storage: ut::UnificationTableStorage>, /// Map from integral variable to the kind of integer it represents. - int_unification_table: ut::UnificationTable>, + int_unification_storage: ut::UnificationTableStorage, /// Map from floating variable to the kind of float it represents. - float_unification_table: ut::UnificationTable>, + float_unification_storage: ut::UnificationTableStorage, /// Tracks the set of region variables and the constraints between them. /// This is initially `Some(_)` but when /// `resolve_regions_and_report_errors` is invoked, this gets set to `None` /// -- further attempts to perform unification, etc., may fail if new /// region constraints would've been added. - region_constraints: Option>, + region_constraint_storage: Option>, /// A set of constraints that regionck must validate. Each /// constraint has the form `T:'a`, meaning "some type `T` must @@ -190,24 +200,78 @@ pub struct InferCtxtInner<'tcx> { /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. - pub region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>, + region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>, + + undo_log: InferCtxtUndoLogs<'tcx>, } impl<'tcx> InferCtxtInner<'tcx> { fn new() -> InferCtxtInner<'tcx> { InferCtxtInner { projection_cache: Default::default(), - type_variables: type_variable::TypeVariableTable::new(), - const_unification_table: ut::UnificationTable::new(), - int_unification_table: ut::UnificationTable::new(), - float_unification_table: ut::UnificationTable::new(), - region_constraints: Some(RegionConstraintCollector::new()), + type_variable_storage: type_variable::TypeVariableStorage::new(), + undo_log: InferCtxtUndoLogs::default(), + const_unification_storage: ut::UnificationTableStorage::new(), + int_unification_storage: ut::UnificationTableStorage::new(), + float_unification_storage: ut::UnificationTableStorage::new(), + region_constraint_storage: Some(RegionConstraintStorage::new()), region_obligations: vec![], } } - pub fn unwrap_region_constraints(&mut self) -> &mut RegionConstraintCollector<'tcx> { - self.region_constraints.as_mut().expect("region constraints already solved") + pub fn region_obligations(&self) -> &[(hir::HirId, RegionObligation<'tcx>)] { + &self.region_obligations + } + + pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> { + self.projection_cache.with_log(&mut self.undo_log) + } + + fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> { + self.type_variable_storage.with_log(&mut self.undo_log) + } + + fn int_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::IntVid, + &mut ut::UnificationStorage, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.int_unification_storage.with_log(&mut self.undo_log) + } + + fn float_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::FloatVid, + &mut ut::UnificationStorage, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.float_unification_storage.with_log(&mut self.undo_log) + } + + fn const_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::ConstVid<'tcx>, + &mut ut::UnificationStorage>, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.const_unification_storage.with_log(&mut self.undo_log) + } + + pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> { + self.region_constraint_storage + .as_mut() + .expect("region constraints already solved") + .with_log(&mut self.undo_log) } } @@ -643,16 +707,10 @@ impl<'tcx> InferOk<'tcx, ()> { #[must_use = "once you start a snapshot, you should always consume it"] pub struct CombinedSnapshot<'a, 'tcx> { - projection_cache_snapshot: traits::ProjectionCacheSnapshot, - type_snapshot: type_variable::Snapshot<'tcx>, - const_snapshot: ut::Snapshot>>, - int_snapshot: ut::Snapshot>, - float_snapshot: ut::Snapshot>, + undo_snapshot: Snapshot<'tcx>, region_constraints_snapshot: RegionSnapshot, - region_obligations_snapshot: usize, universe: ty::UniverseIndex, was_in_snapshot: bool, - was_skip_leak_check: bool, _in_progress_tables: Option>>, } @@ -667,7 +725,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool { match ty.kind { - ty::Infer(ty::TyVar(vid)) => self.inner.borrow().type_variables.var_diverges(vid), + ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid), _ => false, } } @@ -681,14 +739,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; match ty.kind { ty::Infer(ty::IntVar(vid)) => { - if self.inner.borrow_mut().int_unification_table.probe_value(vid).is_some() { + if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() { Neither } else { UnconstrainedInt } } ty::Infer(ty::FloatVar(vid)) => { - if self.inner.borrow_mut().float_unification_table.probe_value(vid).is_some() { + if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() { Neither } else { UnconstrainedFloat @@ -703,21 +761,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // FIXME(const_generics): should there be an equivalent function for const variables? let mut vars: Vec> = inner - .type_variables + .type_variables() .unsolved_variables() .into_iter() .map(|t| self.tcx.mk_ty_var(t)) .collect(); vars.extend( - (0..inner.int_unification_table.len()) + (0..inner.int_unification_table().len()) .map(|i| ty::IntVid { index: i as u32 }) - .filter(|&vid| inner.int_unification_table.probe_value(vid).is_none()) + .filter(|&vid| inner.int_unification_table().probe_value(vid).is_none()) .map(|v| self.tcx.mk_int_var(v)), ); vars.extend( - (0..inner.float_unification_table.len()) + (0..inner.float_unification_table().len()) .map(|i| ty::FloatVid { index: i as u32 }) - .filter(|&vid| inner.float_unification_table.probe_value(vid).is_none()) + .filter(|&vid| inner.float_unification_table().probe_value(vid).is_none()) .map(|v| self.tcx.mk_float_var(v)), ); vars @@ -769,17 +827,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let in_snapshot = self.in_snapshot.replace(true); let mut inner = self.inner.borrow_mut(); + CombinedSnapshot { - projection_cache_snapshot: inner.projection_cache.snapshot(), - type_snapshot: inner.type_variables.snapshot(), - const_snapshot: inner.const_unification_table.snapshot(), - int_snapshot: inner.int_unification_table.snapshot(), - float_snapshot: inner.float_unification_table.snapshot(), + undo_snapshot: inner.undo_log.start_snapshot(), region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), - region_obligations_snapshot: inner.region_obligations.len(), universe: self.universe(), was_in_snapshot: in_snapshot, - was_skip_leak_check: self.skip_leak_check.get(), // Borrow tables "in progress" (i.e., during typeck) // to ban writes from within a snapshot to them. _in_progress_tables: self.in_progress_tables.map(|tables| tables.borrow()), @@ -789,59 +842,34 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) { debug!("rollback_to(cause={})", cause); let CombinedSnapshot { - projection_cache_snapshot, - type_snapshot, - const_snapshot, - int_snapshot, - float_snapshot, + undo_snapshot, region_constraints_snapshot, - region_obligations_snapshot, universe, was_in_snapshot, - was_skip_leak_check, _in_progress_tables, } = snapshot; self.in_snapshot.set(was_in_snapshot); self.universe.set(universe); - self.skip_leak_check.set(was_skip_leak_check); let mut inner = self.inner.borrow_mut(); - inner.projection_cache.rollback_to(projection_cache_snapshot); - inner.type_variables.rollback_to(type_snapshot); - inner.const_unification_table.rollback_to(const_snapshot); - inner.int_unification_table.rollback_to(int_snapshot); - inner.float_unification_table.rollback_to(float_snapshot); + inner.rollback_to(undo_snapshot); inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot); - inner.region_obligations.truncate(region_obligations_snapshot); } fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) { debug!("commit_from()"); let CombinedSnapshot { - projection_cache_snapshot, - type_snapshot, - const_snapshot, - int_snapshot, - float_snapshot, - region_constraints_snapshot, - region_obligations_snapshot: _, + undo_snapshot, + region_constraints_snapshot: _, universe: _, was_in_snapshot, - was_skip_leak_check, _in_progress_tables, } = snapshot; self.in_snapshot.set(was_in_snapshot); - self.skip_leak_check.set(was_skip_leak_check); - let mut inner = self.inner.borrow_mut(); - inner.projection_cache.commit(projection_cache_snapshot); - inner.type_variables.commit(type_snapshot); - inner.const_unification_table.commit(const_snapshot); - inner.int_unification_table.commit(int_snapshot); - inner.float_unification_table.commit(float_snapshot); - inner.unwrap_region_constraints().commit(region_constraints_snapshot); + self.inner.borrow_mut().commit(undo_snapshot); } /// Executes `f` and commit the bindings. @@ -895,10 +923,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { debug!("probe()"); let snapshot = self.start_snapshot(); - let skip_leak_check = should_skip || self.skip_leak_check.get(); - self.skip_leak_check.set(skip_leak_check); + let was_skip_leak_check = self.skip_leak_check.get(); + if should_skip { + self.skip_leak_check.set(true); + } let r = f(&snapshot); self.rollback_to("probe", snapshot); + self.skip_leak_check.set(was_skip_leak_check); r } @@ -914,7 +945,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.inner .borrow_mut() .unwrap_region_constraints() - .region_constraints_added_in_snapshot(&snapshot.region_constraints_snapshot) + .region_constraints_added_in_snapshot(&snapshot.undo_snapshot) } pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) { @@ -1032,7 +1063,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid { - self.inner.borrow_mut().type_variables.new_var(self.universe(), diverging, origin) + self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin) } pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { @@ -1044,7 +1075,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: TypeVariableOrigin, universe: ty::UniverseIndex, ) -> Ty<'tcx> { - let vid = self.inner.borrow_mut().type_variables.new_var(universe, false, origin); + let vid = self.inner.borrow_mut().type_variables().new_var(universe, false, origin); self.tcx.mk_ty_var(vid) } @@ -1069,20 +1100,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let vid = self .inner .borrow_mut() - .const_unification_table + .const_unification_table() .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } }); self.tcx.mk_const_var(vid, ty) } pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> { - self.inner.borrow_mut().const_unification_table.new_key(ConstVarValue { + self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe: self.universe() }, }) } fn next_int_var_id(&self) -> IntVid { - self.inner.borrow_mut().int_unification_table.new_key(None) + self.inner.borrow_mut().int_unification_table().new_key(None) } pub fn next_int_var(&self) -> Ty<'tcx> { @@ -1090,7 +1121,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } fn next_float_var_id(&self) -> FloatVid { - self.inner.borrow_mut().float_unification_table.new_key(None) + self.inner.borrow_mut().float_unification_table().new_key(None) } pub fn next_float_var(&self) -> Ty<'tcx> { @@ -1161,7 +1192,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // used in a path such as `Foo::::new()` will // use an inference variable for `C` with `[T, U]` // as the substitutions for the default, `(T, U)`. - let ty_var_id = self.inner.borrow_mut().type_variables.new_var( + let ty_var_id = self.inner.borrow_mut().type_variables().new_var( self.universe(), false, TypeVariableOrigin { @@ -1181,7 +1212,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span, }; let const_var_id = - self.inner.borrow_mut().const_unification_table.new_key(ConstVarValue { + self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe: self.universe() }, }); @@ -1234,18 +1265,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { outlives_env: &OutlivesEnvironment<'tcx>, mode: RegionckMode, ) { - assert!( - self.is_tainted_by_errors() || self.inner.borrow().region_obligations.is_empty(), - "region_obligations not empty: {:#?}", - self.inner.borrow().region_obligations - ); - let (var_infos, data) = self - .inner - .borrow_mut() - .region_constraints - .take() - .expect("regions already resolved") - .into_infos_and_data(); + let (var_infos, data) = { + let mut inner = self.inner.borrow_mut(); + let inner = &mut *inner; + assert!( + self.is_tainted_by_errors() || inner.region_obligations.is_empty(), + "region_obligations not empty: {:#?}", + inner.region_obligations + ); + inner + .region_constraint_storage + .take() + .expect("regions already resolved") + .with_log(&mut inner.undo_log) + .into_infos_and_data() + }; let region_rels = &RegionRelations::new( self.tcx, @@ -1306,12 +1340,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// called. This is used only during NLL processing to "hand off" ownership /// of the set of region variables into the NLL region context. pub fn take_region_var_origins(&self) -> VarInfos { - let (var_infos, data) = self - .inner - .borrow_mut() - .region_constraints + let mut inner = self.inner.borrow_mut(); + let (var_infos, data) = inner + .region_constraint_storage .take() .expect("regions already resolved") + .with_log(&mut inner.undo_log) .into_infos_and_data(); assert!(data.is_empty()); var_infos @@ -1335,7 +1369,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn probe_ty_var(&self, vid: TyVid) -> Result, ty::UniverseIndex> { use self::type_variable::TypeVariableValue; - match self.inner.borrow_mut().type_variables.probe(vid) { + match self.inner.borrow_mut().type_variables().probe(vid) { TypeVariableValue::Known { value } => Ok(value), TypeVariableValue::Unknown { universe } => Err(universe), } @@ -1357,7 +1391,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid { - self.inner.borrow_mut().type_variables.root_var(var) + self.inner.borrow_mut().type_variables().root_var(var) } /// Where possible, replaces type/const variables in @@ -1395,7 +1429,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, vid: ty::ConstVid<'tcx>, ) -> Result<&'tcx ty::Const<'tcx>, ty::UniverseIndex> { - match self.inner.borrow_mut().const_unification_table.probe_value(vid).val { + match self.inner.borrow_mut().const_unification_table().probe_value(vid).val { ConstVariableValue::Known { value } => Ok(value), ConstVariableValue::Unknown { universe } => Err(universe), } @@ -1513,7 +1547,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn clear_caches(&self) { self.selection_cache.clear(); self.evaluation_cache.clear(); - self.inner.borrow_mut().projection_cache.clear(); + self.inner.borrow_mut().projection_cache().clear(); } fn universe(&self) -> ty::UniverseIndex { @@ -1576,14 +1610,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // // Note: if these two lines are combined into one we get // dynamic borrow errors on `self.inner`. - let known = self.inner.borrow_mut().type_variables.probe(v).known(); + let known = self.inner.borrow_mut().type_variables().probe(v).known(); known.map(|t| self.shallow_resolve_ty(t)).unwrap_or(typ) } ty::Infer(ty::IntVar(v)) => self .inner .borrow_mut() - .int_unification_table + .int_unification_table() .probe_value(v) .map(|v| v.to_type(self.tcx)) .unwrap_or(typ), @@ -1591,7 +1625,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ty::Infer(ty::FloatVar(v)) => self .inner .borrow_mut() - .float_unification_table + .float_unification_table() .probe_value(v) .map(|v| v.to_type(self.tcx)) .unwrap_or(typ), @@ -1611,13 +1645,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// having to resort to storing full `GenericArg`s in `stalled_on`. #[inline(always)] pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar<'tcx>) -> bool { + let mut inner = self.inner.borrow_mut(); match infer_var { TyOrConstInferVar::Ty(v) => { use self::type_variable::TypeVariableValue; // If `inlined_probe` returns a `Known` value, it never equals // `ty::Infer(ty::TyVar(v))`. - match self.inner.borrow_mut().type_variables.inlined_probe(v) { + match inner.type_variables().inlined_probe(v) { TypeVariableValue::Unknown { .. } => false, TypeVariableValue::Known { .. } => true, } @@ -1627,7 +1662,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // If `inlined_probe_value` returns a value it's always a // `ty::Int(_)` or `ty::UInt(_)`, which never matches a // `ty::Infer(_)`. - self.inner.borrow_mut().int_unification_table.inlined_probe_value(v).is_some() + inner.int_unification_table().inlined_probe_value(v).is_some() } TyOrConstInferVar::TyFloat(v) => { @@ -1635,7 +1670,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // `ty::Float(_)`, which never matches a `ty::Infer(_)`. // // Not `inlined_probe_value(v)` because this call site is colder. - self.inner.borrow_mut().float_unification_table.probe_value(v).is_some() + inner.float_unification_table().probe_value(v).is_some() } TyOrConstInferVar::Const(v) => { @@ -1643,7 +1678,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // `ty::ConstKind::Infer(ty::InferConst::Var(v))`. // // Not `inlined_probe_value(v)` because this call site is colder. - match self.inner.borrow_mut().const_unification_table.probe_value(v).val { + match inner.const_unification_table().probe_value(v).val { ConstVariableValue::Unknown { .. } => false, ConstVariableValue::Known { .. } => true, } @@ -1718,7 +1753,7 @@ impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { self.infcx .inner .borrow_mut() - .const_unification_table + .const_unification_table() .probe_value(*vid) .val .known() diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs index a2907e6e373..7aea26987a2 100644 --- a/src/librustc_infer/infer/nll_relate/mod.rs +++ b/src/librustc_infer/infer/nll_relate/mod.rs @@ -311,7 +311,7 @@ where match value_ty.kind { ty::Infer(ty::TyVar(value_vid)) => { // Two type variables: just equate them. - self.infcx.inner.borrow_mut().type_variables.equate(vid, value_vid); + self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); return Ok(value_ty); } @@ -332,7 +332,7 @@ where assert!(!generalized_ty.has_infer_types_or_consts()); } - self.infcx.inner.borrow_mut().type_variables.instantiate(vid, generalized_ty); + self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); // The generalized values we extract from `canonical_var_values` have // been fully instantiated and hence the set of scopes we have @@ -362,7 +362,7 @@ where delegate: &mut self.delegate, first_free_index: ty::INNERMOST, ambient_variance: self.ambient_variance, - for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables.sub_root_var(for_vid), + for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), universe, }; @@ -859,7 +859,8 @@ where } ty::Infer(ty::TyVar(vid)) => { - let variables = &mut self.infcx.inner.borrow_mut().type_variables; + let mut inner = self.infcx.inner.borrow_mut(); + let variables = &mut inner.type_variables(); let vid = variables.root_var(vid); let sub_vid = variables.sub_root_var(vid); if sub_vid == self.for_vid_sub_root { @@ -961,7 +962,8 @@ where bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); } ty::ConstKind::Infer(InferConst::Var(vid)) => { - let variable_table = &mut self.infcx.inner.borrow_mut().const_unification_table; + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); let var_value = variable_table.probe_value(vid); match var_value.val.known() { Some(u) => self.relate(&u, &u), diff --git a/src/librustc_infer/infer/outlives/obligations.rs b/src/librustc_infer/infer/outlives/obligations.rs index c904926e9d9..48f6d937f2f 100644 --- a/src/librustc_infer/infer/outlives/obligations.rs +++ b/src/librustc_infer/infer/outlives/obligations.rs @@ -61,13 +61,16 @@ use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; -use crate::infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound}; +use crate::infer::{ + self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound, +}; use crate::traits::ObligationCause; use rustc_middle::ty::outlives::Component; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeFoldable}; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::undo_log::UndoLogs; use rustc_hir as hir; use smallvec::smallvec; @@ -84,7 +87,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { ) { debug!("register_region_obligation(body_id={:?}, obligation={:?})", body_id, obligation); - self.inner.borrow_mut().region_obligations.push((body_id, obligation)); + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushRegionObligation); + inner.region_obligations.push((body_id, obligation)); } pub fn register_region_obligation_with_cause( diff --git a/src/librustc_infer/infer/region_constraints/leak_check.rs b/src/librustc_infer/infer/region_constraints/leak_check.rs index 18e86162eb5..473550d5433 100644 --- a/src/librustc_infer/infer/region_constraints/leak_check.rs +++ b/src/librustc_infer/infer/region_constraints/leak_check.rs @@ -1,9 +1,10 @@ use super::*; use crate::infer::{CombinedSnapshot, PlaceholderMap}; +use rustc_data_structures::undo_log::UndoLogs; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::relate::RelateResult; -impl<'tcx> RegionConstraintCollector<'tcx> { +impl<'tcx> RegionConstraintCollector<'_, 'tcx> { /// Searches region constraints created since `snapshot` that /// affect one of the placeholders in `placeholder_map`, returning /// an error if any of the placeholders are related to another @@ -31,7 +32,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { ) -> RelateResult<'tcx, ()> { debug!("leak_check(placeholders={:?})", placeholder_map); - assert!(self.in_snapshot()); + assert!(UndoLogs::>::in_snapshot(&self.undo_log)); // Go through each placeholder that we created. for &placeholder_region in placeholder_map.values() { @@ -45,7 +46,11 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // in some way. This means any region that either outlives // or is outlived by a placeholder. let mut taint_set = TaintSet::new(TaintDirections::both(), placeholder_region); - taint_set.fixed_point(tcx, &self.undo_log, &self.data.verifys); + taint_set.fixed_point( + tcx, + self.undo_log.region_constraints(), + &self.storage.data.verifys, + ); let tainted_regions = taint_set.into_set(); // Report an error if two placeholders in the same universe @@ -88,19 +93,21 @@ impl<'tcx> TaintSet<'tcx> { TaintSet { directions, regions } } - fn fixed_point( + fn fixed_point<'a>( &mut self, tcx: TyCtxt<'tcx>, - undo_log: &[UndoLog<'tcx>], + undo_log: impl IntoIterator> + Clone, verifys: &[Verify<'tcx>], - ) { + ) where + 'tcx: 'a, + { let mut prev_len = 0; while prev_len < self.len() { debug!("tainted: prev_len = {:?} new_len = {:?}", prev_len, self.len()); prev_len = self.len(); - for undo_entry in undo_log { + for undo_entry in undo_log.clone() { match undo_entry { &AddConstraint(Constraint::VarSubVar(a, b)) => { self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); diff --git a/src/librustc_infer/infer/region_constraints/mod.rs b/src/librustc_infer/infer/region_constraints/mod.rs index 2be6ec4481c..0c9f002a2a2 100644 --- a/src/librustc_infer/infer/region_constraints/mod.rs +++ b/src/librustc_infer/infer/region_constraints/mod.rs @@ -4,11 +4,15 @@ use self::CombineMapType::*; use self::UndoLog::*; use super::unify_key; -use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin}; +use super::{ + InferCtxtUndoLogs, MiscVariable, RegionVariableOrigin, Rollback, Snapshot, SubregionOrigin, +}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::undo_log::UndoLogs; use rustc_data_structures::unify as ut; +use rustc_data_structures::unify::UnifyKey; use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; use rustc_middle::ty::ReStatic; @@ -26,7 +30,7 @@ mod leak_check; pub use rustc_middle::infer::MemberConstraint; #[derive(Default)] -pub struct RegionConstraintCollector<'tcx> { +pub struct RegionConstraintStorage<'tcx> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. var_infos: IndexVec, @@ -42,20 +46,6 @@ pub struct RegionConstraintCollector<'tcx> { /// exist). This prevents us from making many such regions. glbs: CombineMap<'tcx>, - /// The undo log records actions that might later be undone. - /// - /// Note: `num_open_snapshots` is used to track if we are actively - /// snapshotting. When the `start_snapshot()` method is called, we - /// increment `num_open_snapshots` to indicate that we are now actively - /// snapshotting. The reason for this is that otherwise we end up adding - /// entries for things like the lower bound on a variable and so forth, - /// which can never be rolled back. - undo_log: Vec>, - - /// The number of open snapshots, i.e., those that haven't been committed or - /// rolled back. - num_open_snapshots: usize, - /// When we add a R1 == R2 constriant, we currently add (a) edges /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this /// table. You can then call `opportunistic_resolve_var` early @@ -64,13 +54,31 @@ pub struct RegionConstraintCollector<'tcx> { /// is iterating to a fixed point, because otherwise we sometimes /// would wind up with a fresh stream of region variables that /// have been equated but appear distinct. - unification_table: ut::UnificationTable>, + pub(super) unification_table: ut::UnificationTableStorage, /// a flag set to true when we perform any unifications; this is used /// to micro-optimize `take_and_reset_data` any_unifications: bool, } +pub struct RegionConstraintCollector<'a, 'tcx> { + storage: &'a mut RegionConstraintStorage<'tcx>, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + +impl std::ops::Deref for RegionConstraintCollector<'_, 'tcx> { + type Target = RegionConstraintStorage<'tcx>; + fn deref(&self) -> &RegionConstraintStorage<'tcx> { + self.storage + } +} + +impl std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> { + fn deref_mut(&mut self) -> &mut RegionConstraintStorage<'tcx> { + self.storage + } +} + pub type VarInfos = IndexVec; /// The full set of region constraints gathered up by the collector. @@ -258,13 +266,13 @@ pub enum VerifyBound<'tcx> { } #[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct TwoRegions<'tcx> { +pub(crate) struct TwoRegions<'tcx> { a: Region<'tcx>, b: Region<'tcx>, } #[derive(Copy, Clone, PartialEq)] -enum UndoLog<'tcx> { +pub(crate) enum UndoLog<'tcx> { /// We added `RegionVid`. AddVar(RegionVid), @@ -290,7 +298,7 @@ enum UndoLog<'tcx> { } #[derive(Copy, Clone, PartialEq)] -enum CombineMapType { +pub(crate) enum CombineMapType { Lub, Glb, } @@ -304,8 +312,6 @@ pub struct RegionVariableInfo { } pub struct RegionSnapshot { - length: usize, - region_snapshot: ut::Snapshot>, any_unifications: bool, } @@ -334,129 +340,16 @@ impl TaintDirections { } } -impl<'tcx> RegionConstraintCollector<'tcx> { +impl<'tcx> RegionConstraintStorage<'tcx> { pub fn new() -> Self { Self::default() } - pub fn num_region_vars(&self) -> usize { - self.var_infos.len() - } - - pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> { - &self.data - } - - /// Once all the constraints have been gathered, extract out the final data. - /// - /// Not legal during a snapshot. - pub fn into_infos_and_data(self) -> (VarInfos, RegionConstraintData<'tcx>) { - assert!(!self.in_snapshot()); - (self.var_infos, self.data) - } - - /// Takes (and clears) the current set of constraints. Note that - /// the set of variables remains intact, but all relationships - /// between them are reset. This is used during NLL checking to - /// grab the set of constraints that arose from a particular - /// operation. - /// - /// We don't want to leak relationships between variables between - /// points because just because (say) `r1 == r2` was true at some - /// point P in the graph doesn't imply that it will be true at - /// some other point Q, in NLL. - /// - /// Not legal during a snapshot. - pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { - assert!(!self.in_snapshot()); - - // If you add a new field to `RegionConstraintCollector`, you - // should think carefully about whether it needs to be cleared - // or updated in some way. - let RegionConstraintCollector { - var_infos: _, - data, - lubs, - glbs, - undo_log: _, - num_open_snapshots: _, - unification_table, - any_unifications, - } = self; - - // Clear the tables of (lubs, glbs), so that we will create - // fresh regions if we do a LUB operation. As it happens, - // LUB/GLB are not performed by the MIR type-checker, which is - // the one that uses this method, but it's good to be correct. - lubs.clear(); - glbs.clear(); - - // Clear all unifications and recreate the variables a "now - // un-unified" state. Note that when we unify `a` and `b`, we - // also insert `a <= b` and a `b <= a` edges, so the - // `RegionConstraintData` contains the relationship here. - if *any_unifications { - unification_table.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid }); - *any_unifications = false; - } - - mem::take(data) - } - - pub fn data(&self) -> &RegionConstraintData<'tcx> { - &self.data - } - - fn in_snapshot(&self) -> bool { - self.num_open_snapshots > 0 - } - - pub fn start_snapshot(&mut self) -> RegionSnapshot { - let length = self.undo_log.len(); - debug!("RegionConstraintCollector: start_snapshot({})", length); - self.num_open_snapshots += 1; - RegionSnapshot { - length, - region_snapshot: self.unification_table.snapshot(), - any_unifications: self.any_unifications, - } - } - - fn assert_open_snapshot(&self, snapshot: &RegionSnapshot) { - assert!(self.undo_log.len() >= snapshot.length); - assert!(self.num_open_snapshots > 0); - } - - pub fn commit(&mut self, snapshot: RegionSnapshot) { - debug!("RegionConstraintCollector: commit({})", snapshot.length); - self.assert_open_snapshot(&snapshot); - - if self.num_open_snapshots == 1 { - // The root snapshot. It's safe to clear the undo log because - // there's no snapshot further out that we might need to roll back - // to. - assert!(snapshot.length == 0); - self.undo_log.clear(); - } - - self.num_open_snapshots -= 1; - - self.unification_table.commit(snapshot.region_snapshot); - } - - pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { - debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); - self.assert_open_snapshot(&snapshot); - - while self.undo_log.len() > snapshot.length { - let undo_entry = self.undo_log.pop().unwrap(); - self.rollback_undo_entry(undo_entry); - } - - self.num_open_snapshots -= 1; - - self.unification_table.rollback_to(snapshot.region_snapshot); - self.any_unifications = snapshot.any_unifications; + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> RegionConstraintCollector<'a, 'tcx> { + RegionConstraintCollector { storage: self, undo_log } } fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) { @@ -486,6 +379,87 @@ impl<'tcx> RegionConstraintCollector<'tcx> { } } } +} + +impl<'tcx> RegionConstraintCollector<'_, 'tcx> { + pub fn num_region_vars(&self) -> usize { + self.var_infos.len() + } + + pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> { + &self.data + } + + /// Once all the constraints have been gathered, extract out the final data. + /// + /// Not legal during a snapshot. + pub fn into_infos_and_data(self) -> (VarInfos, RegionConstraintData<'tcx>) { + assert!(!UndoLogs::>::in_snapshot(&self.undo_log)); + (mem::take(&mut self.storage.var_infos), mem::take(&mut self.storage.data)) + } + + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { + assert!(!UndoLogs::>::in_snapshot(&self.undo_log)); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintStorage { + var_infos: _, + data, + lubs, + glbs, + unification_table: _, + any_unifications, + } = self.storage; + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + let data = mem::take(data); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + if *any_unifications { + *any_unifications = false; + self.unification_table() + .reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid }); + } + + data + } + + pub fn data(&self) -> &RegionConstraintData<'tcx> { + &self.data + } + + pub fn start_snapshot(&mut self) -> RegionSnapshot { + debug!("RegionConstraintCollector: start_snapshot"); + RegionSnapshot { any_unifications: self.any_unifications } + } + + pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); + self.any_unifications = snapshot.any_unifications; + } pub fn new_region_var( &mut self, @@ -494,11 +468,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> { ) -> RegionVid { let vid = self.var_infos.push(RegionVariableInfo { origin, universe }); - let u_vid = self.unification_table.new_key(unify_key::RegionVidKey { min_vid: vid }); + let u_vid = self.unification_table().new_key(unify_key::RegionVidKey { min_vid: vid }); assert_eq!(vid, u_vid); - if self.in_snapshot() { - self.undo_log.push(AddVar(vid)); - } + self.undo_log.push(AddVar(vid)); debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin); vid } @@ -520,19 +492,29 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn pop_placeholders(&mut self, placeholders: &FxHashSet>) { debug!("pop_placeholders(placeholders={:?})", placeholders); - assert!(self.in_snapshot()); + assert!(UndoLogs::>::in_snapshot(&self.undo_log)); let constraints_to_kill: Vec = self .undo_log .iter() .enumerate() .rev() - .filter(|&(_, undo_entry)| kill_constraint(placeholders, undo_entry)) + .filter(|&(_, undo_entry)| match undo_entry { + super::UndoLog::RegionConstraintCollector(undo_entry) => { + kill_constraint(placeholders, undo_entry) + } + _ => false, + }) .map(|(index, _)| index) .collect(); for index in constraints_to_kill { - let undo_entry = mem::replace(&mut self.undo_log[index], Purged); + let undo_entry = match &mut self.undo_log[index] { + super::UndoLog::RegionConstraintCollector(undo_entry) => { + mem::replace(undo_entry, Purged) + } + _ => unreachable!(), + }; self.rollback_undo_entry(undo_entry); } @@ -566,12 +548,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> { // never overwrite an existing (constraint, origin) - only insert one if it isn't // present in the map yet. This prevents origins from outside the snapshot being // replaced with "less informative" origins e.g., during calls to `can_eq` - let in_snapshot = self.in_snapshot(); let undo_log = &mut self.undo_log; - self.data.constraints.entry(constraint).or_insert_with(|| { - if in_snapshot { - undo_log.push(AddConstraint(constraint)); - } + self.storage.data.constraints.entry(constraint).or_insert_with(|| { + undo_log.push(AddConstraint(constraint)); origin }); } @@ -589,9 +568,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { let index = self.data.verifys.len(); self.data.verifys.push(verify); - if self.in_snapshot() { - self.undo_log.push(AddVerify(index)); - } + self.undo_log.push(AddVerify(index)); } pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { @@ -599,9 +576,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { if self.data.givens.insert((sub, sup)) { debug!("add_given({:?} <= {:?})", sub, sup); - if self.in_snapshot() { - self.undo_log.push(AddGiven(sub, sup)); - } + self.undo_log.push(AddGiven(sub, sup)); } } @@ -619,7 +594,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) { debug!("make_eqregion: uniying {:?} with {:?}", sub, sup); - self.unification_table.union(sub, sup); + self.unification_table().union(sub, sup); self.any_unifications = true; } } @@ -741,7 +716,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { tcx: TyCtxt<'tcx>, rid: RegionVid, ) -> ty::Region<'tcx> { - let vid = self.unification_table.probe_value(rid).min_vid; + let vid = self.unification_table().probe_value(rid).min_vid; tcx.mk_region(ty::ReVar(vid)) } @@ -769,9 +744,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> { let c_universe = cmp::max(a_universe, b_universe); let c = self.new_region_var(c_universe, MiscVariable(origin.span())); self.combine_map(t).insert(vars, c); - if self.in_snapshot() { - self.undo_log.push(AddCombination(t, vars)); - } + self.undo_log.push(AddCombination(t, vars)); let new_r = tcx.mk_region(ReVar(c)); for &old_r in &[a, b] { match t { @@ -799,9 +772,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { pub fn vars_since_snapshot( &self, - mark: &RegionSnapshot, + value_count: usize, ) -> (Range, Vec) { - let range = self.unification_table.vars_since_snapshot(&mark.region_snapshot); + let range = RegionVid::from_index(value_count as u32) + ..RegionVid::from_index(self.unification_table.len() as u32); ( range.clone(), (range.start.index()..range.end.index()) @@ -810,10 +784,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> { ) } - /// See `InferCtxt::region_constraints_added_in_snapshot`. - pub fn region_constraints_added_in_snapshot(&self, mark: &RegionSnapshot) -> Option { - self.undo_log[mark.length..] - .iter() + /// See [`RegionInference::region_constraints_added_in_snapshot`]. + pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Option { + self.undo_log + .region_constraints_in_snapshot(mark) .map(|&elt| match elt { AddConstraint(constraint) => Some(constraint.involves_placeholders()), _ => None, @@ -821,11 +795,15 @@ impl<'tcx> RegionConstraintCollector<'tcx> { .max() .unwrap_or(None) } + + fn unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, ty::RegionVid> { + ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) + } } impl fmt::Debug for RegionSnapshot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RegionSnapshot(length={})", self.length) + write!(f, "RegionSnapshot") } } @@ -910,3 +888,9 @@ impl<'tcx> RegionConstraintData<'tcx> { && givens.is_empty() } } + +impl<'tcx> Rollback> for RegionConstraintStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + self.rollback_undo_entry(undo) + } +} diff --git a/src/librustc_infer/infer/resolve.rs b/src/librustc_infer/infer/resolve.rs index bd9d108cfe8..e28cf49c7f2 100644 --- a/src/librustc_infer/infer/resolve.rs +++ b/src/librustc_infer/infer/resolve.rs @@ -123,7 +123,8 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { // Since we called `shallow_resolve` above, this must // be an (as yet...) unresolved inference variable. let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty { - let ty_vars = &self.infcx.inner.borrow().type_variables; + let mut inner = self.infcx.inner.borrow_mut(); + let ty_vars = &inner.type_variables(); if let TypeVariableOrigin { kind: TypeVariableOriginKind::TypeParameterDefinition(_, _), span, diff --git a/src/librustc_infer/infer/sub.rs b/src/librustc_infer/infer/sub.rs index 080af37492d..0abcc15d6fc 100644 --- a/src/librustc_infer/infer/sub.rs +++ b/src/librustc_infer/infer/sub.rs @@ -80,8 +80,8 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { } let infcx = self.fields.infcx; - let a = infcx.inner.borrow_mut().type_variables.replace_if_possible(a); - let b = infcx.inner.borrow_mut().type_variables.replace_if_possible(b); + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); match (&a.kind, &b.kind) { (&ty::Infer(TyVar(a_vid)), &ty::Infer(TyVar(b_vid))) => { // Shouldn't have any LBR here, so we can safely put @@ -95,7 +95,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> { // have to record in the `type_variables` tracker that // the two variables are equal modulo subtyping, which // is important to the occurs check later on. - infcx.inner.borrow_mut().type_variables.sub(a_vid, b_vid); + infcx.inner.borrow_mut().type_variables().sub(a_vid, b_vid); self.fields.obligations.push(Obligation::new( self.fields.trace.cause.clone(), self.fields.param_env, diff --git a/src/librustc_infer/infer/type_variable.rs b/src/librustc_infer/infer/type_variable.rs index 1de820cdb62..f68692391a2 100644 --- a/src/librustc_infer/infer/type_variable.rs +++ b/src/librustc_infer/infer/type_variable.rs @@ -3,19 +3,68 @@ use rustc_middle::ty::{self, Ty, TyVid}; use rustc_span::symbol::Symbol; use rustc_span::Span; +use crate::infer::InferCtxtUndoLogs; + use rustc_data_structures::snapshot_vec as sv; use rustc_data_structures::unify as ut; use std::cmp; use std::marker::PhantomData; use std::ops::Range; -pub struct TypeVariableTable<'tcx> { - values: sv::SnapshotVec, +use rustc_data_structures::undo_log::{Rollback, UndoLogs}; + +/// Represents a single undo-able action that affects a type inference variable. +pub(crate) enum UndoLog<'tcx> { + EqRelation(sv::UndoLog>>), + SubRelation(sv::UndoLog>), + Values(sv::UndoLog), +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>>) -> Self { + UndoLog::EqRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>) -> Self { + UndoLog::SubRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From> for UndoLog<'tcx> { + fn from(l: sv::UndoLog) -> Self { + UndoLog::Values(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From for UndoLog<'tcx> { + fn from(l: Instantiate) -> Self { + UndoLog::Values(sv::UndoLog::Other(l)) + } +} + +impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo), + UndoLog::Values(undo) => self.values.reverse(undo), + } + } +} + +pub struct TypeVariableStorage<'tcx> { + values: sv::SnapshotVecStorage, /// Two variables are unified in `eq_relations` when we have a /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. - eq_relations: ut::UnificationTable>>, + eq_relations: ut::UnificationTableStorage>, /// Two variables are unified in `sub_relations` when we have a /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second @@ -34,7 +83,17 @@ pub struct TypeVariableTable<'tcx> { /// This is reasonable because, in Rust, subtypes have the same /// "skeleton" and hence there is no possible type such that /// (e.g.) `Box <: ?3` for any `?3`. - sub_relations: ut::UnificationTable>, + sub_relations: ut::UnificationTableStorage, +} + +pub struct TypeVariableTable<'a, 'tcx> { + values: &'a mut sv::SnapshotVecStorage, + + eq_relations: &'a mut ut::UnificationTableStorage>, + + sub_relations: &'a mut ut::UnificationTableStorage, + + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, } #[derive(Copy, Clone, Debug)] @@ -62,7 +121,7 @@ pub enum TypeVariableOriginKind { LatticeVariable, } -struct TypeVariableData { +pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, diverging: bool, } @@ -91,27 +150,31 @@ impl<'tcx> TypeVariableValue<'tcx> { } } -pub struct Snapshot<'tcx> { - snapshot: sv::Snapshot, - eq_snapshot: ut::Snapshot>>, - sub_snapshot: ut::Snapshot>, -} - -struct Instantiate { +pub(crate) struct Instantiate { vid: ty::TyVid, } -struct Delegate; +pub(crate) struct Delegate; -impl<'tcx> TypeVariableTable<'tcx> { - pub fn new() -> TypeVariableTable<'tcx> { - TypeVariableTable { - values: sv::SnapshotVec::new(), - eq_relations: ut::UnificationTable::new(), - sub_relations: ut::UnificationTable::new(), +impl<'tcx> TypeVariableStorage<'tcx> { + pub fn new() -> TypeVariableStorage<'tcx> { + TypeVariableStorage { + values: sv::SnapshotVecStorage::new(), + eq_relations: ut::UnificationTableStorage::new(), + sub_relations: ut::UnificationTableStorage::new(), } } + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> TypeVariableTable<'a, 'tcx> { + let TypeVariableStorage { values, eq_relations, sub_relations } = self; + TypeVariableTable { values, eq_relations, sub_relations, undo_log } + } +} + +impl<'tcx> TypeVariableTable<'_, 'tcx> { /// Returns the diverges flag given when `vid` was created. /// /// Note that this function does not return care whether @@ -134,8 +197,8 @@ impl<'tcx> TypeVariableTable<'tcx> { pub fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); - self.eq_relations.union(a, b); - self.sub_relations.union(a, b); + self.eq_relations().union(a, b); + self.sub_relations().union(a, b); } /// Records that `a <: b`, depending on `dir`. @@ -144,7 +207,7 @@ impl<'tcx> TypeVariableTable<'tcx> { pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); - self.sub_relations.union(a, b); + self.sub_relations().union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -154,18 +217,18 @@ impl<'tcx> TypeVariableTable<'tcx> { let vid = self.root_var(vid); debug_assert!(self.probe(vid).is_unknown()); debug_assert!( - self.eq_relations.probe_value(vid).is_unknown(), + self.eq_relations().probe_value(vid).is_unknown(), "instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}", vid, ty, - self.eq_relations.probe_value(vid) + self.eq_relations().probe_value(vid) ); - self.eq_relations.union_value(vid, TypeVariableValue::Known { value: ty }); + self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty }); // Hack: we only need this so that `types_escaping_snapshot` // can see what has been unified; see the Delegate impl for // more details. - self.values.record(Instantiate { vid }); + self.undo_log.push(Instantiate { vid }); } /// Creates a new type variable. @@ -184,12 +247,12 @@ impl<'tcx> TypeVariableTable<'tcx> { diverging: bool, origin: TypeVariableOrigin, ) -> ty::TyVid { - let eq_key = self.eq_relations.new_key(TypeVariableValue::Unknown { universe }); + let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); - let sub_key = self.sub_relations.new_key(()); + let sub_key = self.sub_relations().new_key(()); assert_eq!(eq_key.vid, sub_key); - let index = self.values.push(TypeVariableData { origin, diverging }); + let index = self.values().push(TypeVariableData { origin, diverging }); assert_eq!(eq_key.vid.index, index as u32); debug!( @@ -211,7 +274,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// algorithm), so `root_var(a) == root_var(b)` implies that `a == /// b` (transitively). pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { - self.eq_relations.find(vid).vid + self.eq_relations().find(vid).vid } /// Returns the "root" variable of `vid` in the `sub_relations` @@ -222,7 +285,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// /// exists X. (a <: X || X <: a) && (b <: X || X <: b) pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { - self.sub_relations.find(vid) + self.sub_relations().find(vid) } /// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some @@ -240,7 +303,7 @@ impl<'tcx> TypeVariableTable<'tcx> { /// An always-inlined variant of `probe`, for very hot call sites. #[inline(always)] pub fn inlined_probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { - self.eq_relations.inlined_probe_value(vid) + self.eq_relations().inlined_probe_value(vid) } /// If `t` is a type-inference variable, and it has been @@ -256,56 +319,29 @@ impl<'tcx> TypeVariableTable<'tcx> { } } - /// Creates a snapshot of the type variable state. This snapshot - /// must later be committed (`commit()`) or rolled back - /// (`rollback_to()`). Nested snapshots are permitted, but must - /// be processed in a stack-like fashion. - pub fn snapshot(&mut self) -> Snapshot<'tcx> { - Snapshot { - snapshot: self.values.start_snapshot(), - eq_snapshot: self.eq_relations.snapshot(), - sub_snapshot: self.sub_relations.snapshot(), - } + fn values( + &mut self, + ) -> sv::SnapshotVec, &mut InferCtxtUndoLogs<'tcx>> { + self.values.with_log(self.undo_log) } - /// Undoes all changes since the snapshot was created. Any - /// snapshots created since that point must already have been - /// committed or rolled back. - pub fn rollback_to(&mut self, s: Snapshot<'tcx>) { - debug!("rollback_to{:?}", { - for action in self.values.actions_since_snapshot(&s.snapshot) { - if let sv::UndoLog::NewElem(index) = *action { - debug!("inference variable _#{}t popped", index) - } - } - }); - - let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; - self.values.rollback_to(snapshot); - self.eq_relations.rollback_to(eq_snapshot); - self.sub_relations.rollback_to(sub_snapshot); + fn eq_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidEqKey<'tcx>> { + self.eq_relations.with_log(self.undo_log) } - /// Commits all changes since the snapshot was created, making - /// them permanent (unless this snapshot was created within - /// another snapshot). Any snapshots created since that point - /// must already have been committed or rolled back. - pub fn commit(&mut self, s: Snapshot<'tcx>) { - let Snapshot { snapshot, eq_snapshot, sub_snapshot } = s; - self.values.commit(snapshot); - self.eq_relations.commit(eq_snapshot); - self.sub_relations.commit(sub_snapshot); + fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> { + self.sub_relations.with_log(self.undo_log) } /// Returns a range of the type variables created during the snapshot. pub fn vars_since_snapshot( &mut self, - s: &Snapshot<'tcx>, + value_count: usize, ) -> (Range, Vec) { - let range = self.eq_relations.vars_since_snapshot(&s.eq_snapshot); + let range = TyVid { index: value_count as u32 }..TyVid { index: self.num_vars() as u32 }; ( - range.start.vid..range.end.vid, - (range.start.vid.index..range.end.vid.index) + range.start..range.end, + (range.start.index..range.end.index) .map(|index| self.values.get(index as usize).origin) .collect(), ) @@ -317,14 +353,15 @@ impl<'tcx> TypeVariableTable<'tcx> { /// a type variable `V0`, then we started the snapshot, then we /// created a type variable `V1`, unified `V0` with `T0`, and /// unified `V1` with `T1`, this function would return `{T0}`. - pub fn types_escaping_snapshot(&mut self, s: &Snapshot<'tcx>) -> Vec> { + pub fn types_escaping_snapshot(&mut self, s: &super::Snapshot<'tcx>) -> Vec> { let mut new_elem_threshold = u32::MAX; let mut escaping_types = Vec::new(); - let actions_since_snapshot = self.values.actions_since_snapshot(&s.snapshot); + let actions_since_snapshot = self.undo_log.actions_since_snapshot(s); debug!("actions_since_snapshot.len() = {}", actions_since_snapshot.len()); - for action in actions_since_snapshot { - match *action { - sv::UndoLog::NewElem(index) => { + for i in 0..actions_since_snapshot.len() { + let actions_since_snapshot = self.undo_log.actions_since_snapshot(s); + match actions_since_snapshot[i] { + super::UndoLog::TypeVariables(UndoLog::Values(sv::UndoLog::NewElem(index))) => { // if any new variables were created during the // snapshot, remember the lower index (which will // always be the first one we see). Note that this @@ -334,11 +371,17 @@ impl<'tcx> TypeVariableTable<'tcx> { debug!("NewElem({}) new_elem_threshold={}", index, new_elem_threshold); } - sv::UndoLog::Other(Instantiate { vid, .. }) => { + super::UndoLog::TypeVariables(UndoLog::Values(sv::UndoLog::Other( + Instantiate { vid, .. }, + ))) => { if vid.index < new_elem_threshold { // quick check to see if this variable was // created since the snapshot started or not. - let escaping_type = match self.eq_relations.probe_value(vid) { + let mut eq_relations = ut::UnificationTable::with_log( + &mut *self.eq_relations, + &mut *self.undo_log, + ); + let escaping_type = match eq_relations.probe_value(vid) { TypeVariableValue::Unknown { .. } => bug!(), TypeVariableValue::Known { value } => value, }; @@ -395,7 +438,7 @@ impl sv::SnapshotVecDelegate for Delegate { /// for the `eq_relations`; they carry a `TypeVariableValue` along /// with them. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -struct TyVidEqKey<'tcx> { +pub(crate) struct TyVidEqKey<'tcx> { vid: ty::TyVid, // in the table, we map each ty-vid to one of these: diff --git a/src/librustc_infer/infer/undo_log.rs b/src/librustc_infer/infer/undo_log.rs new file mode 100644 index 00000000000..56cb182dbf0 --- /dev/null +++ b/src/librustc_infer/infer/undo_log.rs @@ -0,0 +1,217 @@ +use std::marker::PhantomData; + +use rustc_data_structures::snapshot_vec as sv; +use rustc_data_structures::undo_log::{Rollback, UndoLogs}; +use rustc_data_structures::unify as ut; +use rustc_middle::ty; + +use crate::{ + infer::{region_constraints, type_variable, InferCtxtInner}, + traits, +}; + +pub struct Snapshot<'tcx> { + pub(crate) undo_len: usize, + _marker: PhantomData<&'tcx ()>, +} + +/// Records the 'undo' data fora single operation that affects some form of inference variable. +pub(crate) enum UndoLog<'tcx> { + TypeVariables(type_variable::UndoLog<'tcx>), + ConstUnificationTable(sv::UndoLog>>), + IntUnificationTable(sv::UndoLog>), + FloatUnificationTable(sv::UndoLog>), + RegionConstraintCollector(region_constraints::UndoLog<'tcx>), + RegionUnificationTable(sv::UndoLog>), + ProjectionCache(traits::UndoLog<'tcx>), + PushRegionObligation, +} + +macro_rules! impl_from { + ($($ctor: ident ($ty: ty),)*) => { + $( + impl<'tcx> From<$ty> for UndoLog<'tcx> { + fn from(x: $ty) -> Self { + UndoLog::$ctor(x.into()) + } + } + )* + } +} + +// Upcast from a single kind of "undoable action" to the general enum +impl_from! { + RegionConstraintCollector(region_constraints::UndoLog<'tcx>), + TypeVariables(type_variable::UndoLog<'tcx>), + + TypeVariables(sv::UndoLog>>), + TypeVariables(sv::UndoLog>), + TypeVariables(sv::UndoLog), + TypeVariables(type_variable::Instantiate), + + IntUnificationTable(sv::UndoLog>), + + FloatUnificationTable(sv::UndoLog>), + + ConstUnificationTable(sv::UndoLog>>), + + RegionUnificationTable(sv::UndoLog>), + ProjectionCache(traits::UndoLog<'tcx>), +} + +/// The Rollback trait defines how to rollback a particular action. +impl<'tcx> Rollback> for InferCtxtInner<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), + UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), + UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), + UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo), + UndoLog::RegionConstraintCollector(undo) => { + self.region_constraint_storage.as_mut().unwrap().reverse(undo) + } + UndoLog::RegionUnificationTable(undo) => { + self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) + } + UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo), + UndoLog::PushRegionObligation => { + self.region_obligations.pop(); + } + } + } +} + +/// The combined undo log for all the various unification tables. For each change to the storage +/// for any kind of inference variable, we record an UndoLog entry in the vector here. +pub(crate) struct InferCtxtUndoLogs<'tcx> { + logs: Vec>, + num_open_snapshots: usize, +} + +impl Default for InferCtxtUndoLogs<'_> { + fn default() -> Self { + Self { logs: Default::default(), num_open_snapshots: Default::default() } + } +} + +/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any +/// action that is convertable into a UndoLog (per the From impls above). +impl<'tcx, T> UndoLogs for InferCtxtUndoLogs<'tcx> +where + UndoLog<'tcx>: From, +{ + fn num_open_snapshots(&self) -> usize { + self.num_open_snapshots + } + + fn push(&mut self, undo: T) { + if self.in_snapshot() { + self.logs.push(undo.into()) + } + } + + fn clear(&mut self) { + self.logs.clear(); + self.num_open_snapshots = 0; + } + + fn extend(&mut self, undos: J) + where + Self: Sized, + J: IntoIterator, + { + if self.in_snapshot() { + self.logs.extend(undos.into_iter().map(UndoLog::from)) + } + } +} + +impl<'tcx> InferCtxtInner<'tcx> { + pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) { + debug!("rollback_to({})", snapshot.undo_len); + self.undo_log.assert_open_snapshot(&snapshot); + + while self.undo_log.logs.len() > snapshot.undo_len { + let undo = self.undo_log.logs.pop().unwrap(); + self.reverse(undo); + } + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } + + pub fn commit(&mut self, snapshot: Snapshot<'tcx>) { + debug!("commit({})", snapshot.undo_len); + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } +} + +impl<'tcx> InferCtxtUndoLogs<'tcx> { + pub fn actions_since_snapshot(&self, snapshot: &Snapshot<'tcx>) -> &[UndoLog<'tcx>] { + &self.logs[snapshot.undo_len..] + } + + pub fn start_snapshot(&mut self) -> Snapshot<'tcx> { + self.num_open_snapshots += 1; + Snapshot { undo_len: self.logs.len(), _marker: PhantomData } + } + + pub(crate) fn region_constraints_in_snapshot( + &self, + s: &Snapshot<'tcx>, + ) -> impl Iterator> + Clone { + self.logs[s.undo_len..].iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + pub(crate) fn region_constraints( + &self, + ) -> impl Iterator> + Clone { + self.logs.iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) { + // Failures here may indicate a failure to follow a stack discipline. + assert!(self.logs.len() >= snapshot.undo_len); + assert!(self.num_open_snapshots > 0); + } + + pub(crate) fn iter(&self) -> std::slice::Iter<'_, UndoLog<'tcx>> { + self.logs.iter() + } +} + +impl<'tcx> std::ops::Index for InferCtxtUndoLogs<'tcx> { + type Output = UndoLog<'tcx>; + + fn index(&self, key: usize) -> &Self::Output { + &self.logs[key] + } +} + +impl<'tcx> std::ops::IndexMut for InferCtxtUndoLogs<'tcx> { + fn index_mut(&mut self, key: usize) -> &mut Self::Output { + &mut self.logs[key] + } +} diff --git a/src/librustc_infer/traits/mod.rs b/src/librustc_infer/traits/mod.rs index 2210c663d14..a8585fd2935 100644 --- a/src/librustc_infer/traits/mod.rs +++ b/src/librustc_infer/traits/mod.rs @@ -20,9 +20,10 @@ pub use self::Vtable::*; pub use self::engine::{TraitEngine, TraitEngineExt}; pub use self::project::MismatchedProjectionTypes; +pub(crate) use self::project::UndoLog; pub use self::project::{ Normalized, NormalizedTy, ProjectionCache, ProjectionCacheEntry, ProjectionCacheKey, - ProjectionCacheSnapshot, Reveal, + ProjectionCacheStorage, Reveal, }; crate use self::util::elaborate_predicates; diff --git a/src/librustc_infer/traits/project.rs b/src/librustc_infer/traits/project.rs index 48375a9ddf4..f0d21a7d022 100644 --- a/src/librustc_infer/traits/project.rs +++ b/src/librustc_infer/traits/project.rs @@ -2,12 +2,19 @@ use super::PredicateObligation; -use rustc_data_structures::snapshot_map::{Snapshot, SnapshotMap}; -use rustc_middle::ty::fold::TypeFoldable; +use crate::infer::InferCtxtUndoLogs; + +use rustc_data_structures::{ + snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage}, + undo_log::Rollback, +}; use rustc_middle::ty::{self, Ty}; pub use rustc_middle::traits::Reveal; +pub(crate) type UndoLog<'tcx> = + snapshot_map::UndoLog, ProjectionCacheEntry<'tcx>>; + #[derive(Clone)] pub struct MismatchedProjectionTypes<'tcx> { pub err: ty::error::TypeError<'tcx>, @@ -58,9 +65,14 @@ impl<'tcx, T> Normalized<'tcx, T> { // // FIXME: we probably also want some sort of cross-infcx cache here to // reduce the amount of duplication. Let's see what we get with the Chalk reforms. +pub struct ProjectionCache<'a, 'tcx> { + map: &'a mut SnapshotMapStorage, ProjectionCacheEntry<'tcx>>, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + #[derive(Default)] -pub struct ProjectionCache<'tcx> { - map: SnapshotMap, ProjectionCacheEntry<'tcx>>, +pub struct ProjectionCacheStorage<'tcx> { + map: SnapshotMapStorage, ProjectionCacheEntry<'tcx>>, } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -82,30 +94,29 @@ pub enum ProjectionCacheEntry<'tcx> { NormalizedTy(NormalizedTy<'tcx>), } -// N.B., intentionally not Clone -pub struct ProjectionCacheSnapshot { - snapshot: Snapshot, +impl<'tcx> ProjectionCacheStorage<'tcx> { + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> ProjectionCache<'a, 'tcx> { + ProjectionCache { map: &mut self.map, undo_log } + } } -impl<'tcx> ProjectionCache<'tcx> { +impl<'tcx> ProjectionCache<'_, 'tcx> { + fn map( + &mut self, + ) -> SnapshotMapRef< + '_, + ProjectionCacheKey<'tcx>, + ProjectionCacheEntry<'tcx>, + InferCtxtUndoLogs<'tcx>, + > { + self.map.with_log(self.undo_log) + } + pub fn clear(&mut self) { - self.map.clear(); - } - - pub fn snapshot(&mut self) -> ProjectionCacheSnapshot { - ProjectionCacheSnapshot { snapshot: self.map.snapshot() } - } - - pub fn rollback_to(&mut self, snapshot: ProjectionCacheSnapshot) { - self.map.rollback_to(snapshot.snapshot); - } - - pub fn rollback_placeholder(&mut self, snapshot: &ProjectionCacheSnapshot) { - self.map.partial_rollback(&snapshot.snapshot, &|k| k.ty.has_re_placeholders()); - } - - pub fn commit(&mut self, snapshot: ProjectionCacheSnapshot) { - self.map.commit(snapshot.snapshot); + self.map().clear(); } /// Try to start normalize `key`; returns an error if @@ -115,11 +126,12 @@ impl<'tcx> ProjectionCache<'tcx> { &mut self, key: ProjectionCacheKey<'tcx>, ) -> Result<(), ProjectionCacheEntry<'tcx>> { - if let Some(entry) = self.map.get(&key) { + let mut map = self.map(); + if let Some(entry) = map.get(&key) { return Err(entry.clone()); } - self.map.insert(key, ProjectionCacheEntry::InProgress); + map.insert(key, ProjectionCacheEntry::InProgress); Ok(()) } @@ -129,7 +141,7 @@ impl<'tcx> ProjectionCache<'tcx> { "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", key, value ); - let fresh_key = self.map.insert(key, ProjectionCacheEntry::NormalizedTy(value)); + let fresh_key = self.map().insert(key, ProjectionCacheEntry::NormalizedTy(value)); assert!(!fresh_key, "never started projecting `{:?}`", key); } @@ -138,7 +150,8 @@ impl<'tcx> ProjectionCache<'tcx> { /// snapshot - if the snapshot is rolled back, the obligations will be /// marked as incomplete again). pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) { - let ty = match self.map.get(&key) { + let mut map = self.map(); + let ty = match map.get(&key) { Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => { debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty); ty.value @@ -151,7 +164,7 @@ impl<'tcx> ProjectionCache<'tcx> { } }; - self.map.insert( + map.insert( key, ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }), ); @@ -163,7 +176,7 @@ impl<'tcx> ProjectionCache<'tcx> { // We want to insert `ty` with no obligations. If the existing value // already has no obligations (as is common) we don't insert anything. if !ty.obligations.is_empty() { - self.map.insert( + self.map().insert( key, ProjectionCacheEntry::NormalizedTy(Normalized { value: ty.value, @@ -178,14 +191,20 @@ impl<'tcx> ProjectionCache<'tcx> { /// type information (in which case, the "fully resolved" key will /// be different). pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { - let fresh = self.map.insert(key, ProjectionCacheEntry::Ambiguous); + let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous); assert!(!fresh, "never started projecting `{:?}`", key); } /// Indicates that trying to normalize `key` resulted in /// error. pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) { - let fresh = self.map.insert(key, ProjectionCacheEntry::Error); + let fresh = self.map().insert(key, ProjectionCacheEntry::Error); assert!(!fresh, "never started projecting `{:?}`", key); } } + +impl<'tcx> Rollback> for ProjectionCacheStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + self.map.reverse(undo); + } +} diff --git a/src/librustc_middle/infer/unify_key.rs b/src/librustc_middle/infer/unify_key.rs index e205453a48c..2580ac6bebd 100644 --- a/src/librustc_middle/infer/unify_key.rs +++ b/src/librustc_middle/infer/unify_key.rs @@ -1,6 +1,9 @@ use crate::ty::{self, FloatVarValue, InferConst, IntVarValue, Ty, TyCtxt}; -use rustc_data_structures::unify::InPlace; -use rustc_data_structures::unify::{EqUnifyValue, NoError, UnificationTable, UnifyKey, UnifyValue}; +use rustc_data_structures::snapshot_vec; +use rustc_data_structures::undo_log::UndoLogs; +use rustc_data_structures::unify::{ + self, EqUnifyValue, InPlace, NoError, UnificationTable, UnifyKey, UnifyValue, +}; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; @@ -212,10 +215,14 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { impl<'tcx> EqUnifyValue for &'tcx ty::Const<'tcx> {} -pub fn replace_if_possible( - table: &mut UnificationTable>>, +pub fn replace_if_possible( + table: &mut UnificationTable, V, L>>, c: &'tcx ty::Const<'tcx>, -) -> &'tcx ty::Const<'tcx> { +) -> &'tcx ty::Const<'tcx> +where + V: snapshot_vec::VecLike>>, + L: UndoLogs>>>, +{ if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = c { match table.probe_value(*vid).val.known() { Some(c) => c, diff --git a/src/librustc_trait_selection/traits/auto_trait.rs b/src/librustc_trait_selection/traits/auto_trait.rs index 6326a87c5ed..e19ddcd9e5e 100644 --- a/src/librustc_trait_selection/traits/auto_trait.rs +++ b/src/librustc_trait_selection/traits/auto_trait.rs @@ -195,7 +195,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { let body_id_map: FxHashMap<_, _> = infcx .inner .borrow() - .region_obligations + .region_obligations() .iter() .map(|&(id, _)| (id, vec![])) .collect(); diff --git a/src/librustc_trait_selection/traits/fulfill.rs b/src/librustc_trait_selection/traits/fulfill.rs index 300acf95c99..1e056c96acd 100644 --- a/src/librustc_trait_selection/traits/fulfill.rs +++ b/src/librustc_trait_selection/traits/fulfill.rs @@ -240,9 +240,15 @@ struct FulfillProcessor<'a, 'b, 'tcx> { register_region_obligations: bool, } -fn mk_pending(os: Vec>) -> Vec> { +fn mk_pending( + infcx: &InferCtxt<'_, 'tcx>, + os: Vec>, +) -> Vec> { os.into_iter() - .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) + .map(|mut o| { + o.predicate = infcx.resolve_vars_if_possible(&o.predicate); + PendingPredicateObligation { obligation: o, stalled_on: vec![] } + }) .collect() } @@ -312,6 +318,8 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); + let infcx = self.selcx.infcx(); + match obligation.predicate { ty::Predicate::Trait(ref data, _) => { let trait_obligation = obligation.with(*data); @@ -319,7 +327,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { if data.is_global() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. - if self.selcx.infcx().predicate_must_hold_considering_regions(&obligation) { + if infcx.predicate_must_hold_considering_regions(&obligation) { debug!( "selecting trait `{:?}` at depth {} evaluated to holds", data, obligation.recursion_depth @@ -334,7 +342,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { "selecting trait `{:?}` at depth {} yielded Ok(Some)", data, obligation.recursion_depth ); - ProcessResult::Changed(mk_pending(vtable.nested_obligations())) + ProcessResult::Changed(mk_pending(infcx, vtable.nested_obligations())) } Ok(None) => { debug!( @@ -351,7 +359,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { debug!( "process_predicate: pending obligation {:?} now stalled on {:?}", - self.selcx.infcx().resolve_vars_if_possible(obligation), + infcx.resolve_vars_if_possible(obligation), pending_obligation.stalled_on ); @@ -369,7 +377,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::Predicate::RegionOutlives(ref binder) => { - match self.selcx.infcx().region_outlives_predicate(&obligation.cause, binder) { + match infcx.region_outlives_predicate(&obligation.cause, binder) { Ok(()) => ProcessResult::Changed(vec![]), Err(_) => ProcessResult::Error(CodeSelectionError(Unimplemented)), } @@ -428,7 +436,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { trait_ref_type_vars(self.selcx, data.to_poly_trait_ref(tcx)); ProcessResult::Unchanged } - Ok(Some(os)) => ProcessResult::Changed(mk_pending(os)), + Ok(Some(os)) => ProcessResult::Changed(mk_pending(infcx, os)), Err(e) => ProcessResult::Error(CodeProjectionError(e)), } } @@ -467,7 +475,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { vec![TyOrConstInferVar::maybe_from_ty(ty).unwrap()]; ProcessResult::Unchanged } - Some(os) => ProcessResult::Changed(mk_pending(os)), + Some(os) => ProcessResult::Changed(mk_pending(infcx, os)), } } @@ -485,7 +493,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { ]; ProcessResult::Unchanged } - Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Some(Ok(ok)) => ProcessResult::Changed(mk_pending(infcx, ok.obligations)), Some(Err(err)) => { let expected_found = ExpectedFound::new( subtype.skip_binder().a_is_expected, diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs index 2b4a0409fd1..7536b444351 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -471,7 +471,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // bounds. It might be the case that we want two distinct caches, // or else another kind of cache entry. - let cache_result = infcx.inner.borrow_mut().projection_cache.try_start(cache_key); + let cache_result = infcx.inner.borrow_mut().projection_cache().try_start(cache_key); match cache_result { Ok(()) => {} Err(ProjectionCacheEntry::Ambiguous) => { @@ -537,7 +537,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // Once we have inferred everything we need to know, we // can ignore the `obligations` from that point on. if infcx.unresolved_type_vars(&ty.value).is_none() { - infcx.inner.borrow_mut().projection_cache.complete_normalized(cache_key, &ty); + infcx.inner.borrow_mut().projection_cache().complete_normalized(cache_key, &ty); // No need to extend `obligations`. } else { obligations.extend(ty.obligations); @@ -604,7 +604,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( }; let cache_value = prune_cache_value_obligations(infcx, &result); - infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, cache_value); + infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, cache_value); obligations.extend(result.obligations); Some(result.value) } @@ -615,7 +615,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( projected_ty ); let result = Normalized { value: projected_ty, obligations: vec![] }; - infcx.inner.borrow_mut().projection_cache.insert_ty(cache_key, result.clone()); + infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); // No need to extend `obligations`. Some(result.value) } @@ -624,7 +624,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( "opt_normalize_projection_type: \ too many candidates" ); - infcx.inner.borrow_mut().projection_cache.ambiguous(cache_key); + infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); None } Err(ProjectionTyError::TraitSelectionError(_)) => { @@ -634,7 +634,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // Trait`, which when processed will cause the error to be // reported later - infcx.inner.borrow_mut().projection_cache.error(cache_key); + infcx.inner.borrow_mut().projection_cache().error(cache_key); let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); obligations.extend(result.obligations); Some(result.value) diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index dfbb0742448..38590abf1f7 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -471,7 +471,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate(self, data) { - self.infcx.inner.borrow_mut().projection_cache.complete(key); + self.infcx.inner.borrow_mut().projection_cache().complete(key); } result }