From: Niko Matsakis Date: Fri, 28 Apr 2017 10:00:48 +0000 (-0400) Subject: introduce idea of "stealable" MIR X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=29263fdb541fe5d59ef116ebf3666cd573d077ab;p=rust.git introduce idea of "stealable" MIR This is a more principled version of the `RefCell` we were using before. We now allocate a `Steal>` for each intermediate MIR pass; when the next pass steals the entry, any later attempts to use it will panic (there is no way to *test* if MIR is stolen, you're just supposed to *know*). --- diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 7c9b8665197..80a4d9a9ff1 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -13,7 +13,7 @@ use hir::map::DefPathData; use mir::{Mir, Promoted}; use ty::TyCtxt; -use std::cell::{Ref, RefCell}; +use std::cell::Ref; use std::rc::Rc; use syntax::ast::NodeId; @@ -98,7 +98,7 @@ pub trait MirCtxt<'a, 'tcx: 'a> { fn pass_num(&self) -> MirPassIndex; fn source(&self) -> MirSource; fn read_previous_mir(&self) -> Ref<'tcx, Mir<'tcx>>; - fn steal_previous_mir(&self) -> &'tcx RefCell>; + fn steal_previous_mir(&self) -> Mir<'tcx>; } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -132,7 +132,7 @@ fn name<'a>(&'a self) -> Cow<'a, str> { default_name::() } - fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell>; + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> Mir<'tcx>; } /// A streamlined trait that you can implement to create a pass; the @@ -154,14 +154,14 @@ fn name<'a>(&'a self) -> Cow<'a, str> { MirPass::name(self) } - fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell> { + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> Mir<'tcx> { let tcx = mir_cx.tcx(); let source = mir_cx.source(); - let mir = mir_cx.steal_previous_mir(); - MirPass::run_pass(self, tcx, source, &mut mir.borrow_mut()); + let mut mir = mir_cx.steal_previous_mir(); + MirPass::run_pass(self, tcx, source, &mut mir); let item_id = source.item_id(); - for (promoted_index, promoted_mir) in mir.borrow_mut().promoted.iter_enumerated_mut() { + for (promoted_index, promoted_mir) in mir.promoted.iter_enumerated_mut() { let promoted_source = MirSource::Promoted(item_id, promoted_index); MirPass::run_pass(self, tcx, promoted_source, promoted_mir); } diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 0c189853c61..0dd23eb7e70 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -40,6 +40,7 @@ use ty::layout::{Layout, TargetDataLayout}; use ty::inhabitedness::DefIdForest; use ty::maps; +use ty::steal::Steal; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; use util::nodemap::{FxHashMap, FxHashSet}; use rustc_data_structures::accumulate_vec::AccumulateVec; @@ -70,7 +71,8 @@ pub struct GlobalArenas<'tcx> { generics: TypedArena, trait_def: TypedArena, adt_def: TypedArena, - mir: TypedArena>>, + steal_mir: TypedArena>>, + mir: TypedArena>, tables: TypedArena>, } @@ -81,6 +83,7 @@ pub fn new() -> GlobalArenas<'tcx> { generics: TypedArena::new(), trait_def: TypedArena::new(), adt_def: TypedArena::new(), + steal_mir: TypedArena::new(), mir: TypedArena::new(), tables: TypedArena::new(), } @@ -622,8 +625,12 @@ pub fn alloc_generics(self, generics: ty::Generics) -> &'gcx ty::Generics { self.global_arenas.generics.alloc(generics) } - pub fn alloc_mir(self, mir: Mir<'gcx>) -> &'gcx RefCell> { - self.global_arenas.mir.alloc(RefCell::new(mir)) + pub fn alloc_steal_mir(self, mir: Mir<'gcx>) -> &'gcx Steal> { + self.global_arenas.steal_mir.alloc(Steal::new(mir)) + } + + pub fn alloc_mir(self, mir: Mir<'gcx>) -> &'gcx Mir<'gcx> { + self.global_arenas.mir.alloc(mir) } pub fn alloc_tables(self, tables: ty::TypeckTables<'gcx>) -> &'gcx ty::TypeckTables<'gcx> { diff --git a/src/librustc/ty/maps.rs b/src/librustc/ty/maps.rs index 58a38cc903d..09bdca4915a 100644 --- a/src/librustc/ty/maps.rs +++ b/src/librustc/ty/maps.rs @@ -20,6 +20,7 @@ use session::CompileResult; use ty::{self, CrateInherentImpls, Ty, TyCtxt}; use ty::item_path; +use ty::steal::Steal; use ty::subst::Substs; use util::nodemap::{DefIdSet, NodeSet}; @@ -32,7 +33,7 @@ use syntax_pos::{Span, DUMMY_SP}; use syntax::symbol::Symbol; -trait Key { +trait Key: Clone { fn map_crate(&self) -> CrateNum; fn default_span(&self, tcx: TyCtxt) -> Span; } @@ -339,13 +340,13 @@ fn describe(tcx: TyCtxt, def_id: DefId) -> String { impl<'tcx> QueryDescription for queries::mir_suite<'tcx> { fn describe(_: TyCtxt, (suite, _): (MirSuite, DefId)) -> String { - format!("MIR passes #{}.*", suite.0) + format!("MIR suite #{}.*", suite.0) } } impl<'tcx> QueryDescription for queries::mir_pass<'tcx> { - fn describe(_: TyCtxt, (pass_set, pass_num, _): (MirSuite, MirPassIndex, DefId)) -> String { - format!("MIR pass #{}.{}", pass_set.0, pass_num.0) + fn describe(_: TyCtxt, (suite, pass_num, _): (MirSuite, MirPassIndex, DefId)) -> String { + format!("MIR pass #{}.{}", suite.0, pass_num.0) } } @@ -586,22 +587,22 @@ fn default() -> Self { /// Performs the initial MIR construction. You almost certainly do not /// want to use this query, because its output is intended to be stolen /// immediately by the MIR passes below. Consider `optimized_mir` instead. - [] mir_build: Mir(DefId) -> &'tcx RefCell>, + [] mir_build: Mir(DefId) -> &'tcx Steal>, /// Fetch the MIR for a given def-id after the given set of passes has ben /// applied to it. This is mostly an "intermediate" query. Normally, you would /// prefer to use `optimized_mir(def_id)`, which will fetch the MIR after all /// optimizations and so forth. - [] mir_suite: mir_suite((MirSuite, DefId)) -> &'tcx RefCell>, + [] mir_suite: mir_suite((MirSuite, DefId)) -> &'tcx Steal>, /// Fetch the MIR for a given def-id after a given pass has been executed. This is /// **only** intended to be used by the `mir_suite` provider -- if you are using it /// manually, you're doing it wrong. - [] mir_pass: mir_pass((MirSuite, MirPassIndex, DefId)) -> &'tcx RefCell>, + [] mir_pass: mir_pass((MirSuite, MirPassIndex, DefId)) -> &'tcx Steal>, /// MIR after our optimization passes have run. This is MIR that is ready /// for trans. This is also the only query that can fetch non-local MIR, at present. - [] optimized_mir: Mir(DefId) -> &'tcx RefCell>, + [] optimized_mir: Mir(DefId) -> &'tcx mir::Mir<'tcx>, /// Records the type of each closure. The def ID is the ID of the /// expression defining the closure. @@ -650,7 +651,7 @@ fn default() -> Self { /// fn item. [] region_maps: RegionMaps(DefId) -> Rc>, - [] mir_shims: mir_shim_dep_node(ty::InstanceDef<'tcx>) -> &'tcx RefCell>, + [] mir_shims: mir_shim_dep_node(ty::InstanceDef<'tcx>) -> &'tcx mir::Mir<'tcx>, [] def_symbol_name: SymbolName(DefId) -> ty::SymbolName, [] symbol_name: symbol_name_dep_node(ty::Instance<'tcx>) -> ty::SymbolName, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index cf66c83800d..537846bc0f4 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -35,7 +35,7 @@ use util::nodemap::{NodeSet, DefIdMap, FxHashMap, FxHashSet}; use serialize::{self, Encodable, Encoder}; -use std::cell::{Cell, RefCell, Ref}; +use std::cell::{Cell, RefCell}; use std::collections::BTreeMap; use std::cmp; use std::fmt; @@ -96,6 +96,7 @@ pub mod maps; pub mod outlives; pub mod relate; +pub mod steal; pub mod subst; pub mod trait_def; pub mod walk; @@ -2324,13 +2325,13 @@ pub fn item_name(self, id: DefId) -> ast::Name { } /// Given the did of an item, returns its (optimized) MIR, borrowed immutably. - pub fn item_mir(self, did: DefId) -> Ref<'gcx, Mir<'gcx>> { - self.optimized_mir(did).borrow() + pub fn item_mir(self, did: DefId) -> &'gcx Mir<'gcx> { + self.optimized_mir(did) } /// Return the possibly-auto-generated MIR of a (DefId, Subst) pair. pub fn instance_mir(self, instance: ty::InstanceDef<'gcx>) - -> Ref<'gcx, Mir<'gcx>> + -> &'gcx Mir<'gcx> { match instance { ty::InstanceDef::Item(did) => { @@ -2341,14 +2342,14 @@ pub fn instance_mir(self, instance: ty::InstanceDef<'gcx>) ty::InstanceDef::Virtual(..) | ty::InstanceDef::ClosureOnceShim { .. } | ty::InstanceDef::DropGlue(..) => { - self.mir_shims(instance).borrow() + self.mir_shims(instance) } } } /// Given the DefId of an item, returns its MIR, borrowed immutably. /// Returns None if there is no MIR for the DefId - pub fn maybe_item_mir(self, did: DefId) -> Option>> { + pub fn maybe_item_mir(self, did: DefId) -> Option<&'gcx Mir<'gcx>> { if did.is_local() && !self.mir_keys(LOCAL_CRATE).contains(&did) { return None; } diff --git a/src/librustc/ty/steal.rs b/src/librustc/ty/steal.rs new file mode 100644 index 00000000000..0da937d0366 --- /dev/null +++ b/src/librustc/ty/steal.rs @@ -0,0 +1,27 @@ +use std::cell::{Ref, RefCell}; +use std::mem; + +pub struct Steal { + value: RefCell> +} + +impl Steal { + pub fn new(value: T) -> Self { + Steal { + value: RefCell::new(Some(value)) + } + } + + pub fn borrow(&self) -> Ref { + Ref::map(self.value.borrow(), |opt| match *opt { + None => panic!("attempted to read from stolen value"), + Some(ref v) => v + }) + } + + pub fn steal(&self) -> T { + let value_ref = &mut *self.value.borrow_mut(); + let value = mem::replace(value_ref, None); + value.expect("attempt to read from stolen value") + } +} diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 4ecce3cc132..7bc27596e40 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -30,7 +30,6 @@ use rustc_back::PanicStrategy; use std::any::Any; -use std::mem; use std::rc::Rc; use syntax::ast; @@ -102,9 +101,6 @@ pub fn provide<$lt>(providers: &mut Providers<$lt>) { let mir = tcx.alloc_mir(mir); - // Perma-borrow MIR from extern crates to prevent mutation. - mem::forget(mir.borrow()); - mir } mir_const_qualif => { cdata.mir_const_qualif(def_id.index) } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 46f7c34c06e..e51a7a410e0 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -29,6 +29,7 @@ use rustc::traits::Reveal; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; +use rustc::ty::steal::Steal; use rustc::ty::subst::Substs; use rustc::hir; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; @@ -37,7 +38,6 @@ use syntax::ast; use syntax_pos::Span; -use std::cell::RefCell; use std::mem; use std::rc::Rc; @@ -98,7 +98,7 @@ fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, 'tcx> { Rc::new(set) } -fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx RefCell> { +fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal> { let id = tcx.hir.as_local_node_id(def_id).unwrap(); let unsupported = || { span_bug!(tcx.hir.span(id), "can't build MIR for {:?}", def_id); @@ -196,7 +196,7 @@ fn mir_build<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx RefC mir_util::dump_mir(tcx, None, "mir_map", &0, src, &mir); - tcx.alloc_mir(mir) + tcx.alloc_steal_mir(mir) }) } @@ -233,7 +233,7 @@ fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>) { fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ctor_id: ast::NodeId, v: &'tcx hir::VariantData) - -> &'tcx RefCell> + -> &'tcx Steal> { let span = tcx.hir.span(ctor_id); if let hir::VariantData::Tuple(ref fields, ctor_id) = *v { @@ -255,7 +255,7 @@ fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_util::dump_mir(tcx, None, "mir_map", &0, src, &mir); - tcx.alloc_mir(mir) + tcx.alloc_steal_mir(mir) }) } else { span_bug!(span, "attempting to create MIR for non-tuple variant {:?}", v); diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index f2a550ec23a..a899c69ca15 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -24,10 +24,8 @@ use syntax::ast; use syntax_pos::Span; -use std::cell::RefCell; use std::fmt; use std::iter; -use std::mem; use transform::{add_call_guards, no_landing_pads, simplify}; use util::elaborate_drops::{self, DropElaborator, DropStyle, DropFlagMode}; @@ -39,7 +37,7 @@ pub fn provide(providers: &mut Providers) { fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, instance: ty::InstanceDef<'tcx>) - -> &'tcx RefCell> + -> &'tcx Mir<'tcx> { debug!("make_shim({:?})", instance); let did = instance.def_id(); @@ -117,8 +115,6 @@ fn make_shim<'a, 'tcx>(tcx: ty::TyCtxt<'a, 'tcx, 'tcx>, debug!("make_shim({:?}) = {:?}", instance, result); let result = tcx.alloc_mir(result); - // Perma-borrow MIR from shims to prevent mutation. - mem::forget(result.borrow()); result } diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs index a76ba8a8b68..c00817f9179 100644 --- a/src/librustc_mir/transform/dump_mir.rs +++ b/src/librustc_mir/transform/dump_mir.rs @@ -11,7 +11,6 @@ //! This pass just dumps MIR at a specified point. use std::borrow::Cow; -use std::cell::RefCell; use std::fmt; use std::fs::File; use std::io; @@ -29,7 +28,7 @@ fn name<'a>(&'a self) -> Cow<'a, str> { Cow::Borrowed(self.0) } - fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell> { + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> Mir<'tcx> { mir_cx.steal_previous_mir() } } @@ -53,9 +52,9 @@ fn on_mir_pass<'a, 'tcx: 'a>(&self, mir: Option<&Mir<'tcx>>) { let tcx = mir_cx.tcx(); - let pass_set = mir_cx.pass_set(); + let suite = mir_cx.suite(); let pass_num = mir_cx.pass_num(); - let pass = tcx.mir_passes.pass(pass_set, pass_num); + let pass = tcx.mir_passes.pass(suite, pass_num); let name = &pass.name(); let source = mir_cx.source(); if mir_util::dump_enabled(tcx, name, source) { @@ -68,7 +67,7 @@ fn on_mir_pass<'a, 'tcx: 'a>(&self, } }; mir_util::dump_mir(tcx, - Some((pass_set, pass_num)), + Some((suite, pass_num)), name, &Disambiguator { is_after: mir.is_some() }, source, diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 49cb254cdf2..d30495efab5 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -11,10 +11,10 @@ use rustc::hir::def_id::DefId; use rustc::mir::Mir; use rustc::mir::transform::{MirCtxt, MirPassIndex, MirSuite, MirSource, MIR_OPTIMIZED}; +use rustc::ty::steal::Steal; use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; -use std::cell::{Ref, RefCell}; -use std::mem; +use std::cell::Ref; pub mod simplify_branches; pub mod simplify; @@ -40,19 +40,14 @@ pub fn provide(providers: &mut Providers) { }; } -fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx RefCell> { - let mir = tcx.mir_suite((MIR_OPTIMIZED, def_id)); - - // "lock" the ref cell into read mode; after this point, - // there ought to be no more changes to the MIR. - mem::drop(mir.borrow()); - - mir +fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Mir<'tcx> { + let mir = tcx.mir_suite((MIR_OPTIMIZED, def_id)).steal(); + tcx.alloc_mir(mir) } fn mir_suite<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (suite, def_id): (MirSuite, DefId)) - -> &'tcx RefCell> + -> &'tcx Steal> { let passes = &tcx.mir_passes; let len = passes.len_passes(suite); @@ -62,7 +57,7 @@ fn mir_suite<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn mir_pass<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (suite, pass_num, def_id): (MirSuite, MirPassIndex, DefId)) - -> &'tcx RefCell> + -> &'tcx Steal> { let passes = &tcx.mir_passes; let pass = passes.pass(suite, pass_num); @@ -75,10 +70,10 @@ fn mir_pass<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mir = pass.run_pass(&mir_ctxt); for hook in passes.hooks() { - hook.on_mir_pass(&mir_ctxt, Some(&mir.borrow())); + hook.on_mir_pass(&mir_ctxt, Some(&mir)); } - mir + tcx.alloc_steal_mir(mir) } struct MirCtxtImpl<'a, 'tcx: 'a> { @@ -112,10 +107,16 @@ fn source(&self) -> MirSource { } fn read_previous_mir(&self) -> Ref<'tcx, Mir<'tcx>> { - self.steal_previous_mir().borrow() + self.previous_mir().borrow() } - fn steal_previous_mir(&self) -> &'tcx RefCell> { + fn steal_previous_mir(&self) -> Mir<'tcx> { + self.previous_mir().steal() + } +} + +impl<'a, 'tcx> MirCtxtImpl<'a, 'tcx> { + fn previous_mir(&self) -> &'tcx Steal> { let MirSuite(suite) = self.suite; let MirPassIndex(pass_num) = self.pass_num; if pass_num > 0 { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index cb002acf009..8fc264ac1d4 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -33,7 +33,6 @@ use syntax::feature_gate::UnstableFeatures; use syntax_pos::{Span, DUMMY_SP}; -use std::cell::RefCell; use std::fmt; use std::usize; @@ -925,7 +924,7 @@ pub fn provide(providers: &mut Providers) { fn qualify_const_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> u8 { - let mir = &tcx.mir_pass_set((MIR_CONST, def_id)).borrow(); + let mir = &tcx.mir_suite((MIR_CONST, def_id)).borrow(); if mir.return_ty.references_error() { return Qualif::NOT_CONST.bits(); } @@ -940,7 +939,7 @@ fn qualify_const_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pub struct QualifyAndPromoteConstants; impl DefIdPass for QualifyAndPromoteConstants { - fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell> { + fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> Mir<'tcx> { let tcx = mir_cx.tcx(); match mir_cx.source() { MirSource::Const(_) => { @@ -953,8 +952,8 @@ fn run_pass<'a, 'tcx: 'a>(&self, mir_cx: &MirCtxt<'a, 'tcx>) -> &'tcx RefCell { - let mir = mir_cx.steal_previous_mir(); - self.run_pass(tcx, src, &mut mir.borrow_mut()); + let mut mir = mir_cx.steal_previous_mir(); + self.run_pass(tcx, src, &mut mir); mir } } diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index c3a30f7b882..4f94c4d122b 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -93,7 +93,7 @@ fn dump_matched_mir_node<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } else { match pass_num { None => format!(".-------"), - Some((pass_set, pass_num)) => format!(".{:03}-{:03}", pass_set.0, pass_num.0), + Some((suite, pass_num)) => format!(".{:03}-{:03}", suite.0, pass_num.0), } };