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