1use std::fmt;
2
3use rustc_data_structures::fx::FxIndexMap;
4use rustc_data_structures::graph;
5use rustc_index::bit_set::DenseBitSet;
6use rustc_middle::mir::{
7 self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
8};
9use rustc_middle::ty::{RegionVid, TyCtxt};
10use rustc_mir_dataflow::fmt::DebugWithContext;
11use rustc_mir_dataflow::impls::{
12 EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
13 MaybeUninitializedPlacesDomain,
14};
15use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
16use tracing::debug;
17
18use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
19
20pub(crate) struct Borrowck<'a, 'tcx> {
24 pub(crate) borrows: Borrows<'a, 'tcx>,
25 pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
26 pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
27}
28
29impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
30 type Domain = BorrowckDomain;
31
32 const NAME: &'static str = "borrowck";
33
34 fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
35 BorrowckDomain {
36 borrows: self.borrows.bottom_value(body),
37 uninits: self.uninits.bottom_value(body),
38 ever_inits: self.ever_inits.bottom_value(body),
39 }
40 }
41
42 fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Domain) {
43 unreachable!();
45 }
46
47 fn apply_early_statement_effect(
48 &mut self,
49 state: &mut Self::Domain,
50 stmt: &mir::Statement<'tcx>,
51 loc: Location,
52 ) {
53 self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
54 self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
55 self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
56 }
57
58 fn apply_primary_statement_effect(
59 &mut self,
60 state: &mut Self::Domain,
61 stmt: &mir::Statement<'tcx>,
62 loc: Location,
63 ) {
64 self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
65 self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
66 self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
67 }
68
69 fn apply_early_terminator_effect(
70 &mut self,
71 state: &mut Self::Domain,
72 term: &mir::Terminator<'tcx>,
73 loc: Location,
74 ) {
75 self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
76 self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
77 self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
78 }
79
80 fn apply_primary_terminator_effect<'mir>(
81 &mut self,
82 state: &mut Self::Domain,
83 term: &'mir mir::Terminator<'tcx>,
84 loc: Location,
85 ) -> TerminatorEdges<'mir, 'tcx> {
86 self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
87 self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
88 self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
89
90 TerminatorEdges::None
93 }
94
95 fn apply_call_return_effect(
96 &mut self,
97 _state: &mut Self::Domain,
98 _block: BasicBlock,
99 _return_places: CallReturnPlaces<'_, 'tcx>,
100 ) {
101 unreachable!();
103 }
104}
105
106impl JoinSemiLattice for BorrowckDomain {
107 fn join(&mut self, _other: &Self) -> bool {
108 unreachable!();
110 }
111}
112
113impl<'tcx, C> DebugWithContext<C> for BorrowckDomain
114where
115 C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
116{
117 fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 f.write_str("borrows: ")?;
119 self.borrows.fmt_with(ctxt, f)?;
120 f.write_str(" uninits: ")?;
121 self.uninits.fmt_with(ctxt, f)?;
122 f.write_str(" ever_inits: ")?;
123 self.ever_inits.fmt_with(ctxt, f)?;
124 Ok(())
125 }
126
127 fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 if self == old {
129 return Ok(());
130 }
131
132 if self.borrows != old.borrows {
133 f.write_str("borrows: ")?;
134 self.borrows.fmt_diff_with(&old.borrows, ctxt, f)?;
135 f.write_str("\n")?;
136 }
137
138 if self.uninits != old.uninits {
139 f.write_str("uninits: ")?;
140 self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?;
141 f.write_str("\n")?;
142 }
143
144 if self.ever_inits != old.ever_inits {
145 f.write_str("ever_inits: ")?;
146 self.ever_inits.fmt_diff_with(&old.ever_inits, ctxt, f)?;
147 f.write_str("\n")?;
148 }
149
150 Ok(())
151 }
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub(crate) struct BorrowckDomain {
157 pub(crate) borrows: BorrowsDomain,
158 pub(crate) uninits: MaybeUninitializedPlacesDomain,
159 pub(crate) ever_inits: EverInitializedPlacesDomain,
160}
161
162rustc_index::newtype_index! {
163 #[orderable]
164 #[debug_format = "bw{}"]
165 pub struct BorrowIndex {}
166}
167
168pub struct Borrows<'a, 'tcx> {
176 tcx: TyCtxt<'tcx>,
177 body: &'a Body<'tcx>,
178 borrow_set: &'a BorrowSet<'tcx>,
179 borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
180}
181
182struct OutOfScopePrecomputer<'a, 'tcx> {
183 visited: DenseBitSet<mir::BasicBlock>,
184 visit_stack: Vec<mir::BasicBlock>,
185 body: &'a Body<'tcx>,
186 regioncx: &'a RegionInferenceContext<'tcx>,
187 borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
188}
189
190impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
191 fn compute(
192 body: &Body<'tcx>,
193 regioncx: &RegionInferenceContext<'tcx>,
194 borrow_set: &BorrowSet<'tcx>,
195 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
196 let mut prec = OutOfScopePrecomputer {
197 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
198 visit_stack: vec![],
199 body,
200 regioncx,
201 borrows_out_of_scope_at_location: FxIndexMap::default(),
202 };
203 for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
204 let borrow_region = borrow_data.region;
205 let location = borrow_data.reserve_location;
206 prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
207 }
208
209 prec.borrows_out_of_scope_at_location
210 }
211
212 fn precompute_borrows_out_of_scope(
213 &mut self,
214 borrow_index: BorrowIndex,
215 borrow_region: RegionVid,
216 first_location: Location,
217 ) {
218 let first_block = first_location.block;
219 let first_bb_data = &self.body.basic_blocks[first_block];
220
221 let first_lo = first_location.statement_index;
224 let first_hi = first_bb_data.statements.len();
225
226 if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
227 borrow_region,
228 first_block,
229 first_lo,
230 first_hi,
231 ) {
232 let kill_location = Location { block: first_block, statement_index: kill_stmt };
233 debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
236 self.borrows_out_of_scope_at_location
237 .entry(kill_location)
238 .or_default()
239 .push(borrow_index);
240
241 return;
243 }
244
245 for succ_bb in first_bb_data.terminator().successors() {
247 if self.visited.insert(succ_bb) {
248 self.visit_stack.push(succ_bb);
249 }
250 }
251
252 while let Some(block) = self.visit_stack.pop() {
256 let bb_data = &self.body[block];
257 let num_stmts = bb_data.statements.len();
258 if let Some(kill_stmt) =
259 self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
260 {
261 let kill_location = Location { block, statement_index: kill_stmt };
262 debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
265 self.borrows_out_of_scope_at_location
266 .entry(kill_location)
267 .or_default()
268 .push(borrow_index);
269
270 continue;
272 }
273
274 for succ_bb in bb_data.terminator().successors() {
276 if self.visited.insert(succ_bb) {
277 self.visit_stack.push(succ_bb);
278 }
279 }
280 }
281
282 self.visited.clear();
283 }
284}
285
286pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
288 body: &Body<'tcx>,
289 regioncx: &RegionInferenceContext<'tcx>,
290 borrow_set: &BorrowSet<'tcx>,
291) -> FxIndexMap<Location, Vec<BorrowIndex>> {
292 OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
293}
294
295struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
296 visited: DenseBitSet<mir::BasicBlock>,
297 visit_stack: Vec<mir::BasicBlock>,
298 body: &'a Body<'tcx>,
299 regioncx: &'a RegionInferenceContext<'tcx>,
300
301 loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
302}
303
304impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
305 fn compute(
306 body: &Body<'tcx>,
307 regioncx: &RegionInferenceContext<'tcx>,
308 borrow_set: &BorrowSet<'tcx>,
309 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
310 let mut prec = PoloniusOutOfScopePrecomputer {
313 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
314 visit_stack: vec![],
315 body,
316 regioncx,
317 loans_out_of_scope_at_location: FxIndexMap::default(),
318 };
319 for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
320 let issuing_region = loan_data.region;
321 let loan_issued_at = loan_data.reserve_location;
322 prec.precompute_loans_out_of_scope(loan_idx, issuing_region, loan_issued_at);
323 }
324
325 prec.loans_out_of_scope_at_location
326 }
327
328 fn precompute_loans_out_of_scope(
332 &mut self,
333 loan_idx: BorrowIndex,
334 issuing_region: RegionVid,
335 loan_issued_at: Location,
336 ) {
337 let sccs = self.regioncx.constraint_sccs();
338 let universal_regions = self.regioncx.universal_regions();
339
340 for successor in graph::depth_first_search(&self.regioncx.region_graph(), issuing_region) {
350 let scc = sccs.scc(successor);
363 for constraint in self.regioncx.applied_member_constraints(scc) {
364 if universal_regions.is_universal_region(constraint.min_choice) {
365 return;
366 }
367 }
368 }
369
370 let first_block = loan_issued_at.block;
371 let first_bb_data = &self.body.basic_blocks[first_block];
372
373 let first_lo = loan_issued_at.statement_index;
376 let first_hi = first_bb_data.statements.len();
377
378 if let Some(kill_location) =
379 self.loan_kill_location(loan_idx, loan_issued_at, first_block, first_lo, first_hi)
380 {
381 debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
382 self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
383
384 return;
386 }
387
388 for succ_bb in first_bb_data.terminator().successors() {
390 if self.visited.insert(succ_bb) {
391 self.visit_stack.push(succ_bb);
392 }
393 }
394
395 while let Some(block) = self.visit_stack.pop() {
399 let bb_data = &self.body[block];
400 let num_stmts = bb_data.statements.len();
401 if let Some(kill_location) =
402 self.loan_kill_location(loan_idx, loan_issued_at, block, 0, num_stmts)
403 {
404 debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
405 self.loans_out_of_scope_at_location
406 .entry(kill_location)
407 .or_default()
408 .push(loan_idx);
409
410 continue;
412 }
413
414 for succ_bb in bb_data.terminator().successors() {
416 if self.visited.insert(succ_bb) {
417 self.visit_stack.push(succ_bb);
418 }
419 }
420 }
421
422 self.visited.clear();
423 assert!(self.visit_stack.is_empty(), "visit stack should be empty");
424 }
425
426 fn loan_kill_location(
430 &self,
431 loan_idx: BorrowIndex,
432 loan_issued_at: Location,
433 block: BasicBlock,
434 start: usize,
435 end: usize,
436 ) -> Option<Location> {
437 for statement_index in start..=end {
438 let location = Location { block, statement_index };
439
440 if location == loan_issued_at {
444 continue;
445 }
446
447 if self.regioncx.is_loan_live_at(loan_idx, location) {
456 continue;
457 }
458
459 return Some(location);
462 }
463
464 None
465 }
466}
467
468impl<'a, 'tcx> Borrows<'a, 'tcx> {
469 pub fn new(
470 tcx: TyCtxt<'tcx>,
471 body: &'a Body<'tcx>,
472 regioncx: &RegionInferenceContext<'tcx>,
473 borrow_set: &'a BorrowSet<'tcx>,
474 ) -> Self {
475 let borrows_out_of_scope_at_location =
476 if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
477 calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
478 } else {
479 PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
480 };
481 Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
482 }
483
484 fn kill_loans_out_of_scope_at_location(
487 &self,
488 state: &mut <Self as Analysis<'tcx>>::Domain,
489 location: Location,
490 ) {
491 if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
503 state.kill_all(indices.iter().copied());
504 }
505 }
506
507 fn kill_borrows_on_place(
509 &self,
510 state: &mut <Self as Analysis<'tcx>>::Domain,
511 place: Place<'tcx>,
512 ) {
513 debug!("kill_borrows_on_place: place={:?}", place);
514
515 let other_borrows_of_local = self
516 .borrow_set
517 .local_map
518 .get(&place.local)
519 .into_iter()
520 .flat_map(|bs| bs.iter())
521 .copied();
522
523 if place.projection.is_empty() {
527 if !self.body.local_decls[place.local].is_ref_to_static() {
528 state.kill_all(other_borrows_of_local);
529 }
530 return;
531 }
532
533 let definitely_conflicting_borrows = other_borrows_of_local.filter(|&i| {
538 places_conflict(
539 self.tcx,
540 self.body,
541 self.borrow_set[i].borrowed_place,
542 place,
543 PlaceConflictBias::NoOverlap,
544 )
545 });
546
547 state.kill_all(definitely_conflicting_borrows);
548 }
549}
550
551type BorrowsDomain = DenseBitSet<BorrowIndex>;
552
553impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
561 type Domain = BorrowsDomain;
562
563 const NAME: &'static str = "borrows";
564
565 fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
566 DenseBitSet::new_empty(self.borrow_set.len())
568 }
569
570 fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
571 }
574
575 fn apply_early_statement_effect(
576 &mut self,
577 state: &mut Self::Domain,
578 _statement: &mir::Statement<'tcx>,
579 location: Location,
580 ) {
581 self.kill_loans_out_of_scope_at_location(state, location);
582 }
583
584 fn apply_primary_statement_effect(
585 &mut self,
586 state: &mut Self::Domain,
587 stmt: &mir::Statement<'tcx>,
588 location: Location,
589 ) {
590 match &stmt.kind {
591 mir::StatementKind::Assign(box (lhs, rhs)) => {
592 if let mir::Rvalue::Ref(_, _, place) = rhs {
593 if place.ignore_borrow(
594 self.tcx,
595 self.body,
596 &self.borrow_set.locals_state_at_exit,
597 ) {
598 return;
599 }
600 let index = self.borrow_set.get_index_of(&location).unwrap_or_else(|| {
601 panic!("could not find BorrowIndex for location {location:?}");
602 });
603
604 state.gen_(index);
605 }
606
607 self.kill_borrows_on_place(state, *lhs);
610 }
611
612 mir::StatementKind::StorageDead(local) => {
613 self.kill_borrows_on_place(state, Place::from(*local));
616 }
617
618 mir::StatementKind::FakeRead(..)
619 | mir::StatementKind::SetDiscriminant { .. }
620 | mir::StatementKind::Deinit(..)
621 | mir::StatementKind::StorageLive(..)
622 | mir::StatementKind::Retag { .. }
623 | mir::StatementKind::PlaceMention(..)
624 | mir::StatementKind::AscribeUserType(..)
625 | mir::StatementKind::Coverage(..)
626 | mir::StatementKind::Intrinsic(..)
627 | mir::StatementKind::ConstEvalCounter
628 | mir::StatementKind::BackwardIncompatibleDropHint { .. }
629 | mir::StatementKind::Nop => {}
630 }
631 }
632
633 fn apply_early_terminator_effect(
634 &mut self,
635 state: &mut Self::Domain,
636 _terminator: &mir::Terminator<'tcx>,
637 location: Location,
638 ) {
639 self.kill_loans_out_of_scope_at_location(state, location);
640 }
641
642 fn apply_primary_terminator_effect<'mir>(
643 &mut self,
644 state: &mut Self::Domain,
645 terminator: &'mir mir::Terminator<'tcx>,
646 _location: Location,
647 ) -> TerminatorEdges<'mir, 'tcx> {
648 if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
649 for op in operands {
650 if let mir::InlineAsmOperand::Out { place: Some(place), .. }
651 | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
652 {
653 self.kill_borrows_on_place(state, place);
654 }
655 }
656 }
657 terminator.edges()
658 }
659}
660
661impl<C> DebugWithContext<C> for BorrowIndex {}