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::{
14AccessDepth, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict,
15};
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`. Because it's faster that way than having a
20// single analysis where the domain has three components.
21pub(crate) struct Borrowck<'a, 'tcx> {
22pub(crate) borrows: Borrows<'a, 'tcx>,
23pub(crate) uninits: MaybeUninitializedPlaces<'a, 'tcx>,
24pub(crate) ever_inits: EverInitializedPlaces<'a, 'tcx>,
25}
2627impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
28type Domain = BorrowckDomain;
2930const NAME: &'static str = "borrowck";
3132fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
33BorrowckDomain {
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 }
3940fn 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 }
4445fn apply_early_statement_effect(
46&self,
47 state: &mut Self::Domain,
48 stmt: &mir::Statement<'tcx>,
49 loc: Location,
50 ) {
51self.borrows.apply_early_statement_effect(&mut state.borrows, stmt, loc);
52self.uninits.apply_early_statement_effect(&mut state.uninits, stmt, loc);
53self.ever_inits.apply_early_statement_effect(&mut state.ever_inits, stmt, loc);
54 }
5556fn apply_primary_statement_effect(
57&self,
58 state: &mut Self::Domain,
59 stmt: &mir::Statement<'tcx>,
60 loc: Location,
61 ) {
62self.borrows.apply_primary_statement_effect(&mut state.borrows, stmt, loc);
63self.uninits.apply_primary_statement_effect(&mut state.uninits, stmt, loc);
64self.ever_inits.apply_primary_statement_effect(&mut state.ever_inits, stmt, loc);
65 }
6667fn apply_early_terminator_effect(
68&self,
69 state: &mut Self::Domain,
70 term: &mir::Terminator<'tcx>,
71 loc: Location,
72 ) {
73self.borrows.apply_early_terminator_effect(&mut state.borrows, term, loc);
74self.uninits.apply_early_terminator_effect(&mut state.uninits, term, loc);
75self.ever_inits.apply_early_terminator_effect(&mut state.ever_inits, term, loc);
76 }
7778fn apply_primary_terminator_effect(
79&self,
80 state: &mut Self::Domain,
81 term: &mir::Terminator<'tcx>,
82 loc: Location,
83 ) {
84self.borrows.apply_primary_terminator_effect(&mut state.borrows, term, loc);
85self.uninits.apply_primary_terminator_effect(&mut state.uninits, term, loc);
86self.ever_inits.apply_primary_terminator_effect(&mut state.ever_inits, term, loc);
87 }
8889fn 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}
99100impl JoinSemiLatticefor BorrowckDomain {
101fn 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}
106107/// 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 {
110pub(crate) borrows: BorrowsDomain,
111pub(crate) uninits: MaybeUninitializedPlacesDomain,
112pub(crate) ever_inits: EverInitializedPlacesDomain,
113}
114115impl Clonefor BorrowckDomain {
116fn clone(&self) -> Self {
117Self {
118 borrows: self.borrows.clone(),
119 uninits: self.uninits.clone(),
120 ever_inits: self.ever_inits.clone(),
121 }
122 }
123124fn clone_from(&mut self, source: &Self) {
125self.borrows.clone_from(&source.borrows);
126self.uninits.clone_from(&source.uninits);
127self.ever_inits.clone_from(&source.ever_inits);
128 }
129}
130131#[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{}"]
134pub struct BorrowIndex {}
135}136137/// `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}
150151struct 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}
158159impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
160fn compute(
161 body: &Body<'tcx>,
162 regioncx: &RegionInferenceContext<'tcx>,
163 borrow_set: &BorrowSet<'tcx>,
164 ) -> FxIndexMap<Location, Vec<BorrowIndex>> {
165let mut prec = OutOfScopePrecomputer {
166 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
167 visit_stack: ::alloc::vec::Vec::new()vec![],
168body,
169regioncx,
170 borrows_out_of_scope_at_location: FxIndexMap::default(),
171 };
172for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
173let borrow_region = borrow_data.region;
174let location = borrow_data.reserve_location;
175 prec.precompute_borrows_out_of_scope(borrow_index, borrow_region, location);
176 }
177178prec.borrows_out_of_scope_at_location
179 }
180181fn precompute_borrows_out_of_scope(
182&mut self,
183 borrow_index: BorrowIndex,
184 borrow_region: RegionVid,
185 first_location: Location,
186 ) {
187let first_block = first_location.block;
188let first_bb_data = &self.body.basic_blocks[first_block];
189190// This is the first block, we only want to visit it from the creation of the borrow at
191 // `first_location`.
192let first_lo = first_location.statement_index;
193let first_hi = first_bb_data.statements.len();
194195if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
196borrow_region,
197first_block,
198first_lo,
199first_hi,
200 ) {
201let 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);
205self.borrows_out_of_scope_at_location
206 .entry(kill_location)
207 .or_default()
208 .push(borrow_index);
209210// The borrow is already dead, there is no need to visit other blocks.
211return;
212 }
213214// The borrow is not dead. Add successor BBs to the work list, if necessary.
215for succ_bb in first_bb_data.terminator().successors() {
216if self.visited.insert(succ_bb) {
217self.visit_stack.push(succ_bb);
218 }
219 }
220221// 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.
224while let Some(block) = self.visit_stack.pop() {
225let bb_data = &self.body[block];
226let num_stmts = bb_data.statements.len();
227if let Some(kill_stmt) =
228self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
229 {
230let 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);
234self.borrows_out_of_scope_at_location
235 .entry(kill_location)
236 .or_default()
237 .push(borrow_index);
238239// We killed the borrow, so we do not visit this block's successors.
240continue;
241 }
242243// Add successor BBs to the work list, if necessary.
244for succ_bb in bb_data.terminator().successors() {
245if self.visited.insert(succ_bb) {
246self.visit_stack.push(succ_bb);
247 }
248 }
249 }
250251self.visited.clear();
252 }
253}
254255// 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>> {
261OutOfScopePrecomputer::compute(body, regioncx, borrow_set)
262}
263264struct PoloniusOutOfScopePrecomputer<'a, 'tcx> {
265 visited: DenseBitSet<mir::BasicBlock>,
266 visit_stack: Vec<mir::BasicBlock>,
267 body: &'a Body<'tcx>,
268 regioncx: &'a RegionInferenceContext<'tcx>,
269270 loans_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
271}
272273impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
274fn 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.
281let mut prec = PoloniusOutOfScopePrecomputer {
282 visited: DenseBitSet::new_empty(body.basic_blocks.len()),
283 visit_stack: ::alloc::vec::Vec::new()vec![],
284body,
285regioncx,
286 loans_out_of_scope_at_location: FxIndexMap::default(),
287 };
288for (loan_idx, loan_data) in borrow_set.iter_enumerated() {
289let loan_issued_at = loan_data.reserve_location;
290 prec.precompute_loans_out_of_scope(loan_idx, loan_issued_at);
291 }
292293prec.loans_out_of_scope_at_location
294 }
295296/// 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.
299fn precompute_loans_out_of_scope(&mut self, loan_idx: BorrowIndex, loan_issued_at: Location) {
300let first_block = loan_issued_at.block;
301let first_bb_data = &self.body.basic_blocks[first_block];
302303// 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`.
305let first_lo = loan_issued_at.statement_index;
306let first_hi = first_bb_data.statements.len();
307308if let Some(kill_location) =
309self.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);
312self.loans_out_of_scope_at_location.entry(kill_location).or_default().push(loan_idx);
313314// The loan dies within the first block, we're done and can early return.
315return;
316 }
317318// The loan is not dead. Add successor BBs to the work list, if necessary.
319for succ_bb in first_bb_data.terminator().successors() {
320if self.visited.insert(succ_bb) {
321self.visit_stack.push(succ_bb);
322 }
323 }
324325// 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.
328while let Some(block) = self.visit_stack.pop() {
329let bb_data = &self.body[block];
330let num_stmts = bb_data.statements.len();
331if let Some(kill_location) =
332self.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);
335self.loans_out_of_scope_at_location
336 .entry(kill_location)
337 .or_default()
338 .push(loan_idx);
339340// The loan dies within this block, so we don't need to visit its successors.
341continue;
342 }
343344// Add successor BBs to the work list, if necessary.
345for succ_bb in bb_data.terminator().successors() {
346if self.visited.insert(succ_bb) {
347self.visit_stack.push(succ_bb);
348 }
349 }
350 }
351352self.visited.clear();
353if !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 }
355356/// 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.
359fn 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> {
367for statement_index in start..=end {
368let location = Location { block, statement_index };
369370// 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.
373if location == loan_issued_at {
374continue;
375 }
376377// - 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.
385if self.regioncx.is_loan_live_at(loan_idx, location) {
386continue;
387 }
388389// No live region is reachable from the issuing region: the loan is killed at this
390 // point.
391return Some(location);
392 }
393394None395 }
396}
397398impl<'a, 'tcx> Borrows<'a, 'tcx> {
399pub fn new(
400 tcx: TyCtxt<'tcx>,
401 body: &'a Body<'tcx>,
402 regioncx: &RegionInferenceContext<'tcx>,
403 borrow_set: &'a BorrowSet<'tcx>,
404 ) -> Self {
405let borrows_out_of_scope_at_location =
406if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
407calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set)
408 } else {
409PoloniusOutOfScopePrecomputer::compute(body, regioncx, borrow_set)
410 };
411Borrows { tcx, body, borrow_set, borrows_out_of_scope_at_location }
412 }
413414/// 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
416fn 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.
432if let Some(indices) = self.borrows_out_of_scope_at_location.get(&location) {
433state.kill_all(indices.iter().copied());
434 }
435 }
436437/// Kill any borrows that conflict with `place`.
438fn 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);
444445let other_borrows_of_local = self446 .borrow_set
447 .borrows_on_local(place.local)
448 .map(|bs| bs.iter().copied())
449 .into_flat_iter();
450451// 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.
455let definitely_conflicting_borrows = other_borrows_of_local.filter(|&i| {
456 places_conflict::borrow_conflicts_with_place(
457self.tcx,
458self.body,
459self.borrow_set[i].borrowed_place,
460self.borrow_set[i].kind,
461place.as_ref(),
462 AccessDepth::Deep,
463 PlaceConflictBias::NoOverlap,
464 )
465 });
466467state.kill_all(definitely_conflicting_borrows);
468 }
469}
470471type BorrowsDomain = MixedBitSet<BorrowIndex>;
472473/// 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> {
481type Domain = BorrowsDomain;
482483const NAME: &'static str = "borrows";
484485fn bottom_value(&self, _: &mir::Body<'tcx>) -> Self::Domain {
486// bottom = nothing is reserved or activated yet;
487MixedBitSet::new_empty(self.borrow_set.len())
488 }
489490fn 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}
494495fn apply_early_statement_effect(
496&self,
497 state: &mut Self::Domain,
498 _statement: &mir::Statement<'tcx>,
499 location: Location,
500 ) {
501self.kill_loans_out_of_scope_at_location(state, location);
502 }
503504fn apply_primary_statement_effect(
505&self,
506 state: &mut Self::Domain,
507 stmt: &mir::Statement<'tcx>,
508 location: Location,
509 ) {
510match &stmt.kind {
511 mir::StatementKind::Assign((lhs, rhs)) => {
512if let mir::Rvalue::Ref(_, _, place) | mir::Rvalue::Reborrow(_, _, place) = rhs {
513if place.ignore_borrow(
514self.tcx,
515self.body,
516&self.borrow_set.locals_state_at_exit(),
517 ) {
518return;
519 }
520let idxs =
521self.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 });
524525for index in idxs {
526 state.gen_(*index);
527 }
528 }
529530// Make sure there are no remaining borrows for variables
531 // that are assigned over.
532self.kill_borrows_on_place(state, *lhs);
533 }
534535 mir::StatementKind::StorageDead(local) => {
536// Make sure there are no remaining borrows for locals that
537 // are gone out of scope.
538self.kill_borrows_on_place(state, Place::from(*local));
539 }
540541 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::ConstEvalCounter549 | mir::StatementKind::BackwardIncompatibleDropHint { .. }
550 | mir::StatementKind::Nop => {}
551 }
552 }
553554fn apply_early_terminator_effect(
555&self,
556 state: &mut Self::Domain,
557 _terminator: &mir::Terminator<'tcx>,
558 location: Location,
559 ) {
560self.kill_loans_out_of_scope_at_location(state, location);
561 }
562563fn apply_primary_terminator_effect(
564&self,
565 state: &mut Self::Domain,
566 terminator: &mir::Terminator<'tcx>,
567 _location: Location,
568 ) {
569if let mir::TerminatorKind::InlineAsm { operands, .. } = &terminator.kind {
570for op in operands {
571if let mir::InlineAsmOperand::Out { place: Some(place), .. }
572 | mir::InlineAsmOperand::InOut { out_place: Some(place), .. } = *op
573 {
574self.kill_borrows_on_place(state, place);
575 }
576 }
577 }
578 }
579}
580581impl<C> DebugWithContext<C> for BorrowIndex {}