Skip to main content

rustc_borrowck/
nll.rs

1//! The entry point of the NLL borrow checker.
2
3use std::io;
4use std::path::PathBuf;
5use std::rc::Rc;
6use std::str::FromStr;
7
8use polonius_engine::{Algorithm, AllFacts, Output};
9use rustc_data_structures::frozen::Frozen;
10use rustc_hir::find_attr;
11use rustc_index::IndexSlice;
12use rustc_middle::mir::pretty::PrettyPrintMirOptions;
13use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
14use rustc_middle::ty::print::with_no_trimmed_paths;
15use rustc_middle::ty::{self, RegionExt, TyCtxt};
16use rustc_mir_dataflow::move_paths::MoveData;
17use rustc_mir_dataflow::points::DenseLocationMap;
18use rustc_session::config::MirIncludeSpans;
19use tracing::{debug, instrument};
20
21use crate::borrow_set::BorrowSet;
22use crate::consumers::RustcFacts;
23use crate::diagnostics::RegionErrors;
24use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
25use crate::polonius::PoloniusContext;
26use crate::polonius::legacy::{
27    PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
28};
29use crate::region_infer::RegionInferenceContext;
30use crate::type_check::MirTypeckRegionConstraints;
31use crate::type_check::free_region_relations::UniversalRegionRelations;
32use crate::universal_regions::UniversalRegions;
33use crate::{
34    BorrowCheckRootCtxt, BorrowckInferCtxt, ClosureOutlivesSubject, ClosureRegionRequirements,
35    polonius, renumber,
36};
37
38/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
39/// closure requirements to propagate, and any generated errors.
40pub(crate) struct NllOutput<'tcx> {
41    pub regioncx: RegionInferenceContext<'tcx>,
42    pub polonius_input: Option<Box<PoloniusFacts>>,
43    pub polonius_output: Option<Box<PoloniusOutput>>,
44    pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
45    pub nll_errors: RegionErrors<'tcx>,
46
47    /// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
48    /// localized typeck and liveness constraints.
49    pub polonius_context: Option<PoloniusContext>,
50}
51
52/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
53/// regions (e.g., region parameters) declared on the function. That set will need to be given to
54/// `compute_regions`.
55{}
#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("replace_regions_in_mir",
                                    "rustc_borrowck::nll", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_borrowck/src/nll.rs"),
                                    ::tracing_core::__macro_support::Option::Some(55u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_borrowck::nll"),
                                    ::tracing_core::field::FieldSet::new(&[],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{ meta.fields().value_set_all(&[]) })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: UniversalRegions<'tcx> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let def = body.source.def_id().expect_local();
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("event /rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_borrowck/src/nll.rs:63",
                                    "rustc_borrowck::nll", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_borrowck/src/nll.rs"),
                                    ::tracing_core::__macro_support::Option::Some(63u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_borrowck::nll"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("def")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("def");
                                                        NAME.as_str()
                                                    }], ::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(&::tracing::field::debug(&def)
                                                        as &dyn ::tracing::field::Value))])
                        });
                } else { ; }
            };
            let universal_regions = UniversalRegions::new(infcx, def);
            renumber::renumber_mir(infcx, body, promoted);
            if let Some(dumper) = MirDumper::new(infcx.tcx, "renumber", body)
                {
                dumper.dump_mir(body);
            }
            universal_regions
        }
    }
}#[instrument(skip(infcx, body, promoted), level = "debug")]
56pub(crate) fn replace_regions_in_mir<'tcx>(
57    infcx: &BorrowckInferCtxt<'tcx>,
58    body: &mut Body<'tcx>,
59    promoted: &mut IndexSlice<Promoted, Body<'tcx>>,
60) -> UniversalRegions<'tcx> {
61    let def = body.source.def_id().expect_local();
62
63    debug!(?def);
64
65    // Compute named region information. This also renumbers the inputs/outputs.
66    let universal_regions = UniversalRegions::new(infcx, def);
67
68    // Replace all remaining regions with fresh inference variables.
69    renumber::renumber_mir(infcx, body, promoted);
70
71    if let Some(dumper) = MirDumper::new(infcx.tcx, "renumber", body) {
72        dumper.dump_mir(body);
73    }
74
75    universal_regions
76}
77
78/// Computes the closure requirements given the current inference state.
79///
80/// This is intended to be used by before [BorrowCheckRootCtxt::handle_opaque_type_uses]
81/// because applying member constraints may rely on closure requirements.
82/// This is frequently the case of async functions where pretty much everything
83/// happens inside of the inner async block but the opaque only gets constrained
84/// in the parent function.
85pub(crate) fn compute_closure_requirements_modulo_opaques<'tcx>(
86    infcx: &BorrowckInferCtxt<'tcx>,
87    body: &Body<'tcx>,
88    location_map: Rc<DenseLocationMap>,
89    universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
90    constraints: &MirTypeckRegionConstraints<'tcx>,
91) -> Option<ClosureRegionRequirements<'tcx>> {
92    // FIXME(#146079): we shouldn't have to clone all this stuff here.
93    // Computing the region graph should take at least some of it by reference/`Rc`.
94    let lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
95        constraints.clone(),
96        &universal_region_relations,
97        infcx,
98    );
99    let mut regioncx = RegionInferenceContext::new(
100        &infcx,
101        lowered_constraints,
102        universal_region_relations.clone(),
103        location_map,
104    );
105
106    let (closure_region_requirements, _nll_errors) = regioncx.solve(infcx, body, None);
107    closure_region_requirements
108}
109
110/// Computes the (non-lexical) regions from the input MIR.
111///
112/// This may result in errors being reported.
113pub(crate) fn compute_regions<'tcx>(
114    root_cx: &BorrowCheckRootCtxt<'_, 'tcx>,
115    infcx: &BorrowckInferCtxt<'tcx>,
116    body: &Body<'tcx>,
117    location_table: &PoloniusLocationTable,
118    move_data: &MoveData<'tcx>,
119    borrow_set: &BorrowSet<'tcx>,
120    location_map: Rc<DenseLocationMap>,
121    universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
122    constraints: MirTypeckRegionConstraints<'tcx>,
123    mut polonius_facts: Option<AllFacts<RustcFacts>>,
124    mut polonius_context: Option<PoloniusContext>,
125) -> NllOutput<'tcx> {
126    let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
127        || infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
128
129    let mut lowered_constraints = compute_sccs_applying_placeholder_outlives_constraints(
130        constraints,
131        &universal_region_relations,
132        infcx,
133    );
134
135    // If requested, emit legacy polonius facts.
136    polonius::legacy::emit_facts(
137        &mut polonius_facts,
138        infcx.tcx,
139        location_table,
140        body,
141        borrow_set,
142        move_data,
143        &universal_region_relations,
144        &lowered_constraints,
145    );
146
147    let num_points = location_map.num_points();
148
149    // If requested for `-Zpolonius=next`, compute loan liveness information.
150    // This is done prior to `RegionInferenceContext::new`, because we may add
151    // additional liveness constraints.
152    if let Some(polonius_context) = polonius_context.as_mut() {
153        let _timer = infcx.tcx.prof.generic_activity("borrowck_polonius_loan_liveness");
154        polonius_context.compute_loan_liveness(
155            &mut lowered_constraints.liveness_constraints,
156            lowered_constraints.outlives_constraints.outlives().iter().copied(),
157            &universal_region_relations.universal_regions,
158            body,
159            borrow_set,
160            num_points,
161        );
162    }
163
164    let mut regioncx = RegionInferenceContext::new(
165        infcx,
166        lowered_constraints,
167        universal_region_relations,
168        location_map,
169    );
170
171    // If requested: dump NLL facts, and run legacy polonius analysis.
172    let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| {
173        if infcx.tcx.sess.opts.unstable_opts.nll_facts {
174            let def_id = body.source.def_id();
175            let def_path = infcx.tcx.def_path(def_id);
176            let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir)
177                .join(def_path.to_filename_friendly_no_crate());
178            polonius_facts.write_to_dir(dir_path, location_table).unwrap();
179        }
180
181        if polonius_output {
182            let algorithm = infcx.tcx.env_var("POLONIUS_ALGORITHM").unwrap_or("Hybrid");
183            let algorithm = Algorithm::from_str(algorithm).unwrap();
184            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_borrowck/src/nll.rs:184",
                        "rustc_borrowck::nll", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_borrowck/src/nll.rs"),
                        ::tracing_core::__macro_support::Option::Some(184u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::nll"),
                        ::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!("compute_regions: using polonius algorithm {0:?}",
                                                    algorithm) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("compute_regions: using polonius algorithm {:?}", algorithm);
185            let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis");
186            Some(Box::new(Output::compute(polonius_facts, algorithm, false)))
187        } else {
188            None
189        }
190    });
191
192    // Solve the region constraints.
193    let (closure_region_requirements, nll_errors) =
194        regioncx.solve(infcx, body, polonius_output.clone());
195
196    NllOutput {
197        regioncx,
198        polonius_input: polonius_facts.map(Box::new),
199        polonius_output,
200        opt_closure_req: closure_region_requirements,
201        nll_errors,
202        polonius_context,
203    }
204}
205
206/// `-Zdump-mir=nll` dumps MIR annotated with NLL specific information:
207/// - free regions
208/// - inferred region values
209/// - region liveness
210/// - inference constraints and their causes
211///
212/// As well as graphviz `.dot` visualizations of:
213/// - the region constraints graph
214/// - the region SCC graph
215pub(super) fn dump_nll_mir<'tcx>(
216    infcx: &BorrowckInferCtxt<'tcx>,
217    body: &Body<'tcx>,
218    regioncx: &RegionInferenceContext<'tcx>,
219    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
220    borrow_set: &BorrowSet<'tcx>,
221) {
222    let tcx = infcx.tcx;
223    let Some(dumper) = MirDumper::new(tcx, "nll", body) else { return };
224
225    // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
226    // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
227    // they're always disabled in mir-opt tests to make working with blessed dumps easier.
228    let options = PrettyPrintMirOptions {
229        include_extra_comments: #[allow(non_exhaustive_omitted_patterns)] match infcx.tcx.sess.opts.unstable_opts.mir_include_spans
    {
    MirIncludeSpans::On | MirIncludeSpans::Nll => true,
    _ => false,
}matches!(
230            infcx.tcx.sess.opts.unstable_opts.mir_include_spans,
231            MirIncludeSpans::On | MirIncludeSpans::Nll
232        ),
233    };
234
235    let extra_data = &|pass_where, out: &mut dyn std::io::Write| {
236        emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
237    };
238
239    let dumper = dumper.set_extra_data(extra_data).set_options(options);
240
241    dumper.dump_mir(body);
242
243    // Also dump the region constraint graph as a graphviz file.
244    let _ = try {
245        let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
246        regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
247    };
248
249    // Also dump the region constraint SCC graph as a graphviz file.
250    let _ = try {
251        let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
252        regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
253    };
254}
255
256/// Produces the actual NLL MIR sections to emit during the dumping process.
257pub(crate) fn emit_nll_mir<'tcx>(
258    tcx: TyCtxt<'tcx>,
259    regioncx: &RegionInferenceContext<'tcx>,
260    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
261    borrow_set: &BorrowSet<'tcx>,
262    pass_where: PassWhere,
263    out: &mut dyn io::Write,
264) -> io::Result<()> {
265    match pass_where {
266        // Before the CFG, dump out the values for each region variable.
267        PassWhere::BeforeCFG => {
268            regioncx.dump_mir(tcx, out)?;
269            out.write_fmt(format_args!("|\n"))writeln!(out, "|")?;
270
271            if let Some(closure_region_requirements) = closure_region_requirements {
272                out.write_fmt(format_args!("| Free Region Constraints\n"))writeln!(out, "| Free Region Constraints")?;
273                for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
274                    out.write_fmt(format_args!("| {0}\n", msg))writeln!(out, "| {msg}")
275                })?;
276                out.write_fmt(format_args!("|\n"))writeln!(out, "|")?;
277            }
278
279            if borrow_set.len() > 0 {
280                out.write_fmt(format_args!("| Borrows\n"))writeln!(out, "| Borrows")?;
281                for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() {
282                    out.write_fmt(format_args!("| {0:?}: issued at {1:?} in {2:?}\n", borrow_idx,
        borrow_data.reserve_location, borrow_data.region))writeln!(
283                        out,
284                        "| {:?}: issued at {:?} in {:?}",
285                        borrow_idx, borrow_data.reserve_location, borrow_data.region
286                    )?;
287                }
288                out.write_fmt(format_args!("|\n"))writeln!(out, "|")?;
289            }
290        }
291
292        PassWhere::BeforeLocation(_) => {}
293
294        PassWhere::AfterTerminator(_) => {}
295
296        PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
297    }
298    Ok(())
299}
300
301pub(super) fn dump_annotation<'tcx>(
302    infcx: &BorrowckInferCtxt<'tcx>,
303    body: &Body<'tcx>,
304    regioncx: &RegionInferenceContext<'tcx>,
305    closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
306) {
307    let tcx = infcx.tcx;
308    let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
309    if !{
        {
            'done:
                {
                for i in
                    ::rustc_attr_ir::HasAttrs::get_attrs(base_def_id, &tcx) {
                    #[allow(unused_imports)]
                    use ::rustc_attr_ir::AttributeKind::*;
                    let i: &::rustc_attr_ir::Attribute = i;
                    match i {
                        ::rustc_attr_ir::Attribute::Parsed(RustcRegions) => {
                            break 'done Some(());
                        }
                        ::rustc_attr_ir::Attribute::Unparsed(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }
    }.is_some()find_attr!(tcx, base_def_id, RustcRegions) {
310        return;
311    }
312
313    // When the enclosing function is tagged with `#[rustc_regions]`,
314    // we dump out various bits of state as warnings. This is useful
315    // for verifying that the compiler is behaving as expected. These
316    // warnings focus on the closure region requirements -- for
317    // viewing the intraprocedural state, the -Zdump-mir output is
318    // better.
319
320    let def_span = tcx.def_span(body.source.def_id());
321    let err = if let Some(closure_region_requirements) = closure_region_requirements {
322        let mut err = infcx.dcx().struct_span_note(def_span, "external requirements");
323
324        regioncx.annotate(tcx, &mut err);
325
326        err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("number of external vids: {0}",
                closure_region_requirements.num_external_vids))
    })format!(
327            "number of external vids: {}",
328            closure_region_requirements.num_external_vids
329        ));
330
331        // Dump the region constraints we are imposing *between* those
332        // newly created variables.
333        for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
334            err.note(msg);
335            Ok(())
336        })
337        .unwrap();
338
339        err
340    } else {
341        let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements");
342        regioncx.annotate(tcx, &mut err);
343        err
344    };
345
346    // FIXME(@lcnr): We currently don't dump the inferred hidden types here.
347    err.emit();
348}
349
350fn for_each_region_constraint<'tcx>(
351    tcx: TyCtxt<'tcx>,
352    closure_region_requirements: &ClosureRegionRequirements<'tcx>,
353    with_msg: &mut dyn FnMut(String) -> io::Result<()>,
354) -> io::Result<()> {
355    for req in &closure_region_requirements.outlives_requirements {
356        let subject = match req.subject {
357            ClosureOutlivesSubject::Region(subject) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0:?}", subject))
    })format!("{subject:?}"),
358            ClosureOutlivesSubject::Ty(ty) => {
359                {
    let _guard = NoTrimmedGuard::new();
    ::alloc::__export::must_use({
            ::alloc::fmt::format(format_args!("{0}",
                    ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid))))
        })
}with_no_trimmed_paths!(format!(
360                    "{}",
361                    ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid))
362                ))
363            }
364        };
365        with_msg(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("where {0}: {1:?}", subject,
                req.outlived_free_region))
    })format!("where {}: {:?}", subject, req.outlived_free_region,))?;
366    }
367    Ok(())
368}
369
370pub(crate) trait ConstraintDescription {
371    fn description(&self) -> &'static str;
372}