- fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
- match stmt.kind {
- mir::StatementKind::StorageLive(l) => trans.gen(l),
- mir::StatementKind::StorageDead(l) => trans.kill(l),
- _ => (),
- }
- }
-
- fn terminator_effect(
- &self,
- _trans: &mut impl GenKill<Self::Idx>,
- _terminator: &mir::Terminator<'tcx>,
- _loc: mir::Location,
- ) {
- }
-
- fn call_return_effect(
- &self,
- _trans: &mut impl GenKill<Self::Idx>,
- _block: mir::BasicBlock,
- _return_places: CallReturnPlaces<'_, 'tcx>,
- ) {
- // Nothing to do when a call returns successfully
- }
-}
-
-/// Collects the possible borrowers of each local.
-/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
-/// possible borrowers of `a`.
-struct PossibleBorrowerVisitor<'a, 'tcx> {
- possible_borrower: TransitiveRelation,
- body: &'a mir::Body<'tcx>,
- cx: &'a LateContext<'tcx>,
- possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
-}
-
-impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
- fn new(
- cx: &'a LateContext<'tcx>,
- body: &'a mir::Body<'tcx>,
- possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
- ) -> Self {
- Self {
- possible_borrower: TransitiveRelation::default(),
- cx,
- body,
- possible_origin,
- }
- }
-
- fn into_map(
- self,
- cx: &LateContext<'tcx>,
- maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
- ) -> PossibleBorrowerMap<'a, 'tcx> {
- let mut map = FxHashMap::default();
- for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
- if is_copy(cx, self.body.local_decls[row].ty) {
- continue;
- }
-
- let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
- borrowers.remove(mir::Local::from_usize(0));
- if !borrowers.is_empty() {
- map.insert(row, borrowers);
- }
- }
-
- let bs = BitSet::new_empty(self.body.local_decls.len());
- PossibleBorrowerMap {
- map,
- maybe_live,
- bitset: (bs.clone(), bs),
- }
- }
-}
-
-impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
- fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
- let lhs = place.local;
- match rvalue {
- mir::Rvalue::Ref(_, _, borrowed) => {
- self.possible_borrower.add(borrowed.local, lhs);
- },
- other => {
- if ContainsRegion
- .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
- .is_continue()
- {
- return;
- }
- rvalue_locals(other, |rhs| {
- if lhs != rhs {
- self.possible_borrower.add(rhs, lhs);
- }
- });
- },
- }
- }
-
- fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
- if let mir::TerminatorKind::Call {
- args,
- destination: Some((mir::Place { local: dest, .. }, _)),
- ..
- } = &terminator.kind
- {
- // TODO add doc
- // If the call returns something with lifetimes,
- // let's conservatively assume the returned value contains lifetime of all the arguments.
- // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
-
- let mut immutable_borrowers = vec![];
- let mut mutable_borrowers = vec![];
-
- for op in args {
- match op {
- mir::Operand::Copy(p) | mir::Operand::Move(p) => {
- if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
- mutable_borrowers.push(p.local);
- } else {
- immutable_borrowers.push(p.local);
- }
- },
- mir::Operand::Constant(..) => (),
- }
- }
-
- let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
- .iter()
- .filter_map(|r| self.possible_origin.get(r))
- .flat_map(HybridBitSet::iter)
- .collect();
-
- if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
- mutable_variables.push(*dest);
- }
-
- for y in mutable_variables {
- for x in &immutable_borrowers {
- self.possible_borrower.add(*x, y);
- }
- for x in &mutable_borrowers {
- self.possible_borrower.add(*x, y);
- }
- }
- }
- }
-}
-
-/// Collect possible borrowed for every `&mut` local.
-/// For example, `_1 = &mut _2` generate _1: {_2,...}
-/// Known Problems: not sure all borrowed are tracked
-struct PossibleOriginVisitor<'a, 'tcx> {
- possible_origin: TransitiveRelation,
- body: &'a mir::Body<'tcx>,
-}
-
-impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
- fn new(body: &'a mir::Body<'tcx>) -> Self {
- Self {
- possible_origin: TransitiveRelation::default(),
- body,
- }
- }
-
- fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
- let mut map = FxHashMap::default();
- for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
- if is_copy(cx, self.body.local_decls[row].ty) {
- continue;
- }
-
- let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len());
- borrowers.remove(mir::Local::from_usize(0));
- if !borrowers.is_empty() {
- map.insert(row, borrowers);
- }
- }
- map
- }
-}
-
-impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
- fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
- let lhs = place.local;
- match rvalue {
- // Only consider `&mut`, which can modify origin place
- mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
- // _2: &mut _;
- // _3 = move _2
- mir::Rvalue::Use(mir::Operand::Move(borrowed)) |
- // _3 = move _2 as &mut _;
- mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
- => {
- self.possible_origin.add(lhs, borrowed.local);
- },
- _ => {},
- }
- }
-}
-
-struct ContainsRegion;
-
-impl TypeVisitor<'_> for ContainsRegion {
- type BreakTy = ();
-
- fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::BREAK
- }
-}
-
-fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
- use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use};
-
- let mut visit_op = |op: &mir::Operand<'_>| match op {
- mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
- mir::Operand::Constant(..) => (),
- };
-
- match rvalue {
- Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
- Aggregate(_, ops) => ops.iter().for_each(visit_op),
- BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
- visit_op(lhs);
- visit_op(rhs);