mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
mir-borrowck returns closure requirements, mir-typeck enforces
This commit is contained in:
parent
2ec959fc35
commit
ab1c1bc6bc
@ -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
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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)]
|
||||
|
@ -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, ®ioncx);
|
||||
|
||||
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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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[®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 {
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user