|
1 | 1 | use rustc_hir::{Expr, HirId};
|
2 |
| -use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; |
| 2 | +use rustc_middle::mir::visit::{ |
| 3 | + MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, TyContext, Visitor, |
| 4 | +}; |
3 | 5 | use rustc_middle::mir::{
|
4 |
| - traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, |
| 6 | + traversal, Body, InlineAsmOperand, Local, LocalDecl, Location, Place, Statement, StatementKind, Terminator, |
| 7 | + TerminatorKind, START_BLOCK, |
5 | 8 | };
|
6 |
| -use rustc_middle::ty::TyCtxt; |
| 9 | +use rustc_middle::ty::{Region, Ty, TyCtxt}; |
7 | 10 |
|
8 | 11 | mod possible_borrower;
|
9 | 12 | pub use possible_borrower::PossibleBorrowerMap;
|
10 | 13 |
|
11 |
| -mod possible_origin; |
12 |
| - |
13 |
| -mod transitive_relation; |
14 |
| - |
15 | 14 | #[derive(Clone, Debug, Default)]
|
16 | 15 | pub struct LocalUsage {
|
17 | 16 | /// The locations where the local is used, if any.
|
@@ -119,6 +118,193 @@ pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
|
119 | 118 | })
|
120 | 119 | }
|
121 | 120 |
|
| 121 | +/// Tries to find the local in `to_mir` corresponding to `local` in `from_mir`. |
| 122 | +pub fn translate_local<'tcx>( |
| 123 | + tcx: TyCtxt<'tcx>, |
| 124 | + from_mir: &Body<'tcx>, |
| 125 | + to_mir: &Body<'tcx>, |
| 126 | + local: Local, |
| 127 | +) -> Option<Local> { |
| 128 | + let equiv_decl = |lhs: &LocalDecl<'tcx>, rhs: &LocalDecl<'tcx>| { |
| 129 | + lhs.mutability == rhs.mutability |
| 130 | + && tcx.erase_regions_ty(lhs.ty) == tcx.erase_regions_ty(rhs.ty) |
| 131 | + && lhs.source_info.span == rhs.source_info.span |
| 132 | + }; |
| 133 | + |
| 134 | + let from_decl = &from_mir.local_decls[local]; |
| 135 | + |
| 136 | + // Fast path |
| 137 | + if to_mir |
| 138 | + .local_decls |
| 139 | + .get(local) |
| 140 | + .map_or(false, |to_decl| equiv_decl(from_decl, to_decl)) |
| 141 | + { |
| 142 | + return Some(local); |
| 143 | + } |
| 144 | + |
| 145 | + // Slow path |
| 146 | + to_mir |
| 147 | + .local_decls |
| 148 | + .iter() |
| 149 | + .position(|to_decl| equiv_decl(from_decl, to_decl)) |
| 150 | + .map(Into::into) |
| 151 | +} |
| 152 | + |
| 153 | +/// Tries to find the location in `to_mir` corresponding to `location` in `from_mir`. |
| 154 | +pub fn translate_location<'tcx>( |
| 155 | + tcx: TyCtxt<'tcx>, |
| 156 | + from_mir: &Body<'tcx>, |
| 157 | + to_mir: &Body<'tcx>, |
| 158 | + location: Location, |
| 159 | +) -> Option<Location> { |
| 160 | + let normalized_lhs = from_mir |
| 161 | + .stmt_at(location) |
| 162 | + .map_left(|statement| normalize_statement(tcx, statement)) |
| 163 | + .map_right(|terminator| normalize_terminator(tcx, terminator)); |
| 164 | + |
| 165 | + for (block, block_data) in to_mir.basic_blocks.iter_enumerated() { |
| 166 | + if let Some(location) = normalized_lhs.as_ref().either( |
| 167 | + |normalized_lhs| { |
| 168 | + (0..block_data.statements.len()).find_map(|statement_index| { |
| 169 | + let rhs = &block_data.statements[statement_index]; |
| 170 | + if normalized_lhs.source_info.span == rhs.source_info.span |
| 171 | + && normalized_lhs.kind == normalize_statement(tcx, rhs).kind |
| 172 | + { |
| 173 | + Some(Location { block, statement_index }) |
| 174 | + } else { |
| 175 | + None |
| 176 | + } |
| 177 | + }) |
| 178 | + }, |
| 179 | + |normalized_lhs| { |
| 180 | + if block_data.terminator.as_ref().map_or(false, |rhs| { |
| 181 | + normalized_lhs.source_info.span == rhs.source_info.span |
| 182 | + && normalized_lhs.kind == normalize_terminator(tcx, rhs).kind |
| 183 | + }) { |
| 184 | + Some(Location { |
| 185 | + block, |
| 186 | + statement_index: block_data.statements.len(), |
| 187 | + }) |
| 188 | + } else { |
| 189 | + None |
| 190 | + } |
| 191 | + }, |
| 192 | + ) { |
| 193 | + return Some(location); |
| 194 | + } |
| 195 | + } |
| 196 | + |
| 197 | + None |
| 198 | +} |
| 199 | + |
| 200 | +fn normalize_statement<'tcx>(tcx: TyCtxt<'tcx>, statement: &Statement<'tcx>) -> Statement<'tcx> { |
| 201 | + let mut statement = statement.clone(); |
| 202 | + Normalizer { tcx }.visit_statement(&mut statement, Location::START); |
| 203 | + statement |
| 204 | +} |
| 205 | + |
| 206 | +fn normalize_terminator<'tcx>(tcx: TyCtxt<'tcx>, terminator: &Terminator<'tcx>) -> Terminator<'tcx> { |
| 207 | + let mut terminator = terminator.clone(); |
| 208 | + Normalizer { tcx }.visit_terminator(&mut terminator, Location::START); |
| 209 | + match terminator.kind { |
| 210 | + // No basic blocks |
| 211 | + TerminatorKind::Abort |
| 212 | + | TerminatorKind::GeneratorDrop |
| 213 | + | TerminatorKind::Resume |
| 214 | + | TerminatorKind::Return |
| 215 | + | TerminatorKind::Unreachable => {}, |
| 216 | + |
| 217 | + // One basic block |
| 218 | + TerminatorKind::Goto { ref mut target } => { |
| 219 | + *target = Location::START.block; |
| 220 | + }, |
| 221 | + |
| 222 | + // Two basic blocks |
| 223 | + TerminatorKind::FalseEdge { |
| 224 | + ref mut real_target, |
| 225 | + ref mut imaginary_target, |
| 226 | + } => { |
| 227 | + *real_target = Location::START.block; |
| 228 | + *imaginary_target = Location::START.block; |
| 229 | + }, |
| 230 | + |
| 231 | + // One optional and one non-optional basic block |
| 232 | + TerminatorKind::Assert { |
| 233 | + ref mut target, |
| 234 | + cleanup: ref mut unwind, |
| 235 | + .. |
| 236 | + } |
| 237 | + | TerminatorKind::Drop { |
| 238 | + ref mut target, |
| 239 | + ref mut unwind, |
| 240 | + .. |
| 241 | + } |
| 242 | + | TerminatorKind::DropAndReplace { |
| 243 | + ref mut target, |
| 244 | + ref mut unwind, |
| 245 | + .. |
| 246 | + } |
| 247 | + | TerminatorKind::FalseUnwind { |
| 248 | + real_target: ref mut target, |
| 249 | + ref mut unwind, |
| 250 | + .. |
| 251 | + } |
| 252 | + | TerminatorKind::Yield { |
| 253 | + resume: ref mut target, |
| 254 | + drop: ref mut unwind, |
| 255 | + .. |
| 256 | + } => { |
| 257 | + *target = Location::START.block; |
| 258 | + *unwind = None; |
| 259 | + }, |
| 260 | + |
| 261 | + // Two optional basic blocks |
| 262 | + TerminatorKind::Call { |
| 263 | + ref mut target, |
| 264 | + ref mut cleanup, |
| 265 | + .. |
| 266 | + } |
| 267 | + | TerminatorKind::InlineAsm { |
| 268 | + destination: ref mut target, |
| 269 | + ref mut cleanup, |
| 270 | + .. |
| 271 | + } => { |
| 272 | + *target = None; |
| 273 | + *cleanup = None; |
| 274 | + }, |
| 275 | + |
| 276 | + // Arbitrarily many basic blocks |
| 277 | + TerminatorKind::SwitchInt { ref mut targets, .. } => { |
| 278 | + for (_, ref mut target) in targets.iter() { |
| 279 | + *target = Location::START.block; |
| 280 | + } |
| 281 | + }, |
| 282 | + } |
| 283 | + terminator |
| 284 | +} |
| 285 | + |
| 286 | +struct Normalizer<'tcx> { |
| 287 | + tcx: TyCtxt<'tcx>, |
| 288 | +} |
| 289 | + |
| 290 | +impl<'tcx> MutVisitor<'tcx> for Normalizer<'tcx> { |
| 291 | + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { |
| 292 | + self.tcx |
| 293 | + } |
| 294 | + |
| 295 | + fn visit_local(&mut self, local: &mut Local, _context: PlaceContext, _location: Location) { |
| 296 | + *local = Local::from_u32(0); |
| 297 | + } |
| 298 | + |
| 299 | + fn visit_region(&mut self, region: &mut Region<'tcx>, _: Location) { |
| 300 | + *region = self.tcx.lifetimes.re_erased; |
| 301 | + } |
| 302 | + |
| 303 | + fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) { |
| 304 | + *ty = self.tcx.erase_regions_ty(*ty); |
| 305 | + } |
| 306 | +} |
| 307 | + |
122 | 308 | /// Returns a vector of `mir::Location` where `local` is assigned.
|
123 | 309 | pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec<Location> {
|
124 | 310 | let mut locations = Vec::new();
|
|
0 commit comments