Skip to main content

rustc_borrowck/type_check/liveness/
mod.rs

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