mir-borrowck returns closure requirements, mir-typeck enforces

This commit is contained in:
Niko Matsakis 2017-11-22 17:38:51 -05:00
parent 2ec959fc35
commit ab1c1bc6bc
14 changed files with 1119 additions and 182 deletions

View File

@ -572,3 +572,15 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Literal<'gcx> {
}
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
});

View File

@ -84,6 +84,16 @@ for ty::RegionKind {
}
}
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,

View File

@ -1062,6 +1062,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
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> {

View File

@ -1789,6 +1789,75 @@ pub struct GeneratorLayout<'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
*/

View File

@ -190,8 +190,10 @@ define_maps! { <'tcx>
[] 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.

View File

@ -646,6 +646,17 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> {
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
@ -700,6 +711,32 @@ impl<T> Binder<T> {
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
@ -799,6 +836,9 @@ impl<'tcx> PolyFnSig<'tcx> {
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())
}

View File

@ -1069,7 +1069,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController,
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",

View File

@ -19,6 +19,7 @@ use rustc::ty::maps::Providers;
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};
@ -51,7 +52,10 @@ pub fn provide(providers: &mut Providers) {
};
}
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));
@ -59,21 +63,23 @@ fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
!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);
@ -91,7 +97,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
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;
@ -177,8 +183,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
));
// 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,
@ -186,10 +192,11 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
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
@ -226,6 +233,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
);
mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
opt_closure_req
}
#[allow(dead_code)]

View File

@ -9,7 +9,7 @@
// 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;
@ -35,20 +35,21 @@ use self::region_infer::RegionInferenceContext;
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);
@ -68,7 +69,10 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
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);
@ -76,13 +80,8 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
// 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 {
@ -115,13 +114,13 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
);
// 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, &regioncx);
regioncx
(regioncx, closure_region_requirements)
}
struct LivenessResults {
@ -197,7 +196,7 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
/// 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;

View File

@ -9,12 +9,13 @@
// 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;
@ -52,12 +53,9 @@ pub struct RegionInferenceContext<'tcx> {
/// 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> {
@ -67,9 +65,15 @@ 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)]
@ -98,11 +102,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// 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();
@ -133,11 +137,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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
}
@ -159,25 +162,24 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// 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 {
@ -196,6 +198,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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,
@ -237,19 +247,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.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 = ", ";
}
@ -289,8 +305,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
/// 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);
@ -310,57 +332,135 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// 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);
for (fr, fr_definition) in free_region_definitions {
self.check_free_region(infcx, fr, fr_definition);
// 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);
}
// 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();
debug!(
"check_universal_region: fr={:?} does not outlive shorter_fr={:?}",
longer_fr,
shorter_fr,
);
// 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
),
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
@ -421,8 +521,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
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;
@ -464,7 +562,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// 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);
@ -535,7 +633,11 @@ impl<'tcx> RegionDefinition<'tcx> {
// 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,
}
}
}
@ -551,3 +653,70 @@ impl fmt::Debug for Constraint {
)
}
}
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);
}
}
}

View File

@ -8,10 +8,11 @@
// 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};
@ -25,25 +26,24 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
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);
@ -51,8 +51,6 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
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,
}
@ -74,20 +72,17 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
})
}
/// 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[&region];
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 {
@ -110,12 +105,12 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
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);
}

View File

@ -15,7 +15,6 @@ use rustc::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
@ -25,20 +24,17 @@ use super::region_infer::RegionInferenceContext;
/// 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>,
}
@ -106,10 +102,7 @@ impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, '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)
}
}
}

View File

@ -22,69 +22,671 @@
//! 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>,
indices: UniversalRegionIndices<'tcx>,
/// The map from the typeck tables telling us how to relate universal regions.
pub free_region_map: &'tcx FreeRegionMap<'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,
}
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);
#[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>,
}
let mut indices = FxHashMap();
#[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>,
// `'static` is always free.
insert_free_region(&mut indices, infcx.tcx.types.re_static);
/// 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>,
}
// 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);
#[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,
}
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"
);
region_mapping
}
/// 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())
}
/// 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));
}
UniversalRegions { indices, free_region_map: &tables.free_region_map }
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)
}
}
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);
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]),
};
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)));
}
}
}
}
}
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))),
)
}
}

View File

@ -11,6 +11,7 @@
//! 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};
@ -1135,14 +1136,45 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
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,