1use std::fmt;
23use 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;
1415use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
1617// 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> {
21pub(crate) borrows: Borrows<'a, 'tcx>,
22pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
23pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
24}
2526impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
27type Domain = BorrowckDomain;
2829const NAME: &'static str = "borrowck";
3031fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
32BorrowckDomain {
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 }
3839fn 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 }
4344fn apply_early_statement_effect(
45&self,
46 state: &mut Self::Domain,
47 stmt: &mir::Statement<'tcx>,
48 loc: Location,
49 ) {
50self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
51self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
52self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
53 }
5455fn apply_primary_statement_effect(
56&self,
57 state: &mut Self::Domain,
58 stmt: &mir::Statement<'tcx>,
59 loc: Location,
60 ) {
61self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
62self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
63self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
64 }
6566fn apply_early_terminator_effect(
67&self,
68 state: &mut Self::Domain,
69 term: &mir::Terminator<'tcx>,
70 loc: Location,
71 ) {
72self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
73self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
74self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
75 }
7677fn apply_primary_terminator_effect(
78&self,
79 state: &mut Self::Domain,
80 term: &mir::Terminator<'tcx>,
81 loc: Location,
82 ) {
83self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
84self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
85self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
86 }
8788fn 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}
9899impl JoinSemiLattice for BorrowckDomain {
100fn 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}
105106impl<'tcx, C> DebugWithContext<C> for BorrowckDomain107where
108C: rustc_mir_dataflow::move_paths::HasMoveData<'tcx>,
109{
110fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 f.write_str("borrows: ")?;
112self.borrows.fmt_with(ctxt, f)?;
113 f.write_str(" uninits: ")?;
114self.uninits.fmt_with(ctxt, f)?;
115 f.write_str(" ever_inits: ")?;
116self.ever_inits.fmt_with(ctxt, f)?;
117Ok(())
118 }
119120fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121if self == old {
122return Ok(());
123 }
124125if self.borrows != old.borrows {
126 f.write_str("borrows: ")?;
127self.borrows.fmt_diff_with(&old.borrows, ctxt, f)?;
128 f.write_str("\n")?;
129 }
130131if self.uninits != old.uninits {
132 f.write_str("uninits: ")?;
133self.uninits.fmt_diff_with(&old.uninits, ctxt, f)?;
134 f.write_str("\n")?;
135 }
136137if self.ever_inits != old.ever_inits {
138 f.write_str("ever_inits: ")?;
139self.ever_inits.fmt_diff_with(&old.ever_inits, ctxt, f)?;
140 f.write_str("\n")?;
141 }
142143Ok(())
144 }
145}
146147/// 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 {
150pub(crate) borrows: BorrowsDomain,
151pub(crate) uninits: MaybeUninitializedPlacesDomain,
152pub(crate) ever_inits: EverInitializedPlacesDomain,
153}
154155impl ::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{}"]
158pub struct BorrowIndex {}
159}160161/// `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}
174175struct 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}
182183impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
184fn compute(
185 body: &Body<'tcx>,
186 regioncx: &RegionInferenceContext<'tcx>,
187 borrow_set: &BorrowSet<'tcx>,
188 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
189let mut prec = OutOfScopePrecomputer {
190 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
191 visit_stack: ::alloc::vec::Vec::new()vec![],
192body,
193regioncx,
194 borrows_out_of_scope_at_location: FxIndexMap::default(),
195 };
196for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
197let borrow_region = borrow_data.region;
198let location = borrow_data.reserve_location;
199 prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
200 }
201202prec.borrows_out_of_scope_at_location
203 }
204205fn precompute_borrows_out_of_scope(
206&mut self,
207 borrow_index: BorrowIndex,
208 borrow_region: RegionVid,
209 first_location: Location,
210 ) {
211let first_block = first_location.block;
212let first_bb_data = &self.body.basic_blocks[first_block];
213214// This is the first block, we only want to visit it from the creation of the borrow at
215 // `first_location`.
216let first_lo = first_location.statement_index;
217let first_hi = first_bb_data.statements.len();
218219if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
220borrow_region,
221first_block,
222first_lo,
223first_hi,
224 ) {
225let 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);
229self.borrows_out_of_scope_at_location
230 .entry(kill_location)
231 .or_default()
232 .push(borrow_index);
233234// The borrow is already dead, there is no need to visit other blocks.
235return;
236 }
237238// The borrow is not dead. Add successor BBs to the work list, if necessary.
239for succ_bb in first_bb_data.terminator().successors() {
240if self.visited.insert(succ_bb) {
241self.visit_stack.push(succ_bb);
242 }
243 }
244245// 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.
248while let Some(block) = self.visit_stack.pop() {
249let bb_data = &self.body[block];
250let num_stmts = bb_data.statements.len();
251if let Some(kill_stmt) =
252self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
253 {
254let 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);
258self.borrows_out_of_scope_at_location
259 .entry(kill_location)
260 .or_default()
261 .push(borrow_index);
262263// We killed the borrow, so we do not visit this block's successors.
264continue;
265 }
266267// Add successor BBs to the work list, if necessary.
268for succ_bb in bb_data.terminator().successors() {
269if self.visited.insert(succ_bb) {
270self.visit_stack.push(succ_bb);
271 }
272 }
273 }
274275self.visited.clear();
276 }
277}
278279// 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>> {
285OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
286}
287288struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
289 visited: DenseBitSet<mir::BasicBlock>,
290 visit_stack: Vec<mir::BasicBlock>,
291 body: &'a Body<'tcx>,
292 regioncx: &'a RegionInferenceContext<'tcx>,
293294 loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
295}
296297impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
298fn 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.
305let mut prec = PoloniusOutOfScopePrecomputer {
306 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
307 visit_stack: ::alloc::vec::Vec::new()vec![],
308body,
309regioncx,
310 loans_out_of_scope_at_location: FxIndexMap::default(),
311 };
312for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
313let loan_issued_at = loan_data.reserve_location;
314 prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
315 }
316317prec.loans_out_of_scope_at_location
318 }
319320/// 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.
323fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
324let first_block = loan_issued_at.block;
325let first_bb_data = &self.body.basic_blocks[first_block];
326327// 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`.
329let first_lo = loan_issued_at.statement_index;
330let first_hi = first_bb_data.statements.len();
331332if let Some(kill_location) =
333self.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);
336self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
337338// The loan dies within the first block, we're done and can early return.
339return;
340 }
341342// The loan is not dead. Add successor BBs to the work list, if necessary.
343for succ_bb in first_bb_data.terminator().successors() {
344if self.visited.insert(succ_bb) {
345self.visit_stack.push(succ_bb);
346 }
347 }
348349// 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.
352while let Some(block) = self.visit_stack.pop() {
353let bb_data = &self.body[block];
354let num_stmts = bb_data.statements.len();
355if let Some(kill_location) =
356self.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);
359self.loans_out_of_scope_at_location
360 .entry(kill_location)
361 .or_default()
362 .push(loan_idx);
363364// The loan dies within this block, so we don't need to visit its successors.
365continue;
366 }
367368// Add successor BBs to the work list, if necessary.
369for succ_bb in bb_data.terminator().successors() {
370if self.visited.insert(succ_bb) {
371self.visit_stack.push(succ_bb);
372 }
373 }
374 }
375376self.visited.clear();
377if !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 }
379380/// 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.
383fn 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> {
391for statement_index in start..=end {
392let location = Location { block, statement_index };
393394// 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.
397if location == loan_issued_at {
398continue;
399 }
400401// - 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.
409if self.regioncx.is_loan_live_at(loan_idx, location) {
410continue;
411 }
412413// No live region is reachable from the issuing region: the loan is killed at this
414 // point.
415return Some(location);
416 }
417418None419 }
420}
421422impl<'a, 'tcx> Borrows<'a, 'tcx> {
423pub fn new(
424 tcx: TyCtxt<'tcx>,
425 body: &'a Body<'tcx>,
426 regioncx: &RegionInferenceContext<'tcx>,
427 borrow_set: &'a BorrowSet<'tcx>,
428 ) -> Self {
429let borrows_out_of_scope_at_location =
430if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
431calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
432 } else {
433PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
434 };
435Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
436 }
437438/// 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
440fn 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.
456if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
457state.kill_all(indices.iter().copied());
458 }
459 }
460461/// Kill any borrows that conflict with `place`.
462fn 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);
468469let other_borrows_of_local = self470 .borrow_set
471 .borrows_on_local(place.local)
472 .map(|bs| bs.iter().copied())
473 .into_flat_iter();
474475// 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.
478if place.projection.is_empty() {
479if !self.body.local_decls[place.local].is_ref_to_static() {
480state.kill_all(other_borrows_of_local);
481 }
482return;
483 }
484485// 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.
489let definitely_conflicting_borrows = other_borrows_of_local.filter(|&i| {
490places_conflict(
491self.tcx,
492self.body,
493self.borrow_set[i].borrowed_place,
494place,
495 PlaceConflictBias::NoOverlap,
496 )
497 });
498499state.kill_all(definitely_conflicting_borrows);
500 }
501}
502503type BorrowsDomain = MixedBitSet<BorrowIndex>;
504505/// 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> {
513type Domain = BorrowsDomain;
514515const NAME: &'static str = "borrows";
516517fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
518// bottom = nothing is reserved or activated yet;
519MixedBitSet::new_empty(self.borrow_set.len())
520 }
521522fn 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}
526527fn apply_early_statement_effect(
528&self,
529 state: &mut Self::Domain,
530 _statement: &mir::Statement<'tcx>,
531 location: Location,
532 ) {
533self.kill_loans_out_of_scope_at_location(state, location);
534 }
535536fn apply_primary_statement_effect(
537&self,
538 state: &mut Self::Domain,
539 stmt: &mir::Statement<'tcx>,
540 location: Location,
541 ) {
542match &stmt.kind {
543 mir::StatementKind::Assign((lhs, rhs)) => {
544if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(_, _, place) = rhs {
545if place.ignore_borrow(
546self.tcx,
547self.body,
548&self.borrow_set.locals_state_at_exit(),
549 ) {
550return;
551 }
552let idxs =
553self.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 });
556557for index in idxs {
558 state.gen_(*index);
559 }
560 }
561562// Make sure there are no remaining borrows for variables
563 // that are assigned over.
564self.kill_borrows_on_place(state, *lhs);
565 }
566567 mir::StatementKind::StorageDead(local) => {
568// Make sure there are no remaining borrows for locals that
569 // are gone out of scope.
570self.kill_borrows_on_place(state, Place::from(*local));
571 }
572573 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::ConstEvalCounter581 | mir::StatementKind::BackwardIncompatibleDropHint { .. }
582 | mir::StatementKind::Nop => {}
583 }
584 }
585586fn apply_early_terminator_effect(
587&self,
588 state: &mut Self::Domain,
589 _terminator: &mir::Terminator<'tcx>,
590 location: Location,
591 ) {
592self.kill_loans_out_of_scope_at_location(state, location);
593 }
594595fn apply_primary_terminator_effect(
596&self,
597 state: &mut Self::Domain,
598 terminator: &mir::Terminator<'tcx>,
599 _location: Location,
600 ) {
601if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
602for op in operands {
603if let mir::InlineAsmOperand::Out { place: Some(place), .. }
604 | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
605 {
606self.kill_borrows_on_place(state, place);
607 }
608 }
609 }
610 }
611}
612613impl<C> DebugWithContext<C> for BorrowIndex {}