1use std::io;
4use std::path::PathBuf;
5use std::rc::Rc;
6use std::str::FromStr;
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 = infcx.tcx.env_var("POLONIUS_ALGORITHM").unwrap_or("Hybrid");
166 let algorithm = Algorithm::from_str(algorithm).unwrap();
167 debug!("compute_regions: using polonius algorithm {:?}", algorithm);
168 let _prof_timer = infcx.tcx.prof.generic_activity("polonius_analysis");
169 Some(Box::new(Output::compute(polonius_facts, algorithm, false)))
170 } else {
171 None
172 }
173 });
174
175 let (closure_region_requirements, nll_errors) =
177 regioncx.solve(infcx, body, polonius_output.clone());
178
179 if let Some(guar) = nll_errors.has_errors() {
180 infcx.set_tainted_by_errors(guar);
182 }
183
184 regioncx.infer_opaque_types(infcx, opaque_type_values, &mut concrete_opaque_types);
185
186 NllOutput {
187 regioncx,
188 concrete_opaque_types,
189 polonius_input: polonius_facts.map(Box::new),
190 polonius_output,
191 opt_closure_req: closure_region_requirements,
192 nll_errors,
193 polonius_diagnostics,
194 }
195}
196
197pub(super) fn dump_nll_mir<'tcx>(
207 infcx: &BorrowckInferCtxt<'tcx>,
208 body: &Body<'tcx>,
209 regioncx: &RegionInferenceContext<'tcx>,
210 closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
211 borrow_set: &BorrowSet<'tcx>,
212) {
213 let tcx = infcx.tcx;
214 if !dump_enabled(tcx, "nll", body.source.def_id()) {
215 return;
216 }
217
218 let options = PrettyPrintMirOptions {
222 include_extra_comments: matches!(
223 infcx.tcx.sess.opts.unstable_opts.mir_include_spans,
224 MirIncludeSpans::On | MirIncludeSpans::Nll
225 ),
226 };
227 dump_mir_with_options(
228 tcx,
229 false,
230 "nll",
231 &0,
232 body,
233 |pass_where, out| {
234 emit_nll_mir(tcx, regioncx, closure_region_requirements, borrow_set, pass_where, out)
235 },
236 options,
237 );
238
239 let _: io::Result<()> = try {
241 let mut file = create_dump_file(tcx, "regioncx.all.dot", false, "nll", &0, body)?;
242 regioncx.dump_graphviz_raw_constraints(&mut file)?;
243 };
244
245 let _: io::Result<()> = try {
247 let mut file = create_dump_file(tcx, "regioncx.scc.dot", false, "nll", &0, body)?;
248 regioncx.dump_graphviz_scc_constraints(&mut file)?;
249 };
250}
251
252pub(crate) fn emit_nll_mir<'tcx>(
254 tcx: TyCtxt<'tcx>,
255 regioncx: &RegionInferenceContext<'tcx>,
256 closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
257 borrow_set: &BorrowSet<'tcx>,
258 pass_where: PassWhere,
259 out: &mut dyn io::Write,
260) -> io::Result<()> {
261 match pass_where {
262 PassWhere::BeforeCFG => {
264 regioncx.dump_mir(tcx, out)?;
265 writeln!(out, "|")?;
266
267 if let Some(closure_region_requirements) = closure_region_requirements {
268 writeln!(out, "| Free Region Constraints")?;
269 for_each_region_constraint(tcx, closure_region_requirements, &mut |msg| {
270 writeln!(out, "| {msg}")
271 })?;
272 writeln!(out, "|")?;
273 }
274
275 if borrow_set.len() > 0 {
276 writeln!(out, "| Borrows")?;
277 for (borrow_idx, borrow_data) in borrow_set.iter_enumerated() {
278 writeln!(
279 out,
280 "| {:?}: issued at {:?} in {:?}",
281 borrow_idx, borrow_data.reserve_location, borrow_data.region
282 )?;
283 }
284 writeln!(out, "|")?;
285 }
286 }
287
288 PassWhere::BeforeLocation(_) => {}
289
290 PassWhere::AfterTerminator(_) => {}
291
292 PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
293 }
294 Ok(())
295}
296
297#[allow(rustc::diagnostic_outside_of_impl)]
298#[allow(rustc::untranslatable_diagnostic)]
299pub(super) fn dump_annotation<'tcx, 'infcx>(
300 infcx: &'infcx BorrowckInferCtxt<'tcx>,
301 body: &Body<'tcx>,
302 regioncx: &RegionInferenceContext<'tcx>,
303 closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
304 concrete_opaque_types: &ConcreteOpaqueTypes<'tcx>,
305 diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
306) {
307 let tcx = infcx.tcx;
308 let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
309 if !tcx.has_attr(base_def_id, sym::rustc_regions) {
310 return;
311 }
312
313 let def_span = tcx.def_span(body.source.def_id());
321 let mut 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(format!(
327 "number of external vids: {}",
328 closure_region_requirements.num_external_vids
329 ));
330
331 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
344 err
345 };
346
347 if !concrete_opaque_types.is_empty() {
348 err.note(format!("Inferred opaque type values:\n{concrete_opaque_types:#?}"));
349 }
350
351 diagnostics_buffer.buffer_non_error(err);
352}
353
354fn for_each_region_constraint<'tcx>(
355 tcx: TyCtxt<'tcx>,
356 closure_region_requirements: &ClosureRegionRequirements<'tcx>,
357 with_msg: &mut dyn FnMut(String) -> io::Result<()>,
358) -> io::Result<()> {
359 for req in &closure_region_requirements.outlives_requirements {
360 let subject = match req.subject {
361 ClosureOutlivesSubject::Region(subject) => format!("{subject:?}"),
362 ClosureOutlivesSubject::Ty(ty) => {
363 with_no_trimmed_paths!(format!(
364 "{}",
365 ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid))
366 ))
367 }
368 };
369 with_msg(format!("where {}: {:?}", subject, req.outlived_free_region,))?;
370 }
371 Ok(())
372}
373
374pub(crate) trait ConstraintDescription {
375 fn description(&self) -> &'static str;
376}