Skip to main content

rustc_borrowck/
dataflow.rs

1use rustc_data_structures::fx::FxIndexMap;
2use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
3use rustc_middle::mir::{self, BasicBlock, Body, CallReturnPlaces, Location, Place};
4use rustc_middle::ty::{RegionVid, TyCtxt};
5use rustc_mir_dataflow::fmt::DebugWithContext;
6use rustc_mir_dataflow::impls::{
7    EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
8    MaybeUninitializedPlacesDomain,
9};
10use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
11use tracing::debug;
12
13use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
14
15// This analysis is different to most others. Its results aren't computed with
16// `iterate_to_fixpoint`, but are instead composed from the results of three sub-analyses that are
17// computed individually with `iterate_to_fixpoint`. Because it's faster that way than having a
18// single analysis where the domain has three components.
19pub(crate) struct Borrowck<'a, 'tcx> {
20    pub(crate) borrows: Borrows<'a, 'tcx>,
21    pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
22    pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
23}
24
25impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
26    type Domain = BorrowckDomain;
27
28    const NAME: &'static str = "borrowck";
29
30    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
31        BorrowckDomain {
32            borrows: self.borrows.bottom_value(body),
33            uninits: self.uninits.bottom_value(body),
34            ever_inits: self.ever_inits.bottom_value(body),
35        }
36    }
37
38    fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Domain) {
39        // This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
40        ::core::panicking::panic("internal error: entered unreachable code");unreachable!();
41    }
42
43    fn apply_early_statement_effect(
44        &self,
45        state: &mut Self::Domain,
46        stmt: &mir::Statement<'tcx>,
47        loc: Location,
48    ) {
49        self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
50        self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
51        self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
52    }
53
54    fn apply_primary_statement_effect(
55        &self,
56        state: &mut Self::Domain,
57        stmt: &mir::Statement<'tcx>,
58        loc: Location,
59    ) {
60        self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
61        self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
62        self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
63    }
64
65    fn apply_early_terminator_effect(
66        &self,
67        state: &mut Self::Domain,
68        term: &mir::Terminator<'tcx>,
69        loc: Location,
70    ) {
71        self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
72        self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
73        self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
74    }
75
76    fn apply_primary_terminator_effect(
77        &self,
78        state: &mut Self::Domain,
79        term: &mir::Terminator<'tcx>,
80        loc: Location,
81    ) {
82        self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
83        self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
84        self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
85    }
86
87    fn apply_call_return_effect(
88        &self,
89        _state: &mut Self::Domain,
90        _block: BasicBlock,
91        _return_places: CallReturnPlaces<'_, 'tcx>,
92    ) {
93        // This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
94        ::core::panicking::panic("internal error: entered unreachable code");unreachable!();
95    }
96}
97
98impl JoinSemiLattice for BorrowckDomain {
99    fn join(&mut self, _other: &Self) -> bool {
100        // This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
101        ::core::panicking::panic("internal error: entered unreachable code");unreachable!();
102    }
103}
104
105/// The transient state of the dataflow analyses used by the borrow checker.
106#[derive(#[automatically_derived]
impl ::core::clone::Clone for BorrowckDomain {
    #[inline]
    fn clone(&self) -> BorrowckDomain {
        BorrowckDomain {
            borrows: ::core::clone::Clone::clone(&self.borrows),
            uninits: ::core::clone::Clone::clone(&self.uninits),
            ever_inits: ::core::clone::Clone::clone(&self.ever_inits),
        }
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for BorrowckDomain {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "BorrowckDomain", "borrows", &self.borrows, "uninits",
            &self.uninits, "ever_inits", &&self.ever_inits)
    }
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for BorrowckDomain {
    #[inline]
    fn eq(&self, other: &BorrowckDomain) -> bool {
        self.borrows == other.borrows && self.uninits == other.uninits &&
            self.ever_inits == other.ever_inits
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for BorrowckDomain {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<BorrowsDomain>;
        let _: ::core::cmp::AssertParamIsEq<MaybeUninitializedPlacesDomain>;
        let _: ::core::cmp::AssertParamIsEq<EverInitializedPlacesDomain>;
    }
}Eq)]
107pub(crate) struct BorrowckDomain {
108    pub(crate) borrows: BorrowsDomain,
109    pub(crate) uninits: MaybeUninitializedPlacesDomain,
110    pub(crate) ever_inits: EverInitializedPlacesDomain,
111}
112
113impl ::std::fmt::Debug for BorrowIndex {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_fmt(format_args!("bw{0}", self.as_u32()))
    }
}rustc_index::newtype_index! {
114    #[orderable]
115    #[debug_format = "bw{}"]
116    pub struct BorrowIndex {}
117}
118
119/// `Borrows` stores the data used in the analyses that track the flow
120/// of borrows.
121///
122/// It uniquely identifies every borrow (`Rvalue::Ref`) by a
123/// `BorrowIndex`, and maps each such index to a `BorrowData`
124/// describing the borrow. These indexes are used for representing the
125/// borrows in compact bitvectors.
126pub struct Borrows<'a, 'tcx> {
127    tcx: TyCtxt<'tcx>,
128    body: &'a Body<'tcx>,
129    borrow_set: &'a BorrowSet<'tcx>,
130    borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
131}
132
133struct OutOfScopePrecomputer<'a, 'tcx> {
134    visited: DenseBitSet<mir::BasicBlock>,
135    visit_stack: Vec<mir::BasicBlock>,
136    body: &'a Body<'tcx>,
137    regioncx: &'a RegionInferenceContext<'tcx>,
138    borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
139}
140
141impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
142    fn compute(
143        body: &Body<'tcx>,
144        regioncx: &RegionInferenceContext<'tcx>,
145        borrow_set: &BorrowSet<'tcx>,
146    ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
147        let mut prec = OutOfScopePrecomputer {
148            visited: DenseBitSet::new_empty(body.basic_blocks.len()),
149            visit_stack: ::alloc::vec::Vec::new()vec![],
150            body,
151            regioncx,
152            borrows_out_of_scope_at_location: FxIndexMap::default(),
153        };
154        for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
155            let borrow_region = borrow_data.region;
156            let location = borrow_data.reserve_location;
157            prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
158        }
159
160        prec.borrows_out_of_scope_at_location
161    }
162
163    fn precompute_borrows_out_of_scope(
164        &mut self,
165        borrow_index: BorrowIndex,
166        borrow_region: RegionVid,
167        first_location: Location,
168    ) {
169        let first_block = first_location.block;
170        let first_bb_data = &self.body.basic_blocks[first_block];
171
172        // This is the first block, we only want to visit it from the creation of the borrow at
173        // `first_location`.
174        let first_lo = first_location.statement_index;
175        let first_hi = first_bb_data.statements.len();
176
177        if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
178            borrow_region,
179            first_block,
180            first_lo,
181            first_hi,
182        ) {
183            let kill_location = Location { block: first_block, statement_index: kill_stmt };
184            // If region does not contain a point at the location, then add to list and skip
185            // successor locations.
186            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/dataflow.rs:186",
                        "rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
                        ::tracing_core::__macro_support::Option::Some(186u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::dataflow"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("borrow {0:?} gets killed at {1:?}",
                                                    borrow_index, kill_location) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
187            self.borrows_out_of_scope_at_location
188                .entry(kill_location)
189                .or_default()
190                .push(borrow_index);
191
192            // The borrow is already dead, there is no need to visit other blocks.
193            return;
194        }
195
196        // The borrow is not dead. Add successor BBs to the work list, if necessary.
197        for succ_bb in first_bb_data.terminator().successors() {
198            if self.visited.insert(succ_bb) {
199                self.visit_stack.push(succ_bb);
200            }
201        }
202
203        // We may end up visiting `first_block` again. This is not an issue: we know at this point
204        // that it does not kill the borrow in the `first_lo..=first_hi` range, so checking the
205        // `0..first_lo` range and the `0..first_hi` range give the same result.
206        while let Some(block) = self.visit_stack.pop() {
207            let bb_data = &self.body[block];
208            let num_stmts = bb_data.statements.len();
209            if let Some(kill_stmt) =
210                self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
211            {
212                let kill_location = Location { block, statement_index: kill_stmt };
213                // If region does not contain a point at the location, then add to list and skip
214                // successor locations.
215                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/dataflow.rs:215",
                        "rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
                        ::tracing_core::__macro_support::Option::Some(215u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::dataflow"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("borrow {0:?} gets killed at {1:?}",
                                                    borrow_index, kill_location) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
216                self.borrows_out_of_scope_at_location
217                    .entry(kill_location)
218                    .or_default()
219                    .push(borrow_index);
220
221                // We killed the borrow, so we do not visit this block's successors.
222                continue;
223            }
224
225            // Add successor BBs to the work list, if necessary.
226            for succ_bb in bb_data.terminator().successors() {
227                if self.visited.insert(succ_bb) {
228                    self.visit_stack.push(succ_bb);
229                }
230            }
231        }
232
233        self.visited.clear();
234    }
235}
236
237// This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`.
238pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
239    body: &Body<'tcx>,
240    regioncx: &RegionInferenceContext<'tcx>,
241    borrow_set: &BorrowSet<'tcx>,
242) -> FxIndexMap<Location, Vec<BorrowIndex>> {
243    OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
244}
245
246struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
247    visited: DenseBitSet<mir::BasicBlock>,
248    visit_stack: Vec<mir::BasicBlock>,
249    body: &'a Body<'tcx>,
250    regioncx: &'a RegionInferenceContext<'tcx>,
251
252    loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
253}
254
255impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
256    fn compute(
257        body: &Body<'tcx>,
258        regioncx: &RegionInferenceContext<'tcx>,
259        borrow_set: &BorrowSet<'tcx>,
260    ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
261        // The in-tree polonius analysis computes loans going out of scope using the
262        // set-of-loans model.
263        let mut prec = PoloniusOutOfScopePrecomputer {
264            visited: DenseBitSet::new_empty(body.basic_blocks.len()),
265            visit_stack: ::alloc::vec::Vec::new()vec![],
266            body,
267            regioncx,
268            loans_out_of_scope_at_location: FxIndexMap::default(),
269        };
270        for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
271            let loan_issued_at = loan_data.reserve_location;
272            prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
273        }
274
275        prec.loans_out_of_scope_at_location
276    }
277
278    /// Loans are in scope while they are live: whether they are contained within any live region.
279    /// In the location-insensitive analysis, a loan will be contained in a region if the issuing
280    /// region can reach it in the subset graph. So this is a reachability problem.
281    fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
282        let first_block = loan_issued_at.block;
283        let first_bb_data = &self.body.basic_blocks[first_block];
284
285        // The first block we visit is the one where the loan is issued, starting from the statement
286        // where the loan is issued: at `loan_issued_at`.
287        let first_lo = loan_issued_at.statement_index;
288        let first_hi = first_bb_data.statements.len();
289
290        if let Some(kill_location) =
291            self.loan_kill_location(loan_idx, loan_issued_at, first_block, first_lo, first_hi)
292        {
293            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/dataflow.rs:293",
                        "rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
                        ::tracing_core::__macro_support::Option::Some(293u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::dataflow"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("loan {0:?} gets killed at {1:?}",
                                                    loan_idx, kill_location) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
294            self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
295
296            // The loan dies within the first block, we're done and can early return.
297            return;
298        }
299
300        // The loan is not dead. Add successor BBs to the work list, if necessary.
301        for succ_bb in first_bb_data.terminator().successors() {
302            if self.visited.insert(succ_bb) {
303                self.visit_stack.push(succ_bb);
304            }
305        }
306
307        // We may end up visiting `first_block` again. This is not an issue: we know at this point
308        // that the loan is not killed in the `first_lo..=first_hi` range, so checking the
309        // `0..first_lo` range and the `0..first_hi` range gives the same result.
310        while let Some(block) = self.visit_stack.pop() {
311            let bb_data = &self.body[block];
312            let num_stmts = bb_data.statements.len();
313            if let Some(kill_location) =
314                self.loan_kill_location(loan_idx, loan_issued_at, block, 0, num_stmts)
315            {
316                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/dataflow.rs:316",
                        "rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
                        ::tracing_core::__macro_support::Option::Some(316u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::dataflow"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("loan {0:?} gets killed at {1:?}",
                                                    loan_idx, kill_location) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
317                self.loans_out_of_scope_at_location
318                    .entry(kill_location)
319                    .or_default()
320                    .push(loan_idx);
321
322                // The loan dies within this block, so we don't need to visit its successors.
323                continue;
324            }
325
326            // Add successor BBs to the work list, if necessary.
327            for succ_bb in bb_data.terminator().successors() {
328                if self.visited.insert(succ_bb) {
329                    self.visit_stack.push(succ_bb);
330                }
331            }
332        }
333
334        self.visited.clear();
335        if !self.visit_stack.is_empty() {
    {
        ::core::panicking::panic_fmt(format_args!("visit stack should be empty"));
    }
};assert!(self.visit_stack.is_empty(), "visit stack should be empty");
336    }
337
338    /// Returns the lowest statement in `start..=end`, where the loan goes out of scope, if any.
339    /// This is the statement where the issuing region can't reach any of the regions that are live
340    /// at this point.
341    fn loan_kill_location(
342        &self,
343        loan_idx: BorrowIndex,
344        loan_issued_at: Location,
345        block: BasicBlock,
346        start: usize,
347        end: usize,
348    ) -> Option<Location> {
349        for statement_index in start..=end {
350            let location = Location { block, statement_index };
351
352            // Check whether the issuing region can reach local regions that are live at this point:
353            // - a loan is always live at its issuing location because it can reach the issuing
354            // region, which is always live at this location.
355            if location == loan_issued_at {
356                continue;
357            }
358
359            // - the loan goes out of scope at `location` if it's not contained within any regions
360            // live at this point.
361            //
362            // FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and `r` is
363            // live at point `q`, then it's guaranteed that `i` would reach `r` at point `q`.
364            // Reachability is location-insensitive, and we could take advantage of that, by jumping
365            // to a further point than just the next statement: we can jump to the furthest point
366            // within the block where `r` is live.
367            if self.regioncx.is_loan_live_at(loan_idx, location) {
368                continue;
369            }
370
371            // No live region is reachable from the issuing region: the loan is killed at this
372            // point.
373            return Some(location);
374        }
375
376        None
377    }
378}
379
380impl<'a, 'tcx> Borrows<'a, 'tcx> {
381    pub fn new(
382        tcx: TyCtxt<'tcx>,
383        body: &'a Body<'tcx>,
384        regioncx: &RegionInferenceContext<'tcx>,
385        borrow_set: &'a BorrowSet<'tcx>,
386    ) -> Self {
387        let borrows_out_of_scope_at_location =
388            if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
389                calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
390            } else {
391                PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
392            };
393        Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
394    }
395
396    /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
397    /// That means they went out of a nonlexical scope
398    fn kill_loans_out_of_scope_at_location(
399        &self,
400        state: &mut <Self as Analysis<'tcx>>::Domain,
401        location: Location,
402    ) {
403        // NOTE: The state associated with a given `location`
404        // reflects the dataflow on entry to the statement.
405        // Iterate over each of the borrows that we've precomputed
406        // to have went out of scope at this location and kill them.
407        //
408        // We are careful always to call this function *before* we
409        // set up the gen-bits for the statement or
410        // terminator. That way, if the effect of the statement or
411        // terminator *does* introduce a new loan of the same
412        // region, then setting that gen-bit will override any
413        // potential kill introduced here.
414        if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
415            state.kill_all(indices.iter().copied());
416        }
417    }
418
419    /// Kill any borrows that conflict with `place`.
420    fn kill_borrows_on_place(
421        &self,
422        state: &mut <Self as Analysis<'tcx>>::Domain,
423        place: Place<'tcx>,
424    ) {
425        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/dataflow.rs:425",
                        "rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
                        ::tracing_core::__macro_support::Option::Some(425u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::dataflow"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("kill_borrows_on_place: place={0:?}",
                                                    place) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("kill_borrows_on_place: place={:?}", place);
426
427        let other_borrows_of_local = self
428            .borrow_set
429            .borrows_on_local(place.local)
430            .map(|bs| bs.iter().copied())
431            .into_flat_iter();
432
433        // If the borrowed place is a local with no projections, all other borrows of this
434        // local must conflict. This is purely an optimization so we don't have to call
435        // `places_conflict` for every borrow.
436        if place.projection.is_empty() {
437            if !self.body.local_decls[place.local].is_ref_to_static() {
438                state.kill_all(other_borrows_of_local);
439            }
440            return;
441        }
442
443        // By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
444        // pair of array indices are not equal, so that when `places_conflict` returns true, we
445        // will be assured that two places being compared definitely denotes the same sets of
446        // locations.
447        let definitely_conflicting_borrows = other_borrows_of_local.filter(|&i| {
448            places_conflict(
449                self.tcx,
450                self.body,
451                self.borrow_set[i].borrowed_place,
452                place,
453                PlaceConflictBias::NoOverlap,
454            )
455        });
456
457        state.kill_all(definitely_conflicting_borrows);
458    }
459}
460
461type BorrowsDomain = MixedBitSet<BorrowIndex>;
462
463/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
464/// - we gen the introduced loans
465/// - we kill loans on locals going out of (regular) scope
466/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
467///   region stops containing the CFG points reachable from the issuing location.
468/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
469///   `a.b.c` when `a` is overwritten.
470impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
471    type Domain = BorrowsDomain;
472
473    const NAME: &'static str = "borrows";
474
475    fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
476        // bottom = nothing is reserved or activated yet;
477        MixedBitSet::new_empty(self.borrow_set.len())
478    }
479
480    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
481        // no borrows of code region_scopes have been taken prior to
482        // function execution, so this method has no effect.
483    }
484
485    fn apply_early_statement_effect(
486        &self,
487        state: &mut Self::Domain,
488        _statement: &mir::Statement<'tcx>,
489        location: Location,
490    ) {
491        self.kill_loans_out_of_scope_at_location(state, location);
492    }
493
494    fn apply_primary_statement_effect(
495        &self,
496        state: &mut Self::Domain,
497        stmt: &mir::Statement<'tcx>,
498        location: Location,
499    ) {
500        match &stmt.kind {
501            mir::StatementKind::Assign((lhs, rhs)) => {
502                if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(_, _, place) = rhs {
503                    if place.ignore_borrow(
504                        self.tcx,
505                        self.body,
506                        &self.borrow_set.locals_state_at_exit(),
507                    ) {
508                        return;
509                    }
510                    let idxs =
511                        self.borrow_set.borrows_at_location(&location).unwrap_or_else(|| {
512                            {
    ::core::panicking::panic_fmt(format_args!("could not find BorrowIndex for location {0:?}",
            location));
};panic!("could not find BorrowIndex for location {location:?}");
513                        });
514
515                    for index in idxs {
516                        state.gen_(*index);
517                    }
518                }
519
520                // Make sure there are no remaining borrows for variables
521                // that are assigned over.
522                self.kill_borrows_on_place(state, *lhs);
523            }
524
525            mir::StatementKind::StorageDead(local) => {
526                // Make sure there are no remaining borrows for locals that
527                // are gone out of scope.
528                self.kill_borrows_on_place(state, Place::from(*local));
529            }
530
531            mir::StatementKind::FakeRead(..)
532            | mir::StatementKind::SetDiscriminant { .. }
533            | mir::StatementKind::StorageLive(..)
534            | mir::StatementKind::PlaceMention(..)
535            | mir::StatementKind::AscribeUserType(..)
536            | mir::StatementKind::Coverage(..)
537            | mir::StatementKind::Intrinsic(..)
538            | mir::StatementKind::ConstEvalCounter
539            | mir::StatementKind::BackwardIncompatibleDropHint { .. }
540            | mir::StatementKind::Nop => {}
541        }
542    }
543
544    fn apply_early_terminator_effect(
545        &self,
546        state: &mut Self::Domain,
547        _terminator: &mir::Terminator<'tcx>,
548        location: Location,
549    ) {
550        self.kill_loans_out_of_scope_at_location(state, location);
551    }
552
553    fn apply_primary_terminator_effect(
554        &self,
555        state: &mut Self::Domain,
556        terminator: &mir::Terminator<'tcx>,
557        _location: Location,
558    ) {
559        if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
560            for op in operands {
561                if let mir::InlineAsmOperand::Out { place: Some(place), .. }
562                | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
563                {
564                    self.kill_borrows_on_place(state, place);
565                }
566            }
567        }
568    }
569}
570
571impl<C> DebugWithContext<C> for BorrowIndex {}