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