1use std::iter;
7
8use either::Either;
9use hir_def::HasModule;
10use la_arena::ArenaMap;
11use rustc_hash::FxHashMap;
12use salsa::Update;
13use stdx::never;
14
15use crate::{
16 InferBodyId,
17 closure_analysis::ProjectionKind as HirProjectionKind,
18 db::{HirDatabase, InternedClosureId},
19 display::DisplayTarget,
20 mir::{OperandKind, PlaceTy},
21 next_solver::{
22 DbInterner, ParamEnv, StoredTy, TypingMode,
23 infer::{DbInternerInferExt, InferCtxt},
24 },
25};
26
27use super::{
28 BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, MutBorrowKind, Operand,
29 Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind,
30};
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum MutabilityReason {
35 Mut { spans: Vec<MirSpan> },
36 Not,
37 Unused,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct MovedOutOfRef {
42 pub ty: StoredTy,
43 pub span: MirSpan,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct PartiallyMoved {
48 pub ty: StoredTy,
49 pub span: MirSpan,
50 pub local: LocalId,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct BorrowRegion {
55 pub local: LocalId,
56 pub kind: BorrowKind,
57 pub places: Vec<MirSpan>,
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Update)]
61pub struct BorrowckResult<'db> {
62 owner: Either<InferBodyId<'db>, InternedClosureId<'db>>,
63 pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
64 pub moved_out_of_ref: Vec<MovedOutOfRef>,
65 pub partially_moved: Vec<PartiallyMoved>,
66 pub borrow_regions: Vec<BorrowRegion>,
67}
68
69impl<'db> BorrowckResult<'db> {
70 pub fn mir_body(&self, db: &'db dyn HirDatabase) -> &'db MirBody<'db> {
71 match self.owner {
72 Either::Left(it) => db.mir_body(it).unwrap(),
73 Either::Right(it) => db.mir_body_for_closure(it).unwrap(),
74 }
75 }
76}
77
78fn all_mir_bodies<'db>(
79 db: &'db dyn HirDatabase,
80 def: InferBodyId<'db>,
81 mut cb: impl FnMut(
82 &'db MirBody<'db>,
83 Either<InferBodyId<'db>, InternedClosureId<'db>>,
84 ) -> BorrowckResult<'db>,
85 mut merge_from_closures: impl FnMut(
86 (&mut BorrowckResult<'db>, &'db MirBody<'db>),
87 (&BorrowckResult<'db>, &'db MirBody<'db>),
88 ),
89) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> {
90 fn for_closure<'db>(
91 db: &'db dyn HirDatabase,
92 c: InternedClosureId<'db>,
93 results: &mut Vec<(BorrowckResult<'db>, &'db MirBody<'db>)>,
94 cb: &mut impl FnMut(
95 &'db MirBody<'db>,
96 Either<InferBodyId<'db>, InternedClosureId<'db>>,
97 ) -> BorrowckResult<'db>,
98 merge_from_closures: &mut impl FnMut(
99 (&mut BorrowckResult<'db>, &'db MirBody<'db>),
100 (&BorrowckResult<'db>, &'db MirBody<'db>),
101 ),
102 ) -> Result<(), MirLowerError<'db>> {
103 match db.mir_body_for_closure(c) {
104 Ok(body) => {
105 let parent_index = results.len();
106 results.push((cb(body, Either::Right(c)), body));
107 body.closures
108 .iter()
109 .try_for_each(|&it| for_closure(db, it, results, cb, merge_from_closures))?;
110 merge(results, merge_from_closures, parent_index);
111 Ok(())
112 }
113 Err(e) => Err(e.clone()),
114 }
115 }
116
117 fn merge<'db>(
118 results: &mut [(BorrowckResult<'db>, &'db MirBody<'db>)],
119 merge: &mut impl FnMut(
120 (&mut BorrowckResult<'db>, &'db MirBody<'db>),
121 (&BorrowckResult<'db>, &'db MirBody<'db>),
122 ),
123 parent_index: usize,
124 ) {
125 let (parent_and_before, children) = results.split_at_mut(parent_index + 1);
126 let (parent, parent_mir_body) = &mut parent_and_before[parent_and_before.len() - 1];
127 children.iter().for_each(|(child, child_mir_body)| {
128 merge((parent, parent_mir_body), (child, child_mir_body))
129 });
130 }
131
132 let mut results = Vec::new();
133 match db.mir_body(def) {
134 Ok(body) => {
135 results.push((cb(body, Either::Left(def)), body));
136 body.closures.iter().try_for_each(|&it| {
137 for_closure(db, it, &mut results, &mut cb, &mut merge_from_closures)
138 })?;
139 merge(&mut results, &mut merge_from_closures, 0);
140 Ok(results.into_iter().map(|(it, _)| it).collect())
141 }
142 Err(e) => Err(e.clone()),
143 }
144}
145
146impl<'db> InferBodyId<'db> {
147 pub fn borrowck(
148 self,
149 db: &'db dyn HirDatabase,
150 ) -> Result<&'db [BorrowckResult<'db>], MirLowerError<'db>> {
151 return borrowck_query(db, self).map_err(|e| e.clone());
152
153 #[salsa::tracked(returns(as_deref), lru = 2024)]
154 fn borrowck_query<'db>(
155 db: &'db dyn HirDatabase,
156 def: InferBodyId<'db>,
157 ) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> {
158 let _p = tracing::info_span!("InferBodyId::borrowck").entered();
159 let module = def.module(db);
160 let interner = DbInterner::new_with(db, module.krate(db));
161 let env = db.trait_environment(def.generic_def(db));
162 let typing_mode = TypingMode::borrowck(interner, def.into());
164 all_mir_bodies(
165 db,
166 def,
167 |body, owner| {
168 let infcx = interner.infer_ctxt().build(typing_mode);
170 BorrowckResult {
171 owner,
172 mutability_of_locals: mutability_of_locals(&infcx, env, body),
173 moved_out_of_ref: moved_out_of_ref(&infcx, env, body),
174 partially_moved: partially_moved(&infcx, env, body),
175 borrow_regions: borrow_regions(db, body),
176 }
177 },
178 |(parent, parent_mir_body), (child, child_mir_body)| {
179 for (upvar, child_locals) in &child_mir_body.upvar_locals {
180 let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else {
181 continue;
182 };
183 for (child_local, capture_place) in child_locals {
184 if !capture_place
185 .projections
186 .iter()
187 .any(|proj| matches!(proj.kind, HirProjectionKind::Deref))
188 {
189 let parent_mol = &mut parent.mutability_of_locals[parent_local];
190 match (&*parent_mol, &child.mutability_of_locals[*child_local]) {
191 (MutabilityReason::Mut { .. }, _) => {}
192 (_, MutabilityReason::Mut { .. }) => {
193 *parent_mol = MutabilityReason::Mut { spans: Vec::new() }
195 }
196 (MutabilityReason::Not, _) => {}
197 (_, MutabilityReason::Not) => {
198 *parent_mol = MutabilityReason::Not
199 }
200 (MutabilityReason::Unused, MutabilityReason::Unused) => {}
201 }
202 }
203 }
204 }
205 },
206 )
207 }
208 }
209}
210
211fn moved_out_of_ref<'db>(
212 infcx: &InferCtxt<'db>,
213 env: ParamEnv<'db>,
214 body: &MirBody<'db>,
215) -> Vec<MovedOutOfRef> {
216 let db = infcx.interner.db;
217 let mut result = vec![];
218 let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind {
219 OperandKind::Copy(p) | OperandKind::Move(p) => {
220 let mut ty = PlaceTy::from_ty(body.locals[p.local].ty.as_ref());
221 let mut is_dereference_of_ref = false;
222 for proj in p.projection.lookup() {
223 if *proj == ProjectionElem::Deref && ty.ty.as_reference().is_some() {
224 is_dereference_of_ref = true;
225 }
226 ty = ty.projection_ty(infcx, proj, env);
227 }
228 if is_dereference_of_ref
229 && !infcx.type_is_copy_modulo_regions(env, ty.ty)
230 && !ty.ty.references_non_lt_error()
231 {
232 result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.ty.store() });
233 }
234 }
235 OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
236 };
237 for (_, block) in body.basic_blocks.iter() {
238 db.unwind_if_revision_cancelled();
239 for statement in &block.statements {
240 match &statement.kind {
241 StatementKind::Assign(_, r) => match r {
242 Rvalue::ShallowInitBoxWithAlloc(_) => (),
243 Rvalue::ShallowInitBox(o, _)
244 | Rvalue::UnaryOp(_, o)
245 | Rvalue::Cast(_, o, _)
246 | Rvalue::Repeat(o, _)
247 | Rvalue::Use(o) => for_operand(o, statement.span),
248 Rvalue::CopyForDeref(_)
249 | Rvalue::Discriminant(_)
250 | Rvalue::Len(_)
251 | Rvalue::Ref(_, _) => (),
252 Rvalue::CheckedBinaryOp(_, o1, o2) => {
253 for_operand(o1, statement.span);
254 for_operand(o2, statement.span);
255 }
256 Rvalue::Aggregate(_, ops) => {
257 for op in ops.iter() {
258 for_operand(op, statement.span);
259 }
260 }
261 Rvalue::ThreadLocalRef(n)
262 | Rvalue::AddressOf(n)
263 | Rvalue::BinaryOp(n)
264 | Rvalue::NullaryOp(n) => match *n {},
265 },
266 StatementKind::FakeRead(_)
267 | StatementKind::Deinit(_)
268 | StatementKind::StorageLive(_)
269 | StatementKind::StorageDead(_)
270 | StatementKind::Nop => (),
271 }
272 }
273 match &block.terminator {
274 Some(terminator) => match &terminator.kind {
275 TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
276 TerminatorKind::FalseEdge { .. }
277 | TerminatorKind::FalseUnwind { .. }
278 | TerminatorKind::Goto { .. }
279 | TerminatorKind::UnwindResume
280 | TerminatorKind::CoroutineDrop
281 | TerminatorKind::Abort
282 | TerminatorKind::Return
283 | TerminatorKind::Unreachable
284 | TerminatorKind::Drop { .. } => (),
285 TerminatorKind::DropAndReplace { value, .. } => {
286 for_operand(value, terminator.span);
287 }
288 TerminatorKind::Call { func, args, .. } => {
289 for_operand(func, terminator.span);
290 args.iter().for_each(|it| for_operand(it, terminator.span));
291 }
292 TerminatorKind::Assert { cond, .. } => {
293 for_operand(cond, terminator.span);
294 }
295 TerminatorKind::Yield { value, .. } => {
296 for_operand(value, terminator.span);
297 }
298 },
299 None => (),
300 }
301 }
302 result.shrink_to_fit();
303 result
304}
305
306fn partially_moved<'db>(
307 infcx: &InferCtxt<'db>,
308 env: ParamEnv<'db>,
309 body: &MirBody<'db>,
310) -> Vec<PartiallyMoved> {
311 let db = infcx.interner.db;
312 let mut result = vec![];
313 let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind {
314 OperandKind::Copy(p) | OperandKind::Move(p) => {
315 let ty = p.as_ref().ty(body, infcx, env).ty;
316 if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() {
317 result.push(PartiallyMoved { span, ty: ty.store(), local: p.local });
318 }
319 }
320 OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
321 };
322 for (_, block) in body.basic_blocks.iter() {
323 db.unwind_if_revision_cancelled();
324 for statement in &block.statements {
325 match &statement.kind {
326 StatementKind::Assign(_, r) => match r {
327 Rvalue::ShallowInitBoxWithAlloc(_) => (),
328 Rvalue::ShallowInitBox(o, _)
329 | Rvalue::UnaryOp(_, o)
330 | Rvalue::Cast(_, o, _)
331 | Rvalue::Repeat(o, _)
332 | Rvalue::Use(o) => for_operand(o, statement.span),
333 Rvalue::CopyForDeref(_)
334 | Rvalue::Discriminant(_)
335 | Rvalue::Len(_)
336 | Rvalue::Ref(_, _) => (),
337 Rvalue::CheckedBinaryOp(_, o1, o2) => {
338 for_operand(o1, statement.span);
339 for_operand(o2, statement.span);
340 }
341 Rvalue::Aggregate(_, ops) => {
342 for op in ops.iter() {
343 for_operand(op, statement.span);
344 }
345 }
346 Rvalue::ThreadLocalRef(n)
347 | Rvalue::AddressOf(n)
348 | Rvalue::BinaryOp(n)
349 | Rvalue::NullaryOp(n) => match *n {},
350 },
351 StatementKind::FakeRead(_)
352 | StatementKind::Deinit(_)
353 | StatementKind::StorageLive(_)
354 | StatementKind::StorageDead(_)
355 | StatementKind::Nop => (),
356 }
357 }
358 match &block.terminator {
359 Some(terminator) => match &terminator.kind {
360 TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
361 TerminatorKind::FalseEdge { .. }
362 | TerminatorKind::FalseUnwind { .. }
363 | TerminatorKind::Goto { .. }
364 | TerminatorKind::UnwindResume
365 | TerminatorKind::CoroutineDrop
366 | TerminatorKind::Abort
367 | TerminatorKind::Return
368 | TerminatorKind::Unreachable
369 | TerminatorKind::Drop { .. } => (),
370 TerminatorKind::DropAndReplace { value, .. } => {
371 for_operand(value, terminator.span);
372 }
373 TerminatorKind::Call { func, args, .. } => {
374 for_operand(func, terminator.span);
375 args.iter().for_each(|it| for_operand(it, terminator.span));
376 }
377 TerminatorKind::Assert { cond, .. } => {
378 for_operand(cond, terminator.span);
379 }
380 TerminatorKind::Yield { value, .. } => {
381 for_operand(value, terminator.span);
382 }
383 },
384 None => (),
385 }
386 }
387 result.shrink_to_fit();
388 result
389}
390
391fn borrow_regions<'db>(db: &'db dyn HirDatabase, body: &MirBody<'db>) -> Vec<BorrowRegion> {
392 let mut borrows = FxHashMap::default();
393 for (_, block) in body.basic_blocks.iter() {
394 db.unwind_if_revision_cancelled();
395 for statement in &block.statements {
396 if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind {
397 borrows
398 .entry(p.local)
399 .and_modify(|it: &mut BorrowRegion| {
400 it.places.push(statement.span);
401 })
402 .or_insert_with(|| BorrowRegion {
403 local: p.local,
404 kind: *kind,
405 places: vec![statement.span],
406 });
407 }
408 }
409 match &block.terminator {
410 Some(terminator) => match &terminator.kind {
411 TerminatorKind::FalseEdge { .. }
412 | TerminatorKind::FalseUnwind { .. }
413 | TerminatorKind::Goto { .. }
414 | TerminatorKind::UnwindResume
415 | TerminatorKind::CoroutineDrop
416 | TerminatorKind::Abort
417 | TerminatorKind::Return
418 | TerminatorKind::Unreachable
419 | TerminatorKind::Drop { .. } => (),
420 TerminatorKind::DropAndReplace { .. } => {}
421 TerminatorKind::Call { .. } => {}
422 _ => (),
423 },
424 None => (),
425 }
426 }
427
428 borrows.into_values().collect()
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq)]
432enum ProjectionCase {
433 Direct,
435 DirectPart,
437 Indirect,
439}
440
441fn place_case<'db>(
442 infcx: &InferCtxt<'db>,
443 env: ParamEnv<'db>,
444 body: &MirBody<'db>,
445 lvalue: &Place,
446) -> ProjectionCase {
447 let mut is_part_of = false;
448 let mut ty = PlaceTy::from_ty(body.locals[lvalue.local].ty.as_ref());
449 for proj in lvalue.projection.lookup().iter() {
450 match proj {
451 ProjectionElem::Deref if ty.ty.as_adt().is_none() => return ProjectionCase::Indirect, ProjectionElem::Deref | ProjectionElem::ConstantIndex { .. }
454 | ProjectionElem::Subslice { .. }
455 | ProjectionElem::Field(_)
456 | ProjectionElem::Index(_) => {
457 is_part_of = true;
458 }
459 ProjectionElem::Downcast(_) => (),
460 }
461 ty = ty.projection_ty(infcx, proj, env);
462 }
463 if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct }
464}
465
466fn ever_initialized_map(
470 db: &dyn HirDatabase,
471 body: &MirBody<'_>,
472) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
473 let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
474 body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect();
475 fn dfs(
476 db: &dyn HirDatabase,
477 body: &MirBody<'_>,
478 l: LocalId,
479 stack: &mut Vec<BasicBlockId>,
480 result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
481 ) {
482 while let Some(b) = stack.pop() {
483 let mut is_ever_initialized = result[b][l]; let block = &body.basic_blocks[b];
485 for statement in &block.statements {
486 match &statement.kind {
487 StatementKind::Assign(p, _) => {
488 if p.projection.is_empty() && p.local == l {
489 is_ever_initialized = true;
490 }
491 }
492 StatementKind::StorageDead(p) => {
493 if *p == l {
494 is_ever_initialized = false;
495 }
496 }
497 StatementKind::Deinit(_)
498 | StatementKind::FakeRead(_)
499 | StatementKind::Nop
500 | StatementKind::StorageLive(_) => (),
501 }
502 }
503 let Some(terminator) = &block.terminator else {
504 never!(
505 "Terminator should be none only in construction.\nThe body:\n{}",
506 body.pretty_print(db, DisplayTarget::from_crate(db, body.owner.krate(db)))
507 );
508 return;
509 };
510 let mut process = |target, is_ever_initialized| {
511 if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
512 result[target].insert(l, is_ever_initialized);
513 stack.push(target);
514 }
515 };
516 match &terminator.kind {
517 TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
518 TerminatorKind::SwitchInt { targets, .. } => {
519 targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
520 }
521 TerminatorKind::UnwindResume
522 | TerminatorKind::Abort
523 | TerminatorKind::Return
524 | TerminatorKind::Unreachable => (),
525 TerminatorKind::Call { target, cleanup, destination, .. } => {
526 if destination.projection.is_empty() && destination.local == l {
527 is_ever_initialized = true;
528 }
529 target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
530 }
531 TerminatorKind::Drop { target, unwind, place: _ } => {
532 iter::once(target)
533 .chain(unwind)
534 .for_each(|&it| process(it, is_ever_initialized));
535 }
536 TerminatorKind::DropAndReplace { .. }
537 | TerminatorKind::Assert { .. }
538 | TerminatorKind::Yield { .. }
539 | TerminatorKind::CoroutineDrop
540 | TerminatorKind::FalseEdge { .. }
541 | TerminatorKind::FalseUnwind { .. } => {
542 never!("We don't emit these MIR terminators yet");
543 }
544 }
545 }
546 }
547 let mut stack = Vec::new();
548 for &l in &body.param_locals {
549 result[body.start_block].insert(l, true);
550 stack.clear();
551 stack.push(body.start_block);
552 dfs(db, body, l, &mut stack, &mut result);
553 }
554 for l in body.locals.iter().map(|it| it.0) {
555 db.unwind_if_revision_cancelled();
556 if !result[body.start_block].contains_idx(l) {
557 result[body.start_block].insert(l, false);
558 stack.clear();
559 stack.push(body.start_block);
560 dfs(db, body, l, &mut stack, &mut result);
561 }
562 }
563 result
564}
565
566fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) {
567 match &mut result[local] {
568 MutabilityReason::Mut { spans } => spans.push(span),
569 it @ (MutabilityReason::Not | MutabilityReason::Unused) => {
570 *it = MutabilityReason::Mut { spans: vec![span] }
571 }
572 };
573}
574
575fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) {
576 if let it @ MutabilityReason::Unused = &mut result[local] {
577 *it = MutabilityReason::Not;
578 };
579}
580
581fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) {
582 if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind {
583 record_usage(p.local, result);
584 }
585}
586
587fn mutability_of_locals<'db>(
588 infcx: &InferCtxt<'db>,
589 env: ParamEnv<'db>,
590 body: &MirBody<'db>,
591) -> ArenaMap<LocalId, MutabilityReason> {
592 let db = infcx.interner.db;
593 let mut result: ArenaMap<LocalId, MutabilityReason> =
594 body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect();
595
596 let ever_init_maps = ever_initialized_map(db, body);
597 for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
598 let block = &body.basic_blocks[block_id];
599 for statement in &block.statements {
600 match &statement.kind {
601 StatementKind::Assign(place, value) => {
602 match place_case(infcx, env, body, place) {
603 ProjectionCase::Direct => {
604 if ever_init_map.get(place.local).copied().unwrap_or_default() {
605 push_mut_span(place.local, statement.span, &mut result);
606 } else {
607 ever_init_map.insert(place.local, true);
608 }
609 }
610 ProjectionCase::DirectPart => {
611 push_mut_span(place.local, statement.span, &mut result);
613 }
614 ProjectionCase::Indirect => {
615 record_usage(place.local, &mut result);
616 }
617 }
618 match value {
619 Rvalue::CopyForDeref(p)
620 | Rvalue::Discriminant(p)
621 | Rvalue::Len(p)
622 | Rvalue::Ref(_, p) => {
623 record_usage(p.local, &mut result);
624 }
625 Rvalue::Use(o)
626 | Rvalue::Repeat(o, _)
627 | Rvalue::Cast(_, o, _)
628 | Rvalue::UnaryOp(_, o) => record_usage_for_operand(o, &mut result),
629 Rvalue::CheckedBinaryOp(_, o1, o2) => {
630 for o in [o1, o2] {
631 record_usage_for_operand(o, &mut result);
632 }
633 }
634 Rvalue::Aggregate(_, args) => {
635 for arg in args.iter() {
636 record_usage_for_operand(arg, &mut result);
637 }
638 }
639 Rvalue::ShallowInitBox(_, _) | Rvalue::ShallowInitBoxWithAlloc(_) => (),
640 Rvalue::ThreadLocalRef(n)
641 | Rvalue::AddressOf(n)
642 | Rvalue::BinaryOp(n)
643 | Rvalue::NullaryOp(n) => match *n {},
644 }
645 if let Rvalue::Ref(
646 BorrowKind::Mut {
647 kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
648 },
649 p,
650 ) = value
651 && place_case(infcx, env, body, p) != ProjectionCase::Indirect
652 {
653 push_mut_span(p.local, statement.span, &mut result);
654 }
655 }
656 StatementKind::FakeRead(p) => {
657 record_usage(p.local, &mut result);
658 }
659 StatementKind::StorageDead(p) => {
660 ever_init_map.insert(*p, false);
661 }
662 StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::Nop => (),
663 }
664 }
665 let Some(terminator) = &block.terminator else {
666 never!("Terminator should be none only in construction");
667 continue;
668 };
669 match &terminator.kind {
670 TerminatorKind::Goto { .. }
671 | TerminatorKind::UnwindResume
672 | TerminatorKind::Abort
673 | TerminatorKind::Return
674 | TerminatorKind::Unreachable
675 | TerminatorKind::FalseEdge { .. }
676 | TerminatorKind::FalseUnwind { .. }
677 | TerminatorKind::CoroutineDrop
678 | TerminatorKind::Drop { .. }
679 | TerminatorKind::DropAndReplace { .. }
680 | TerminatorKind::Assert { .. }
681 | TerminatorKind::Yield { .. } => (),
682 TerminatorKind::SwitchInt { discr, targets: _ } => {
683 record_usage_for_operand(discr, &mut result);
684 }
685 TerminatorKind::Call { destination, args, func, .. } => {
686 record_usage_for_operand(func, &mut result);
687 for arg in args.iter() {
688 record_usage_for_operand(arg, &mut result);
689 }
690 if destination.projection.is_empty() {
691 if ever_init_map.get(destination.local).copied().unwrap_or_default() {
692 push_mut_span(destination.local, terminator.span, &mut result);
693 } else {
694 ever_init_map.insert(destination.local, true);
695 }
696 }
697 }
698 }
699 }
700 result
701}