]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/mod.rs
Changes the type `mir::Mir` into `mir::Body`
[rust.git] / src / librustc_mir / borrow_check / nll / mod.rs
1 use crate::borrow_check::borrow_set::BorrowSet;
2 use crate::borrow_check::location::{LocationIndex, LocationTable};
3 use crate::borrow_check::nll::facts::AllFactsExt;
4 use crate::borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints};
5 use crate::borrow_check::nll::region_infer::values::RegionValueElements;
6 use crate::dataflow::indexes::BorrowIndex;
7 use crate::dataflow::move_paths::MoveData;
8 use crate::dataflow::FlowAtLocation;
9 use crate::dataflow::MaybeInitializedPlaces;
10 use crate::transform::MirSource;
11 use crate::borrow_check::Upvar;
12 use rustc::hir::def_id::DefId;
13 use rustc::infer::InferCtxt;
14 use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Body};
15 use rustc::ty::{self, RegionKind, RegionVid};
16 use rustc_errors::Diagnostic;
17 use std::fmt::Debug;
18 use std::env;
19 use std::io;
20 use std::path::PathBuf;
21 use std::rc::Rc;
22 use std::str::FromStr;
23 use syntax::symbol::sym;
24
25 use self::mir_util::PassWhere;
26 use polonius_engine::{Algorithm, Output};
27 use crate::util as mir_util;
28 use crate::util::pretty;
29
30 mod constraint_generation;
31 pub mod explain_borrow;
32 mod facts;
33 mod invalidation;
34 crate mod region_infer;
35 mod renumber;
36 crate mod type_check;
37 mod universal_regions;
38
39 mod constraints;
40
41 use self::facts::AllFacts;
42 use self::region_infer::RegionInferenceContext;
43 use self::universal_regions::UniversalRegions;
44
45 /// Rewrites the regions in the MIR to use NLL variables, also
46 /// scraping out the set of universal regions (e.g., region parameters)
47 /// declared on the function. That set will need to be given to
48 /// `compute_regions`.
49 pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
50     infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
51     def_id: DefId,
52     param_env: ty::ParamEnv<'tcx>,
53     mir: &mut Body<'tcx>,
54 ) -> UniversalRegions<'tcx> {
55     debug!("replace_regions_in_mir(def_id={:?})", def_id);
56
57     // Compute named region information. This also renumbers the inputs/outputs.
58     let universal_regions = UniversalRegions::new(infcx, def_id, param_env);
59
60     // Replace all remaining regions with fresh inference variables.
61     renumber::renumber_mir(infcx, mir);
62
63     let source = MirSource::item(def_id);
64     mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, mir, |_, _| Ok(()));
65
66     universal_regions
67 }
68
69 /// Computes the (non-lexical) regions from the input MIR.
70 ///
71 /// This may result in errors being reported.
72 pub(in crate::borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
73     infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
74     def_id: DefId,
75     universal_regions: UniversalRegions<'tcx>,
76     mir: &Body<'tcx>,
77     upvars: &[Upvar],
78     location_table: &LocationTable,
79     param_env: ty::ParamEnv<'gcx>,
80     flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
81     move_data: &MoveData<'tcx>,
82     borrow_set: &BorrowSet<'tcx>,
83     errors_buffer: &mut Vec<Diagnostic>,
84 ) -> (
85     RegionInferenceContext<'tcx>,
86     Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
87     Option<ClosureRegionRequirements<'gcx>>,
88 ) {
89     let mut all_facts = if AllFacts::enabled(infcx.tcx) {
90         Some(AllFacts::default())
91     } else {
92         None
93     };
94
95     let universal_regions = Rc::new(universal_regions);
96
97     let elements = &Rc::new(RegionValueElements::new(mir));
98
99     // Run the MIR type-checker.
100     let MirTypeckResults {
101         constraints,
102         universal_region_relations,
103     } = type_check::type_check(
104         infcx,
105         param_env,
106         mir,
107         def_id,
108         &universal_regions,
109         location_table,
110         borrow_set,
111         &mut all_facts,
112         flow_inits,
113         move_data,
114         elements,
115     );
116
117     if let Some(all_facts) = &mut all_facts {
118         all_facts
119             .universal_region
120             .extend(universal_regions.universal_regions());
121     }
122
123     // Create the region inference context, taking ownership of the
124     // region inference data that was contained in `infcx`, and the
125     // base constraints generated by the type-check.
126     let var_origins = infcx.take_region_var_origins();
127     let MirTypeckRegionConstraints {
128         placeholder_indices,
129         placeholder_index_to_region: _,
130         mut liveness_constraints,
131         outlives_constraints,
132         closure_bounds_mapping,
133         type_tests,
134     } = constraints;
135     let placeholder_indices = Rc::new(placeholder_indices);
136
137     constraint_generation::generate_constraints(
138         infcx,
139         &mut liveness_constraints,
140         &mut all_facts,
141         location_table,
142         &mir,
143         borrow_set,
144     );
145
146     let mut regioncx = RegionInferenceContext::new(
147         var_origins,
148         universal_regions,
149         placeholder_indices,
150         universal_region_relations,
151         mir,
152         outlives_constraints,
153         closure_bounds_mapping,
154         type_tests,
155         liveness_constraints,
156         elements,
157     );
158
159     // Generate various additional constraints.
160     invalidation::generate_invalidates(
161         infcx.tcx,
162         &mut all_facts,
163         location_table,
164         &mir,
165         borrow_set,
166     );
167
168     // Dump facts if requested.
169     let polonius_output = all_facts.and_then(|all_facts| {
170         if infcx.tcx.sess.opts.debugging_opts.nll_facts {
171             let def_path = infcx.tcx.hir().def_path(def_id);
172             let dir_path =
173                 PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate());
174             all_facts.write_to_dir(dir_path, location_table).unwrap();
175         }
176
177         if infcx.tcx.sess.opts.debugging_opts.polonius {
178             let algorithm = env::var("POLONIUS_ALGORITHM")
179                 .unwrap_or_else(|_| String::from("Hybrid"));
180             let algorithm = Algorithm::from_str(&algorithm).unwrap();
181             debug!("compute_regions: using polonius algorithm {:?}", algorithm);
182             Some(Rc::new(Output::compute(
183                 &all_facts,
184                 algorithm,
185                 false,
186             )))
187         } else {
188             None
189         }
190     });
191
192     // Solve the region constraints.
193     let closure_region_requirements =
194         regioncx.solve(infcx, &mir, upvars, def_id, errors_buffer);
195
196     // Dump MIR results into a file, if that is enabled. This let us
197     // write unit-tests, as well as helping with debugging.
198     dump_mir_results(
199         infcx,
200         MirSource::item(def_id),
201         &mir,
202         &regioncx,
203         &closure_region_requirements,
204     );
205
206     // We also have a `#[rustc_nll]` annotation that causes us to dump
207     // information
208     dump_annotation(infcx, &mir, def_id, &regioncx, &closure_region_requirements, errors_buffer);
209
210     (regioncx, polonius_output, closure_region_requirements)
211 }
212
213 fn dump_mir_results<'a, 'gcx, 'tcx>(
214     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
215     source: MirSource<'tcx>,
216     mir: &Body<'tcx>,
217     regioncx: &RegionInferenceContext<'_>,
218     closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,
219 ) {
220     if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
221         return;
222     }
223
224     mir_util::dump_mir(
225         infcx.tcx,
226         None,
227         "nll",
228         &0,
229         source,
230         mir,
231         |pass_where, out| {
232             match pass_where {
233                 // Before the CFG, dump out the values for each region variable.
234                 PassWhere::BeforeCFG => {
235                     regioncx.dump_mir(out)?;
236                     writeln!(out, "|")?;
237
238                     if let Some(closure_region_requirements) = closure_region_requirements {
239                         writeln!(out, "| Free Region Constraints")?;
240                         for_each_region_constraint(closure_region_requirements, &mut |msg| {
241                             writeln!(out, "| {}", msg)
242                         })?;
243                         writeln!(out, "|")?;
244                     }
245                 }
246
247                 PassWhere::BeforeLocation(_) => {
248                 }
249
250                 PassWhere::AfterTerminator(_) => {
251                 }
252
253                 PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
254             }
255             Ok(())
256         },
257     );
258
259     // Also dump the inference graph constraints as a graphviz file.
260     let _: io::Result<()> = try {
261         let mut file =
262             pretty::create_dump_file(infcx.tcx, "regioncx.all.dot", None, "nll", &0, source)?;
263         regioncx.dump_graphviz_raw_constraints(&mut file)?;
264     };
265
266     // Also dump the inference graph constraints as a graphviz file.
267     let _: io::Result<()> = try {
268         let mut file =
269             pretty::create_dump_file(infcx.tcx, "regioncx.scc.dot", None, "nll", &0, source)?;
270         regioncx.dump_graphviz_scc_constraints(&mut file)?;
271     };
272 }
273
274 fn dump_annotation<'a, 'gcx, 'tcx>(
275     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
276     mir: &Body<'tcx>,
277     mir_def_id: DefId,
278     regioncx: &RegionInferenceContext<'tcx>,
279     closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,
280     errors_buffer: &mut Vec<Diagnostic>,
281 ) {
282     let tcx = infcx.tcx;
283     let base_def_id = tcx.closure_base_def_id(mir_def_id);
284     if !tcx.has_attr(base_def_id, sym::rustc_regions) {
285         return;
286     }
287
288     // When the enclosing function is tagged with `#[rustc_regions]`,
289     // we dump out various bits of state as warnings. This is useful
290     // for verifying that the compiler is behaving as expected.  These
291     // warnings focus on the closure region requirements -- for
292     // viewing the intraprocedural state, the -Zdump-mir output is
293     // better.
294
295     if let Some(closure_region_requirements) = closure_region_requirements {
296         let mut err = tcx
297             .sess
298             .diagnostic()
299             .span_note_diag(mir.span, "External requirements");
300
301         regioncx.annotate(tcx, &mut err);
302
303         err.note(&format!(
304             "number of external vids: {}",
305             closure_region_requirements.num_external_vids
306         ));
307
308         // Dump the region constraints we are imposing *between* those
309         // newly created variables.
310         for_each_region_constraint(closure_region_requirements, &mut |msg| {
311             err.note(msg);
312             Ok(())
313         }).unwrap();
314
315         err.buffer(errors_buffer);
316     } else {
317         let mut err = tcx
318             .sess
319             .diagnostic()
320             .span_note_diag(mir.span, "No external requirements");
321         regioncx.annotate(tcx, &mut err);
322
323         err.buffer(errors_buffer);
324     }
325 }
326
327 fn for_each_region_constraint(
328     closure_region_requirements: &ClosureRegionRequirements<'_>,
329     with_msg: &mut dyn FnMut(&str) -> io::Result<()>,
330 ) -> io::Result<()> {
331     for req in &closure_region_requirements.outlives_requirements {
332         let subject: &dyn Debug = match &req.subject {
333             ClosureOutlivesSubject::Region(subject) => subject,
334             ClosureOutlivesSubject::Ty(ty) => ty,
335         };
336         with_msg(&format!(
337             "where {:?}: {:?}",
338             subject, req.outlived_free_region,
339         ))?;
340     }
341     Ok(())
342 }
343
344 /// Right now, we piggy back on the `ReVar` to store our NLL inference
345 /// regions. These are indexed with `RegionVid`. This method will
346 /// assert that the region is a `ReVar` and extract its internal index.
347 /// This is reasonable because in our MIR we replace all universal regions
348 /// with inference variables.
349 pub trait ToRegionVid {
350     fn to_region_vid(self) -> RegionVid;
351 }
352
353 impl<'tcx> ToRegionVid for &'tcx RegionKind {
354     fn to_region_vid(self) -> RegionVid {
355         if let ty::ReVar(vid) = self {
356             *vid
357         } else {
358             bug!("region is not an ReVar: {:?}", self)
359         }
360     }
361 }
362
363 impl ToRegionVid for RegionVid {
364     fn to_region_vid(self) -> RegionVid {
365         self
366     }
367 }
368
369 crate trait ConstraintDescription {
370     fn description(&self) -> &'static str;
371 }