1use std::fmt;
2
3use rustc_data_structures::fx::FxIndexMap;
4use rustc_data_structures::graph;
5use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
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 = MixedBitSet<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        MixedBitSet::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 {}