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