|
| 1 | +use crate::MirPass; |
| 2 | +use rustc_middle::mir::patch::MirPatch; |
| 3 | +use rustc_middle::mir::*; |
| 4 | +use rustc_middle::ty::TyCtxt; |
| 5 | +pub struct Derefer; |
| 6 | + |
| 7 | +pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
| 8 | + let mut patch = MirPatch::new(body); |
| 9 | + let (basic_blocks, local_decl) = body.basic_blocks_and_local_decls_mut(); |
| 10 | + for (block, data) in basic_blocks.iter_enumerated_mut() { |
| 11 | + for (i, stmt) in data.statements.iter_mut().enumerate() { |
| 12 | + match stmt.kind { |
| 13 | + StatementKind::Assign(box (og_place, Rvalue::Ref(region, borrow_knd, place))) => { |
| 14 | + for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() { |
| 15 | + if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() { |
| 16 | + // The type that we are derefing. |
| 17 | + let ty = p_ref.ty(local_decl, tcx).ty; |
| 18 | + let temp = patch.new_temp(ty, stmt.source_info.span); |
| 19 | + |
| 20 | + // Because we are assigning this right before original statement |
| 21 | + // we are using index i of statement. |
| 22 | + let loc = Location { block: block, statement_index: i }; |
| 23 | + patch.add_statement(loc, StatementKind::StorageLive(temp)); |
| 24 | + |
| 25 | + // We are adding current p_ref's projections to our |
| 26 | + // temp value. |
| 27 | + let deref_place = |
| 28 | + Place::from(p_ref.local).project_deeper(p_ref.projection, tcx); |
| 29 | + patch.add_assign( |
| 30 | + loc, |
| 31 | + Place::from(temp), |
| 32 | + Rvalue::Use(Operand::Move(deref_place)), |
| 33 | + ); |
| 34 | + |
| 35 | + // We are creating a place by using our temp value's location |
| 36 | + // and copying derefed values which we need to create new statement. |
| 37 | + let temp_place = |
| 38 | + Place::from(temp).project_deeper(&place.projection[idx..], tcx); |
| 39 | + let new_stmt = Statement { |
| 40 | + source_info: stmt.source_info, |
| 41 | + kind: StatementKind::Assign(Box::new(( |
| 42 | + og_place, |
| 43 | + Rvalue::Ref(region, borrow_knd, temp_place), |
| 44 | + ))), |
| 45 | + }; |
| 46 | + |
| 47 | + // Replace current statement with newly created one. |
| 48 | + *stmt = new_stmt; |
| 49 | + |
| 50 | + // Since our job with the temp is done it should be gone |
| 51 | + let loc = Location { block: block, statement_index: i + 1 }; |
| 52 | + patch.add_statement(loc, StatementKind::StorageDead(temp)); |
| 53 | + |
| 54 | + // As all projections are off the base projection, if there are |
| 55 | + // multiple derefs in the middle of projection, it might cause |
| 56 | + // unsoundness, to not let that happen we break the loop. |
| 57 | + break; |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + _ => (), |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + patch.apply(body); |
| 66 | +} |
| 67 | + |
| 68 | +impl<'tcx> MirPass<'tcx> for Derefer { |
| 69 | + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { |
| 70 | + deref_finder(tcx, body); |
| 71 | + } |
| 72 | +} |
0 commit comments