diff options
Diffstat (limited to 'compiler/rustc_mir_dataflow')
-rw-r--r-- | compiler/rustc_mir_dataflow/Cargo.toml | 10 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/debuginfo.rs | 20 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/drop_flag_effects.rs | 83 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/elaborate_drops.rs | 10 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/framework/graphviz.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/framework/mod.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/impls/initialized.rs | 18 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/impls/liveness.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs | 6 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/lib.rs | 4 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/move_paths/builder.rs | 230 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/move_paths/mod.rs | 56 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/rustc_peek.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_mir_dataflow/src/value_analysis.rs | 165 |
15 files changed, 307 insertions, 311 deletions
diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 4a296bb33..61664eb2a 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -3,13 +3,10 @@ name = "rustc_mir_dataflow" version = "0.0.0" edition = "2021" -[lib] - [dependencies] +# tidy-alphabetical-start polonius-engine = "0.13.0" regex = "1" -smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } -tracing = "0.1" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } @@ -20,5 +17,8 @@ rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_serialize = { path = "../rustc_serialize" } -rustc_target = { path = "../rustc_target" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +tracing = "0.1" +# tidy-alphabetical-end diff --git a/compiler/rustc_mir_dataflow/src/debuginfo.rs b/compiler/rustc_mir_dataflow/src/debuginfo.rs new file mode 100644 index 000000000..fd5e8cf29 --- /dev/null +++ b/compiler/rustc_mir_dataflow/src/debuginfo.rs @@ -0,0 +1,20 @@ +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::visit::*; +use rustc_middle::mir::*; + +/// Return the set of locals that appear in debuginfo. +pub fn debuginfo_locals(body: &Body<'_>) -> BitSet<Local> { + let mut visitor = DebuginfoLocals(BitSet::new_empty(body.local_decls.len())); + for debuginfo in body.var_debug_info.iter() { + visitor.visit_var_debug_info(debuginfo); + } + visitor.0 +} + +struct DebuginfoLocals(BitSet<Local>); + +impl Visitor<'_> for DebuginfoLocals { + fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) { + self.0.insert(local); + } +} diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index 0d466bbe5..163d74cc9 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -1,6 +1,6 @@ use crate::elaborate_drops::DropFlagState; use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_target::abi::VariantIdx; use super::indexes::MovePathIndex; @@ -55,60 +55,6 @@ pub fn on_all_children_bits<'tcx, F>( ) where F: FnMut(MovePathIndex), { - #[inline] - fn is_terminal_path<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - move_data: &MoveData<'tcx>, - path: MovePathIndex, - ) -> bool { - let place = move_data.move_paths[path].place; - - // When enumerating the child fragments of a path, don't recurse into - // paths (1.) past arrays, slices, and pointers, nor (2.) into a type - // that implements `Drop`. - // - // Places behind references or arrays are not tracked by elaboration - // and are always assumed to be initialized when accessible. As - // references and indexes can be reseated, trying to track them can - // only lead to trouble. - // - // Places behind ADT's with a Drop impl are not tracked by - // elaboration since they can never have a drop-flag state that - // differs from that of the parent with the Drop impl. - // - // In both cases, the contents can only be accessed if and only if - // their parents are initialized. This implies for example that there - // is no need to maintain separate drop flags to track such state. - // - // FIXME: we have to do something for moving slice patterns. - let ty = place.ty(body, tcx).ty; - match ty.kind() { - ty::Adt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => { - debug!( - "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true", - place, ty - ); - true - } - ty::Array(..) => { - debug!( - "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false", - place, ty - ); - false - } - ty::Slice(..) | ty::Ref(..) | ty::RawPtr(..) => { - debug!( - "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true", - place, ty - ); - true - } - _ => false, - } - } - fn on_all_children_bits<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -120,10 +66,6 @@ pub fn on_all_children_bits<'tcx, F>( { each_child(move_path_index); - if is_terminal_path(tcx, body, move_data, move_path_index) { - return; - } - let mut next_child_index = move_data.move_paths[move_path_index].first_child; while let Some(child_index) = next_child_index { on_all_children_bits(tcx, body, move_data, child_index, each_child); @@ -133,29 +75,6 @@ pub fn on_all_children_bits<'tcx, F>( on_all_children_bits(tcx, body, move_data, move_path_index, &mut each_child); } -pub fn on_all_drop_children_bits<'tcx, F>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - ctxt: &MoveDataParamEnv<'tcx>, - path: MovePathIndex, - mut each_child: F, -) where - F: FnMut(MovePathIndex), -{ - on_all_children_bits(tcx, body, &ctxt.move_data, path, |child| { - let place = &ctxt.move_data.move_paths[path].place; - let ty = place.ty(body, tcx).ty; - debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, place, ty); - - let erased_ty = tcx.erase_regions(ty); - if erased_ty.needs_drop(tcx, ctxt.param_env) { - each_child(child); - } else { - debug!("on_all_drop_children_bits - skipping") - } - }) -} - pub fn drop_flag_effects_for_function_entry<'tcx, F>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index c9991e499..25ba67a63 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -860,13 +860,13 @@ where let ty = self.place_ty(self.place); match ty.kind() { ty::Closure(_, args) => self.open_drop_for_tuple(&args.as_closure().upvar_tys()), - // Note that `elaborate_drops` only drops the upvars of a generator, + // Note that `elaborate_drops` only drops the upvars of a coroutine, // and this is ok because `open_drop` here can only be reached - // within that own generator's resume function. + // within that own coroutine's resume function. // This should only happen for the self argument on the resume function. - // It effectively only contains upvars until the generator transformation runs. - // See librustc_body/transform/generator.rs for more details. - ty::Generator(_, args, _) => self.open_drop_for_tuple(&args.as_generator().upvar_tys()), + // It effectively only contains upvars until the coroutine transformation runs. + // See librustc_body/transform/coroutine.rs for more details. + ty::Coroutine(_, args, _) => self.open_drop_for_tuple(&args.as_coroutine().upvar_tys()), ty::Tuple(fields) => self.open_drop_for_tuple(fields), ty::Adt(def, args) => self.open_drop_for_adt(*def, args), ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind), diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index bdddaaebc..c12ccba1e 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -267,7 +267,7 @@ where mir::TerminatorKind::Yield { resume, resume_arg, .. } => { self.write_row(w, "", "(on yield resume)", |this, w, fmt| { - let state_on_generator_drop = this.results.get().clone(); + let state_on_coroutine_drop = this.results.get().clone(); this.results.apply_custom_effect(|analysis, state| { analysis.apply_call_return_effect( state, @@ -283,7 +283,7 @@ where fmt = fmt, diff = diff_pretty( this.results.get(), - &state_on_generator_drop, + &state_on_coroutine_drop, this.results.analysis() ), ) diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index ce30c642f..5020a1cf0 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -48,7 +48,7 @@ mod visitor; pub use self::cursor::{AnalysisResults, ResultsClonedCursor, ResultsCursor, ResultsRefCursor}; pub use self::direction::{Backward, Direction, Forward}; pub use self::engine::{Engine, EntrySets, Results, ResultsCloned}; -pub use self::lattice::{JoinSemiLattice, MaybeReachable, MeetSemiLattice}; +pub use self::lattice::{JoinSemiLattice, MaybeReachable}; pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor}; /// Analysis domains are all bitsets of various kinds. This trait holds @@ -114,7 +114,7 @@ pub trait AnalysisDomain<'tcx> { // // FIXME: For backward dataflow analyses, the initial state should be applied to every basic // block where control flow could exit the MIR body (e.g., those terminated with `return` or - // `resume`). It's not obvious how to handle `yield` points in generators, however. + // `resume`). It's not obvious how to handle `yield` points in coroutines, however. fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain); } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index f6398c8d0..01acc380f 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -10,7 +10,7 @@ use crate::{AnalysisDomain, GenKill, GenKillAnalysis}; /// /// At present, this is used as a very limited form of alias analysis. For example, /// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for -/// immovable generators. +/// immovable coroutines. #[derive(Clone, Copy)] pub struct MaybeBorrowedLocals; @@ -141,7 +141,7 @@ where | TerminatorKind::Call { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::GeneratorDrop + | TerminatorKind::CoroutineDrop | TerminatorKind::Goto { .. } | TerminatorKind::InlineAsm { .. } | TerminatorKind::UnwindResume diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index e6d383d62..c968e7aea 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -10,7 +10,7 @@ use crate::framework::SwitchIntEdgeEffects; use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use crate::on_lookup_result_bits; use crate::MoveDataParamEnv; -use crate::{drop_flag_effects, on_all_children_bits, on_all_drop_children_bits}; +use crate::{drop_flag_effects, on_all_children_bits}; use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis, MaybeReachable}; /// `MaybeInitializedPlaces` tracks all places that might be @@ -72,7 +72,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> { ) -> bool { if let LookupResult::Exact(path) = self.move_data().rev_lookup.find(place.as_ref()) { let mut maybe_live = false; - on_all_drop_children_bits(self.tcx, self.body, self.mdpe, path, |child| { + on_all_children_bits(self.tcx, self.body, self.move_data(), path, |child| { maybe_live |= state.contains(child); }); !maybe_live @@ -690,9 +690,13 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { if let mir::StatementKind::StorageDead(local) = stmt.kind { // End inits for StorageDead, so that an immutable variable can // be reinitialized on the next iteration of the loop. - let move_path_index = rev_lookup.find_local(local); - debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]); - trans.kill_all(init_path_map[move_path_index].iter().copied()); + if let Some(move_path_index) = rev_lookup.find_local(local) { + debug!( + "clears the ever initialized status of {:?}", + init_path_map[move_path_index] + ); + trans.kill_all(init_path_map[move_path_index].iter().copied()); + } } } @@ -763,9 +767,9 @@ fn switch_on_enum_discriminant<'mir, 'tcx>( ty::Adt(def, _) => return Some((*discriminated, *def)), // `Rvalue::Discriminant` is also used to get the active yield point for a - // generator, but we do not need edge-specific effects in that case. This may + // coroutine, but we do not need edge-specific effects in that case. This may // change in the future. - ty::Generator(..) => return None, + ty::Coroutine(..) => return None, t => bug!("`discriminant` called on unexpected type {:?}", t), } diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 664703795..bdfb6a6ff 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -98,7 +98,7 @@ where { fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) { if let PlaceContext::MutatingUse(MutatingUseContext::Yield) = context { - // The resume place is evaluated and assigned to only after generator resumes, so its + // The resume place is evaluated and assigned to only after coroutine resumes, so its // effect is handled separately in `call_resume_effect`. return; } diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 94d6eb67d..5a58e3af8 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -268,7 +268,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { // Note that we do *not* gen the `resume_arg` of `Yield` terminators. The reason for // that is that a `yield` will return from the function, and `resume_arg` is written - // only when the generator is later resumed. Unlike `Call`, this doesn't require the + // only when the coroutine is later resumed. Unlike `Call`, this doesn't require the // place to have storage *before* the yield, only after. TerminatorKind::Yield { .. } => {} @@ -296,7 +296,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::GeneratorDrop + | TerminatorKind::CoroutineDrop | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return @@ -333,7 +333,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> { | TerminatorKind::Drop { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::GeneratorDrop + | TerminatorKind::CoroutineDrop | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 0cdbee19d..eea0e030e 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -23,8 +23,7 @@ use rustc_span::symbol::{sym, Symbol}; pub use self::drop_flag_effects::{ drop_flag_effects_for_function_entry, drop_flag_effects_for_location, - move_path_children_matching, on_all_children_bits, on_all_drop_children_bits, - on_lookup_result_bits, + move_path_children_matching, on_all_children_bits, on_lookup_result_bits, }; pub use self::framework::{ fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward, @@ -35,6 +34,7 @@ pub use self::framework::{ use self::move_paths::MoveData; +pub mod debuginfo; pub mod drop_flag_effects; pub mod elaborate_drops; mod errors; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 7a5b3585d..ccf3dc794 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -1,58 +1,66 @@ use rustc_index::IndexVec; -use rustc_middle::mir::tcx::RvalueInitializationState; +use rustc_middle::mir::tcx::{PlaceTy, RvalueInitializationState}; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use smallvec::{smallvec, SmallVec}; use std::mem; use super::abs_domain::Lift; -use super::IllegalMoveOriginKind::*; -use super::{Init, InitIndex, InitKind, InitLocation, LookupResult, MoveError}; +use super::{Init, InitIndex, InitKind, InitLocation, LookupResult}; use super::{ LocationMap, MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex, MovePathLookup, }; -struct MoveDataBuilder<'a, 'tcx> { +struct MoveDataBuilder<'a, 'tcx, F> { body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, data: MoveData<'tcx>, - errors: Vec<(Place<'tcx>, MoveError<'tcx>)>, + filter: F, } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn new(body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { +impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { + fn new( + body: &'a Body<'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + filter: F, + ) -> Self { let mut move_paths = IndexVec::new(); let mut path_map = IndexVec::new(); let mut init_path_map = IndexVec::new(); + let locals = body + .local_decls + .iter_enumerated() + .map(|(i, l)| { + if l.is_deref_temp() { + return None; + } + if filter(l.ty) { + Some(new_move_path( + &mut move_paths, + &mut path_map, + &mut init_path_map, + None, + Place::from(i), + )) + } else { + None + } + }) + .collect(); + MoveDataBuilder { body, tcx, param_env, - errors: Vec::new(), data: MoveData { moves: IndexVec::new(), loc_map: LocationMap::new(body), rev_lookup: MovePathLookup { - locals: body - .local_decls - .iter_enumerated() - .map(|(i, l)| { - if l.is_deref_temp() { - MovePathIndex::MAX - } else { - Self::new_move_path( - &mut move_paths, - &mut path_map, - &mut init_path_map, - None, - Place::from(i), - ) - } - }) - .collect(), + locals, projections: Default::default(), un_derefer: Default::default(), }, @@ -62,35 +70,42 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { init_loc_map: LocationMap::new(body), init_path_map, }, + filter, } } +} - fn new_move_path( - move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>, - path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>, - init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>, - parent: Option<MovePathIndex>, - place: Place<'tcx>, - ) -> MovePathIndex { - let move_path = - move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place }); +fn new_move_path<'tcx>( + move_paths: &mut IndexVec<MovePathIndex, MovePath<'tcx>>, + path_map: &mut IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>, + init_path_map: &mut IndexVec<MovePathIndex, SmallVec<[InitIndex; 4]>>, + parent: Option<MovePathIndex>, + place: Place<'tcx>, +) -> MovePathIndex { + let move_path = + move_paths.push(MovePath { next_sibling: None, first_child: None, parent, place }); + + if let Some(parent) = parent { + let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path)); + move_paths[move_path].next_sibling = next_sibling; + } - if let Some(parent) = parent { - let next_sibling = mem::replace(&mut move_paths[parent].first_child, Some(move_path)); - move_paths[move_path].next_sibling = next_sibling; - } + let path_map_ent = path_map.push(smallvec![]); + assert_eq!(path_map_ent, move_path); - let path_map_ent = path_map.push(smallvec![]); - assert_eq!(path_map_ent, move_path); + let init_path_map_ent = init_path_map.push(smallvec![]); + assert_eq!(init_path_map_ent, move_path); - let init_path_map_ent = init_path_map.push(smallvec![]); - assert_eq!(init_path_map_ent, move_path); + move_path +} - move_path - } +enum MovePathResult { + Path(MovePathIndex), + Union(MovePathIndex), + Error, } -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { +impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { /// This creates a MovePath for a given place, returning an `MovePathError` /// if that place can't be moved from. /// @@ -98,11 +113,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { /// problematic for borrowck. /// /// Maybe we should have separate "borrowck" and "moveck" modes. - fn move_path_for(&mut self, place: Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> { + fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult { let data = &mut self.builder.data; debug!("lookup({:?})", place); - let mut base = data.rev_lookup.find_local(place.local); + let Some(mut base) = data.rev_lookup.find_local(place.local) else { + return MovePathResult::Error; + }; // The move path index of the first union that we find. Once this is // some we stop creating child move paths, since moves from unions @@ -118,12 +135,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { match elem { ProjectionElem::Deref => match place_ty.kind() { ty::Ref(..) | ty::RawPtr(..) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - BorrowedContent { - target_place: place_ref.project_deeper(&[elem], tcx), - }, - )); + return MovePathResult::Error; } ty::Adt(adt, _) => { if !adt.is_box() { @@ -143,8 +155,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | ty::FnPtr(_) | ty::Dynamic(_, _, _) | ty::Closure(_, _) - | ty::Generator(_, _, _) - | ty::GeneratorWitness(..) + | ty::Coroutine(_, _, _) + | ty::CoroutineWitness(..) | ty::Never | ty::Tuple(_) | ty::Alias(_, _) @@ -159,16 +171,13 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { ProjectionElem::Field(_, _) => match place_ty.kind() { ty::Adt(adt, _) => { if adt.has_dtor(tcx) { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfTypeWithDestructor { container_ty: place_ty }, - )); + return MovePathResult::Error; } if adt.is_union() { union_path.get_or_insert(base); } } - ty::Closure(_, _) | ty::Generator(_, _, _) | ty::Tuple(_) => (), + ty::Closure(_, _) | ty::Coroutine(_, _, _) | ty::Tuple(_) => (), ty::Bool | ty::Char | ty::Int(_) @@ -183,7 +192,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | ty::FnDef(_, _) | ty::FnPtr(_) | ty::Dynamic(_, _, _) - | ty::GeneratorWitness(..) + | ty::CoroutineWitness(..) | ty::Never | ty::Alias(_, _) | ty::Param(_) @@ -197,33 +206,15 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { match place_ty.kind() { ty::Slice(_) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { - ty: place_ty, - is_index: matches!(elem, ProjectionElem::Index(..)), - }, - )); + return MovePathResult::Error; } ty::Array(_, _) => (), _ => bug!("Unexpected type {:#?}", place_ty.is_array()), } } ProjectionElem::Index(_) => match place_ty.kind() { - ty::Array(..) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { ty: place_ty, is_index: true }, - )); - } - ty::Slice(_) => { - return Err(MoveError::cannot_move_out_of( - self.loc, - InteriorOfSliceOrArray { - ty: place_ty, - is_index: matches!(elem, ProjectionElem::Index(..)), - }, - )); + ty::Array(..) | ty::Slice(_) => { + return MovePathResult::Error; } _ => bug!("Unexpected type {place_ty:#?}"), }, @@ -235,11 +226,15 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | ProjectionElem::Subtype(_) | ProjectionElem::Downcast(_, _) => (), } + let elem_ty = PlaceTy::from_ty(place_ty).projection_ty(tcx, elem).ty; + if !(self.builder.filter)(elem_ty) { + return MovePathResult::Error; + } if union_path.is_none() { // inlined from add_move_path because of a borrowck conflict with the iterator base = *data.rev_lookup.projections.entry((base, elem.lift())).or_insert_with(|| { - MoveDataBuilder::new_move_path( + new_move_path( &mut data.move_paths, &mut data.path_map, &mut data.init_path_map, @@ -252,9 +247,9 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { if let Some(base) = union_path { // Move out of union - always move the entire union. - Err(MoveError::UnionMove { path: base }) + MovePathResult::Union(base) } else { - Ok(base) + MovePathResult::Path(base) } } @@ -270,13 +265,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { .. } = self.builder; *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || { - MoveDataBuilder::new_move_path( - move_paths, - path_map, - init_path_map, - Some(base), - mk_place(*tcx), - ) + new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx)) }) } @@ -287,11 +276,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } } -pub type MoveDat<'tcx> = - Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)>; - -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { - fn finalize(self) -> MoveDat<'tcx> { +impl<'a, 'tcx, F> MoveDataBuilder<'a, 'tcx, F> { + fn finalize(self) -> MoveData<'tcx> { debug!("{}", { debug!("moves for {:?}:", self.body.span); for (j, mo) in self.data.moves.iter_enumerated() { @@ -304,7 +290,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { "done dumping moves" }); - if self.errors.is_empty() { Ok(self.data) } else { Err((self.data, self.errors)) } + self.data } } @@ -312,8 +298,9 @@ pub(super) fn gather_moves<'tcx>( body: &Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> MoveDat<'tcx> { - let mut builder = MoveDataBuilder::new(body, tcx, param_env); + filter: impl Fn(Ty<'tcx>) -> bool, +) -> MoveData<'tcx> { + let mut builder = MoveDataBuilder::new(body, tcx, param_env, filter); builder.gather_args(); @@ -330,20 +317,20 @@ pub(super) fn gather_moves<'tcx>( builder.finalize() } -impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { +impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { fn gather_args(&mut self) { for arg in self.body.args_iter() { - let path = self.data.rev_lookup.find_local(arg); + if let Some(path) = self.data.rev_lookup.find_local(arg) { + let init = self.data.inits.push(Init { + path, + kind: InitKind::Deep, + location: InitLocation::Argument(arg), + }); - let init = self.data.inits.push(Init { - path, - kind: InitKind::Deep, - location: InitLocation::Argument(arg), - }); + debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg); - debug!("gather_args: adding init {:?} of {:?} for argument {:?}", init, path, arg); - - self.data.init_path_map[path].push(init); + self.data.init_path_map[path].push(init); + } } } @@ -358,12 +345,12 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> { } } -struct Gatherer<'b, 'a, 'tcx> { - builder: &'b mut MoveDataBuilder<'a, 'tcx>, +struct Gatherer<'b, 'a, 'tcx, F> { + builder: &'b mut MoveDataBuilder<'a, 'tcx, F>, loc: Location, } -impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { +impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { fn gather_statement(&mut self, stmt: &Statement<'tcx>) { match &stmt.kind { StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => { @@ -454,7 +441,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { | TerminatorKind::Return | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) - | TerminatorKind::GeneratorDrop + | TerminatorKind::CoroutineDrop | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } => {} @@ -546,13 +533,12 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { let base_place = Place { local: place.local, projection: self.builder.tcx.mk_place_elems(base) }; let base_path = match self.move_path_for(base_place) { - Ok(path) => path, - Err(MoveError::UnionMove { path }) => { + MovePathResult::Path(path) => path, + MovePathResult::Union(path) => { self.record_move(place, path); return; } - Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((base_place, error)); + MovePathResult::Error => { return; } }; @@ -572,10 +558,10 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { } } else { match self.move_path_for(place) { - Ok(path) | Err(MoveError::UnionMove { path }) => self.record_move(place, path), - Err(error @ MoveError::IllegalMove { .. }) => { - self.builder.errors.push((place, error)); + MovePathResult::Path(path) | MovePathResult::Union(path) => { + self.record_move(place, path) } + MovePathResult::Error => {} }; } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 0c7aa6676..7ab1a9ed0 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -1,4 +1,3 @@ -use crate::move_paths::builder::MoveDat; use crate::un_derefer::UnDerefer; use rustc_data_structures::fx::FxHashMap; use rustc_index::{IndexSlice, IndexVec}; @@ -291,7 +290,7 @@ impl Init { /// Tables mapping from a place to its MovePathIndex. #[derive(Debug)] pub struct MovePathLookup<'tcx> { - locals: IndexVec<Local, MovePathIndex>, + locals: IndexVec<Local, Option<MovePathIndex>>, /// projections are made from a base-place and a projection /// elem. The base-place will have a unique MovePathIndex; we use @@ -318,7 +317,9 @@ impl<'tcx> MovePathLookup<'tcx> { // unknown place, but will rather return the nearest available // parent. pub fn find(&self, place: PlaceRef<'tcx>) -> LookupResult { - let mut result = self.find_local(place.local); + let Some(mut result) = self.find_local(place.local) else { + return LookupResult::Parent(None); + }; for (_, elem) in self.un_derefer.iter_projections(place) { if let Some(&subpath) = self.projections.get(&(result, elem.lift())) { @@ -332,7 +333,7 @@ impl<'tcx> MovePathLookup<'tcx> { } #[inline] - pub fn find_local(&self, local: Local) -> MovePathIndex { + pub fn find_local(&self, local: Local) -> Option<MovePathIndex> { self.locals[local] } @@ -340,46 +341,8 @@ impl<'tcx> MovePathLookup<'tcx> { /// `MovePathIndex`es. pub fn iter_locals_enumerated( &self, - ) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + ExactSizeIterator + '_ { - self.locals.iter_enumerated().map(|(l, &idx)| (l, idx)) - } -} - -#[derive(Debug)] -pub struct IllegalMoveOrigin<'tcx> { - pub location: Location, - pub kind: IllegalMoveOriginKind<'tcx>, -} - -#[derive(Debug)] -pub enum IllegalMoveOriginKind<'tcx> { - /// Illegal move due to attempt to move from behind a reference. - BorrowedContent { - /// The place the reference refers to: if erroneous code was trying to - /// move from `(*x).f` this will be `*x`. - target_place: Place<'tcx>, - }, - - /// Illegal move due to attempt to move from field of an ADT that - /// implements `Drop`. Rust maintains invariant that all `Drop` - /// ADT's remain fully-initialized so that user-defined destructor - /// can safely read from all of the ADT's fields. - InteriorOfTypeWithDestructor { container_ty: Ty<'tcx> }, - - /// Illegal move due to attempt to move out of a slice or array. - InteriorOfSliceOrArray { ty: Ty<'tcx>, is_index: bool }, -} - -#[derive(Debug)] -pub enum MoveError<'tcx> { - IllegalMove { cannot_move_out_of: IllegalMoveOrigin<'tcx> }, - UnionMove { path: MovePathIndex }, -} - -impl<'tcx> MoveError<'tcx> { - fn cannot_move_out_of(location: Location, kind: IllegalMoveOriginKind<'tcx>) -> Self { - let origin = IllegalMoveOrigin { location, kind }; - MoveError::IllegalMove { cannot_move_out_of: origin } + ) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + '_ { + self.locals.iter_enumerated().filter_map(|(l, &idx)| Some((l, idx?))) } } @@ -388,8 +351,9 @@ impl<'tcx> MoveData<'tcx> { body: &Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, - ) -> MoveDat<'tcx> { - builder::gather_moves(body, tcx, param_env) + filter: impl Fn(Ty<'tcx>) -> bool, + ) -> MoveData<'tcx> { + builder::gather_moves(body, tcx, param_env, filter) } /// For the move path `mpi`, returns the root local variable (if any) that starts the path. diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 1ebb59b3a..d3dce641b 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -34,7 +34,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { } let param_env = tcx.param_env(def_id); - let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap(); + let move_data = MoveData::gather_moves(&body, tcx, param_env, |_| true); let mdpe = MoveDataParamEnv { move_data, param_env }; if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 83766f311..025d2ddfd 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -274,7 +274,7 @@ pub trait ValueAnalysis<'tcx> { | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::Assert { .. } - | TerminatorKind::GeneratorDrop + | TerminatorKind::CoroutineDrop | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } => { // These terminators have no effect on the analysis. @@ -463,7 +463,19 @@ impl<V: Clone> Clone for State<V> { } } -impl<V: Clone + HasTop + HasBottom> State<V> { +impl<V: Clone> State<V> { + pub fn new(init: V, map: &Map) -> State<V> { + let values = IndexVec::from_elem_n(init, map.value_count); + State(StateData::Reachable(values)) + } + + pub fn all(&self, f: impl Fn(&V) -> bool) -> bool { + match self.0 { + StateData::Unreachable => true, + StateData::Reachable(ref values) => values.iter().all(f), + } + } + pub fn is_reachable(&self) -> bool { matches!(&self.0, StateData::Reachable(_)) } @@ -472,7 +484,10 @@ impl<V: Clone + HasTop + HasBottom> State<V> { self.0 = StateData::Unreachable; } - pub fn flood_all(&mut self) { + pub fn flood_all(&mut self) + where + V: HasTop, + { self.flood_all_with(V::TOP) } @@ -481,28 +496,52 @@ impl<V: Clone + HasTop + HasBottom> State<V> { values.raw.fill(value); } + /// Assign `value` to all places that are contained in `place` or may alias one. pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) { - let StateData::Reachable(values) = &mut self.0 else { return }; - map.for_each_aliasing_place(place, None, &mut |vi| { - values[vi] = value.clone(); - }); + self.flood_with_tail_elem(place, None, map, value) } - pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) { + /// Assign `TOP` to all places that are contained in `place` or may alias one. + pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map) + where + V: HasTop, + { self.flood_with(place, map, V::TOP) } + /// Assign `value` to the discriminant of `place` and all places that may alias it. pub fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) { - let StateData::Reachable(values) = &mut self.0 else { return }; - map.for_each_aliasing_place(place, Some(TrackElem::Discriminant), &mut |vi| { - values[vi] = value.clone(); - }); + self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value) } - pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) { + /// Assign `TOP` to the discriminant of `place` and all places that may alias it. + pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map) + where + V: HasTop, + { self.flood_discr_with(place, map, V::TOP) } + /// This method is the most general version of the `flood_*` method. + /// + /// Assign `value` on the given place and all places that may alias it. In particular, when + /// the given place has a variant downcast, we invoke the function on all the other variants. + /// + /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track + /// as such. + pub fn flood_with_tail_elem( + &mut self, + place: PlaceRef<'_>, + tail_elem: Option<TrackElem>, + map: &Map, + value: V, + ) { + let StateData::Reachable(values) = &mut self.0 else { return }; + map.for_each_aliasing_place(place, tail_elem, &mut |vi| { + values[vi] = value.clone(); + }); + } + /// Low-level method that assigns to a place. /// This does nothing if the place is not tracked. /// @@ -553,7 +592,10 @@ impl<V: Clone + HasTop + HasBottom> State<V> { } /// Helper method to interpret `target = result`. - pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) { + pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) + where + V: HasTop, + { self.flood(target, map); if let Some(target) = map.find(target) { self.insert_idx(target, result, map); @@ -561,36 +603,93 @@ impl<V: Clone + HasTop + HasBottom> State<V> { } /// Helper method for assignments to a discriminant. - pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) { + pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map) + where + V: HasTop, + { self.flood_discr(target, map); if let Some(target) = map.find_discr(target) { self.insert_idx(target, result, map); } } + /// Retrieve the value stored for a place, or `None` if it is not tracked. + pub fn try_get(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> { + let place = map.find(place)?; + self.try_get_idx(place, map) + } + + /// Retrieve the discriminant stored for a place, or `None` if it is not tracked. + pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> { + let place = map.find_discr(place)?; + self.try_get_idx(place, map) + } + + /// Retrieve the slice length stored for a place, or `None` if it is not tracked. + pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> { + let place = map.find_len(place)?; + self.try_get_idx(place, map) + } + + /// Retrieve the value stored for a place index, or `None` if it is not tracked. + pub fn try_get_idx(&self, place: PlaceIndex, map: &Map) -> Option<V> { + match &self.0 { + StateData::Reachable(values) => { + map.places[place].value_index.map(|v| values[v].clone()) + } + StateData::Unreachable => None, + } + } + /// Retrieve the value stored for a place, or ⊤ if it is not tracked. - pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V { - map.find(place).map(|place| self.get_idx(place, map)).unwrap_or(V::TOP) + /// + /// This method returns ⊥ if the place is tracked and the state is unreachable. + pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V + where + V: HasBottom + HasTop, + { + match &self.0 { + StateData::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP), + // Because this is unreachable, we can return any value we want. + StateData::Unreachable => V::BOTTOM, + } } /// Retrieve the value stored for a place, or ⊤ if it is not tracked. - pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V { - match map.find_discr(place) { - Some(place) => self.get_idx(place, map), - None => V::TOP, + /// + /// This method returns ⊥ the current state is unreachable. + pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V + where + V: HasBottom + HasTop, + { + match &self.0 { + StateData::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP), + // Because this is unreachable, we can return any value we want. + StateData::Unreachable => V::BOTTOM, } } /// Retrieve the value stored for a place, or ⊤ if it is not tracked. - pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V { - match map.find_len(place) { - Some(place) => self.get_idx(place, map), - None => V::TOP, + /// + /// This method returns ⊥ the current state is unreachable. + pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V + where + V: HasBottom + HasTop, + { + match &self.0 { + StateData::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP), + // Because this is unreachable, we can return any value we want. + StateData::Unreachable => V::BOTTOM, } } /// Retrieve the value stored for a place index, or ⊤ if it is not tracked. - pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V { + /// + /// This method returns ⊥ the current state is unreachable. + pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V + where + V: HasBottom + HasTop, + { match &self.0 { StateData::Reachable(values) => { map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::TOP) @@ -685,8 +784,10 @@ impl Map { // `elem1` is either `Some(Variant(i))` or `None`. while let Some((mut place, elem1, elem2, ty)) = worklist.pop_front() { // The user requires a bound on the number of created values. - if let Some(value_limit) = value_limit && self.value_count >= value_limit { - break + if let Some(value_limit) = value_limit + && self.value_count >= value_limit + { + break; } // Create a place for this projection. @@ -717,7 +818,9 @@ impl Map { // Trim useless places. for opt_place in self.locals.iter_mut() { - if let Some(place) = *opt_place && self.inner_values[place].is_empty() { + if let Some(place) = *opt_place + && self.inner_values[place].is_empty() + { *opt_place = None; } } @@ -772,7 +875,7 @@ impl Map { assert!(old.is_none()); // Allocate a value slot since it doesn't have one. - assert!( self.places[len].value_index.is_none() ); + assert!(self.places[len].value_index.is_none()); self.places[len].value_index = Some(self.value_count.into()); self.value_count += 1; } @@ -911,7 +1014,7 @@ impl Map { ) { for sibling in self.children(parent) { let elem = self.places[sibling].proj_elem; - // Only invalidate variants and discriminant. Fields (for generators) are not + // Only invalidate variants and discriminant. Fields (for coroutines) are not // invalidated by assignment to a variant. if let Some(TrackElem::Variant(..) | TrackElem::Discriminant) = elem // Only invalidate the other variants, the current one is fine. |