1use std::path::PathBuf;
4use std::rc::Rc;
5use std::str::FromStr;
6use std::{env, io};
7
8use polonius_engine::{Algorithm, Output};
9use rustc_index::IndexSlice;
10use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
11use rustc_middle::mir::{
12 Body, ClosureOutlivesSubject, ClosureRegionRequirements, PassWhere, Promoted, create_dump_file,
13 dump_enabled, dump_mir,
14};
15use rustc_middle::ty::print::with_no_trimmed_paths;
16use rustc_middle::ty::{self, TyCtxt};
17use rustc_mir_dataflow::ResultsCursor;
18use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
19use rustc_mir_dataflow::move_paths::MoveData;
20use rustc_mir_dataflow::points::DenseLocationMap;
21use rustc_session::config::MirIncludeSpans;
22use rustc_span::sym;
23use tracing::{debug, instrument};
24
25use crate::borrow_set::BorrowSet;
26use crate::consumers::ConsumerOptions;
27use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
28use crate::opaque_types::ConcreteOpaqueTypes;
29use crate::polonius::PoloniusDiagnosticsContext;
30use crate::polonius::legacy::{
31 PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
32};
33use crate::region_infer::RegionInferenceContext;
34use crate::type_check::{self, MirTypeckResults};
35use crate::universal_regions::UniversalRegions;
36use crate::{BorrowckInferCtxt, polonius, renumber};
37
38pub(crate) struct NllOutput<'tcx> {
41 pub regioncx: RegionInferenceContext<'tcx>,
42 pub concrete_opaque_types: ConcreteOpaqueTypes<'tcx>,
43 pub polonius_input: Option<Box<PoloniusFacts>>,
44 pub polonius_output: Option<Box<PoloniusOutput>>,
45 pub opt_closure_req: Option<ClosureRegionRequirements<'tcx>>,
46 pub nll_errors: RegionErrors<'tcx>,
47
48 pub polonius_diagnostics: Option<PoloniusDiagnosticsContext>,
51}
52
53#[instrument(skip(infcx, body, promoted), level = "debug")]
57pub(crate) fn replace_regions_in_mir<'tcx>(
58 infcx: &BorrowckInferCtxt<'tcx>,
59 body: &mut Body<'tcx>,
60 promoted: &mut IndexSlice<Promoted, Body<'tcx>>,
61) -> UniversalRegions<'tcx> {
62 let def = body.source.def_id().expect_local();
63
64 debug!(?def);
65
66 let universal_regions = UniversalRegions::new(infcx, def);
68
69 renumber::renumber_mir(infcx, body, promoted);
71
72 dump_mir(infcx.tcx, false, "renumber", &0, body, |_, _| Ok(()));
73
74 universal_regions
75}
76
77pub(crate) fn compute_regions<'a, 'tcx>(
81 infcx: &BorrowckInferCtxt<'tcx>,
82 universal_regions: UniversalRegions<'tcx>,
83 body: &Body<'tcx>,
84 promoted: &IndexSlice<Promoted, Body<'tcx>>,
85 location_table: &PoloniusLocationTable,
86 flow_inits: ResultsCursor<'a, 'tcx, MaybeInitializedPlaces<'a, 'tcx>>,
87 move_data: &MoveData<'tcx>,
88 borrow_set: &BorrowSet<'tcx>,
89 consumer_options: Option<ConsumerOptions>,
90) -> NllOutput<'tcx> {
91 let is_polonius_legacy_enabled = infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
92 let polonius_input = consumer_options.map(|c| c.polonius_input()).unwrap_or_default()
93 || is_polonius_legacy_enabled;
94 let polonius_output = consumer_options.map(|c| c.polonius_output()).unwrap_or_default()
95 || is_polonius_legacy_enabled;
96 let mut polonius_facts =
97 (polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());
98
99 let location_map = Rc::new(DenseLocationMap::new(body));
100
101 let mut concrete_opaque_types = ConcreteOpaqueTypes::default();
102
103 let MirTypeckResults {
105 constraints,
106 universal_region_relations,
107 opaque_type_values,
108 polonius_context,
109 } = type_check::type_check(
110 infcx,
111 body,
112 promoted,
113 universal_regions,
114 location_table,
115 borrow_set,
116 &mut polonius_facts,
117 flow_inits,
118 move_data,
119 Rc::clone(&location_map),
120 &mut concrete_opaque_types,
121 );
122
123 let var_infos = infcx.get_region_var_infos();
127
128 polonius::legacy::emit_facts(
130 &mut polonius_facts,
131 infcx.tcx,
132 location_table,
133 body,
134 borrow_set,
135 move_data,
136 &universal_region_relations,
137 &constraints,
138 );
139
140 let mut regioncx = RegionInferenceContext::new(
141 infcx,
142 var_infos,
143 constraints,
144 universal_region_relations,
145 location_map,
146 );
147
148 let polonius_diagnostics = polonius_context.map(|polonius_context| {
151 polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set)
152 });
153
154 let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| {
156 if infcx.tcx.sess.opts.unstable_opts.nll_facts {
157 let def_id = body.source.def_id();
158 let def_path = infcx.tcx.def_path(def_id);
159 let dir_path = PathBuf::from(&infcx.tcx.sess.opts.unstable_opts.nll_facts_dir)
160 .join(def_path.to_filename_friendly_no_crate());
161 polonius_facts.write_to_dir(dir_path, location_table).unwrap();
162 }
163
164 if polonius_output {
165 let algorithm =
166 env::var("POLONIUS_ALGORITHM").unwrap_or_else(|_| String::from("Hybrid"));
167 let algorithm = Algorithm::from_str(&algorithm).unwrap();
168 debug!("compute_regions: using polonius algorithm {:?}", algorithm);
169 let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis");
170 Some(Box::new(Output::compute(polonius_facts, algorithm, false)))
171 } else {
172 None
173 }
174 });
175
176 let (closure_region_requirements, nll_errors) =
178 regioncx.solve(infcx, body, polonius_output.clone());
179
180 if let Some(guar) = nll_errors.has_errors() {
181 infcx.set_tainted_by_errors(guar);
183 }
184
185 regioncx.infer_opaque_types(infcx, opaque_type_values, &mut concrete_opaque_types);
186
187 NllOutput {
188 regioncx,
189 concrete_opaque_types,
190 polonius_input: polonius_facts.map(Box::new),
191 polonius_output,
192 opt_closure_req: closure_region_requirements,
193 nll_errors,
194 polonius_diagnostics,
195 }
196}
197
198pub(super) fn dump_nll_mir<'tcx>(
208 infcx: &BorrowckInferCtxt<'tcx>,
209 body: &Body<'tcx>,
210 regioncx: &RegionInferenceContext<'tcx>,
211 closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
212 borrow_set: &BorrowSet<'tcx>,
213) {
214 let tcx = infcx.tcx;
215 if !dump_enabled(tcx, "nll", body.source.def_id()) {
216 return;
217 }
218
219 let options = PrettyPrintMirOptions {
223 include_extra_comments: matches!(
224 infcx.tcx.sess.opts.unstable_opts.mir_include_spans,
225 MirIncludeSpans::On | MirIncludeSpans::Nll
226 ),
227 };
228 dump_mir_with_options(
229 tcx,
230 false,
231 "nll",
232 &0,
233 body,
234 |pass_where, out| {
235 emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
236 },
237 options,
238 );
239
240 let _: io::Result<()> = try {
242 let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
243 regioncx.dump_graphviz_raw_constraints(&mut file)?;
244 };
245
246 let _: io::Result<()> = try {
248 let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
249 regioncx.dump_graphviz_scc_constraints(&mut file)?;
250 };
251}
252
253pub(crate) fn emit_nll_mir<'tcx>(
255 tcx: TyCtxt<'tcx>,
256 regioncx: &RegionInferenceContext<'tcx>,
257 closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
258 borrow_set: &BorrowSet<'tcx>,
259 pass_where: PassWhere,
260 out: &mut dyn io::Write,
261) -> io::Result<()> {
262 match pass_where {
263 PassWhere::BeforeCFG => {
265 regioncx.dump_mir(tcx, out)?;
266 writeln!(out, "|")?;
267
268 if let Some(closure_region_requirements) = closure_region_requirements {
269 writeln!(out, "| Free Region Constraints")?;
270 for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
271 writeln!(out, "| {msg}")
272 })?;
273 writeln!(out, "|")?;
274 }
275
276 if borrow_set.len() > 0 {
277 writeln!(out, "| Borrows")?;
278 for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() {
279 writeln!(
280 out,
281 "| {:?}: issued at {:?} in {:?}",
282 borrow_idx, borrow_data.reserve_location, borrow_data.region
283 )?;
284 }
285 writeln!(out, "|")?;
286 }
287 }
288
289 PassWhere::BeforeLocation(_) => {}
290
291 PassWhere::AfterTerminator(_) => {}
292
293 PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
294 }
295 Ok(())
296}
297
298#[allow(rustc::diagnostic_outside_of_impl)]
299#[allow(rustc::untranslatable_diagnostic)]
300pub(super) fn dump_annotation<'tcx, 'infcx>(
301 infcx: &'infcx BorrowckInferCtxt<'tcx>,
302 body: &Body<'tcx>,
303 regioncx: &RegionInferenceContext<'tcx>,
304 closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
305 concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>,
306 diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
307) {
308 let tcx = infcx.tcx;
309 let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
310 if !tcx.has_attr(base_def_id, sym::rustc_regions) {
311 return;
312 }
313
314 let def_span = tcx.def_span(body.source.def_id());
322 let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
323 let mut err = infcx.dcx().struct_span_note(def_span, "external requirements");
324
325 regioncx.annotate(tcx, &mut err);
326
327 err.note(format!(
328 "number of external vids: {}",
329 closure_region_requirements.num_external_vids
330 ));
331
332 for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
335 err.note(msg);
336 Ok(())
337 })
338 .unwrap();
339
340 err
341 } else {
342 let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements");
343 regioncx.annotate(tcx, &mut err);
344
345 err
346 };
347
348 if !concrete_opaque_types.is_empty() {
349 err.note(format!("Inferred opaque type values:\n{concrete_opaque_types:#?}"));
350 }
351
352 diagnostics_buffer.buffer_non_error(err);
353}
354
355fn for_each_region_constraint<'tcx>(
356 tcx: TyCtxt<'tcx>,
357 closure_region_requirements: &ClosureRegionRequirements<'tcx>,
358 with_msg: &mut dyn FnMut(String) -> io::Result<()>,
359) -> io::Result<()> {
360 for req in &closure_region_requirements.outlives_requirements {
361 let subject = match req.subject {
362 ClosureOutlivesSubject::Region(subject) => format!("{subject:?}"),
363 ClosureOutlivesSubject::Ty(ty) => {
364 with_no_trimmed_paths!(format!(
365 "{}",
366 ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid))
367 ))
368 }
369 };
370 with_msg(format!("where {}: {:?}", subject, req.outlived_free_region,))?;
371 }
372 Ok(())
373}
374
375pub(crate) trait ConstraintDescription {
376 fn description(&self) -> &'static str;
377}