Skip to main content

rustc_borrowck/
dataflow.rs

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