提交 ab1c1bc6 编写于 作者: N Niko Matsakis

mir-borrowck returns closure requirements, mir-typeck enforces

上级 2ec959fc
......@@ -572,3 +572,15 @@ fn hash_stable<W: StableHasherResult>(&self,
}
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 @@ fn hash_stable<W: StableHasherResult>(&self,
}
}
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 @@ pub fn next_region_var(&self, origin: RegionVariableOrigin)
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 @@
[] 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 @@ pub fn input_types<'a>(&'a self) -> impl DoubleEndedIterator<Item=Ty<'tcx>> + 'a
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 @@ pub fn no_late_bound_regions<'tcx>(self) -> Option<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 @@ pub fn inputs(&self) -> Binder<&'tcx [Ty<'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::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 @@
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;
......
......@@ -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 @@ pub fn new(
inferred_values: None,
constraints: Vec::new(),
point_indices,
num_universal_regions,
free_region_map: universal_regions.free_region_map,
universal_regions,
};
result.init_universal_regions(universal_regions);
result.init_universal_regions();
result
}
......@@ -159,25 +162,24 @@ pub fn new(
/// 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 @@ pub fn regions(&self) -> impl Iterator<Item = RegionVid> {
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 @@ pub(super) fn region_value_str(&self, r: RegionVid) -> String {
.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 @@ pub(super) fn add_outlives(
}
/// 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 @@ pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'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);
// Go through each of the universal regions `fr` and check that
// they did not grow too large, accumulating any requirements
// for our caller into the `outlives_requirements` vector.
let mut outlives_requirements = vec![];
for (fr, _) in universal_definitions {
self.check_universal_region(infcx, fr, &mut outlives_requirements);
}
for (fr, fr_definition) in free_region_definitions {
self.check_free_region(infcx, fr, fr_definition);
// If this is not a closure, then there is no caller to which we can
// "pass the buck". So if there are any outlives-requirements that were
// not satisfied, we just have to report a hard error here.
if !tcx.is_closure(mir_def_id) {
for outlives_requirement in outlives_requirements {
self.report_error(
infcx,
outlives_requirement.free_region,
outlives_requirement.outlived_free_region,
outlives_requirement.blame_span,
);
}
return None;
}
let num_external_vids = self.universal_regions.num_global_and_external_regions();
Some(ClosureRegionRequirements {
num_external_vids,
outlives_requirements,
})
}
fn check_free_region(
/// Check the final value for the free region `fr` to see if it
/// grew too large. In particular, examine what `end(X)` points
/// wound up in `fr`'s final value; for each `end(X)` where `X !=
/// fr`, we want to check that `fr: X`. If not, that's either an
/// error, or something we have to propagate to our creator.
///
/// Things that are to be propagated are accumulated into the
/// `outlives_requirements` vector.
fn check_universal_region(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
longer_fr: RegionVid,
longer_definition: &RegionDefinition<'tcx>,
outlives_requirements: &mut Vec<ClosureOutlivesRequirement>,
) {
let inferred_values = self.inferred_values.as_ref().unwrap();
let longer_name = longer_definition.name.unwrap();
let longer_value = inferred_values.iter(longer_fr.index());
// Find every region `shorter` such that `longer: shorter`
// (because `longer` includes `end(shorter)`).
for shorter_fr in longer_value.take_while(|&i| i < self.num_universal_regions) {
let shorter_fr = RegionVid::new(shorter_fr);
debug!("check_universal_region(fr={:?})", longer_fr);
// `fr` includes `end(fr)`, that's not especially
// interesting.
if longer_fr == shorter_fr {
// Find every region `o` such that `fr: o`
// (because `fr` includes `end(o)`).
let shorter_frs = longer_value
.take_while(|&i| i < self.universal_regions.len())
.map(RegionVid::new);
for shorter_fr in shorter_frs {
// If it is known that `fr: o`, carry on.
if self.universal_regions.outlives(longer_fr, shorter_fr) {
continue;
}
let shorter_definition = &self.definitions[shorter_fr];
let shorter_name = shorter_definition.name.unwrap();
// Check that `o <= fr`. If not, report an error.
if !self.free_region_map
.sub_free_regions(shorter_name, longer_name)
{
// FIXME: worst error msg ever
let blame_span = self.blame_span(longer_fr, shorter_fr);
infcx.tcx.sess.span_err(
blame_span,
&format!(
"free region `{}` does not outlive `{}`",
longer_name,
shorter_name
),
debug!(
"check_universal_region: fr={:?} does not outlive shorter_fr={:?}",
longer_fr,
shorter_fr,
);
let blame_span = self.blame_span(longer_fr, shorter_fr);
// Shrink `fr` until we find a non-local region (if we do).
// We'll call that `fr-` -- it's ever so slightly smaller than `fr`.
if let Some(fr_minus) = self.universal_regions.non_local_lower_bound(longer_fr) {
debug!("check_universal_region: fr_minus={:?}", fr_minus);
// Grow `shorter_fr` until we find a non-local
// regon. (We always will.) We'll call that
// `shorter_fr+` -- it's ever so slightly larger than
// `fr`.
let shorter_fr_plus = self.universal_regions.non_local_upper_bound(shorter_fr);
debug!(
"check_universal_region: shorter_fr_plus={:?}",
shorter_fr_plus
);
// Push the constraint `fr-: shorter_fr+`
outlives_requirements.push(ClosureOutlivesRequirement {
free_region: fr_minus,
outlived_free_region: shorter_fr_plus,
blame_span: blame_span,
});
return;
}
// If we could not shrink `fr` to something smaller that
// the external users care about, then we can't pass the
// buck; just report an error.
self.report_error(infcx, longer_fr, shorter_fr, blame_span);
}
}
fn report_error(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
fr: RegionVid,
outlived_fr: RegionVid,
blame_span: Span,
) {
// Obviously uncool error reporting.
let fr_string = match self.definitions[fr].external_name {
Some(r) => format!("free region `{}`", r),
None => format!("free region `{:?}`", fr),
};
let outlived_fr_string = match self.definitions[outlived_fr].external_name {
Some(r) => format!("free region `{}`", r),
None => format!("free region `{:?}`", outlived_fr),
};
infcx.tcx.sess.span_err(
blame_span,
&format!("{} does not outlive {}", fr_string, outlived_fr_string,),
);
}
/// Propagate the region constraints: this will grow the values
/// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be
......@@ -421,8 +521,6 @@ fn copy(
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 @@ fn copy(
// 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 @@ fn new(origin: RegionVariableOrigin) -> Self {
// 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 @@ fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
)
}
}
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 @@ fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
})
}
/// 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 @@ fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
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 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 @@
/// 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 @@ fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
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)
}
}
}
......@@ -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 @@ fn check_aggregate_rvalue(
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,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册