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