]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/borrow_check/nll/mod.rs
16506800c9e16025f63d81db5b3e0e42fe76f24e
[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::MirTypeckRegionConstraints;
15 use dataflow::indexes::BorrowIndex;
16 use dataflow::move_paths::MoveData;
17 use dataflow::FlowAtLocation;
18 use dataflow::MaybeInitializedPlaces;
19 use rustc::hir::def_id::DefId;
20 use rustc::infer::InferCtxt;
21 use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Mir};
22 use rustc::ty::{self, RegionKind, RegionVid};
23 use rustc::util::nodemap::FxHashMap;
24 use std::collections::BTreeSet;
25 use std::fmt::Debug;
26 use std::env;
27 use std::io;
28 use std::path::PathBuf;
29 use std::rc::Rc;
30 use std::str::FromStr;
31 use transform::MirSource;
32 use util::liveness::{LivenessResults, LocalSet};
33
34 use self::mir_util::PassWhere;
35 use polonius_engine::{Algorithm, Output};
36 use util as mir_util;
37 use util::pretty::{self, ALIGN};
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 crate mod constraint_set;
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 ) -> (
92     RegionInferenceContext<'tcx>,
93     Option<Rc<Output<RegionVid, BorrowIndex, LocationIndex>>>,
94     Option<ClosureRegionRequirements<'gcx>>,
95 ) {
96     let mut all_facts = if AllFacts::enabled(infcx.tcx) {
97         Some(AllFacts::default())
98     } else {
99         None
100     };
101
102     // Run the MIR type-checker.
103     let liveness = &LivenessResults::compute(mir);
104     let constraint_sets = type_check::type_check(
105         infcx,
106         param_env,
107         mir,
108         def_id,
109         &universal_regions,
110         location_table,
111         &liveness,
112         &mut all_facts,
113         flow_inits,
114         move_data,
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         liveness_set,
129         outlives_constraints,
130         type_tests,
131     } = constraint_sets;
132     let mut regioncx = RegionInferenceContext::new(
133         var_origins,
134         universal_regions,
135         mir,
136         outlives_constraints,
137         type_tests,
138     );
139
140     // Generate various additional constraints.
141     constraint_generation::generate_constraints(
142         infcx,
143         &mut regioncx,
144         &mut all_facts,
145         location_table,
146         &mir,
147         borrow_set,
148         &liveness_set,
149     );
150     invalidation::generate_invalidates(
151         infcx,
152         &mut all_facts,
153         location_table,
154         &mir,
155         def_id,
156         borrow_set,
157     );
158
159     // Dump facts if requested.
160     let polonius_output = all_facts.and_then(|all_facts| {
161         if infcx.tcx.sess.opts.debugging_opts.nll_facts {
162             let def_path = infcx.tcx.hir.def_path(def_id);
163             let dir_path =
164                 PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate());
165             all_facts.write_to_dir(dir_path, location_table).unwrap();
166         }
167
168         if infcx.tcx.sess.opts.debugging_opts.polonius {
169             let algorithm = env::var("POLONIUS_ALGORITHM")
170                 .unwrap_or(String::from("DatafrogOpt"));
171             let algorithm = Algorithm::from_str(&algorithm).unwrap();
172             debug!("compute_regions: using polonius algorithm {:?}", algorithm);
173             Some(Rc::new(Output::compute(
174                 &all_facts,
175                 algorithm,
176                 false,
177             )))
178         } else {
179             None
180         }
181     });
182
183     // Solve the region constraints.
184     let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
185
186     // Dump MIR results into a file, if that is enabled. This let us
187     // write unit-tests, as well as helping with debugging.
188     dump_mir_results(
189         infcx,
190         liveness,
191         MirSource::item(def_id),
192         &mir,
193         &regioncx,
194         &closure_region_requirements,
195     );
196
197     // We also have a `#[rustc_nll]` annotation that causes us to dump
198     // information
199     dump_annotation(infcx, &mir, def_id, &regioncx, &closure_region_requirements);
200
201     (regioncx, polonius_output, closure_region_requirements)
202 }
203
204 fn dump_mir_results<'a, 'gcx, 'tcx>(
205     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
206     liveness: &LivenessResults,
207     source: MirSource,
208     mir: &Mir<'tcx>,
209     regioncx: &RegionInferenceContext,
210     closure_region_requirements: &Option<ClosureRegionRequirements>,
211 ) {
212     if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
213         return;
214     }
215
216     let regular_liveness_per_location: FxHashMap<_, _> = mir
217         .basic_blocks()
218         .indices()
219         .flat_map(|bb| {
220             let mut results = vec![];
221             liveness
222                 .regular
223                 .simulate_block(&mir, bb, |location, local_set| {
224                     results.push((location, local_set.clone()));
225                 });
226             results
227         })
228         .collect();
229
230     let drop_liveness_per_location: FxHashMap<_, _> = mir
231         .basic_blocks()
232         .indices()
233         .flat_map(|bb| {
234             let mut results = vec![];
235             liveness
236                 .drop
237                 .simulate_block(&mir, bb, |location, local_set| {
238                     results.push((location, local_set.clone()));
239                 });
240             results
241         })
242         .collect();
243
244     mir_util::dump_mir(
245         infcx.tcx,
246         None,
247         "nll",
248         &0,
249         source,
250         mir,
251         |pass_where, out| {
252             match pass_where {
253                 // Before the CFG, dump out the values for each region variable.
254                 PassWhere::BeforeCFG => {
255                     regioncx.dump_mir(out)?;
256
257                     if let Some(closure_region_requirements) = closure_region_requirements {
258                         writeln!(out, "|")?;
259                         writeln!(out, "| Free Region Constraints")?;
260                         for_each_region_constraint(closure_region_requirements, &mut |msg| {
261                             writeln!(out, "| {}", msg)
262                         })?;
263                     }
264                 }
265
266                 PassWhere::BeforeLocation(location) => {
267                     let s = live_variable_set(
268                         &regular_liveness_per_location[&location],
269                         &drop_liveness_per_location[&location],
270                     );
271                     writeln!(
272                         out,
273                         "{:ALIGN$} | Live variables on entry to {:?}: {}",
274                         "",
275                         location,
276                         s,
277                         ALIGN = ALIGN
278                     )?;
279                 }
280
281                 // After each basic block, dump out the values
282                 // that are live on exit from the basic block.
283                 PassWhere::AfterTerminator(bb) => {
284                     let s = live_variable_set(&liveness.regular.outs[bb], &liveness.drop.outs[bb]);
285                     writeln!(out, "    | Live variables on exit from {:?}: {}", bb, s)?;
286                 }
287
288                 PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
289             }
290             Ok(())
291         },
292     );
293
294     // Also dump the inference graph constraints as a graphviz file.
295     let _: io::Result<()> = do catch {
296         let mut file =
297             pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?;
298         regioncx.dump_graphviz(&mut file)?;
299     };
300 }
301
302 fn dump_annotation<'a, 'gcx, 'tcx>(
303     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
304     mir: &Mir<'tcx>,
305     mir_def_id: DefId,
306     regioncx: &RegionInferenceContext,
307     closure_region_requirements: &Option<ClosureRegionRequirements>,
308 ) {
309     let tcx = infcx.tcx;
310     let base_def_id = tcx.closure_base_def_id(mir_def_id);
311     if !tcx.has_attr(base_def_id, "rustc_regions") {
312         return;
313     }
314
315     // When the enclosing function is tagged with `#[rustc_regions]`,
316     // we dump out various bits of state as warnings. This is useful
317     // for verifying that the compiler is behaving as expected.  These
318     // warnings focus on the closure region requirements -- for
319     // viewing the intraprocedural state, the -Zdump-mir output is
320     // better.
321
322     if let Some(closure_region_requirements) = closure_region_requirements {
323         let mut err = tcx
324             .sess
325             .diagnostic()
326             .span_note_diag(mir.span, "External requirements");
327
328         regioncx.annotate(&mut err);
329
330         err.note(&format!(
331             "number of external vids: {}",
332             closure_region_requirements.num_external_vids
333         ));
334
335         // Dump the region constraints we are imposing *between* those
336         // newly created variables.
337         for_each_region_constraint(closure_region_requirements, &mut |msg| {
338             err.note(msg);
339             Ok(())
340         }).unwrap();
341
342         err.emit();
343     } else {
344         let mut err = tcx
345             .sess
346             .diagnostic()
347             .span_note_diag(mir.span, "No external requirements");
348         regioncx.annotate(&mut err);
349         err.emit();
350     }
351 }
352
353 fn for_each_region_constraint(
354     closure_region_requirements: &ClosureRegionRequirements,
355     with_msg: &mut dyn FnMut(&str) -> io::Result<()>,
356 ) -> io::Result<()> {
357     for req in &closure_region_requirements.outlives_requirements {
358         let subject: &dyn Debug = match &req.subject {
359             ClosureOutlivesSubject::Region(subject) => subject,
360             ClosureOutlivesSubject::Ty(ty) => ty,
361         };
362         with_msg(&format!(
363             "where {:?}: {:?}",
364             subject, req.outlived_free_region,
365         ))?;
366     }
367     Ok(())
368 }
369
370 /// Right now, we piggy back on the `ReVar` to store our NLL inference
371 /// regions. These are indexed with `RegionVid`. This method will
372 /// assert that the region is a `ReVar` and extract its internal index.
373 /// This is reasonable because in our MIR we replace all universal regions
374 /// with inference variables.
375 pub trait ToRegionVid {
376     fn to_region_vid(self) -> RegionVid;
377 }
378
379 impl<'tcx> ToRegionVid for &'tcx RegionKind {
380     fn to_region_vid(self) -> RegionVid {
381         if let ty::ReVar(vid) = self {
382             *vid
383         } else {
384             bug!("region is not an ReVar: {:?}", self)
385         }
386     }
387 }
388
389 impl ToRegionVid for RegionVid {
390     fn to_region_vid(self) -> RegionVid {
391         self
392     }
393 }
394
395 fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String {
396     // sort and deduplicate:
397     let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect();
398
399     // construct a string with each local, including `(drop)` if it is
400     // only dropped, versus a regular use.
401     let mut string = String::new();
402     for local in all_locals {
403         string.push_str(&format!("{:?}", local));
404
405         if !regular.contains(&local) {
406             assert!(drops.contains(&local));
407             string.push_str(" (drop)");
408         }
409
410         string.push_str(", ");
411     }
412
413     let len = if string.is_empty() {
414         0
415     } else {
416         string.len() - 2
417     };
418
419     format!("[{}]", &string[..len])
420 }