diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 1498a6b23c09..8a62533ba337 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -42,44 +42,70 @@ impl MirPass for NLL { return; } - tcx.infer_ctxt().enter(|ref infcx| { - // Clone mir so we can mutate it without disturbing the rest of the compiler - let mir = &mut input_mir.clone(); - - // Replace all regions with fresh inference variables. - let num_region_variables = renumber::renumber_mir(infcx, mir); - - // Compute what is live where. - let liveness = &LivenessResults { - regular: liveness::liveness_of_locals( - mir, - LivenessMode { - include_regular_use: true, - include_drops: false, - }, - ), - - drop: liveness::liveness_of_locals( - mir, - LivenessMode { - include_regular_use: false, - include_drops: true, - }, - ), - }; - - // Create the region inference context, generate the constraints, - // and then solve them. - let regioncx = &mut RegionInferenceContext::new(num_region_variables); - constraint_generation::generate_constraints(infcx, regioncx, mir, source, liveness); - regioncx.solve(infcx, mir); - - // Dump MIR results into a file, if that is enabled. - dump_mir_results(infcx, liveness, source, regioncx, mir); - }) + tcx.infer_ctxt() + .enter(|ref infcx| drop(compute_regions(infcx, source, input_mir))); } } +pub struct RegionComputation<'tcx> { + /// A rewritten version of the input MIR where all the regions are + /// rewritten to refer to inference variables. + pub mir: Mir<'tcx>, + + /// The definitions (along with their final values) for all regions. + pub regioncx: RegionInferenceContext, +} + +/// Computes the (non-lexical) regions from the input MIR. +/// +/// This may result in errors being reported. +pub fn compute_regions<'a, 'gcx, 'tcx>( + infcx: &InferCtxt<'a, 'gcx, 'tcx>, + source: MirSource, + input_mir: &Mir<'tcx>, +) -> RegionComputation<'tcx> { + // Clone mir so we can mutate it without disturbing the rest of the compiler + let mut mir = input_mir.clone(); + + // Replace all regions with fresh inference variables. + let num_region_variables = renumber::renumber_mir(infcx, &mut mir); + + // Compute what is live where. + let liveness = &LivenessResults { + regular: liveness::liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: true, + include_drops: false, + }, + ), + + drop: liveness::liveness_of_locals( + &mir, + LivenessMode { + include_regular_use: false, + include_drops: true, + }, + ), + }; + + // Create the region inference context, generate the constraints, + // and then solve them. + let mut regioncx = RegionInferenceContext::new(num_region_variables); + constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); + let errors = regioncx.solve(infcx, &mir); + + assert!(errors.is_empty(), "FIXME: report region inference failures"); + + let computation = RegionComputation { mir, regioncx }; + + // Dump MIR results into a file, if that is enabled. This let us + // write unit-tests. + dump_mir_results(infcx, liveness, source, &computation); + + computation +} + struct LivenessResults { regular: LivenessResult, drop: LivenessResult, @@ -89,13 +115,17 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, liveness: &LivenessResults, source: MirSource, - regioncx: &RegionInferenceContext, - mir: &Mir<'tcx>, + computation: &RegionComputation<'tcx>, ) { if !mir_util::dump_enabled(infcx.tcx, "nll", source) { return; } + let RegionComputation { + ref mir, + ref regioncx, + } = *computation; + let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks() .indices() .flat_map(|bb| { @@ -126,7 +156,12 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => for region in regioncx.regions() { - writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?; + writeln!( + out, + "| {:?}: {:?}", + region, + regioncx.region_value(region) + )?; }, // Before each basic block, dump out the values @@ -141,12 +176,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( ®ular_liveness_per_location[&location], &drop_liveness_per_location[&location], ); - writeln!( - out, - " | Live variables at {:?}: {}", - location, - s - )?; + writeln!(out, " | Live variables at {:?}: {}", location, s)?; } PassWhere::AfterCFG => {} @@ -217,7 +247,11 @@ fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String { string.push_str(", "); } - let len = if string.is_empty() { 0 } else { string.len() - 2 }; + let len = if string.is_empty() { + 0 + } else { + string.len() - 2 + }; format!("[{}]", &string[..len]) } diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs index 75abd4d3ff53..c23d73e784ae 100644 --- a/src/librustc_mir/transform/nll/region_infer.rs +++ b/src/librustc_mir/transform/nll/region_infer.rs @@ -12,13 +12,22 @@ use super::{Region, RegionIndex}; use std::mem; use rustc::infer::InferCtxt; use rustc::mir::{Location, Mir}; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::fx::FxHashSet; pub struct RegionInferenceContext { - definitions: IndexVec, - constraints: IndexVec, - errors: IndexVec, + /// Contains the definition for every region variable. Region + /// variables are identified by their index (`RegionIndex`). The + /// definition contains information about where the region came + /// from as well as its final inferred value. + definitions: IndexVec, + + /// The constraints we have accumulated and used during solving. + constraints: Vec, + + /// List of errors we have accumulated as we add constraints. + /// After solving is done, this is replaced with an empty vector. + errors: Vec, } pub struct InferenceError { @@ -26,10 +35,8 @@ pub struct InferenceError { pub name: (), // FIXME(nashenas88) RegionName } -newtype_index!(InferenceErrorIndex); - #[derive(Default)] -struct VarDefinition { +struct RegionDefinition { name: (), // FIXME(nashenas88) RegionName value: Region, capped: bool, @@ -42,26 +49,43 @@ pub struct Constraint { point: Location, } -newtype_index!(ConstraintIndex); - impl RegionInferenceContext { pub fn new(num_region_variables: usize) -> Self { Self { definitions: (0..num_region_variables) - .map(|_| VarDefinition::default()) + .map(|_| RegionDefinition::default()) .collect(), - constraints: IndexVec::new(), - errors: IndexVec::new(), + constraints: Vec::new(), + errors: Vec::new(), } } + + /// Returns an iterator over all the region indices. + pub fn regions(&self) -> impl Iterator { + self.definitions.indices() + } + + /// Returns the inferred value for the region `r`. + /// + /// Until `solve()` executes, this value is not particularly meaningful. + pub fn region_value(&self, r: RegionIndex) -> &Region { + &self.definitions[r].value + } + + /// Flags a region as being "capped" -- this means that if its + /// value is required to grow as a result of some constraint + /// (e.g., `add_live_point` or `add_outlives`), that indicates an + /// error. This is used for the regions representing named + /// lifetime parameters on a function: they get initialized to + /// their complete value, and then "capped" so that they can no + /// longer grow. #[allow(dead_code)] - pub fn cap_var(&mut self, v: RegionIndex) { + pub(super) fn cap_var(&mut self, v: RegionIndex) { self.definitions[v].capped = true; } - #[allow(dead_code)] - pub fn add_live_point(&mut self, v: RegionIndex, point: Location) { + pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) { debug!("add_live_point({:?}, {:?})", v, point); let definition = &mut self.definitions[v]; if definition.value.add_point(point) { @@ -74,28 +98,17 @@ impl RegionInferenceContext { } } - #[allow(dead_code)] - pub fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { + pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); self.constraints.push(Constraint { sup, sub, point }); } - /// Returns an iterator over all the region indices. - pub fn regions(&self) -> impl Iterator { - self.definitions.indices() - } - - /// Returns the current value for the region `v`. This is only - /// really meaningful after `solve` has executed. - pub fn region_value(&self, v: RegionIndex) -> &Region { - &self.definitions[v].value - } - - pub fn solve<'a, 'gcx, 'tcx>( + /// Perform region inference. + pub(super) fn solve<'a, 'gcx, 'tcx>( &mut self, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>, - ) -> IndexVec + ) -> Vec where 'gcx: 'tcx + 'a, 'tcx: 'a, @@ -138,7 +151,7 @@ impl RegionInferenceContext { debug!("\n"); } - mem::replace(&mut self.errors, IndexVec::new()) + mem::replace(&mut self.errors, Vec::new()) } }