1use itertools::{Either, Itertools};
2use rustc_data_structures::fx::FxHashSet;
3use rustc_middle::mir::visit::{TyContext, Visitor};
4use rustc_middle::mir::{Body, Local, Location, SourceInfo};
5use rustc_middle::span_bug;
6use rustc_middle::ty::relate::Relate;
7use rustc_middle::ty::{
8GenericArgsRef, Region, RegionUtilitiesExt, RegionVid, Ty, TyCtxt, TypeVisitable,
9};
10use rustc_mir_dataflow::move_paths::MoveData;
11use rustc_mir_dataflow::points::DenseLocationMap;
12use tracing::debug;
1314use super::TypeChecker;
15use crate::constraints::OutlivesConstraintSet;
16use crate::polonius::PoloniusContext;
17use crate::region_infer::values::LivenessValues;
18use crate::universal_regions::UniversalRegions;
1920mod local_use_map;
21mod trace;
2223/// Combines liveness analysis with initialization analysis to
24/// determine which variables are live at which points, both due to
25/// ordinary uses and drops. Returns a set of (ty, location) pairs
26/// that indicate which types must be live at which point in the CFG.
27/// This vector is consumed by `constraint_generation`.
28///
29/// N.B., this computation requires normalization; therefore, it must be
30/// performed before
31pub(super) fn generate<'tcx>(
32 typeck: &mut TypeChecker<'_, 'tcx>,
33 location_map: &DenseLocationMap,
34 move_data: &MoveData<'tcx>,
35) {
36{
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/type_check/liveness/mod.rs:36",
"rustc_borrowck::type_check::liveness",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/type_check/liveness/mod.rs"),
::tracing_core::__macro_support::Option::Some(36u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::type_check::liveness"),
::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!("liveness::generate")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("liveness::generate");
3738let mut free_regions = regions_that_outlive_free_regions(
39typeck.infcx.num_region_vars(),
40&typeck.universal_regions,
41&typeck.constraints.outlives_constraints,
42 );
4344// NLLs can avoid computing some liveness data here because its constraints are
45 // location-insensitive, but that doesn't work in polonius: locals whose type contains a region
46 // that outlives a free region are not necessarily live everywhere in a flow-sensitive setting,
47 // unlike NLLs.
48 // We do record these regions in the polonius context, since they're used to differentiate
49 // relevant and boring locals, which is a key distinction used later in diagnostics.
50if typeck.tcx().sess.opts.unstable_opts.polonius.is_next_enabled() {
51let (_, boring_locals) =
52compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
53typeck.polonius_context.as_mut().unwrap().boring_nll_locals =
54boring_locals.into_iter().collect();
55free_regions = typeck.universal_regions.universal_regions_iter().collect();
56 }
57let (relevant_live_locals, boring_locals) =
58compute_relevant_live_locals(typeck.tcx(), &free_regions, typeck.body);
5960 trace::trace(typeck, location_map, move_data, relevant_live_locals, boring_locals);
6162// Mark regions that should be live where they appear within rvalues or within a call: like
63 // args, regions, and types.
64record_regular_live_regions(
65typeck.tcx(),
66&mut typeck.constraints.liveness_constraints,
67&typeck.universal_regions,
68&mut typeck.polonius_context,
69typeck.body,
70 );
71}
7273// The purpose of `compute_relevant_live_locals` is to define the subset of `Local`
74// variables for which we need to do a liveness computation. We only need
75// to compute whether a variable `X` is live if that variable contains
76// some region `R` in its type where `R` is not known to outlive a free
77// region (i.e., where `R` may be valid for just a subset of the fn body).
78fn compute_relevant_live_locals<'tcx>(
79 tcx: TyCtxt<'tcx>,
80 free_regions: &FxHashSet<RegionVid>,
81 body: &Body<'tcx>,
82) -> (Vec<Local>, Vec<Local>) {
83let (boring_locals, relevant_live_locals): (Vec<_>, Vec<_>) =
84body.local_decls.iter_enumerated().partition_map(|(local, local_decl)| {
85if tcx.all_free_regions_meet(&local_decl.ty, |r| free_regions.contains(&r.as_var())) {
86 Either::Left(local)
87 } else {
88 Either::Right(local)
89 }
90 });
9192{
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/type_check/liveness/mod.rs:92",
"rustc_borrowck::type_check::liveness",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/type_check/liveness/mod.rs"),
::tracing_core::__macro_support::Option::Some(92u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::type_check::liveness"),
::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!("{0} total variables",
body.local_decls.len()) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("{} total variables", body.local_decls.len());
93{
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/type_check/liveness/mod.rs:93",
"rustc_borrowck::type_check::liveness",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/type_check/liveness/mod.rs"),
::tracing_core::__macro_support::Option::Some(93u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::type_check::liveness"),
::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!("{0} variables need liveness",
relevant_live_locals.len()) as
&dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("{} variables need liveness", relevant_live_locals.len());
94{
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/type_check/liveness/mod.rs:94",
"rustc_borrowck::type_check::liveness",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/type_check/liveness/mod.rs"),
::tracing_core::__macro_support::Option::Some(94u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::type_check::liveness"),
::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!("{0} regions outlive free regions",
free_regions.len()) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("{} regions outlive free regions", free_regions.len());
9596 (relevant_live_locals, boring_locals)
97}
9899/// Computes all regions that are (currently) known to outlive free
100/// regions. For these regions, we do not need to compute
101/// liveness, since the outlives constraints will ensure that they
102/// are live over the whole fn body anyhow.
103fn regions_that_outlive_free_regions<'tcx>(
104 num_region_vars: usize,
105 universal_regions: &UniversalRegions<'tcx>,
106 constraint_set: &OutlivesConstraintSet<'tcx>,
107) -> FxHashSet<RegionVid> {
108// Build a graph of the outlives constraints thus far. This is
109 // a reverse graph, so for each constraint `R1: R2` we have an
110 // edge `R2 -> R1`. Therefore, if we find all regions
111 // reachable from each free region, we will have all the
112 // regions that are forced to outlive some free region.
113let rev_constraint_graph = constraint_set.reverse_graph(num_region_vars);
114let fr_static = universal_regions.fr_static;
115let rev_region_graph = rev_constraint_graph.region_graph(constraint_set, fr_static);
116117// Stack for the depth-first search. Start out with all the free regions.
118let mut stack: Vec<_> = universal_regions.universal_regions_iter().collect();
119120// Set of all free regions, plus anything that outlives them. Initially
121 // just contains the free regions.
122let mut outlives_free_region: FxHashSet<_> = stack.iter().cloned().collect();
123124// Do the DFS -- for each thing in the stack, find all things
125 // that outlive it and add them to the set. If they are not,
126 // push them onto the stack for later.
127while let Some(sub_region) = stack.pop() {
128 stack.extend(
129 rev_region_graph
130 .outgoing_regions(sub_region)
131 .filter(|&r| outlives_free_region.insert(r)),
132 );
133 }
134135// Return the final set of things we visited.
136outlives_free_region137}
138139/// Some variables are "regular live" at `location` -- i.e., they may be used later. This means that
140/// all regions appearing in their type must be live at `location`.
141fn record_regular_live_regions<'tcx>(
142 tcx: TyCtxt<'tcx>,
143 liveness_constraints: &mut LivenessValues,
144 universal_regions: &UniversalRegions<'tcx>,
145 polonius_context: &mut Option<PoloniusContext>,
146 body: &Body<'tcx>,
147) {
148let mut visitor =
149LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context };
150for (bb, data) in body.basic_blocks.iter_enumerated() {
151 visitor.visit_basic_block_data(bb, data);
152 }
153}
154155/// Visitor looking for regions that should be live within rvalues or calls.
156struct LiveVariablesVisitor<'a, 'tcx> {
157 tcx: TyCtxt<'tcx>,
158 liveness_constraints: &'a mut LivenessValues,
159 universal_regions: &'a UniversalRegions<'tcx>,
160 polonius_context: &'a mut Option<PoloniusContext>,
161}
162163impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> {
164/// We sometimes have `args` within an rvalue, or within a
165 /// call. Make them live at the location where they appear.
166fn visit_args(&mut self, args: &GenericArgsRef<'tcx>, location: Location) {
167self.record_regions_live_at(*args, location);
168self.super_args(args);
169 }
170171/// We sometimes have `region`s within an rvalue, or within a
172 /// call. Make them live at the location where they appear.
173fn visit_region(&mut self, region: Region<'tcx>, location: Location) {
174self.record_regions_live_at(region, location);
175self.super_region(region);
176 }
177178/// We sometimes have `ty`s within an rvalue, or within a
179 /// call. Make them live at the location where they appear.
180fn visit_ty(&mut self, ty: Ty<'tcx>, ty_context: TyContext) {
181match ty_context {
182 TyContext::ReturnTy(SourceInfo { span, .. })
183 | TyContext::YieldTy(SourceInfo { span, .. })
184 | TyContext::ResumeTy(SourceInfo { span, .. })
185 | TyContext::UserTy(span)
186 | TyContext::LocalDecl { source_info: SourceInfo { span, .. }, .. } => {
187::rustc_middle::util::bug::span_bug_fmt(span,
format_args!("should not be visiting outside of the CFG: {0:?}",
ty_context));span_bug!(span, "should not be visiting outside of the CFG: {:?}", ty_context);
188 }
189 TyContext::Location(location) => {
190self.record_regions_live_at(ty, location);
191 }
192 }
193194self.super_ty(ty);
195 }
196}
197198impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> {
199/// Some variable is "regular live" at `location` -- i.e., it may be used later. This means that
200 /// all regions appearing in the type of `value` must be live at `location`.
201fn record_regions_live_at<T>(&mut self, value: T, location: Location)
202where
203T: TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
204 {
205{
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/type_check/liveness/mod.rs:205",
"rustc_borrowck::type_check::liveness",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/type_check/liveness/mod.rs"),
::tracing_core::__macro_support::Option::Some(205u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::type_check::liveness"),
::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!("record_regions_live_at(value={0:?}, location={1:?})",
value, location) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("record_regions_live_at(value={:?}, location={:?})", value, location);
206self.tcx.for_each_free_region(&value, |live_region| {
207let live_region_vid = live_region.as_var();
208self.liveness_constraints.add_location(live_region_vid, location);
209 });
210211// When using `-Zpolonius=next`, we record the variance of each live region.
212if let Some(polonius_context) = self.polonius_context {
213polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value);
214 }
215 }
216}