• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 //! The entry point of the NLL borrow checker.
4 
5 use rustc_data_structures::fx::FxIndexMap;
6 use rustc_hir::def_id::LocalDefId;
7 use rustc_index::IndexSlice;
8 use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere};
9 use rustc_middle::mir::{
10     Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted,
11     START_BLOCK,
12 };
13 use rustc_middle::ty::{self, OpaqueHiddenType, TyCtxt};
14 use rustc_span::symbol::sym;
15 use std::env;
16 use std::io;
17 use std::path::PathBuf;
18 use std::rc::Rc;
19 use std::str::FromStr;
20 
21 use polonius_engine::{Algorithm, Output};
22 
23 use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
24 use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
25 use rustc_mir_dataflow::ResultsCursor;
26 
27 use crate::{
28     borrow_set::BorrowSet,
29     constraint_generation,
30     consumers::ConsumerOptions,
31     diagnostics::RegionErrors,
32     facts::{AllFacts, AllFactsExt, RustcFacts},
33     invalidation,
34     location::LocationTable,
35     region_infer::{values::RegionValueElements, RegionInferenceContext},
36     renumber,
37     type_check::{self, MirTypeckRegionConstraints, MirTypeckResults},
38     universal_regions::UniversalRegions,
39     BorrowckInferCtxt, Upvar,
40 };
41 
42 pub type PoloniusOutput = Output<RustcFacts>;
43 
44 /// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
45 /// closure requirements to propagate, and any generated errors.
46 pub(crate) struct NllOutput<'tcx> {
47     pub regioncx: RegionInferenceContext<'tcx>,
48     pub opaque_type_values: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
49     pub polonius_input: Option<Box<AllFacts>>,
50     pub polonius_output: Option<Rc<PoloniusOutput>>,
51     pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
52     pub nll_errors: RegionErrors<'tcx>,
53 }
54 
55 /// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
56 /// regions (e.g., region parameters) declared on the function. That set will need to be given to
57 /// `compute_regions`.
58 #[instrument(skip(infcx, param_env, body, promoted), level = "debug")]
replace_regions_in_mir<'tcx>( infcx: &BorrowckInferCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, body: &mut Body<'tcx>, promoted: &mut IndexSlice<Promoted, Body<'tcx>>, ) -> UniversalRegions<'tcx>59 pub(crate) fn replace_regions_in_mir<'tcx>(
60     infcx: &BorrowckInferCtxt<'_, 'tcx>,
61     param_env: ty::ParamEnv<'tcx>,
62     body: &mut Body<'tcx>,
63     promoted: &mut IndexSlice<Promoted, Body<'tcx>>,
64 ) -> UniversalRegions<'tcx> {
65     let def = body.source.def_id().expect_local();
66 
67     debug!(?def);
68 
69     // Compute named region information. This also renumbers the inputs/outputs.
70     let universal_regions = UniversalRegions::new(infcx, def, param_env);
71 
72     // Replace all remaining regions with fresh inference variables.
73     renumber::renumber_mir(infcx, body, promoted);
74 
75     dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
76 
77     universal_regions
78 }
79 
80 // This function populates an AllFacts instance with base facts related to
81 // MovePaths and needed for the move analysis.
populate_polonius_move_facts( all_facts: &mut AllFacts, move_data: &MoveData<'_>, location_table: &LocationTable, body: &Body<'_>, )82 fn populate_polonius_move_facts(
83     all_facts: &mut AllFacts,
84     move_data: &MoveData<'_>,
85     location_table: &LocationTable,
86     body: &Body<'_>,
87 ) {
88     all_facts
89         .path_is_var
90         .extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
91 
92     for (child, move_path) in move_data.move_paths.iter_enumerated() {
93         if let Some(parent) = move_path.parent {
94             all_facts.child_path.push((child, parent));
95         }
96     }
97 
98     let fn_entry_start =
99         location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
100 
101     // initialized_at
102     for init in move_data.inits.iter() {
103         match init.location {
104             InitLocation::Statement(location) => {
105                 let block_data = &body[location.block];
106                 let is_terminator = location.statement_index == block_data.statements.len();
107 
108                 if is_terminator && init.kind == InitKind::NonPanicPathOnly {
109                     // We are at the terminator of an init that has a panic path,
110                     // and where the init should not happen on panic
111 
112                     for successor in block_data.terminator().successors() {
113                         if body[successor].is_cleanup {
114                             continue;
115                         }
116 
117                         // The initialization happened in (or rather, when arriving at)
118                         // the successors, but not in the unwind block.
119                         let first_statement = Location { block: successor, statement_index: 0 };
120                         all_facts
121                             .path_assigned_at_base
122                             .push((init.path, location_table.start_index(first_statement)));
123                     }
124                 } else {
125                     // In all other cases, the initialization just happens at the
126                     // midpoint, like any other effect.
127                     all_facts
128                         .path_assigned_at_base
129                         .push((init.path, location_table.mid_index(location)));
130                 }
131             }
132             // Arguments are initialized on function entry
133             InitLocation::Argument(local) => {
134                 assert!(body.local_kind(local) == LocalKind::Arg);
135                 all_facts.path_assigned_at_base.push((init.path, fn_entry_start));
136             }
137         }
138     }
139 
140     for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
141         if body.local_kind(local) != LocalKind::Arg {
142             // Non-arguments start out deinitialised; we simulate this with an
143             // initial move:
144             all_facts.path_moved_at_base.push((path, fn_entry_start));
145         }
146     }
147 
148     // moved_out_at
149     // deinitialisation is assumed to always happen!
150     all_facts
151         .path_moved_at_base
152         .extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
153 }
154 
155 /// Computes the (non-lexical) regions from the input MIR.
156 ///
157 /// This may result in errors being reported.
compute_regions<'cx, 'tcx>( infcx: &BorrowckInferCtxt<'_, 'tcx>, universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, promoted: &IndexSlice<Promoted, Body<'tcx>>, location_table: &LocationTable, param_env: ty::ParamEnv<'tcx>, flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, upvars: &[Upvar<'tcx>], consumer_options: Option<ConsumerOptions>, ) -> NllOutput<'tcx>158 pub(crate) fn compute_regions<'cx, 'tcx>(
159     infcx: &BorrowckInferCtxt<'_, 'tcx>,
160     universal_regions: UniversalRegions<'tcx>,
161     body: &Body<'tcx>,
162     promoted: &IndexSlice<Promoted, Body<'tcx>>,
163     location_table: &LocationTable,
164     param_env: ty::ParamEnv<'tcx>,
165     flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
166     move_data: &MoveData<'tcx>,
167     borrow_set: &BorrowSet<'tcx>,
168     upvars: &[Upvar<'tcx>],
169     consumer_options: Option<ConsumerOptions>,
170 ) -> NllOutput<'tcx> {
171     let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
172         || infcx.tcx.sess.opts.unstable_opts.polonius;
173     let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
174         || infcx.tcx.sess.opts.unstable_opts.polonius;
175     let mut all_facts =
176         (polonius_input || AllFacts::enabled(infcx.tcx)).then_some(AllFacts::default());
177 
178     let universal_regions = Rc::new(universal_regions);
179 
180     let elements = &Rc::new(RegionValueElements::new(&body));
181 
182     // Run the MIR type-checker.
183     let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
184         type_check::type_check(
185             infcx,
186             param_env,
187             body,
188             promoted,
189             &universal_regions,
190             location_table,
191             borrow_set,
192             &mut all_facts,
193             flow_inits,
194             move_data,
195             elements,
196             upvars,
197             polonius_input,
198         );
199 
200     if let Some(all_facts) = &mut all_facts {
201         let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation");
202         all_facts.universal_region.extend(universal_regions.universal_regions());
203         populate_polonius_move_facts(all_facts, move_data, location_table, &body);
204 
205         // Emit universal regions facts, and their relations, for Polonius.
206         //
207         // 1: universal regions are modeled in Polonius as a pair:
208         // - the universal region vid itself.
209         // - a "placeholder loan" associated to this universal region. Since they don't exist in
210         //   the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
211         //   added to the existing number of loans, as if they succeeded them in the set.
212         //
213         let borrow_count = borrow_set.len();
214         debug!(
215             "compute_regions: polonius placeholders, num_universals={}, borrow_count={}",
216             universal_regions.len(),
217             borrow_count
218         );
219 
220         for universal_region in universal_regions.universal_regions() {
221             let universal_region_idx = universal_region.index();
222             let placeholder_loan_idx = borrow_count + universal_region_idx;
223             all_facts.placeholder.push((universal_region, placeholder_loan_idx.into()));
224         }
225 
226         // 2: the universal region relations `outlives` constraints are emitted as
227         //  `known_placeholder_subset` facts.
228         for (fr1, fr2) in universal_region_relations.known_outlives() {
229             if fr1 != fr2 {
230                 debug!(
231                     "compute_regions: emitting polonius `known_placeholder_subset` \
232                      fr1={:?}, fr2={:?}",
233                     fr1, fr2
234                 );
235                 all_facts.known_placeholder_subset.push((fr1, fr2));
236             }
237         }
238     }
239 
240     // Create the region inference context, taking ownership of the
241     // region inference data that was contained in `infcx`, and the
242     // base constraints generated by the type-check.
243     let var_origins = infcx.get_region_var_origins();
244     let MirTypeckRegionConstraints {
245         placeholder_indices,
246         placeholder_index_to_region: _,
247         mut liveness_constraints,
248         outlives_constraints,
249         member_constraints,
250         universe_causes,
251         type_tests,
252     } = constraints;
253     let placeholder_indices = Rc::new(placeholder_indices);
254 
255     constraint_generation::generate_constraints(
256         infcx,
257         &mut liveness_constraints,
258         &mut all_facts,
259         location_table,
260         &body,
261         borrow_set,
262     );
263 
264     let mut regioncx = RegionInferenceContext::new(
265         infcx,
266         var_origins,
267         universal_regions,
268         placeholder_indices,
269         universal_region_relations,
270         outlives_constraints,
271         member_constraints,
272         universe_causes,
273         type_tests,
274         liveness_constraints,
275         elements,
276     );
277 
278     // Generate various additional constraints.
279     invalidation::generate_invalidates(infcx.tcx, &mut all_facts, location_table, body, borrow_set);
280 
281     let def_id = body.source.def_id();
282 
283     // Dump facts if requested.
284     let polonius_output = all_facts.as_ref().and_then(|all_facts| {
285         if infcx.tcx.sess.opts.unstable_opts.nll_facts {
286             let def_path = infcx.tcx.def_path(def_id);
287             let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir)
288                 .join(def_path.to_filename_friendly_no_crate());
289             all_facts.write_to_dir(dir_path, location_table).unwrap();
290         }
291 
292         if polonius_output {
293             let algorithm =
294                 env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid"));
295             let algorithm = Algorithm::from_str(&algorithm).unwrap();
296             debug!("compute_regions: using polonius algorithm {:?}", algorithm);
297             let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis");
298             Some(Rc::new(Output::compute(&all_facts, algorithm, false)))
299         } else {
300             None
301         }
302     });
303 
304     // Solve the region constraints.
305     let (closure_region_requirements, nll_errors) =
306         regioncx.solve(infcx, param_env, &body, polonius_output.clone());
307 
308     if !nll_errors.is_empty() {
309         // Suppress unhelpful extra errors in `infer_opaque_types`.
310         infcx.set_tainted_by_errors(infcx.tcx.sess.delay_span_bug(
311             body.span,
312             "`compute_regions` tainted `infcx` with errors but did not emit any errors",
313         ));
314     }
315 
316     let remapped_opaque_tys = regioncx.infer_opaque_types(&infcx, opaque_type_values);
317 
318     NllOutput {
319         regioncx,
320         opaque_type_values: remapped_opaque_tys,
321         polonius_input: all_facts.map(Box::new),
322         polonius_output,
323         opt_closure_req: closure_region_requirements,
324         nll_errors,
325     }
326 }
327 
dump_mir_results<'tcx>( infcx: &BorrowckInferCtxt<'_, 'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, )328 pub(super) fn dump_mir_results<'tcx>(
329     infcx: &BorrowckInferCtxt<'_, 'tcx>,
330     body: &Body<'tcx>,
331     regioncx: &RegionInferenceContext<'tcx>,
332     closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
333 ) {
334     if !dump_enabled(infcx.tcx, "nll", body.source.def_id()) {
335         return;
336     }
337 
338     dump_mir(infcx.tcx, false, "nll", &0, body, |pass_where, out| {
339         match pass_where {
340             // Before the CFG, dump out the values for each region variable.
341             PassWhere::BeforeCFG => {
342                 regioncx.dump_mir(infcx.tcx, out)?;
343                 writeln!(out, "|")?;
344 
345                 if let Some(closure_region_requirements) = closure_region_requirements {
346                     writeln!(out, "| Free Region Constraints")?;
347                     for_each_region_constraint(
348                         infcx.tcx,
349                         closure_region_requirements,
350                         &mut |msg| writeln!(out, "| {}", msg),
351                     )?;
352                     writeln!(out, "|")?;
353                 }
354             }
355 
356             PassWhere::BeforeLocation(_) => {}
357 
358             PassWhere::AfterTerminator(_) => {}
359 
360             PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
361         }
362         Ok(())
363     });
364 
365     // Also dump the inference graph constraints as a graphviz file.
366     let _: io::Result<()> = try {
367         let mut file = create_dump_file(infcx.tcx, "regioncx.all.dot", false, "nll", &0, body)?;
368         regioncx.dump_graphviz_raw_constraints(&mut file)?;
369     };
370 
371     // Also dump the inference graph constraints as a graphviz file.
372     let _: io::Result<()> = try {
373         let mut file = create_dump_file(infcx.tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
374         regioncx.dump_graphviz_scc_constraints(&mut file)?;
375     };
376 }
377 
378 #[allow(rustc::diagnostic_outside_of_impl)]
379 #[allow(rustc::untranslatable_diagnostic)]
dump_annotation<'tcx>( infcx: &BorrowckInferCtxt<'_, 'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>, errors: &mut crate::error::BorrowckErrors<'tcx>, )380 pub(super) fn dump_annotation<'tcx>(
381     infcx: &BorrowckInferCtxt<'_, 'tcx>,
382     body: &Body<'tcx>,
383     regioncx: &RegionInferenceContext<'tcx>,
384     closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
385     opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
386     errors: &mut crate::error::BorrowckErrors<'tcx>,
387 ) {
388     let tcx = infcx.tcx;
389     let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
390     if !tcx.has_attr(base_def_id, sym::rustc_regions) {
391         return;
392     }
393 
394     // When the enclosing function is tagged with `#[rustc_regions]`,
395     // we dump out various bits of state as warnings. This is useful
396     // for verifying that the compiler is behaving as expected. These
397     // warnings focus on the closure region requirements -- for
398     // viewing the intraprocedural state, the -Zdump-mir output is
399     // better.
400 
401     let def_span = tcx.def_span(body.source.def_id());
402     let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
403         let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "external requirements");
404 
405         regioncx.annotate(tcx, &mut err);
406 
407         err.note(format!(
408             "number of external vids: {}",
409             closure_region_requirements.num_external_vids
410         ));
411 
412         // Dump the region constraints we are imposing *between* those
413         // newly created variables.
414         for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
415             err.note(msg);
416             Ok(())
417         })
418         .unwrap();
419 
420         err
421     } else {
422         let mut err = tcx.sess.diagnostic().span_note_diag(def_span, "no external requirements");
423         regioncx.annotate(tcx, &mut err);
424 
425         err
426     };
427 
428     if !opaque_type_values.is_empty() {
429         err.note(format!("Inferred opaque type values:\n{:#?}", opaque_type_values));
430     }
431 
432     errors.buffer_non_error_diag(err);
433 }
434 
for_each_region_constraint<'tcx>( tcx: TyCtxt<'tcx>, closure_region_requirements: &ClosureRegionRequirements<'tcx>, with_msg: &mut dyn FnMut(String) -> io::Result<()>, ) -> io::Result<()>435 fn for_each_region_constraint<'tcx>(
436     tcx: TyCtxt<'tcx>,
437     closure_region_requirements: &ClosureRegionRequirements<'tcx>,
438     with_msg: &mut dyn FnMut(String) -> io::Result<()>,
439 ) -> io::Result<()> {
440     for req in &closure_region_requirements.outlives_requirements {
441         let subject = match req.subject {
442             ClosureOutlivesSubject::Region(subject) => format!("{:?}", subject),
443             ClosureOutlivesSubject::Ty(ty) => {
444                 format!("{:?}", ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid)))
445             }
446         };
447         with_msg(format!("where {}: {:?}", subject, req.outlived_free_region,))?;
448     }
449     Ok(())
450 }
451 
452 pub(crate) trait ConstraintDescription {
description(&self) -> &'static str453     fn description(&self) -> &'static str;
454 }
455