}
impl_stable_hash_for!(struct mir::Location { block, statement_index });
+
+impl_stable_hash_for!(struct mir::ClosureRegionRequirements {
+ num_external_vids,
+ outlives_requirements
+});
+
+impl_stable_hash_for!(struct mir::ClosureOutlivesRequirement {
+ free_region,
+ outlived_free_region,
+ blame_span
+});
+
}
}
+impl<'gcx> HashStable<StableHashingContext<'gcx>> for ty::RegionVid {
+ #[inline]
+ fn hash_stable<W: StableHasherResult>(&self,
+ hcx: &mut StableHashingContext<'gcx>,
+ hasher: &mut StableHasher<W>) {
+ use rustc_data_structures::indexed_vec::Idx;
+ self.index().hash_stable(hcx, hasher);
+ }
+}
+
impl<'gcx> HashStable<StableHashingContext<'gcx>>
for ty::adjustment::AutoBorrow<'gcx> {
fn hash_stable<W: StableHasherResult>(&self,
self.tcx.mk_region(ty::ReVar(self.borrow_region_constraints().new_region_var(origin)))
}
+ /// Number of region variables created so far.
+ pub fn num_region_vars(&self) -> usize {
+ self.borrow_region_constraints().var_origins().len()
+ }
+
/// Just a convenient wrapper of `next_region_var` for using during NLL.
pub fn next_nll_region_var(&self, origin: NLLRegionVariableOrigin)
-> ty::Region<'tcx> {
pub fields: Vec<LocalDecl<'tcx>>,
}
+/// After we borrow check a closure, we are left with various
+/// requirements that we have inferred between the free regions that
+/// appear in the closure's signature or on its field types. These
+/// requirements are then verified and proved by the closure's
+/// creating function. This struct encodes those requirements.
+///
+/// The requirements are listed as being between various
+/// `RegionVid`. The 0th region refers to `'static`; subsequent region
+/// vids refer to the free regions that appear in the closure (or
+/// generator's) type, in order of appearance. (This numbering is
+/// actually defined by the `UniversalRegions` struct in the NLL
+/// region checker. See for example
+/// `UniversalRegions::closure_mapping`.) Note that we treat the free
+/// regions in the closure's type "as if" they were erased, so their
+/// precise identity is not important, only their position.
+///
+/// Example: If type check produces a closure with the closure substs:
+///
+/// ```
+/// ClosureSubsts = [
+/// i8, // the "closure kind"
+/// for<'x> fn(&'a &'x u32) -> &'x u32, // the "closure signature"
+/// &'a String, // some upvar
+/// ]
+/// ```
+///
+/// here, there is one unique free region (`'a`) but it appears
+/// twice. We would "renumber" each occurence to a unique vid, as follows:
+///
+/// ```
+/// ClosureSubsts = [
+/// i8, // the "closure kind"
+/// for<'x> fn(&'1 &'x u32) -> &'x u32, // the "closure signature"
+/// &'2 String, // some upvar
+/// ]
+/// ```
+///
+/// Now the code might impose a requirement like `'1: '2`. When an
+/// instance of the closure is created, the corresponding free regions
+/// can be extracted from its type and constrained to have the given
+/// outlives relationship.
+#[derive(Clone, Debug)]
+pub struct ClosureRegionRequirements {
+ /// The number of external regions defined on the closure. In our
+ /// example above, it would be 3 -- one for `'static`, then `'1`
+ /// and `'2`. This is just used for a sanity check later on, to
+ /// make sure that the number of regions we see at the callsite
+ /// matches.
+ pub num_external_vids: usize,
+
+ /// Requirements between the various free regions defined in
+ /// indices.
+ pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
+}
+
+/// Indicates an outlives constraint between two free-regions declared
+/// on the closure.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct ClosureOutlivesRequirement {
+ // This region ...
+ pub free_region: ty::RegionVid,
+
+ // .. must outlive this one.
+ pub outlived_free_region: ty::RegionVid,
+
+ // If not, report an error here.
+ pub blame_span: Span,
+}
+
/*
* TypeFoldable implementations for MIR types
*/
[] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (),
[] fn borrowck: BorrowCheck(DefId) -> Rc<BorrowCheckResult>,
- // FIXME: shouldn't this return a `Result<(), BorrowckErrors>` instead?
- [] fn mir_borrowck: MirBorrowCheck(DefId) -> (),
+
+ /// Borrow checks the function body. If this is a closure, returns
+ /// additional requirements that the closure's creator must verify.
+ [] fn mir_borrowck: MirBorrowCheck(DefId) -> Option<mir::ClosureRegionRequirements>,
/// Gets a complete map from all types to their inherent impls.
/// Not meant to be used directly outside of coherence.
pub struct Binder<T>(pub T);
impl<T> Binder<T> {
+ /// Wraps `value` in a binder, asserting that `value` does not
+ /// contain any bound regions that would be bound by the
+ /// binder. This is commonly used to 'inject' a value T into a
+ /// different binding level.
+ pub fn new_not_binding<'tcx>(value: T) -> Binder<T>
+ where T: TypeFoldable<'tcx>
+ {
+ assert!(!value.has_escaping_regions());
+ Binder(value)
+ }
+
/// Skips the binder and returns the "bound" value. This is a
/// risky thing to do because it's easy to get confused about
/// debruijn indices and the like. It is usually better to
Some(self.skip_binder().clone())
}
}
+
+ /// Given two things that have the same binder level,
+ /// and an operation that wraps on their contents, execute the operation
+ /// and then wrap its result.
+ ///
+ /// `f` should consider bound regions at depth 1 to be free, and
+ /// anything it produces with bound regions at depth 1 will be
+ /// bound in the resulting return value.
+ pub fn fuse<U,F,R>(self, u: Binder<U>, f: F) -> Binder<R>
+ where F: FnOnce(T, U) -> R
+ {
+ ty::Binder(f(self.0, u.0))
+ }
+
+ /// Split the contents into two things that share the same binder
+ /// level as the original, returning two distinct binders.
+ ///
+ /// `f` should consider bound regions at depth 1 to be free, and
+ /// anything it produces with bound regions at depth 1 will be
+ /// bound in the resulting return values.
+ pub fn split<U,V,F>(self, f: F) -> (Binder<U>, Binder<V>)
+ where F: FnOnce(T) -> (U, V)
+ {
+ let (u, v) = f(self.0);
+ (ty::Binder(u), ty::Binder(v))
+ }
}
/// Represents the projection of an associated type. In explicit UFCS
pub fn input(&self, index: usize) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.inputs()[index])
}
+ pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice<Ty<'tcx>>> {
+ self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output)
+ }
pub fn output(&self) -> ty::Binder<Ty<'tcx>> {
self.map_bound_ref(|fn_sig| fn_sig.output().clone())
}
time(time_passes,
"MIR borrow checking",
- || for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id) });
+ || for def_id in tcx.body_owners() { tcx.mir_borrowck(def_id); });
time(time_passes,
"MIR effect checking",
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
+use rustc::mir::ClosureRegionRequirements;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_set::{self, IdxSetBuf};
};
}
-fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
+fn mir_borrowck<'a, 'tcx>(
+ tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ def_id: DefId,
+) -> Option<ClosureRegionRequirements> {
let input_mir = tcx.mir_validated(def_id);
debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
!tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
&& !tcx.sess.opts.debugging_opts.nll
} {
- return;
+ return None;
}
- tcx.infer_ctxt().enter(|infcx| {
+ let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
let input_mir: &Mir = &input_mir.borrow();
- do_mir_borrowck(&infcx, input_mir, def_id);
+ do_mir_borrowck(&infcx, input_mir, def_id)
});
debug!("mir_borrowck done");
+
+ opt_closure_req
}
fn do_mir_borrowck<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
input_mir: &Mir<'gcx>,
def_id: DefId,
-) {
+) -> Option<ClosureRegionRequirements> {
let tcx = infcx.tcx;
let attributes = tcx.get_attrs(def_id);
let param_env = tcx.param_env(def_id);
let mir = &mut mir;
// Replace all regions with fresh inference variables.
- Some(nll::replace_regions_in_mir(infcx, def_id, mir))
+ Some(nll::replace_regions_in_mir(infcx, def_id, param_env, mir))
};
let mir = &mir;
));
// If we are in non-lexical mode, compute the non-lexical lifetimes.
- let opt_regioncx = if let Some(free_regions) = free_regions {
- Some(nll::compute_regions(
+ let (opt_regioncx, opt_closure_req) = if let Some(free_regions) = free_regions {
+ let (regioncx, opt_closure_req) = nll::compute_regions(
infcx,
def_id,
free_regions,
param_env,
&mut flow_inits,
&mdpe.move_data,
- ))
+ );
+ (Some(regioncx), opt_closure_req)
} else {
assert!(!tcx.sess.opts.debugging_opts.nll);
- None
+ (None, None)
};
let flow_inits = flow_inits; // remove mut
);
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
+
+ opt_closure_req
}
#[allow(dead_code)]
// except according to those terms.
use rustc::hir::def_id::DefId;
-use rustc::mir::Mir;
+use rustc::mir::{ClosureRegionRequirements, Mir};
use rustc::infer::InferCtxt;
use rustc::ty::{self, RegionKind, RegionVid};
use rustc::util::nodemap::FxHashMap;
mod renumber;
/// Rewrites the regions in the MIR to use NLL variables, also
-/// scraping out the set of free regions (e.g., region parameters)
+/// scraping out the set of universal regions (e.g., region parameters)
/// declared on the function. That set will need to be given to
/// `compute_regions`.
pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
def_id: DefId,
+ param_env: ty::ParamEnv<'tcx>,
mir: &mut Mir<'tcx>,
) -> UniversalRegions<'tcx> {
debug!("replace_regions_in_mir(def_id={:?})", def_id);
- // Compute named region information.
- let universal_regions = universal_regions::universal_regions(infcx, def_id);
+ // Compute named region information. This also renumbers the inputs/outputs.
+ let universal_regions = UniversalRegions::new(infcx, def_id, param_env);
- // Replace all regions with fresh inference variables.
+ // Replace all remaining regions with fresh inference variables.
renumber::renumber_mir(infcx, &universal_regions, mir);
let source = MirSource::item(def_id);
param_env: ty::ParamEnv<'gcx>,
flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
-) -> RegionInferenceContext<'tcx> {
+) -> (
+ RegionInferenceContext<'tcx>,
+ Option<ClosureRegionRequirements>,
+) {
// Run the MIR type-checker.
let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
// Create the region inference context, taking ownership of the region inference
// data that was contained in `infcx`.
let var_origins = infcx.take_region_var_origins();
- let mut regioncx = RegionInferenceContext::new(var_origins, &universal_regions, mir);
- subtype_constraint_generation::generate(
- &mut regioncx,
- &universal_regions,
- mir,
- constraint_sets,
- );
+ let mut regioncx = RegionInferenceContext::new(var_origins, universal_regions, mir);
+ subtype_constraint_generation::generate(&mut regioncx, mir, constraint_sets);
// Compute what is live where.
let liveness = &LivenessResults {
);
// Solve the region constraints.
- regioncx.solve(infcx, &mir);
+ let closure_region_requirements = regioncx.solve(infcx, &mir, def_id);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests.
dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, ®ioncx);
- regioncx
+ (regioncx, closure_region_requirements)
}
struct LivenessResults {
/// Right now, we piggy back on the `ReVar` to store our NLL inference
/// regions. These are indexed with `RegionVid`. This method will
/// assert that the region is a `ReVar` and extract its interal index.
-/// This is reasonable because in our MIR we replace all free regions
+/// This is reasonable because in our MIR we replace all universal regions
/// with inference variables.
pub trait ToRegionVid {
fn to_region_vid(&self) -> RegionVid;
// except according to those terms.
use super::universal_regions::UniversalRegions;
+use rustc::hir::def_id::DefId;
use rustc::infer::InferCtxt;
-use rustc::infer::RegionVariableOrigin;
use rustc::infer::NLLRegionVariableOrigin;
+use rustc::infer::RegionVariableOrigin;
+use rustc::infer::SubregionOrigin;
use rustc::infer::region_constraints::VarOrigins;
-use rustc::infer::outlives::free_region_map::FreeRegionMap;
-use rustc::mir::{Location, Mir};
+use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_data_structures::fx::FxHashSet;
/// the free regions.)
point_indices: BTreeMap<Location, usize>,
- /// Number of universally quantified regions. This is used to
- /// determine the meaning of the bits in `inferred_values` and
- /// friends.
- num_universal_regions: usize,
-
- free_region_map: &'tcx FreeRegionMap<'tcx>,
+ /// Information about the universally quantified regions in scope
+ /// on this function and their (known) relations to one another.
+ universal_regions: UniversalRegions<'tcx>,
}
struct RegionDefinition<'tcx> {
/// late-bound-regions).
origin: RegionVariableOrigin,
- /// If this is a free-region, then this is `Some(X)` where `X` is
- /// the name of the region.
- name: Option<ty::Region<'tcx>>,
+ /// True if this is a universally quantified region. This means a
+ /// lifetime parameter that appears in the function signature (or,
+ /// in the case of a closure, in the closure environment, which of
+ /// course is also in the function signature).
+ is_universal: bool,
+
+ /// If this is 'static or an early-bound region, then this is
+ /// `Some(X)` where `X` is the name of the region.
+ external_name: Option<ty::Region<'tcx>>,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// regions defined in `universal_regions`.
pub fn new(
var_origins: VarOrigins,
- universal_regions: &UniversalRegions<'tcx>,
+ universal_regions: UniversalRegions<'tcx>,
mir: &Mir<'tcx>,
) -> Self {
let num_region_variables = var_origins.len();
- let num_universal_regions = universal_regions.indices.len();
+ let num_universal_regions = universal_regions.len();
let mut num_points = 0;
let mut point_indices = BTreeMap::new();
inferred_values: None,
constraints: Vec::new(),
point_indices,
- num_universal_regions,
- free_region_map: universal_regions.free_region_map,
+ universal_regions,
};
- result.init_universal_regions(universal_regions);
+ result.init_universal_regions();
result
}
/// R1 = { CFG, R0, R1 } // 'b
///
/// Here, R0 represents `'a`, and it contains (a) the entire CFG
- /// and (b) any free regions that it outlives, which in this case
- /// is just itself. R1 (`'b`) in contrast also outlives `'a` and
- /// hence contains R0 and R1.
- fn init_universal_regions(&mut self, universal_regions: &UniversalRegions<'tcx>) {
- let UniversalRegions {
- indices,
- free_region_map: _,
- } = universal_regions;
+ /// and (b) any universally quantified regions that it outlives,
+ /// which in this case is just itself. R1 (`'b`) in contrast also
+ /// outlives `'a` and hence contains R0 and R1.
+ fn init_universal_regions(&mut self) {
+ // Update the names (if any)
+ for (external_name, variable) in self.universal_regions.named_universal_regions() {
+ self.definitions[variable].external_name = Some(external_name);
+ }
// For each universally quantified region X:
- for (free_region, &variable) in indices {
+ for variable in self.universal_regions.universal_regions() {
// These should be free-region variables.
assert!(match self.definitions[variable].origin {
RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
_ => false,
});
- // Initialize the name and a few other details.
- self.definitions[variable].name = Some(free_region);
+ self.definitions[variable].is_universal = true;
// Add all nodes in the CFG to liveness constraints
for (_location, point_index) in &self.point_indices {
self.definitions.indices()
}
+ /// Given a universal region in scope on the MIR, returns the
+ /// corresponding index.
+ ///
+ /// (Panics if `r` is not a registered universal region.)
+ pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+ self.universal_regions.to_region_vid(r)
+ }
+
/// Returns true if the region `r` contains the point `p`.
///
/// Panics if called before `solve()` executes,
.as_ref()
.expect("region values not yet inferred");
+ self.region_value_str_from_matrix(inferred_values, r)
+ }
+
+ fn region_value_str_from_matrix(&self,
+ matrix: &BitMatrix,
+ r: RegionVid) -> String {
let mut result = String::new();
result.push_str("{");
let mut sep = "";
for &point in self.point_indices.keys() {
- if self.region_contains_point_in_matrix(inferred_values, r, point) {
+ if self.region_contains_point_in_matrix(matrix, r, point) {
result.push_str(&format!("{}{:?}", sep, point));
sep = ", ";
}
}
- for fr in (0..self.num_universal_regions).map(RegionVid::new) {
- if self.region_contains_region_in_matrix(inferred_values, r, fr) {
+ for fr in (0..self.universal_regions.len()).map(RegionVid::new) {
+ if self.region_contains_region_in_matrix(matrix, r, fr) {
result.push_str(&format!("{}{:?}", sep, fr));
sep = ", ";
}
}
/// Perform region inference.
- pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
+ pub(super) fn solve(
+ &mut self,
+ infcx: &InferCtxt<'_, '_, 'tcx>,
+ mir: &Mir<'tcx>,
+ mir_def_id: DefId,
+ ) -> Option<ClosureRegionRequirements> {
assert!(self.inferred_values.is_none(), "values already inferred");
+ let tcx = infcx.tcx;
// Find the minimal regions that can solve the constraints. This is infallible.
self.propagate_constraints(mir);
// The universal regions are always found in a prefix of the
// full list.
- let free_region_definitions = self.definitions
+ let universal_definitions = self.definitions
.iter_enumerated()
- .take_while(|(_, fr_definition)| fr_definition.name.is_some());
+ .take_while(|(_, fr_definition)| fr_definition.is_universal);
+
+ // Go through each of the universal regions `fr` and check that
+ // they did not grow too large, accumulating any requirements
+ // for our caller into the `outlives_requirements` vector.
+ let mut outlives_requirements = vec![];
+ for (fr, _) in universal_definitions {
+ self.check_universal_region(infcx, fr, &mut outlives_requirements);
+ }
- for (fr, fr_definition) in free_region_definitions {
- self.check_free_region(infcx, fr, fr_definition);
+ // If this is not a closure, then there is no caller to which we can
+ // "pass the buck". So if there are any outlives-requirements that were
+ // not satisfied, we just have to report a hard error here.
+ if !tcx.is_closure(mir_def_id) {
+ for outlives_requirement in outlives_requirements {
+ self.report_error(
+ infcx,
+ outlives_requirement.free_region,
+ outlives_requirement.outlived_free_region,
+ outlives_requirement.blame_span,
+ );
+ }
+ return None;
}
+
+ let num_external_vids = self.universal_regions.num_global_and_external_regions();
+
+ Some(ClosureRegionRequirements {
+ num_external_vids,
+ outlives_requirements,
+ })
}
- fn check_free_region(
+ /// Check the final value for the free region `fr` to see if it
+ /// grew too large. In particular, examine what `end(X)` points
+ /// wound up in `fr`'s final value; for each `end(X)` where `X !=
+ /// fr`, we want to check that `fr: X`. If not, that's either an
+ /// error, or something we have to propagate to our creator.
+ ///
+ /// Things that are to be propagated are accumulated into the
+ /// `outlives_requirements` vector.
+ fn check_universal_region(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
longer_fr: RegionVid,
- longer_definition: &RegionDefinition<'tcx>,
+ outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
) {
let inferred_values = self.inferred_values.as_ref().unwrap();
- let longer_name = longer_definition.name.unwrap();
let longer_value = inferred_values.iter(longer_fr.index());
- // Find every region `shorter` such that `longer: shorter`
- // (because `longer` includes `end(shorter)`).
- for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) {
- let shorter_fr = RegionVid::new(shorter_fr);
+ debug!("check_universal_region(fr={:?})", longer_fr);
- // `fr` includes `end(fr)`, that's not especially
- // interesting.
- if longer_fr == shorter_fr {
+ // Find every region `o` such that `fr: o`
+ // (because `fr` includes `end(o)`).
+ let shorter_frs = longer_value
+ .take_while(|&i| i < self.universal_regions.len())
+ .map(RegionVid::new);
+ for shorter_fr in shorter_frs {
+ // If it is known that `fr: o`, carry on.
+ if self.universal_regions.outlives(longer_fr, shorter_fr) {
continue;
}
- let shorter_definition = &self.definitions[shorter_fr];
- let shorter_name = shorter_definition.name.unwrap();
-
- // Check that `o <= fr`. If not, report an error.
- if !self.free_region_map
- .sub_free_regions(shorter_name, longer_name)
- {
- // FIXME: worst error msg ever
- let blame_span = self.blame_span(longer_fr, shorter_fr);
- infcx.tcx.sess.span_err(
- blame_span,
- &format!(
- "free region `{}` does not outlive `{}`",
- longer_name,
- shorter_name
- ),
+ debug!(
+ "check_universal_region: fr={:?} does not outlive shorter_fr={:?}",
+ longer_fr,
+ shorter_fr,
+ );
+
+ let blame_span = self.blame_span(longer_fr, shorter_fr);
+
+ // Shrink `fr` until we find a non-local region (if we do).
+ // We'll call that `fr-` -- it's ever so slightly smaller than `fr`.
+ if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) {
+ debug!("check_universal_region: fr_minus={:?}", fr_minus);
+
+ // Grow `shorter_fr` until we find a non-local
+ // regon. (We always will.) We'll call that
+ // `shorter_fr+` -- it's ever so slightly larger than
+ // `fr`.
+ let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr);
+ debug!(
+ "check_universal_region: shorter_fr_plus={:?}",
+ shorter_fr_plus
);
+
+ // Push the constraint `fr-: shorter_fr+`
+ outlives_requirements.push(ClosureOutlivesRequirement {
+ free_region: fr_minus,
+ outlived_free_region: shorter_fr_plus,
+ blame_span: blame_span,
+ });
+ return;
}
+
+ // If we could not shrink `fr` to something smaller that
+ // the external users care about, then we can't pass the
+ // buck; just report an error.
+ self.report_error(infcx, longer_fr, shorter_fr, blame_span);
}
}
+ fn report_error(
+ &self,
+ infcx: &InferCtxt<'_, '_, 'tcx>,
+ fr: RegionVid,
+ outlived_fr: RegionVid,
+ blame_span: Span,
+ ) {
+ // Obviously uncool error reporting.
+
+ let fr_string = match self.definitions[fr].external_name {
+ Some(r) => format!("free region `{}`", r),
+ None => format!("free region `{:?}`", fr),
+ };
+
+ let outlived_fr_string = match self.definitions[outlived_fr].external_name {
+ Some(r) => format!("free region `{}`", r),
+ None => format!("free region `{:?}`", outlived_fr),
+ };
+
+ infcx.tcx.sess.span_err(
+ blame_span,
+ &format!("{} does not outlive {}", fr_string, outlived_fr_string,),
+ );
+ }
+
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
stack.push(start_point);
while let Some(p) = stack.pop() {
- debug!(" copy: p={:?}", p);
-
if !self.region_contains_point_in_matrix(inferred_values, from_region, p) {
debug!(" not in from-region");
continue;
// and make sure they are included in the `to_region`.
let universal_region_indices = inferred_values
.iter(from_region.index())
- .take_while(|&i| i < self.num_universal_regions)
+ .take_while(|&i| i < self.universal_regions.len())
.collect::<Vec<_>>();
for fr in &universal_region_indices {
changed |= inferred_values.add(to_region.index(), *fr);
// Create a new region definition. Note that, for free
// regions, these fields get updated later in
// `init_universal_regions`.
- Self { origin, name: None }
+ Self {
+ origin,
+ is_universal: false,
+ external_name: None,
+ }
}
}
)
}
}
+
+pub trait ClosureRegionRequirementsExt {
+ fn apply_requirements<'tcx>(
+ &self,
+ infcx: &InferCtxt<'_, '_, 'tcx>,
+ location: Location,
+ closure_def_id: DefId,
+ closure_substs: ty::ClosureSubsts<'tcx>,
+ );
+}
+
+impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
+ /// Given an instance T of the closure type, this method
+ /// instantiates the "extra" requirements that we computed for the
+ /// closure into the inference context. This has the effect of
+ /// adding new subregion obligations to existing variables.
+ ///
+ /// As described on `ClosureRegionRequirements`, the extra
+ /// requirements are expressed in terms of regionvids that index
+ /// into the free regions that appear on the closure type. So, to
+ /// do this, we first copy those regions out from the type T into
+ /// a vector. Then we can just index into that vector to extract
+ /// out the corresponding region from T and apply the
+ /// requirements.
+ fn apply_requirements<'tcx>(
+ &self,
+ infcx: &InferCtxt<'_, '_, 'tcx>,
+ location: Location,
+ closure_def_id: DefId,
+ closure_substs: ty::ClosureSubsts<'tcx>,
+ ) {
+ let tcx = infcx.tcx;
+
+ debug!(
+ "apply_requirements(location={:?}, closure_def_id={:?}, closure_substs={:?})",
+ location,
+ closure_def_id,
+ closure_substs
+ );
+
+ // Get Tu.
+ let user_closure_ty = tcx.mk_closure(closure_def_id, closure_substs);
+ debug!("apply_requirements: user_closure_ty={:?}", user_closure_ty);
+
+ // Extract the values of the free regions in `user_closure_ty`
+ // into a vector. These are the regions that we will be
+ // relating to one another.
+ let closure_mapping =
+ UniversalRegions::closure_mapping(infcx, user_closure_ty, self.num_external_vids);
+ debug!("apply_requirements: closure_mapping={:?}", closure_mapping);
+
+ // Create the predicates.
+ for outlives_requirement in &self.outlives_requirements {
+ let region = closure_mapping[outlives_requirement.free_region];
+ let outlived_region = closure_mapping[outlives_requirement.outlived_free_region];
+ debug!(
+ "apply_requirements: region={:?} outlived_region={:?} outlives_requirements={:?}",
+ region,
+ outlived_region,
+ outlives_requirement
+ );
+ // FIXME, this origin is not entirely suitable.
+ let origin = SubregionOrigin::CallRcvr(outlives_requirement.blame_span);
+ infcx.sub_regions(origin, outlived_region, region);
+ }
+ }
+}
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::subst::Substs;
-use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
+use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
+use rustc::mir::RETURN_PLACE;
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
universal_regions: &UniversalRegions<'tcx>,
mir: &mut Mir<'tcx>,
) {
- // Create inference variables for each of the free regions
- // declared on the function signature.
- let free_region_inference_vars = (0..universal_regions.indices.len())
- .map(RegionVid::new)
- .map(|vid_expected| {
- let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
- assert_eq!(vid_expected, r.to_region_vid());
- r
- })
- .collect();
-
debug!("renumber_mir()");
- debug!("renumber_mir: universal_regions={:#?}", universal_regions);
debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
+ // Update the return type and types of the arguments based on the
+ // `universal_regions` computation.
+ debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty);
+ mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty;
+ for (&input_ty, local) in universal_regions
+ .input_tys
+ .iter()
+ .zip((1..).map(Local::new))
+ {
+ debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local);
+ mir.local_decls[local].ty = input_ty;
+ }
+
let mut visitor = NLLVisitor {
infcx,
- universal_regions,
- free_region_inference_vars,
arg_count: mir.arg_count,
};
visitor.visit_mir(mir);
struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
- universal_regions: &'a UniversalRegions<'tcx>,
- free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
arg_count: usize,
}
})
}
- /// Renumbers the regions appearing in `value`, but those regions
- /// are expected to be free regions from the function signature.
- fn renumber_universal_regions<T>(&mut self, value: &T) -> T
+ /// Checks that all the regions appearing in `value` have already
+ /// been renumbered. `FreeRegions` code should have done this.
+ fn assert_free_regions_are_renumbered<T>(&self, value: &T)
where
T: TypeFoldable<'tcx>,
{
- debug!("renumber_universal_regions(value={:?})", value);
+ debug!("assert_free_regions_are_renumbered(value={:?})", value);
- self.infcx
- .tcx
- .fold_regions(value, &mut false, |region, _depth| {
- let index = self.universal_regions.indices[®ion];
- self.free_region_inference_vars[index]
- })
+ self.infcx.tcx.for_each_free_region(value, |region| {
+ region.to_region_vid(); // will panic if `region` is not renumbered
+ });
}
fn is_argument_or_return_slot(&self, local: Local) -> bool {
ty_context
);
- let old_ty = *ty;
- *ty = if is_arg {
- self.renumber_universal_regions(&old_ty)
+ if is_arg {
+ self.assert_free_regions_are_renumbered(ty);
} else {
- self.renumber_regions(ty_context, &old_ty)
- };
+ *ty = self.renumber_regions(ty_context, ty);
+ }
+
debug!("visit_ty: ty={:?}", ty);
}
use transform::type_check::MirTypeckRegionConstraints;
use transform::type_check::OutlivesSet;
-use super::universal_regions::UniversalRegions;
use super::region_infer::RegionInferenceContext;
/// When the MIR type-checker executes, it validates all the types in
/// them into the NLL `RegionInferenceContext`.
pub(super) fn generate<'tcx>(
regioncx: &mut RegionInferenceContext<'tcx>,
- universal_regions: &UniversalRegions<'tcx>,
mir: &Mir<'tcx>,
constraints: &MirTypeckRegionConstraints<'tcx>,
) {
SubtypeConstraintGenerator {
regioncx,
- universal_regions,
mir,
}.generate(constraints);
}
struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
regioncx: &'cx mut RegionInferenceContext<'tcx>,
- universal_regions: &'cx UniversalRegions<'tcx>,
mir: &'cx Mir<'tcx>,
}
if let ty::ReVar(vid) = r {
*vid
} else {
- *self.universal_regions
- .indices
- .get(&r)
- .unwrap_or_else(|| bug!("to_region_vid: bad region {:?}", r))
+ self.regioncx.to_region_vid(r)
}
}
}
//! The code in this file doesn't *do anything* with those results; it
//! just returns them for other code to use.
+use rustc::hir::HirId;
use rustc::hir::def_id::DefId;
-use rustc::infer::InferCtxt;
-use rustc::infer::outlives::free_region_map::FreeRegionMap;
-use rustc::ty::{self, RegionVid};
+use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
+use rustc::infer::region_constraints::GenericKind;
+use rustc::infer::outlives::bounds::{self, OutlivesBound};
+use rustc::ty::{self, RegionVid, Ty, TyCtxt};
+use rustc::ty::fold::TypeFoldable;
use rustc::ty::subst::Substs;
use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::transitive_relation::TransitiveRelation;
+use std::iter;
+use syntax::ast;
+
+use super::ToRegionVid;
#[derive(Debug)]
pub struct UniversalRegions<'tcx> {
- /// Given a universally quantified region defined on this function
- /// (either early- or late-bound), this maps it to its internal
- /// region index. When the region context is created, the first N
- /// variables will be created based on these indices.
- pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
-
- /// The map from the typeck tables telling us how to relate universal regions.
- pub free_region_map: &'tcx FreeRegionMap<'tcx>,
+ indices: UniversalRegionIndices<'tcx>,
+
+ /// The vid assigned to `'static`
+ pub fr_static: RegionVid,
+
+ /// We create region variables such that they are ordered by their
+ /// `RegionClassification`. The first block are globals, then
+ /// externals, then locals. So things from:
+ /// - `FIRST_GLOBAL_INDEX..first_extern_index` are global;
+ /// - `first_extern_index..first_local_index` are external; and
+ /// - first_local_index..num_universals` are local.
+ first_extern_index: usize,
+
+ /// See `first_extern_index`.
+ first_local_index: usize,
+
+ /// The total number of universal region variables instantiated.
+ num_universals: usize,
+
+ /// The "defining" type for this function, with all universal
+ /// regions instantiated. For a closure or generator, this is the
+ /// closure type, but for a top-level function it's the `TyFnDef`.
+ pub defining_ty: Ty<'tcx>,
+
+ /// The return type of this function, with all regions replaced
+ /// by their universal `RegionVid` equivalents.
+ pub output_ty: Ty<'tcx>,
+
+ /// The fully liberated input types of this function, with all
+ /// regions replaced by their universal `RegionVid` equivalents.
+ pub input_tys: &'tcx [Ty<'tcx>],
+
+ /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to
+ /// be true. These encode relationships like `T: 'a` that are
+ /// added via implicit bounds.
+ ///
+ /// Each region here is guaranteed to be a key in the `indices`
+ /// map. We use the "original" regions (i.e., the keys from the
+ /// map, and not the values) because the code in
+ /// `process_registered_region_obligations` has some special-cased
+ /// logic expecting to see (e.g.) `ReStatic`, and if we supplied
+ /// our special inference variable there, we would mess that up.
+ pub region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
+
+ relations: UniversalRegionRelations,
+}
+
+#[derive(Debug)]
+struct UniversalRegionIndices<'tcx> {
+ /// For those regions that may appear in the parameter environment
+ /// ('static and early-bound regions), we maintain a map from the
+ /// `ty::Region` to the internal `RegionVid` we are using. This is
+ /// used because trait matching and type-checking will feed us
+ /// region constraints that reference those regions and we need to
+ /// be able to map them our internal `RegionVid`. This is
+ /// basically equivalent to a `Substs`, except that it also
+ /// contains an entry for `ReStatic` -- it might be nice to just
+ /// use a substs, and then handle `ReStatic` another way.
+ indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
+}
+
+#[derive(Debug)]
+struct UniversalRegionRelations {
+ /// Stores the outlives relations that are known to hold from the
+ /// implied bounds, in-scope where clauses, and that sort of
+ /// thing.
+ outlives: TransitiveRelation<RegionVid>,
+
+ /// This is the `<=` relation; that is, if `a: b`, then `b <= a`,
+ /// and we store that here. This is useful when figuring out how
+ /// to express some local region in terms of external regions our
+ /// caller will understand.
+ inverse_outlives: TransitiveRelation<RegionVid>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum RegionClassification {
+ /// A **global** region is one that can be named from
+ /// anywhere. There is only one, `'static`.
+ Global,
+
+ /// An **external** region is only relevant for closures. In that
+ /// case, it refers to regions that are free in the closure type
+ /// -- basically, something bound in the surrounding context.
+ ///
+ /// Consider this example:
+ ///
+ /// ```
+ /// fn foo<'a, 'b>(a: &'a u32, b: &'b u32, c: &'static u32) {
+ /// let closure = for<'x> |x: &'x u32| { .. };
+ /// ^^^^^^^ pretend this were legal syntax
+ /// for declaring a late-bound region in
+ /// a closure signature
+ /// }
+ /// ```
+ ///
+ /// Here, the lifetimes `'a` and `'b` would be **external** to the
+ /// closure.
+ ///
+ /// If we are not analyzing a closure, there are no external
+ /// lifetimes.
+ External,
+
+ /// A **local** lifetime is one about which we know the full set
+ /// of relevant constraints (that is, relationships to other named
+ /// regions). For a closure, this includes any region bound in
+ /// the closure's signature. For a fn item, this includes all
+ /// regions other than global ones.
+ ///
+ /// Continuing with the example from `External`, if we were
+ /// analyzing the closure, then `'x` would be local (and `'a` and
+ /// `'b` are external). If we are analyzing the function item
+ /// `foo`, then `'a` and `'b` are local (and `'x` is not in
+ /// scope).
+ Local,
}
-pub fn universal_regions<'a, 'gcx, 'tcx>(
- infcx: &InferCtxt<'a, 'gcx, 'tcx>,
- item_def_id: DefId,
-) -> UniversalRegions<'tcx> {
- debug!("universal_regions(item_def_id={:?})", item_def_id);
+const FIRST_GLOBAL_INDEX: usize = 0;
+
+impl<'tcx> UniversalRegions<'tcx> {
+ /// Creates a new and fully initialized `UniversalRegions` that
+ /// contains indices for all the free regions found in the given
+ /// MIR -- that is, all the regions that appear in the function's
+ /// signature. This will also compute the relationships that are
+ /// known between those regions.
+ pub fn new(
+ infcx: &InferCtxt<'_, '_, 'tcx>,
+ mir_def_id: DefId,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Self {
+ let tcx = infcx.tcx;
+ let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).unwrap();
+ let mir_hir_id = tcx.hir.node_to_hir_id(mir_node_id);
+ UniversalRegionsBuilder {
+ infcx,
+ mir_def_id,
+ mir_node_id,
+ mir_hir_id,
+ param_env,
+ region_bound_pairs: vec![],
+ relations: UniversalRegionRelations {
+ outlives: TransitiveRelation::new(),
+ inverse_outlives: TransitiveRelation::new(),
+ },
+ }.build()
+ }
+
+ /// Given a reference to a closure type, extracts all the values
+ /// from its free regions and returns a vector with them. This is
+ /// used when the closure's creator checks that the
+ /// `ClosureRegionRequirements` are met. The requirements from
+ /// `ClosureRegionRequirements` are expressed in terms of
+ /// `RegionVid` entries that map into the returned vector `V`: so
+ /// if the `ClosureRegionRequirements` contains something like
+ /// `'1: '2`, then the caller would impose the constraint that
+ /// `V[1]: V[2]`.
+ pub fn closure_mapping(
+ infcx: &InferCtxt<'_, '_, 'tcx>,
+ closure_ty: Ty<'tcx>,
+ expected_num_vars: usize,
+ ) -> IndexVec<RegionVid, ty::Region<'tcx>> {
+ let mut region_mapping = IndexVec::with_capacity(expected_num_vars);
+ region_mapping.push(infcx.tcx.types.re_static);
+ infcx.tcx.for_each_free_region(&closure_ty, |fr| {
+ region_mapping.push(fr);
+ });
+
+ assert_eq!(
+ region_mapping.len(),
+ expected_num_vars,
+ "index vec had unexpected number of variables"
+ );
- let mut indices = FxHashMap();
+ region_mapping
+ }
- // `'static` is always free.
- insert_free_region(&mut indices, infcx.tcx.types.re_static);
+ /// True if `r` is a member of this set of universal regions.
+ pub fn is_universal_region(&self, r: RegionVid) -> bool {
+ (FIRST_GLOBAL_INDEX..self.num_universals).contains(r.index())
+ }
- // Extract the early regions.
- let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
- for item_subst in item_substs {
- if let Some(region) = item_subst.as_region() {
- insert_free_region(&mut indices, region);
+ /// Classifies `r` as a universal region, returning `None` if this
+ /// is not a member of this set of universal regions.
+ pub fn region_classification(&self, r: RegionVid) -> Option<RegionClassification> {
+ let index = r.index();
+ if (FIRST_GLOBAL_INDEX..self.first_extern_index).contains(index) {
+ Some(RegionClassification::Global)
+ } else if (self.first_extern_index..self.first_local_index).contains(index) {
+ Some(RegionClassification::External)
+ } else if (self.first_local_index..self.num_universals).contains(index) {
+ Some(RegionClassification::Local)
+ } else {
+ None
}
}
- // Extract the late-bound regions. Use the liberated fn sigs,
- // where the late-bound regions will have been converted into free
- // regions, and add them to the map.
- let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap();
- let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
- let tables = infcx.tcx.typeck_tables_of(item_def_id);
- let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
- infcx
- .tcx
- .for_each_free_region(&fn_sig.inputs_and_output, |region| {
- if let ty::ReFree(_) = *region {
- insert_free_region(&mut indices, region);
+ /// Returns an iterator over all the RegionVids corresponding to
+ /// universally quantified free regions.
+ pub fn universal_regions(&self) -> impl Iterator<Item = RegionVid> {
+ (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new)
+ }
+
+ /// True if `r` is classied as a global region.
+ pub fn is_global_free_region(&self, r: RegionVid) -> bool {
+ self.region_classification(r) == Some(RegionClassification::Global)
+ }
+
+ /// True if `r` is classied as an external region.
+ pub fn is_extern_free_region(&self, r: RegionVid) -> bool {
+ self.region_classification(r) == Some(RegionClassification::External)
+ }
+
+ /// True if `r` is classied as an local region.
+ pub fn is_local_free_region(&self, r: RegionVid) -> bool {
+ self.region_classification(r) == Some(RegionClassification::Local)
+ }
+
+ /// Returns the number of universal regions created in any category.
+ pub fn len(&self) -> usize {
+ self.num_universals
+ }
+
+ /// Finds an "upper bound" for `fr` that is not local. In other
+ /// words, returns the smallest (*) known region `fr1` that (a)
+ /// outlives `fr` and (b) is not local. This cannot fail, because
+ /// we will always find `'static` at worst.
+ ///
+ /// (*) If there are multiple competing choices, we pick the "postdominating"
+ /// one. See `TransitiveRelation::postdom_upper_bound` for details.
+ pub fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
+ debug!("non_local_upper_bound(fr={:?})", fr);
+ self.non_local_bound(&self.relations.inverse_outlives, fr)
+ .unwrap_or(self.fr_static)
+ }
+
+ /// Finds a "lower bound" for `fr` that is not local. In other
+ /// words, returns the largest (*) known region `fr1` that (a) is
+ /// outlived by `fr` and (b) is not local. This cannot fail,
+ /// because we will always find `'static` at worst.
+ ///
+ /// (*) If there are multiple competing choices, we pick the "postdominating"
+ /// one. See `TransitiveRelation::postdom_upper_bound` for details.
+ pub fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
+ debug!("non_local_lower_bound(fr={:?})", fr);
+ self.non_local_bound(&self.relations.outlives, fr)
+ }
+
+ /// Returns the number of global plus external universal regions.
+ /// For closures, these are the regions that appear free in the
+ /// closure type (versus those bound in the closure
+ /// signature). They are therefore the regions between which the
+ /// closure may impose constraints that its creator must verify.
+ pub fn num_global_and_external_regions(&self) -> usize {
+ self.first_local_index
+ }
+
+ /// Helper for `non_local_upper_bound` and
+ /// `non_local_lower_bound`. Repeatedly invokes `postdom_parent`
+ /// until we find something that is not local. Returns None if we
+ /// never do so.
+ fn non_local_bound(
+ &self,
+ relation: &TransitiveRelation<RegionVid>,
+ fr0: RegionVid,
+ ) -> Option<RegionVid> {
+ let mut external_parents = vec![];
+ let mut queue = vec![&fr0];
+
+ // Keep expanding `fr` into its parents until we reach
+ // non-local regions.
+ while let Some(fr) = queue.pop() {
+ if !self.is_local_free_region(*fr) {
+ external_parents.push(fr);
+ continue;
}
- });
- debug!("universal_regions: indices={:#?}", indices);
+ queue.extend(relation.parents(fr));
+ }
+
+ debug!("non_local_bound: external_parents={:?}", external_parents);
+
+ // In case we find more than one, reduce to one for
+ // convenience. This is to prevent us from generating more
+ // complex constraints, but it will cause spurious errors.
+ let post_dom = relation
+ .mutual_immediate_postdominator(external_parents)
+ .cloned();
+
+ debug!("non_local_bound: post_dom={:?}", post_dom);
+
+ post_dom.and_then(|post_dom| {
+ // If the mutual immediate postdom is not local, then
+ // there is no non-local result we can return.
+ if !self.is_local_free_region(post_dom) {
+ Some(post_dom)
+ } else {
+ None
+ }
+ })
+ }
+
+ /// True if fr1 is known to outlive fr2.
+ ///
+ /// This will only ever be true for universally quantified regions.
+ pub fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
+ self.relations.outlives.contains(&fr1, &fr2)
+ }
+
+ /// Returns a vector of free regions `x` such that `fr1: x` is
+ /// known to hold.
+ pub fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<&RegionVid> {
+ self.relations.outlives.reachable_from(&fr1)
+ }
+
+ /// Get an iterator over all the early-bound regions that have names.
+ pub fn named_universal_regions<'s>(
+ &'s self,
+ ) -> impl Iterator<Item = (ty::Region<'tcx>, ty::RegionVid)> + 's {
+ self.indices.indices.iter().map(|(&r, &v)| (r, v))
+ }
+
+ /// See `UniversalRegionIndices::to_region_vid`.
+ pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+ self.indices.to_region_vid(r)
+ }
+}
+
+struct UniversalRegionsBuilder<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+ infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+ mir_def_id: DefId,
+ mir_hir_id: HirId,
+ mir_node_id: ast::NodeId,
+ param_env: ty::ParamEnv<'tcx>,
+ region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
+ relations: UniversalRegionRelations,
+}
+
+const FR: NLLRegionVariableOrigin = NLLRegionVariableOrigin::FreeRegion;
+
+impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
+ fn build(mut self) -> UniversalRegions<'tcx> {
+ let param_env = self.param_env;
+
+ assert_eq!(FIRST_GLOBAL_INDEX, self.infcx.num_region_vars());
+
+ // Create the "global" region that is always free in all contexts: 'static.
+ let fr_static = self.infcx.next_nll_region_var(FR).to_region_vid();
+
+ // We've now added all the global regions. The next ones we
+ // add will be external.
+ let first_extern_index = self.infcx.num_region_vars();
+
+ let defining_ty = self.defining_ty();
+
+ let indices = self.compute_indices(fr_static, defining_ty);
+
+ let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
+
+ // "Liberate" the late-bound regions. These correspond to
+ // "local" free regions.
+ let first_local_index = self.infcx.num_region_vars();
+ let inputs_and_output = self.infcx
+ .replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output);
+ let num_universals = self.infcx.num_region_vars();
+
+ // Insert the facts we know from the predicates. Why? Why not.
+ self.add_outlives_bounds(&indices, bounds::explicit_outlives_bounds(param_env));
+
+ // Add the implied bounds from inputs and outputs.
+ for ty in inputs_and_output {
+ self.add_implied_bounds(&indices, ty);
+ }
+
+ // Finally, outlives is reflexive, and static outlives every
+ // other free region.
+ for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) {
+ self.relations.relate_universal_regions(fr, fr);
+ self.relations.relate_universal_regions(fr_static, fr);
+ }
+
+ let (output_ty, input_tys) = inputs_and_output.split_last().unwrap();
+
+ // we should not have created any more variables
+ assert_eq!(self.infcx.num_region_vars(), num_universals);
+
+ debug!("build: global regions = {}..{}",
+ FIRST_GLOBAL_INDEX,
+ first_extern_index);
+ debug!("build: extern regions = {}..{}",
+ first_extern_index,
+ first_local_index);
+ debug!("build: local regions = {}..{}",
+ first_local_index,
+ num_universals);
+
+ UniversalRegions {
+ indices,
+ fr_static,
+ first_extern_index,
+ first_local_index,
+ num_universals,
+ defining_ty,
+ output_ty,
+ input_tys,
+ region_bound_pairs: self.region_bound_pairs,
+ relations: self.relations,
+ }
+ }
+
+ fn defining_ty(&self) -> ty::Ty<'tcx> {
+ let tcx = self.infcx.tcx;
+ let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
+
+ let defining_ty = if self.mir_def_id == closure_base_def_id {
+ tcx.type_of(closure_base_def_id)
+ } else {
+ let tables = tcx.typeck_tables_of(self.mir_def_id);
+ tables.node_id_to_type(self.mir_hir_id)
+ };
+
+ self.infcx
+ .replace_free_regions_with_nll_infer_vars(FR, &defining_ty)
+ }
+
+ fn compute_indices(
+ &self,
+ fr_static: RegionVid,
+ defining_ty: Ty<'tcx>,
+ ) -> UniversalRegionIndices<'tcx> {
+ let tcx = self.infcx.tcx;
+ let gcx = tcx.global_tcx();
+ let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
+ let identity_substs = Substs::identity_for_item(gcx, closure_base_def_id);
+ let fr_substs = match defining_ty.sty {
+ ty::TyClosure(_, substs) | ty::TyGenerator(_, substs, ..) => {
+ // In the case of closures, we rely on the fact that
+ // the first N elements in the ClosureSubsts are
+ // inherited from the `closure_base_def_id`.
+ // Therefore, when we zip together (below) with
+ // `identity_substs`, we will get only those regions
+ // that correspond to early-bound regions declared on
+ // the `closure_base_def_id`.
+ assert!(substs.substs.len() >= identity_substs.len());
+ substs.substs
+ }
+ ty::TyFnDef(_, substs) => substs,
+ _ => bug!(),
+ };
+
+ let global_mapping = iter::once((gcx.types.re_static, fr_static));
+ let subst_mapping = identity_substs
+ .regions()
+ .zip(fr_substs.regions().map(|r| r.to_region_vid()));
+
+ UniversalRegionIndices {
+ indices: global_mapping.chain(subst_mapping).collect(),
+ }
+ }
+
+ fn compute_inputs_and_output(
+ &self,
+ indices: &UniversalRegionIndices<'tcx>,
+ defining_ty: Ty<'tcx>,
+ ) -> ty::Binder<&'tcx ty::Slice<Ty<'tcx>>> {
+ let tcx = self.infcx.tcx;
+ match defining_ty.sty {
+ ty::TyClosure(def_id, substs) => {
+ assert_eq!(self.mir_def_id, def_id);
+ let closure_sig = substs.closure_sig_ty(def_id, tcx).fn_sig(tcx);
+ let inputs_and_output = closure_sig.inputs_and_output();
+ let closure_ty = tcx.closure_env_ty(def_id, substs).unwrap();
+ ty::Binder::fuse(
+ closure_ty,
+ inputs_and_output,
+ |closure_ty, inputs_and_output| {
+ // The "inputs" of the closure in the
+ // signature appear as a tuple. The MIR side
+ // flattens this tuple.
+ let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap();
+ assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs");
+ let inputs = match tuplized_inputs[0].sty {
+ ty::TyTuple(inputs, _) => inputs,
+ _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]),
+ };
- UniversalRegions { indices, free_region_map: &tables.free_region_map }
+ tcx.mk_type_list(
+ iter::once(closure_ty)
+ .chain(inputs.iter().cloned())
+ .chain(iter::once(output)),
+ )
+ },
+ )
+ }
+
+ ty::TyGenerator(def_id, substs, ..) => {
+ assert_eq!(self.mir_def_id, def_id);
+ let output = substs.generator_return_ty(def_id, tcx);
+ let inputs_and_output = self.infcx.tcx.intern_type_list(&[defining_ty, output]);
+ ty::Binder::new_not_binding(inputs_and_output)
+ }
+
+ ty::TyFnDef(def_id, _) => {
+ let sig = tcx.fn_sig(def_id);
+ let sig = indices.fold_to_region_vids(tcx, &sig);
+ return sig.inputs_and_output();
+ }
+
+ _ => span_bug!(
+ tcx.def_span(self.mir_def_id),
+ "unexpected defining type: {:?}",
+ defining_ty
+ ),
+ }
+ }
+
+ /// Update the type of a single local, which should represent
+ /// either the return type of the MIR or one of its arguments. At
+ /// the same time, compute and add any implied bounds that come
+ /// from this local.
+ ///
+ /// Assumes that `universal_regions` indices map is fully constructed.
+ fn add_implied_bounds(&mut self, indices: &UniversalRegionIndices<'tcx>, ty: Ty<'tcx>) {
+ let span = self.infcx.tcx.def_span(self.mir_def_id);
+ let bounds = self.infcx
+ .implied_outlives_bounds(self.param_env, self.mir_node_id, ty, span);
+ self.add_outlives_bounds(indices, bounds);
+ }
+
+ /// Registers the `OutlivesBound` items from `outlives_bounds` in
+ /// the outlives relation as well as the region-bound pairs
+ /// listing.
+ fn add_outlives_bounds<I>(&mut self, indices: &UniversalRegionIndices<'tcx>, outlives_bounds: I)
+ where
+ I: IntoIterator<Item = OutlivesBound<'tcx>>,
+ {
+ for outlives_bound in outlives_bounds {
+ match outlives_bound {
+ OutlivesBound::RegionSubRegion(r1, r2) => {
+ // The bound says that `r1 <= r2`; we store `r2: r1`.
+ let r1 = indices.to_region_vid(r1);
+ let r2 = indices.to_region_vid(r2);
+ self.relations.relate_universal_regions(r2, r1);
+ }
+
+ OutlivesBound::RegionSubParam(r_a, param_b) => {
+ self.region_bound_pairs
+ .push((r_a, GenericKind::Param(param_b)));
+ }
+
+ OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+ self.region_bound_pairs
+ .push((r_a, GenericKind::Projection(projection_b)));
+ }
+ }
+ }
+ }
}
-fn insert_free_region<'tcx>(
- universal_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
- region: ty::Region<'tcx>,
-) {
- let next = RegionVid::new(universal_regions.len());
- universal_regions.entry(region).or_insert(next);
+impl UniversalRegionRelations {
+ /// Records in the `outlives_relation` (and
+ /// `inverse_outlives_relation`) that `fr_a: fr_b`.
+ fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
+ debug!(
+ "relate_universal_regions: fr_a={:?} outlives fr_b={:?}",
+ fr_a,
+ fr_b
+ );
+ self.outlives.add(fr_a, fr_b);
+ self.inverse_outlives.add(fr_b, fr_a);
+ }
+}
+
+pub(crate) trait InferCtxtExt<'tcx> {
+ fn replace_free_regions_with_nll_infer_vars<T>(
+ &self,
+ origin: NLLRegionVariableOrigin,
+ value: &T,
+ ) -> T
+ where
+ T: TypeFoldable<'tcx>;
+
+ fn replace_bound_regions_with_nll_infer_vars<T>(
+ &self,
+ origin: NLLRegionVariableOrigin,
+ value: &ty::Binder<T>,
+ ) -> T
+ where
+ T: TypeFoldable<'tcx>;
+}
+
+impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> {
+ fn replace_free_regions_with_nll_infer_vars<T>(
+ &self,
+ origin: NLLRegionVariableOrigin,
+ value: &T,
+ ) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ self.tcx.fold_regions(
+ value,
+ &mut false,
+ |_region, _depth| self.next_nll_region_var(origin),
+ )
+ }
+
+ fn replace_bound_regions_with_nll_infer_vars<T>(
+ &self,
+ origin: NLLRegionVariableOrigin,
+ value: &ty::Binder<T>,
+ ) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ let (value, _map) = self.tcx
+ .replace_late_bound_regions(value, |_br| self.next_nll_region_var(origin));
+ value
+ }
+}
+
+impl<'tcx> UniversalRegionIndices<'tcx> {
+ /// Converts `r` into a local inference variable: `r` can either
+ /// by a `ReVar` (i.e., already a reference to an inference
+ /// variable) or it can be `'static` or some early-bound
+ /// region. This is useful when taking the results from
+ /// type-checking and trait-matching, which may sometimes
+ /// reference those regions from the `ParamEnv`. It is also used
+ /// during initialization. Relies on the `indices` map having been
+ /// fully initialized.
+ pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+ match r {
+ ty::ReEarlyBound(..) | ty::ReStatic => *self.indices.get(&r).unwrap(),
+ ty::ReVar(..) => r.to_region_vid(),
+ _ => bug!("cannot convert `{:?}` to a region vid", r),
+ }
+ }
+
+ /// Replace all free regions in `value` with region vids, as
+ /// returned by `to_region_vid`.
+ pub fn fold_to_region_vids<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: &T) -> T
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ tcx.fold_regions(
+ value,
+ &mut false,
+ |region, _| tcx.mk_region(ty::ReVar(self.to_region_vid(region))),
+ )
+ }
}
//! This pass type-checks the MIR to ensure it is not broken.
#![allow(unreachable_code)]
+use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
use rustc::infer::region_constraints::RegionConstraintData;
use rustc::traits::{self, FulfillmentContext};
operands: &[Operand<'tcx>],
location: Location,
) {
+ let tcx = self.tcx();
+
match aggregate_kind {
// tuple rvalue field type is always the type of the op. Nothing to check here.
AggregateKind::Tuple => return,
+
+ // For closures, we have some **extra requirements** we
+ // have to check. In particular, in their upvars and
+ // signatures, closures often reference various regions
+ // from the surrounding function -- we call those the
+ // closure's free regions. When we borrow-check (and hence
+ // region-check) closures, we may find that the closure
+ // requires certain relationships between those free
+ // regions. However, because those free regions refer to
+ // portions of the CFG of their caller, the closure is not
+ // in a position to verify those relationships. In that
+ // case, the requirements get "propagated" to us, and so
+ // we have to solve them here where we instantiate the
+ // closure.
+ //
+ // Despite the opacity of the previous parapgrah, this is
+ // actually relatively easy to understand in terms of the
+ // desugaring. A closure gets desugared to a struct, and
+ // these extra requirements are basically like where
+ // clauses on the struct.
+ AggregateKind::Closure(def_id, substs) => {
+ if let Some(closure_region_requirements) = tcx.mir_borrowck(*def_id) {
+ closure_region_requirements.apply_requirements(
+ self.infcx,
+ location,
+ *def_id,
+ *substs,
+ );
+ }
+ }
+
_ => {}
}
- let tcx = self.tcx();
-
for (i, operand) in operands.iter().enumerate() {
let field_ty = match self.aggregate_field_ty(aggregate_kind, i, location) {
Ok(field_ty) => field_ty,