1use std::fmt;
2
3use rustc_data_structures::fx::FxIndexMap;
4use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
5use rustc_middle::mir::{
6 self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
7};
8use rustc_middle::ty::{RegionVid, TyCtxt};
9use rustc_mir_dataflow::fmt::DebugWithContext;
10use rustc_mir_dataflow::impls::{
11 EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
12 MaybeUninitializedPlacesDomain,
13};
14use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
15use tracing::debug;
16
17use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
18
19pub(crate) struct Borrowck<'a, 'tcx> {
23 pub(crate) borrows: Borrows<'a, 'tcx>,
24 pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
25 pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
26}
27
28impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
29 type Domain = BorrowckDomain;
30
31 const NAME: &'static str = "borrowck";
32
33 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
34 BorrowckDomain {
35 borrows: self.borrows.bottom_value(body),
36 uninits: self.uninits.bottom_value(body),
37 ever_inits: self.ever_inits.bottom_value(body),
38 }
39 }
40
41 fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Domain) {
42 unreachable!();
44 }
45
46 fn apply_early_statement_effect(
47 &mut self,
48 state: &mut Self::Domain,
49 stmt: &mir::Statement<'tcx>,
50 loc: Location,
51 ) {
52 self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
53 self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
54 self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
55 }
56
57 fn apply_primary_statement_effect(
58 &mut self,
59 state: &mut Self::Domain,
60 stmt: &mir::Statement<'tcx>,
61 loc: Location,
62 ) {
63 self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
64 self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
65 self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
66 }
67
68 fn apply_early_terminator_effect(
69 &mut self,
70 state: &mut Self::Domain,
71 term: &mir::Terminator<'tcx>,
72 loc: Location,
73 ) {
74 self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
75 self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
76 self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
77 }
78
79 fn apply_primary_terminator_effect<'mir>(
80 &mut self,
81 state: &mut Self::Domain,
82 term: &'mir mir::Terminator<'tcx>,
83 loc: Location,
84 ) -> TerminatorEdges<'mir, 'tcx> {
85 self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
86 self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
87 self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
88
89 TerminatorEdges::None
92 }
93
94 fn apply_call_return_effect(
95 &mut self,
96 _state: &mut Self::Domain,
97 _block: BasicBlock,
98 _return_places: CallReturnPlaces<'_, 'tcx>,
99 ) {
100 unreachable!();
102 }
103}
104
105impl JoinSemiLattice for BorrowckDomain {
106 fn join(&mut self, _other: &Self) -> bool {
107 unreachable!();
109 }
110}
111
112impl<'tcx, C> DebugWithContext<C> for BorrowckDomain
113where
114 C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
115{
116 fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 f.write_str("borrows: ")?;
118 self.borrows.fmt_with(ctxt, f)?;
119 f.write_str(" uninits: ")?;
120 self.uninits.fmt_with(ctxt, f)?;
121 f.write_str(" ever_inits: ")?;
122 self.ever_inits.fmt_with(ctxt, f)?;
123 Ok(())
124 }
125
126 fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 if self == old {
128 return Ok(());
129 }
130
131 if self.borrows != old.borrows {
132 f.write_str("borrows: ")?;
133 self.borrows.fmt_diff_with(&old.borrows, ctxt, f)?;
134 f.write_str("\n")?;
135 }
136
137 if self.uninits != old.uninits {
138 f.write_str("uninits: ")?;
139 self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?;
140 f.write_str("\n")?;
141 }
142
143 if self.ever_inits != old.ever_inits {
144 f.write_str("ever_inits: ")?;
145 self.ever_inits.fmt_diff_with(&old.ever_inits, ctxt, f)?;
146 f.write_str("\n")?;
147 }
148
149 Ok(())
150 }
151}
152
153#[derive(Clone, Debug, PartialEq, Eq)]
155pub(crate) struct BorrowckDomain {
156 pub(crate) borrows: BorrowsDomain,
157 pub(crate) uninits: MaybeUninitializedPlacesDomain,
158 pub(crate) ever_inits: EverInitializedPlacesDomain,
159}
160
161rustc_index::newtype_index! {
162 #[orderable]
163 #[debug_format = "bw{}"]
164 pub struct BorrowIndex {}
165}
166
167pub struct Borrows<'a, 'tcx> {
175 tcx: TyCtxt<'tcx>,
176 body: &'a Body<'tcx>,
177 borrow_set: &'a BorrowSet<'tcx>,
178 borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
179}
180
181struct OutOfScopePrecomputer<'a, 'tcx> {
182 visited: DenseBitSet<mir::BasicBlock>,
183 visit_stack: Vec<mir::BasicBlock>,
184 body: &'a Body<'tcx>,
185 regioncx: &'a RegionInferenceContext<'tcx>,
186 borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
187}
188
189impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
190 fn compute(
191 body: &Body<'tcx>,
192 regioncx: &RegionInferenceContext<'tcx>,
193 borrow_set: &BorrowSet<'tcx>,
194 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
195 let mut prec = OutOfScopePrecomputer {
196 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
197 visit_stack: vec![],
198 body,
199 regioncx,
200 borrows_out_of_scope_at_location: FxIndexMap::default(),
201 };
202 for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
203 let borrow_region = borrow_data.region;
204 let location = borrow_data.reserve_location;
205 prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
206 }
207
208 prec.borrows_out_of_scope_at_location
209 }
210
211 fn precompute_borrows_out_of_scope(
212 &mut self,
213 borrow_index: BorrowIndex,
214 borrow_region: RegionVid,
215 first_location: Location,
216 ) {
217 let first_block = first_location.block;
218 let first_bb_data = &self.body.basic_blocks[first_block];
219
220 let first_lo = first_location.statement_index;
223 let first_hi = first_bb_data.statements.len();
224
225 if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
226 borrow_region,
227 first_block,
228 first_lo,
229 first_hi,
230 ) {
231 let kill_location = Location { block: first_block, statement_index: kill_stmt };
232 debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
235 self.borrows_out_of_scope_at_location
236 .entry(kill_location)
237 .or_default()
238 .push(borrow_index);
239
240 return;
242 }
243
244 for succ_bb in first_bb_data.terminator().successors() {
246 if self.visited.insert(succ_bb) {
247 self.visit_stack.push(succ_bb);
248 }
249 }
250
251 while let Some(block) = self.visit_stack.pop() {
255 let bb_data = &self.body[block];
256 let num_stmts = bb_data.statements.len();
257 if let Some(kill_stmt) =
258 self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
259 {
260 let kill_location = Location { block, statement_index: kill_stmt };
261 debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
264 self.borrows_out_of_scope_at_location
265 .entry(kill_location)
266 .or_default()
267 .push(borrow_index);
268
269 continue;
271 }
272
273 for succ_bb in bb_data.terminator().successors() {
275 if self.visited.insert(succ_bb) {
276 self.visit_stack.push(succ_bb);
277 }
278 }
279 }
280
281 self.visited.clear();
282 }
283}
284
285pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
287 body: &Body<'tcx>,
288 regioncx: &RegionInferenceContext<'tcx>,
289 borrow_set: &BorrowSet<'tcx>,
290) -> FxIndexMap<Location, Vec<BorrowIndex>> {
291 OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
292}
293
294struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
295 visited: DenseBitSet<mir::BasicBlock>,
296 visit_stack: Vec<mir::BasicBlock>,
297 body: &'a Body<'tcx>,
298 regioncx: &'a RegionInferenceContext<'tcx>,
299
300 loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
301}
302
303impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
304 fn compute(
305 body: &Body<'tcx>,
306 regioncx: &RegionInferenceContext<'tcx>,
307 borrow_set: &BorrowSet<'tcx>,
308 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
309 let mut prec = PoloniusOutOfScopePrecomputer {
312 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
313 visit_stack: vec![],
314 body,
315 regioncx,
316 loans_out_of_scope_at_location: FxIndexMap::default(),
317 };
318 for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
319 let loan_issued_at = loan_data.reserve_location;
320 prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
321 }
322
323 prec.loans_out_of_scope_at_location
324 }
325
326 fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
330 let first_block = loan_issued_at.block;
331 let first_bb_data = &self.body.basic_blocks[first_block];
332
333 let first_lo = loan_issued_at.statement_index;
336 let first_hi = first_bb_data.statements.len();
337
338 if let Some(kill_location) =
339 self.loan_kill_location(loan_idx, loan_issued_at, first_block, first_lo, first_hi)
340 {
341 debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
342 self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
343
344 return;
346 }
347
348 for succ_bb in first_bb_data.terminator().successors() {
350 if self.visited.insert(succ_bb) {
351 self.visit_stack.push(succ_bb);
352 }
353 }
354
355 while let Some(block) = self.visit_stack.pop() {
359 let bb_data = &self.body[block];
360 let num_stmts = bb_data.statements.len();
361 if let Some(kill_location) =
362 self.loan_kill_location(loan_idx, loan_issued_at, block, 0, num_stmts)
363 {
364 debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
365 self.loans_out_of_scope_at_location
366 .entry(kill_location)
367 .or_default()
368 .push(loan_idx);
369
370 continue;
372 }
373
374 for succ_bb in bb_data.terminator().successors() {
376 if self.visited.insert(succ_bb) {
377 self.visit_stack.push(succ_bb);
378 }
379 }
380 }
381
382 self.visited.clear();
383 assert!(self.visit_stack.is_empty(), "visit stack should be empty");
384 }
385
386 fn loan_kill_location(
390 &self,
391 loan_idx: BorrowIndex,
392 loan_issued_at: Location,
393 block: BasicBlock,
394 start: usize,
395 end: usize,
396 ) -> Option<Location> {
397 for statement_index in start..=end {
398 let location = Location { block, statement_index };
399
400 if location == loan_issued_at {
404 continue;
405 }
406
407 if self.regioncx.is_loan_live_at(loan_idx, location) {
416 continue;
417 }
418
419 return Some(location);
422 }
423
424 None
425 }
426}
427
428impl<'a, 'tcx> Borrows<'a, 'tcx> {
429 pub fn new(
430 tcx: TyCtxt<'tcx>,
431 body: &'a Body<'tcx>,
432 regioncx: &RegionInferenceContext<'tcx>,
433 borrow_set: &'a BorrowSet<'tcx>,
434 ) -> Self {
435 let borrows_out_of_scope_at_location =
436 if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
437 calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
438 } else {
439 PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
440 };
441 Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
442 }
443
444 fn kill_loans_out_of_scope_at_location(
447 &self,
448 state: &mut <Self as Analysis<'tcx>>::Domain,
449 location: Location,
450 ) {
451 if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
463 state.kill_all(indices.iter().copied());
464 }
465 }
466
467 fn kill_borrows_on_place(
469 &self,
470 state: &mut <Self as Analysis<'tcx>>::Domain,
471 place: Place<'tcx>,
472 ) {
473 debug!("kill_borrows_on_place: place={:?}", place);
474
475 let other_borrows_of_local = self
476 .borrow_set
477 .local_map
478 .get(&place.local)
479 .into_iter()
480 .flat_map(|bs| bs.iter())
481 .copied();
482
483 if place.projection.is_empty() {
487 if !self.body.local_decls[place.local].is_ref_to_static() {
488 state.kill_all(other_borrows_of_local);
489 }
490 return;
491 }
492
493 let definitely_conflicting_borrows = other_borrows_of_local.filter(|&i| {
498 places_conflict(
499 self.tcx,
500 self.body,
501 self.borrow_set[i].borrowed_place,
502 place,
503 PlaceConflictBias::NoOverlap,
504 )
505 });
506
507 state.kill_all(definitely_conflicting_borrows);
508 }
509}
510
511type BorrowsDomain = MixedBitSet<BorrowIndex>;
512
513impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
521 type Domain = BorrowsDomain;
522
523 const NAME: &'static str = "borrows";
524
525 fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
526 MixedBitSet::new_empty(self.borrow_set.len())
528 }
529
530 fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
531 }
534
535 fn apply_early_statement_effect(
536 &mut self,
537 state: &mut Self::Domain,
538 _statement: &mir::Statement<'tcx>,
539 location: Location,
540 ) {
541 self.kill_loans_out_of_scope_at_location(state, location);
542 }
543
544 fn apply_primary_statement_effect(
545 &mut self,
546 state: &mut Self::Domain,
547 stmt: &mir::Statement<'tcx>,
548 location: Location,
549 ) {
550 match &stmt.kind {
551 mir::StatementKind::Assign(box (lhs, rhs)) => {
552 if let mir::Rvalue::Ref(_, _, place) = rhs {
553 if place.ignore_borrow(
554 self.tcx,
555 self.body,
556 &self.borrow_set.locals_state_at_exit,
557 ) {
558 return;
559 }
560 let index = self.borrow_set.get_index_of(&location).unwrap_or_else(|| {
561 panic!("could not find BorrowIndex for location {location:?}");
562 });
563
564 state.gen_(index);
565 }
566
567 self.kill_borrows_on_place(state, *lhs);
570 }
571
572 mir::StatementKind::StorageDead(local) => {
573 self.kill_borrows_on_place(state, Place::from(*local));
576 }
577
578 mir::StatementKind::FakeRead(..)
579 | mir::StatementKind::SetDiscriminant { .. }
580 | mir::StatementKind::Deinit(..)
581 | mir::StatementKind::StorageLive(..)
582 | mir::StatementKind::Retag { .. }
583 | mir::StatementKind::PlaceMention(..)
584 | mir::StatementKind::AscribeUserType(..)
585 | mir::StatementKind::Coverage(..)
586 | mir::StatementKind::Intrinsic(..)
587 | mir::StatementKind::ConstEvalCounter
588 | mir::StatementKind::BackwardIncompatibleDropHint { .. }
589 | mir::StatementKind::Nop => {}
590 }
591 }
592
593 fn apply_early_terminator_effect(
594 &mut self,
595 state: &mut Self::Domain,
596 _terminator: &mir::Terminator<'tcx>,
597 location: Location,
598 ) {
599 self.kill_loans_out_of_scope_at_location(state, location);
600 }
601
602 fn apply_primary_terminator_effect<'mir>(
603 &mut self,
604 state: &mut Self::Domain,
605 terminator: &'mir mir::Terminator<'tcx>,
606 _location: Location,
607 ) -> TerminatorEdges<'mir, 'tcx> {
608 if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
609 for op in operands {
610 if let mir::InlineAsmOperand::Out { place: Some(place), .. }
611 | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
612 {
613 self.kill_borrows_on_place(state, place);
614 }
615 }
616 }
617 terminator.edges()
618 }
619}
620
621impl<C> DebugWithContext<C> for BorrowIndex {}