1use std::fmt;
23use rustc_data_structures::fx::FxIndexMap;
4use rustc_index::bit_set::{DenseBitSet, MixedBitSet};
5use rustc_middle::mir::{
6self, BasicBlock, Body, CallReturnPlaces, Location, Place, TerminatorEdges,
7};
8use rustc_middle::ty::{RegionVid, TyCtxt};
9use rustc_mir_dataflow::fmt::DebugWithContext;
10use rustc_mir_dataflow::impls::{
11 EverInitializedPlaces, EverInitializedPlacesDomain, MaybeUninitializedPlaces,
12 MaybeUninitializedPlacesDomain,
13};
14use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
15use tracing::debug;
1617use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
1819// This analysis is different to most others. Its results aren't computed with
20// `iterate_to_fixpoint`, but are instead composed from the results of three sub-analyses that are
21// computed individually with `iterate_to_fixpoint`.
22pub(crate) struct Borrowck<'a, 'tcx> {
23pub(crate) borrows: Borrows<'a, 'tcx>,
24pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
25pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
26}
2728impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
29type Domain = BorrowckDomain;
3031const NAME: &'static str = "borrowck";
3233fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
34BorrowckDomain {
35 borrows: self.borrows.bottom_value(body),
36 uninits: self.uninits.bottom_value(body),
37 ever_inits: self.ever_inits.bottom_value(body),
38 }
39 }
4041fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _state: &mut Self::Domain) {
42// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
43::core::panicking::panic("internal error: entered unreachable code");unreachable!();
44 }
4546fn apply_early_statement_effect(
47&self,
48 state: &mut Self::Domain,
49 stmt: &mir::Statement<'tcx>,
50 loc: Location,
51 ) {
52self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
53self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
54self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
55 }
5657fn apply_primary_statement_effect(
58&self,
59 state: &mut Self::Domain,
60 stmt: &mir::Statement<'tcx>,
61 loc: Location,
62 ) {
63self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
64self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
65self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
66 }
6768fn apply_early_terminator_effect(
69&self,
70 state: &mut Self::Domain,
71 term: &mir::Terminator<'tcx>,
72 loc: Location,
73 ) {
74self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
75self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
76self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
77 }
7879fn apply_primary_terminator_effect<'mir>(
80&self,
81 state: &mut Self::Domain,
82 term: &'mir mir::Terminator<'tcx>,
83 loc: Location,
84 ) -> TerminatorEdges<'mir, 'tcx> {
85self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
86self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
87self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
8889// This return value doesn't matter. It's only used by `iterate_to_fixpoint`, which this
90 // analysis doesn't use.
91TerminatorEdges::None92 }
9394fn apply_call_return_effect(
95&self,
96 _state: &mut Self::Domain,
97 _block: BasicBlock,
98 _return_places: CallReturnPlaces<'_, 'tcx>,
99 ) {
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}
104105impl JoinSemiLattice for BorrowckDomain {
106fn join(&mut self, _other: &Self) -> bool {
107// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
108::core::panicking::panic("internal error: entered unreachable code");unreachable!();
109 }
110}
111112impl<'tcx, C> DebugWithContext<C> for BorrowckDomain113where
114C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
115{
116fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117f.write_str("borrows: ")?;
118self.borrows.fmt_with(ctxt, f)?;
119f.write_str(" uninits: ")?;
120self.uninits.fmt_with(ctxt, f)?;
121f.write_str(" ever_inits: ")?;
122self.ever_inits.fmt_with(ctxt, f)?;
123Ok(())
124 }
125126fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127if self == old {
128return Ok(());
129 }
130131if self.borrows != old.borrows {
132f.write_str("borrows: ")?;
133self.borrows.fmt_diff_with(&old.borrows, ctxt, f)?;
134f.write_str("\n")?;
135 }
136137if self.uninits != old.uninits {
138f.write_str("uninits: ")?;
139self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?;
140f.write_str("\n")?;
141 }
142143if self.ever_inits != old.ever_inits {
144f.write_str("ever_inits: ")?;
145self.ever_inits.fmt_diff_with(&old.ever_inits, ctxt, f)?;
146f.write_str("\n")?;
147 }
148149Ok(())
150 }
151}
152153/// The transient state of the dataflow analyses used by the borrow checker.
154#[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_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<BorrowsDomain>;
let _: ::core::cmp::AssertParamIsEq<MaybeUninitializedPlacesDomain>;
let _: ::core::cmp::AssertParamIsEq<EverInitializedPlacesDomain>;
}
}Eq)]
155pub(crate) struct BorrowckDomain {
156pub(crate) borrows: BorrowsDomain,
157pub(crate) uninits: MaybeUninitializedPlacesDomain,
158pub(crate) ever_inits: EverInitializedPlacesDomain,
159}
160161impl ::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! {
162#[orderable]
163 #[debug_format = "bw{}"]
164pub struct BorrowIndex {}
165}166167/// `Borrows` stores the data used in the analyses that track the flow
168/// of borrows.
169///
170/// It uniquely identifies every borrow (`Rvalue::Ref`) by a
171/// `BorrowIndex`, and maps each such index to a `BorrowData`
172/// describing the borrow. These indexes are used for representing the
173/// borrows in compact bitvectors.
174pub struct Borrows<'a, 'tcx> {
175 tcx: TyCtxt<'tcx>,
176 body: &'a Body<'tcx>,
177 borrow_set: &'a BorrowSet<'tcx>,
178 borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
179}
180181struct OutOfScopePrecomputer<'a, 'tcx> {
182 visited: DenseBitSet<mir::BasicBlock>,
183 visit_stack: Vec<mir::BasicBlock>,
184 body: &'a Body<'tcx>,
185 regioncx: &'a RegionInferenceContext<'tcx>,
186 borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
187}
188189impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
190fn compute(
191 body: &Body<'tcx>,
192 regioncx: &RegionInferenceContext<'tcx>,
193 borrow_set: &BorrowSet<'tcx>,
194 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
195let mut prec = OutOfScopePrecomputer {
196 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
197 visit_stack: ::alloc::vec::Vec::new()vec![],
198body,
199regioncx,
200 borrows_out_of_scope_at_location: FxIndexMap::default(),
201 };
202for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
203let borrow_region = borrow_data.region;
204let location = borrow_data.reserve_location;
205 prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
206 }
207208prec.borrows_out_of_scope_at_location
209 }
210211fn precompute_borrows_out_of_scope(
212&mut self,
213 borrow_index: BorrowIndex,
214 borrow_region: RegionVid,
215 first_location: Location,
216 ) {
217let first_block = first_location.block;
218let first_bb_data = &self.body.basic_blocks[first_block];
219220// This is the first block, we only want to visit it from the creation of the borrow at
221 // `first_location`.
222let first_lo = first_location.statement_index;
223let first_hi = first_bb_data.statements.len();
224225if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
226borrow_region,
227first_block,
228first_lo,
229first_hi,
230 ) {
231let kill_location = Location { block: first_block, statement_index: kill_stmt };
232// If region does not contain a point at the location, then add to list and skip
233 // successor locations.
234{
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:234",
"rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
::tracing_core::__macro_support::Option::Some(234u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("borrow {0:?} gets killed at {1:?}",
borrow_index, kill_location) as &dyn Value))])
});
} else { ; }
};debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
235self.borrows_out_of_scope_at_location
236 .entry(kill_location)
237 .or_default()
238 .push(borrow_index);
239240// The borrow is already dead, there is no need to visit other blocks.
241return;
242 }
243244// The borrow is not dead. Add successor BBs to the work list, if necessary.
245for succ_bb in first_bb_data.terminator().successors() {
246if self.visited.insert(succ_bb) {
247self.visit_stack.push(succ_bb);
248 }
249 }
250251// We may end up visiting `first_block` again. This is not an issue: we know at this point
252 // that it does not kill the borrow in the `first_lo..=first_hi` range, so checking the
253 // `0..first_lo` range and the `0..first_hi` range give the same result.
254while let Some(block) = self.visit_stack.pop() {
255let bb_data = &self.body[block];
256let num_stmts = bb_data.statements.len();
257if let Some(kill_stmt) =
258self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
259 {
260let kill_location = Location { block, statement_index: kill_stmt };
261// If region does not contain a point at the location, then add to list and skip
262 // successor locations.
263{
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:263",
"rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
::tracing_core::__macro_support::Option::Some(263u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("borrow {0:?} gets killed at {1:?}",
borrow_index, kill_location) as &dyn Value))])
});
} else { ; }
};debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
264self.borrows_out_of_scope_at_location
265 .entry(kill_location)
266 .or_default()
267 .push(borrow_index);
268269// We killed the borrow, so we do not visit this block's successors.
270continue;
271 }
272273// Add successor BBs to the work list, if necessary.
274for succ_bb in bb_data.terminator().successors() {
275if self.visited.insert(succ_bb) {
276self.visit_stack.push(succ_bb);
277 }
278 }
279 }
280281self.visited.clear();
282 }
283}
284285// This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`.
286pub fn calculate_borrows_out_of_scope_at_location<'tcx>(
287 body: &Body<'tcx>,
288 regioncx: &RegionInferenceContext<'tcx>,
289 borrow_set: &BorrowSet<'tcx>,
290) -> FxIndexMap<Location, Vec<BorrowIndex>> {
291OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
292}
293294struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
295 visited: DenseBitSet<mir::BasicBlock>,
296 visit_stack: Vec<mir::BasicBlock>,
297 body: &'a Body<'tcx>,
298 regioncx: &'a RegionInferenceContext<'tcx>,
299300 loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
301}
302303impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
304fn compute(
305 body: &Body<'tcx>,
306 regioncx: &RegionInferenceContext<'tcx>,
307 borrow_set: &BorrowSet<'tcx>,
308 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
309// The in-tree polonius analysis computes loans going out of scope using the
310 // set-of-loans model.
311let mut prec = PoloniusOutOfScopePrecomputer {
312 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
313 visit_stack: ::alloc::vec::Vec::new()vec![],
314body,
315regioncx,
316 loans_out_of_scope_at_location: FxIndexMap::default(),
317 };
318for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
319let loan_issued_at = loan_data.reserve_location;
320 prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
321 }
322323prec.loans_out_of_scope_at_location
324 }
325326/// Loans are in scope while they are live: whether they are contained within any live region.
327 /// In the location-insensitive analysis, a loan will be contained in a region if the issuing
328 /// region can reach it in the subset graph. So this is a reachability problem.
329fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
330let first_block = loan_issued_at.block;
331let first_bb_data = &self.body.basic_blocks[first_block];
332333// The first block we visit is the one where the loan is issued, starting from the statement
334 // where the loan is issued: at `loan_issued_at`.
335let first_lo = loan_issued_at.statement_index;
336let first_hi = first_bb_data.statements.len();
337338if let Some(kill_location) =
339self.loan_kill_location(loan_idx, loan_issued_at, first_block, first_lo, first_hi)
340 {
341{
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:341",
"rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
::tracing_core::__macro_support::Option::Some(341u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("loan {0:?} gets killed at {1:?}",
loan_idx, kill_location) as &dyn Value))])
});
} else { ; }
};debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
342self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
343344// The loan dies within the first block, we're done and can early return.
345return;
346 }
347348// The loan is not dead. Add successor BBs to the work list, if necessary.
349for succ_bb in first_bb_data.terminator().successors() {
350if self.visited.insert(succ_bb) {
351self.visit_stack.push(succ_bb);
352 }
353 }
354355// We may end up visiting `first_block` again. This is not an issue: we know at this point
356 // that the loan is not killed in the `first_lo..=first_hi` range, so checking the
357 // `0..first_lo` range and the `0..first_hi` range gives the same result.
358while let Some(block) = self.visit_stack.pop() {
359let bb_data = &self.body[block];
360let num_stmts = bb_data.statements.len();
361if let Some(kill_location) =
362self.loan_kill_location(loan_idx, loan_issued_at, block, 0, num_stmts)
363 {
364{
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:364",
"rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
::tracing_core::__macro_support::Option::Some(364u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("loan {0:?} gets killed at {1:?}",
loan_idx, kill_location) as &dyn Value))])
});
} else { ; }
};debug!("loan {:?} gets killed at {:?}", loan_idx, kill_location);
365self.loans_out_of_scope_at_location
366 .entry(kill_location)
367 .or_default()
368 .push(loan_idx);
369370// The loan dies within this block, so we don't need to visit its successors.
371continue;
372 }
373374// Add successor BBs to the work list, if necessary.
375for succ_bb in bb_data.terminator().successors() {
376if self.visited.insert(succ_bb) {
377self.visit_stack.push(succ_bb);
378 }
379 }
380 }
381382self.visited.clear();
383if !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");
384 }
385386/// Returns the lowest statement in `start..=end`, where the loan goes out of scope, if any.
387 /// This is the statement where the issuing region can't reach any of the regions that are live
388 /// at this point.
389fn loan_kill_location(
390&self,
391 loan_idx: BorrowIndex,
392 loan_issued_at: Location,
393 block: BasicBlock,
394 start: usize,
395 end: usize,
396 ) -> Option<Location> {
397for statement_index in start..=end {
398let location = Location { block, statement_index };
399400// Check whether the issuing region can reach local regions that are live at this point:
401 // - a loan is always live at its issuing location because it can reach the issuing
402 // region, which is always live at this location.
403if location == loan_issued_at {
404continue;
405 }
406407// - the loan goes out of scope at `location` if it's not contained within any regions
408 // live at this point.
409 //
410 // FIXME: if the issuing region `i` can reach a live region `r` at point `p`, and `r` is
411 // live at point `q`, then it's guaranteed that `i` would reach `r` at point `q`.
412 // Reachability is location-insensitive, and we could take advantage of that, by jumping
413 // to a further point than just the next statement: we can jump to the furthest point
414 // within the block where `r` is live.
415if self.regioncx.is_loan_live_at(loan_idx, location) {
416continue;
417 }
418419// No live region is reachable from the issuing region: the loan is killed at this
420 // point.
421return Some(location);
422 }
423424None425 }
426}
427428impl<'a, 'tcx> Borrows<'a, 'tcx> {
429pub fn new(
430 tcx: TyCtxt<'tcx>,
431 body: &'a Body<'tcx>,
432 regioncx: &RegionInferenceContext<'tcx>,
433 borrow_set: &'a BorrowSet<'tcx>,
434 ) -> Self {
435let borrows_out_of_scope_at_location =
436if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
437calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
438 } else {
439PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
440 };
441Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
442 }
443444/// Add all borrows to the kill set, if those borrows are out of scope at `location`.
445 /// That means they went out of a nonlexical scope
446fn kill_loans_out_of_scope_at_location(
447&self,
448 state: &mut <Self as Analysis<'tcx>>::Domain,
449 location: Location,
450 ) {
451// NOTE: The state associated with a given `location`
452 // reflects the dataflow on entry to the statement.
453 // Iterate over each of the borrows that we've precomputed
454 // to have went out of scope at this location and kill them.
455 //
456 // We are careful always to call this function *before* we
457 // set up the gen-bits for the statement or
458 // terminator. That way, if the effect of the statement or
459 // terminator *does* introduce a new loan of the same
460 // region, then setting that gen-bit will override any
461 // potential kill introduced here.
462if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
463state.kill_all(indices.iter().copied());
464 }
465 }
466467/// Kill any borrows that conflict with `place`.
468fn kill_borrows_on_place(
469&self,
470 state: &mut <Self as Analysis<'tcx>>::Domain,
471 place: Place<'tcx>,
472 ) {
473{
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:473",
"rustc_borrowck::dataflow", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/dataflow.rs"),
::tracing_core::__macro_support::Option::Some(473u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("kill_borrows_on_place: place={0:?}",
place) as &dyn Value))])
});
} else { ; }
};debug!("kill_borrows_on_place: place={:?}", place);
474475let other_borrows_of_local = self476 .borrow_set
477 .local_map
478 .get(&place.local)
479 .into_iter()
480 .flat_map(|bs| bs.iter())
481 .copied();
482483// If the borrowed place is a local with no projections, all other borrows of this
484 // local must conflict. This is purely an optimization so we don't have to call
485 // `places_conflict` for every borrow.
486if place.projection.is_empty() {
487if !self.body.local_decls[place.local].is_ref_to_static() {
488state.kill_all(other_borrows_of_local);
489 }
490return;
491 }
492493// By passing `PlaceConflictBias::NoOverlap`, we conservatively assume that any given
494 // pair of array indices are not equal, so that when `places_conflict` returns true, we
495 // will be assured that two places being compared definitely denotes the same sets of
496 // locations.
497let definitely_conflicting_borrows = other_borrows_of_local.filter(|&i| {
498places_conflict(
499self.tcx,
500self.body,
501self.borrow_set[i].borrowed_place,
502place,
503 PlaceConflictBias::NoOverlap,
504 )
505 });
506507state.kill_all(definitely_conflicting_borrows);
508 }
509}
510511type BorrowsDomain = MixedBitSet<BorrowIndex>;
512513/// Forward dataflow computation of the set of borrows that are in scope at a particular location.
514/// - we gen the introduced loans
515/// - we kill loans on locals going out of (regular) scope
516/// - we kill the loans going out of their region's NLL scope: in NLL terms, the frontier where a
517/// region stops containing the CFG points reachable from the issuing location.
518/// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
519/// `a.b.c` when `a` is overwritten.
520impl<'tcx> rustc_mir_dataflow::Analysis<'tcx> for Borrows<'_, 'tcx> {
521type Domain = BorrowsDomain;
522523const NAME: &'static str = "borrows";
524525fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
526// bottom = nothing is reserved or activated yet;
527MixedBitSet::new_empty(self.borrow_set.len())
528 }
529530fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
531// no borrows of code region_scopes have been taken prior to
532 // function execution, so this method has no effect.
533}
534535fn apply_early_statement_effect(
536&self,
537 state: &mut Self::Domain,
538 _statement: &mir::Statement<'tcx>,
539 location: Location,
540 ) {
541self.kill_loans_out_of_scope_at_location(state, location);
542 }
543544fn apply_primary_statement_effect(
545&self,
546 state: &mut Self::Domain,
547 stmt: &mir::Statement<'tcx>,
548 location: Location,
549 ) {
550match &stmt.kind {
551 mir::StatementKind::Assign(box (lhs, rhs)) => {
552if let mir::Rvalue::Ref(_, _, place) = rhs {
553if place.ignore_borrow(
554self.tcx,
555self.body,
556&self.borrow_set.locals_state_at_exit,
557 ) {
558return;
559 }
560let index = self.borrow_set.get_index_of(&location).unwrap_or_else(|| {
561{
::core::panicking::panic_fmt(format_args!("could not find BorrowIndex for location {0:?}",
location));
};panic!("could not find BorrowIndex for location {location:?}");
562 });
563564state.gen_(index);
565 }
566567// Make sure there are no remaining borrows for variables
568 // that are assigned over.
569self.kill_borrows_on_place(state, *lhs);
570 }
571572 mir::StatementKind::StorageDead(local) => {
573// Make sure there are no remaining borrows for locals that
574 // are gone out of scope.
575self.kill_borrows_on_place(state, Place::from(*local));
576 }
577578 mir::StatementKind::FakeRead(..)
579 | mir::StatementKind::SetDiscriminant { .. }
580 | mir::StatementKind::StorageLive(..)
581 | mir::StatementKind::Retag { .. }
582 | mir::StatementKind::PlaceMention(..)
583 | mir::StatementKind::AscribeUserType(..)
584 | mir::StatementKind::Coverage(..)
585 | mir::StatementKind::Intrinsic(..)
586 | mir::StatementKind::ConstEvalCounter587 | mir::StatementKind::BackwardIncompatibleDropHint { .. }
588 | mir::StatementKind::Nop => {}
589 }
590 }
591592fn apply_early_terminator_effect(
593&self,
594 state: &mut Self::Domain,
595 _terminator: &mir::Terminator<'tcx>,
596 location: Location,
597 ) {
598self.kill_loans_out_of_scope_at_location(state, location);
599 }
600601fn apply_primary_terminator_effect<'mir>(
602&self,
603 state: &mut Self::Domain,
604 terminator: &'mir mir::Terminator<'tcx>,
605 _location: Location,
606 ) -> TerminatorEdges<'mir, 'tcx> {
607if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
608for op in operands {
609if let mir::InlineAsmOperand::Out { place: Some(place), .. }
610 | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
611 {
612self.kill_borrows_on_place(state, place);
613 }
614 }
615 }
616terminator.edges()
617 }
618}
619620impl<C> DebugWithContext<C> for BorrowIndex {}