From ab1c1bc6bc18b70818206e7f07ac5133239607ff Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 22 Nov 2017 17:38:51 -0500 Subject: [PATCH] mir-borrowck returns closure requirements, mir-typeck enforces --- src/librustc/ich/impls_mir.rs | 12 + src/librustc/ich/impls_ty.rs | 10 + src/librustc/infer/mod.rs | 5 + src/librustc/mir/mod.rs | 69 ++ src/librustc/ty/maps/mod.rs | 6 +- src/librustc/ty/sty.rs | 40 + src/librustc_driver/driver.rs | 2 +- src/librustc_mir/borrow_check/mod.rs | 29 +- src/librustc_mir/borrow_check/nll/mod.rs | 31 +- .../borrow_check/nll/region_infer/mod.rs | 299 ++++++-- src/librustc_mir/borrow_check/nll/renumber.rs | 61 +- .../nll/subtype_constraint_generation.rs | 9 +- .../borrow_check/nll/universal_regions.rs | 692 ++++++++++++++++-- src/librustc_mir/transform/type_check.rs | 36 +- 14 files changed, 1119 insertions(+), 182 deletions(-) diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index 331b44ac119..beee34e11b7 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -572,3 +572,15 @@ impl<'gcx> HashStable> 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 +}); + diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 9609ae5a0be..2655e2acbbd 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -84,6 +84,16 @@ for ty::RegionKind { } } +impl<'gcx> HashStable> for ty::RegionVid { + #[inline] + fn hash_stable(&self, + hcx: &mut StableHashingContext<'gcx>, + hasher: &mut StableHasher) { + use rustc_data_structures::indexed_vec::Idx; + self.index().hash_stable(hcx, hasher); + } +} + impl<'gcx> HashStable> for ty::adjustment::AutoBorrow<'gcx> { fn hash_stable(&self, diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 96a980a1545..0d4d294ad36 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -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> { diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index c803e76aebe..720d831a245 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1789,6 +1789,75 @@ pub struct GeneratorLayout<'tcx> { pub fields: Vec>, } +/// 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, +} + +/// 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 */ diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index fb3600182d8..848d2a0a7de 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -190,8 +190,10 @@ define_maps! { <'tcx> [] fn coherent_trait: coherent_trait_dep_node((CrateNum, DefId)) -> (), [] fn borrowck: BorrowCheck(DefId) -> Rc, - // 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, /// Gets a complete map from all types to their inherent impls. /// Not meant to be used directly outside of coherence. diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 05aab27dc2a..24cf14419e4 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -646,6 +646,17 @@ impl<'tcx> PolyExistentialTraitRef<'tcx> { pub struct Binder(pub T); impl Binder { + /// 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 + 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 Binder { 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(self, u: Binder, f: F) -> Binder + 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(self, f: F) -> (Binder, Binder) + 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> { self.map_bound_ref(|fn_sig| fn_sig.inputs()[index]) } + pub fn inputs_and_output(&self) -> ty::Binder<&'tcx Slice>> { + self.map_bound_ref(|fn_sig| fn_sig.inputs_and_output) + } pub fn output(&self) -> ty::Binder> { self.map_bound_ref(|fn_sig| fn_sig.output().clone()) } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index b1e9bc7e47c..b0f61e9a191 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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", diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 8d3491bd1d9..97d8a677fe8 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -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 { 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 { 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)] diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 6d9e24bbb87..3d698abc83f 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -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>, move_data: &MoveData<'tcx>, -) -> RegionInferenceContext<'tcx> { +) -> ( + RegionInferenceContext<'tcx>, + Option, +) { // 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; diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index d1faaf75a53..171deb3e1d7 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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, - /// 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>, + /// 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>, } #[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 { 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, ) { 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::>(); 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); + } + } +} diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs index bb32cf88c75..1262c238a13 100644 --- a/src/librustc_mir/borrow_check/nll/renumber.rs +++ b/src/librustc_mir/borrow_check/nll/renumber.rs @@ -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>, 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(&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(&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); } diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs index e93f29f9bc8..c98a94fa8bc 100644 --- a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs +++ b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs @@ -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) } } } diff --git a/src/librustc_mir/borrow_check/nll/universal_regions.rs b/src/librustc_mir/borrow_check/nll/universal_regions.rs index 3be95a114c3..35c50f94190 100644 --- a/src/librustc_mir/borrow_check/nll/universal_regions.rs +++ b/src/librustc_mir/borrow_check/nll/universal_regions.rs @@ -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, 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, 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, - // `'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, +} - // 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> { + 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 { + 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 { + (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 { + 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, + fr0: RegionVid, + ) -> Option { + 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, 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, 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>> { + 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(&mut self, indices: &UniversalRegionIndices<'tcx>, outlives_bounds: I) + where + I: IntoIterator>, + { + 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( + &self, + origin: NLLRegionVariableOrigin, + value: &T, + ) -> T + where + T: TypeFoldable<'tcx>; + + fn replace_bound_regions_with_nll_infer_vars( + &self, + origin: NLLRegionVariableOrigin, + value: &ty::Binder, + ) -> T + where + T: TypeFoldable<'tcx>; +} + +impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> { + fn replace_free_regions_with_nll_infer_vars( + &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( + &self, + origin: NLLRegionVariableOrigin, + value: &ty::Binder, + ) -> 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(&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))), + ) + } } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index f24aa51eb25..1a74f327001 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -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,