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,
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| {
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
®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 => {}
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])
}
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<RegionIndex, VarDefinition>,
- constraints: IndexVec<ConstraintIndex, Constraint>,
- errors: IndexVec<InferenceErrorIndex, InferenceError>,
+ /// 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<RegionIndex, RegionDefinition>,
+
+ /// The constraints we have accumulated and used during solving.
+ constraints: Vec<Constraint>,
+
+ /// List of errors we have accumulated as we add constraints.
+ /// After solving is done, this is replaced with an empty vector.
+ errors: Vec<InferenceError>,
}
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,
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<Item = RegionIndex> {
+ 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) {
}
}
- #[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<Item = RegionIndex> {
- 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<InferenceErrorIndex, InferenceError>
+ ) -> Vec<InferenceError>
where
'gcx: 'tcx + 'a,
'tcx: 'a,
debug!("\n");
}
- mem::replace(&mut self.errors, IndexVec::new())
+ mem::replace(&mut self.errors, Vec::new())
}
}