]> git.lizzy.rs Git - rust.git/commitdiff
mir-borrowck returns closure requirements, mir-typeck enforces
authorNiko Matsakis <niko@alum.mit.edu>
Wed, 22 Nov 2017 22:38:51 +0000 (17:38 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Thu, 7 Dec 2017 10:28:00 +0000 (05:28 -0500)
14 files changed:
src/librustc/ich/impls_mir.rs
src/librustc/ich/impls_ty.rs
src/librustc/infer/mod.rs
src/librustc/mir/mod.rs
src/librustc/ty/maps/mod.rs
src/librustc/ty/sty.rs
src/librustc_driver/driver.rs
src/librustc_mir/borrow_check/mod.rs
src/librustc_mir/borrow_check/nll/mod.rs
src/librustc_mir/borrow_check/nll/region_infer/mod.rs
src/librustc_mir/borrow_check/nll/renumber.rs
src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
src/librustc_mir/borrow_check/nll/universal_regions.rs
src/librustc_mir/transform/type_check.rs

index 331b44ac119c6b7f0fddb4704cd5585364b0e983..beee34e11b724b7e0a6bada0ddfcb703bed846f0 100644 (file)
@@ -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
+});
+
index 9609ae5a0beb855bfd906627845a6e4915f9c4a8..2655e2acbbdfb369f19437a1c9c35bff1e53149a 100644 (file)
@@ -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,
index 96a980a15457e1715f8278960f8cdf90950015ed..0d4d294ad36f368bcc0e93b7fc77043c44a52f49 100644 (file)
@@ -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> {
index c803e76aebea4a96336ae520c78e881713316c9c..720d831a245335a4a3e22e023c56296b10315e71 100644 (file)
@@ -1789,6 +1789,75 @@ pub struct GeneratorLayout<'tcx> {
     pub fields: Vec<LocalDecl<'tcx>>,
 }
 
+/// After we borrow check a closure, we are left with various
+/// requirements that we have inferred between the free regions that
+/// appear in the closure's signature or on its field types.  These
+/// requirements are then verified and proved by the closure's
+/// creating function. This struct encodes those requirements.
+///
+/// The requirements are listed as being between various
+/// `RegionVid`. The 0th region refers to `'static`; subsequent region
+/// vids refer to the free regions that appear in the closure (or
+/// generator's) type, in order of appearance. (This numbering is
+/// actually defined by the `UniversalRegions` struct in the NLL
+/// region checker. See for example
+/// `UniversalRegions::closure_mapping`.) Note that we treat the free
+/// regions in the closure's type "as if" they were erased, so their
+/// precise identity is not important, only their position.
+///
+/// Example: If type check produces a closure with the closure substs:
+///
+/// ```
+/// ClosureSubsts = [
+///     i8,                                  // the "closure kind"
+///     for<'x> fn(&'a &'x u32) -> &'x u32,  // the "closure signature"
+///     &'a String,                          // some upvar
+/// ]
+/// ```
+///
+/// here, there is one unique free region (`'a`) but it appears
+/// twice. We would "renumber" each occurence to a unique vid, as follows:
+///
+/// ```
+/// ClosureSubsts = [
+///     i8,                                  // the "closure kind"
+///     for<'x> fn(&'1 &'x u32) -> &'x u32,  // the "closure signature"
+///     &'2 String,                          // some upvar
+/// ]
+/// ```
+///
+/// Now the code might impose a requirement like `'1: '2`. When an
+/// instance of the closure is created, the corresponding free regions
+/// can be extracted from its type and constrained to have the given
+/// outlives relationship.
+#[derive(Clone, Debug)]
+pub struct ClosureRegionRequirements {
+    /// The number of external regions defined on the closure.  In our
+    /// example above, it would be 3 -- one for `'static`, then `'1`
+    /// and `'2`. This is just used for a sanity check later on, to
+    /// make sure that the number of regions we see at the callsite
+    /// matches.
+    pub num_external_vids: usize,
+
+    /// Requirements between the various free regions defined in
+    /// indices.
+    pub outlives_requirements: Vec<ClosureOutlivesRequirement>,
+}
+
+/// Indicates an outlives constraint between two free-regions declared
+/// on the closure.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct ClosureOutlivesRequirement {
+    // This region ...
+    pub free_region: ty::RegionVid,
+
+    // .. must outlive this one.
+    pub outlived_free_region: ty::RegionVid,
+
+    // If not, report an error here.
+    pub blame_span: Span,
+}
+
 /*
  * TypeFoldable implementations for MIR types
  */
index fb3600182d8a6ce0901a1468db316f04eb79e8b7..848d2a0a7def71d9a7adef2bca3fcaa72eff0fd2 100644 (file)
     [] 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.
index 05aab27dc2acc5b77b032b114141a89801ec9374..24cf14419e4aed55cb9e372ed5e1e1995ba48398 100644 (file)
@@ -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())
     }
index b1e9bc7e47c76ade4b066e101245e64f06767de4..b0f61e9a19177cfe21bcce6ff29b57b2f1708879 100644 (file)
@@ -1069,7 +1069,7 @@ macro_rules! try_with_f {
 
         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",
index 8d3491bd1d988a211251c94932408e640e3d08b2..97d8a677fe8f15e0fe4454ef1362cdac6d2a3ab3 100644 (file)
@@ -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)]
index 6d9e24bbb876f1f5ed5e0787149ec890267d556c..3d698abc83f4349ab8fb4bed3f83acb76317bf04 100644 (file)
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use rustc::hir::def_id::DefId;
-use rustc::mir::Mir;
+use rustc::mir::{ClosureRegionRequirements, Mir};
 use rustc::infer::InferCtxt;
 use rustc::ty::{self, RegionKind, RegionVid};
 use rustc::util::nodemap::FxHashMap;
 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;
index d1faaf75a53233fd5940f6044ce7973996daf28d..171deb3e1d753797b7a67cc8b58383438770662e 100644 (file)
@@ -9,12 +9,13 @@
 // except according to those terms.
 
 use super::universal_regions::UniversalRegions;
+use rustc::hir::def_id::DefId;
 use rustc::infer::InferCtxt;
-use rustc::infer::RegionVariableOrigin;
 use rustc::infer::NLLRegionVariableOrigin;
+use rustc::infer::RegionVariableOrigin;
+use rustc::infer::SubregionOrigin;
 use rustc::infer::region_constraints::VarOrigins;
-use rustc::infer::outlives::free_region_map::FreeRegionMap;
-use rustc::mir::{Location, Mir};
+use rustc::mir::{ClosureOutlivesRequirement, ClosureRegionRequirements, Location, Mir};
 use rustc::ty::{self, RegionVid};
 use rustc_data_structures::indexed_vec::IndexVec;
 use rustc_data_structures::fx::FxHashSet;
@@ -52,12 +53,9 @@ pub struct RegionInferenceContext<'tcx> {
     /// the free regions.)
     point_indices: BTreeMap<Location, usize>,
 
-    /// Number of universally quantified regions. This is used to
-    /// determine the meaning of the bits in `inferred_values` and
-    /// friends.
-    num_universal_regions: usize,
-
-    free_region_map: &'tcx FreeRegionMap<'tcx>,
+    /// Information about the universally quantified regions in scope
+    /// on this function and their (known) relations to one another.
+    universal_regions: UniversalRegions<'tcx>,
 }
 
 struct RegionDefinition<'tcx> {
@@ -67,9 +65,15 @@ struct RegionDefinition<'tcx> {
     /// late-bound-regions).
     origin: RegionVariableOrigin,
 
-    /// If this is a free-region, then this is `Some(X)` where `X` is
-    /// the name of the region.
-    name: Option<ty::Region<'tcx>>,
+    /// True if this is a universally quantified region. This means a
+    /// lifetime parameter that appears in the function signature (or,
+    /// in the case of a closure, in the closure environment, which of
+    /// course is also in the function signature).
+    is_universal: bool,
+
+    /// If this is 'static or an early-bound region, then this is
+    /// `Some(X)` where `X` is the name of the region.
+    external_name: Option<ty::Region<'tcx>>,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -98,11 +102,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// regions defined in `universal_regions`.
     pub fn new(
         var_origins: VarOrigins,
-        universal_regions: &UniversalRegions<'tcx>,
+        universal_regions: UniversalRegions<'tcx>,
         mir: &Mir<'tcx>,
     ) -> Self {
         let num_region_variables = var_origins.len();
-        let num_universal_regions = universal_regions.indices.len();
+        let num_universal_regions = universal_regions.len();
 
         let mut num_points = 0;
         let mut point_indices = BTreeMap::new();
@@ -133,11 +137,10 @@ 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);
+        }
+    }
+}
index bb32cf88c7514e53c36c44655e47aef7860b0825..1262c238a132cee55492a3b281fdc8b38c22784e 100644 (file)
@@ -8,10 +8,11 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::indexed_vec::Idx;
 use rustc::ty::subst::Substs;
-use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
+use rustc::ty::{self, ClosureSubsts, Ty, TypeFoldable};
 use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
+use rustc::mir::RETURN_PLACE;
 use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
 
@@ -25,25 +26,24 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
     universal_regions: &UniversalRegions<'tcx>,
     mir: &mut Mir<'tcx>,
 ) {
-    // Create inference variables for each of the free regions
-    // declared on the function signature.
-    let free_region_inference_vars = (0..universal_regions.indices.len())
-        .map(RegionVid::new)
-        .map(|vid_expected| {
-            let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
-            assert_eq!(vid_expected, r.to_region_vid());
-            r
-        })
-        .collect();
-
     debug!("renumber_mir()");
-    debug!("renumber_mir: universal_regions={:#?}", universal_regions);
     debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
 
+    // Update the return type and types of the arguments based on the
+    // `universal_regions` computation.
+    debug!("renumber_mir: output_ty={:?}", universal_regions.output_ty);
+    mir.local_decls[RETURN_PLACE].ty = universal_regions.output_ty;
+    for (&input_ty, local) in universal_regions
+        .input_tys
+        .iter()
+        .zip((1..).map(Local::new))
+    {
+        debug!("renumber_mir: input_ty={:?} local={:?}", input_ty, local);
+        mir.local_decls[local].ty = input_ty;
+    }
+
     let mut visitor = NLLVisitor {
         infcx,
-        universal_regions,
-        free_region_inference_vars,
         arg_count: mir.arg_count,
     };
     visitor.visit_mir(mir);
@@ -51,8 +51,6 @@ pub fn renumber_mir<'a, 'gcx, 'tcx>(
 
 struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-    universal_regions: &'a UniversalRegions<'tcx>,
-    free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
     arg_count: usize,
 }
 
@@ -74,20 +72,17 @@ 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);
     }
 
index e93f29f9bc8c54cf186d3cf9adaec131755bf202..c98a94fa8bc106b20a35d25102adec0aba6a435c 100644 (file)
@@ -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
 /// 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)
         }
     }
 }
index 3be95a114c3bc287dd010f92dd76c9dc9533b487..35c50f941907ab20cc27f8bf5a9181e314b0af6d 100644 (file)
 //! The code in this file doesn't *do anything* with those results; it
 //! just returns them for other code to use.
 
+use rustc::hir::HirId;
 use rustc::hir::def_id::DefId;
-use rustc::infer::InferCtxt;
-use rustc::infer::outlives::free_region_map::FreeRegionMap;
-use rustc::ty::{self, RegionVid};
+use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
+use rustc::infer::region_constraints::GenericKind;
+use rustc::infer::outlives::bounds::{self, OutlivesBound};
+use rustc::ty::{self, RegionVid, Ty, TyCtxt};
+use rustc::ty::fold::TypeFoldable;
 use rustc::ty::subst::Substs;
 use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc_data_structures::transitive_relation::TransitiveRelation;
+use std::iter;
+use syntax::ast;
+
+use super::ToRegionVid;
 
 #[derive(Debug)]
 pub struct UniversalRegions<'tcx> {
-    /// Given a universally quantified region defined on this function
-    /// (either early- or late-bound), this maps it to its internal
-    /// region index. When the region context is created, the first N
-    /// variables will be created based on these indices.
-    pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
-
-    /// The map from the typeck tables telling us how to relate universal regions.
-    pub free_region_map: &'tcx FreeRegionMap<'tcx>,
+    indices: UniversalRegionIndices<'tcx>,
+
+    /// The vid assigned to `'static`
+    pub fr_static: RegionVid,
+
+    /// We create region variables such that they are ordered by their
+    /// `RegionClassification`. The first block are globals, then
+    /// externals, then locals. So things from:
+    /// - `FIRST_GLOBAL_INDEX..first_extern_index` are global;
+    /// - `first_extern_index..first_local_index` are external; and
+    /// - first_local_index..num_universals` are local.
+    first_extern_index: usize,
+
+    /// See `first_extern_index`.
+    first_local_index: usize,
+
+    /// The total number of universal region variables instantiated.
+    num_universals: usize,
+
+    /// The "defining" type for this function, with all universal
+    /// regions instantiated.  For a closure or generator, this is the
+    /// closure type, but for a top-level function it's the `TyFnDef`.
+    pub defining_ty: Ty<'tcx>,
+
+    /// The return type of this function, with all regions replaced
+    /// by their universal `RegionVid` equivalents.
+    pub output_ty: Ty<'tcx>,
+
+    /// The fully liberated input types of this function, with all
+    /// regions replaced by their universal `RegionVid` equivalents.
+    pub input_tys: &'tcx [Ty<'tcx>],
+
+    /// Each RBP `('a, GK)` indicates that `GK: 'a` can be assumed to
+    /// be true. These encode relationships like `T: 'a` that are
+    /// added via implicit bounds.
+    ///
+    /// Each region here is guaranteed to be a key in the `indices`
+    /// map.  We use the "original" regions (i.e., the keys from the
+    /// map, and not the values) because the code in
+    /// `process_registered_region_obligations` has some special-cased
+    /// logic expecting to see (e.g.) `ReStatic`, and if we supplied
+    /// our special inference variable there, we would mess that up.
+    pub region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
+
+    relations: UniversalRegionRelations,
+}
+
+#[derive(Debug)]
+struct UniversalRegionIndices<'tcx> {
+    /// For those regions that may appear in the parameter environment
+    /// ('static and early-bound regions), we maintain a map from the
+    /// `ty::Region` to the internal `RegionVid` we are using. This is
+    /// used because trait matching and type-checking will feed us
+    /// region constraints that reference those regions and we need to
+    /// be able to map them our internal `RegionVid`. This is
+    /// basically equivalent to a `Substs`, except that it also
+    /// contains an entry for `ReStatic` -- it might be nice to just
+    /// use a substs, and then handle `ReStatic` another way.
+    indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
+}
+
+#[derive(Debug)]
+struct UniversalRegionRelations {
+    /// Stores the outlives relations that are known to hold from the
+    /// implied bounds, in-scope where clauses, and that sort of
+    /// thing.
+    outlives: TransitiveRelation<RegionVid>,
+
+    /// This is the `<=` relation; that is, if `a: b`, then `b <= a`,
+    /// and we store that here. This is useful when figuring out how
+    /// to express some local region in terms of external regions our
+    /// caller will understand.
+    inverse_outlives: TransitiveRelation<RegionVid>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum RegionClassification {
+    /// A **global** region is one that can be named from
+    /// anywhere. There is only one, `'static`.
+    Global,
+
+    /// An **external** region is only relevant for closures. In that
+    /// case, it refers to regions that are free in the closure type
+    /// -- basically, something bound in the surrounding context.
+    ///
+    /// Consider this example:
+    ///
+    /// ```
+    /// fn foo<'a, 'b>(a: &'a u32, b: &'b u32, c: &'static u32) {
+    ///   let closure = for<'x> |x: &'x u32| { .. };
+    ///                 ^^^^^^^ pretend this were legal syntax
+    ///                         for declaring a late-bound region in
+    ///                         a closure signature
+    /// }
+    /// ```
+    ///
+    /// Here, the lifetimes `'a` and `'b` would be **external** to the
+    /// closure.
+    ///
+    /// If we are not analyzing a closure, there are no external
+    /// lifetimes.
+    External,
+
+    /// A **local** lifetime is one about which we know the full set
+    /// of relevant constraints (that is, relationships to other named
+    /// regions).  For a closure, this includes any region bound in
+    /// the closure's signature.  For a fn item, this includes all
+    /// regions other than global ones.
+    ///
+    /// Continuing with the example from `External`, if we were
+    /// analyzing the closure, then `'x` would be local (and `'a` and
+    /// `'b` are external).  If we are analyzing the function item
+    /// `foo`, then `'a` and `'b` are local (and `'x` is not in
+    /// scope).
+    Local,
 }
 
-pub fn universal_regions<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    item_def_id: DefId,
-) -> UniversalRegions<'tcx> {
-    debug!("universal_regions(item_def_id={:?})", item_def_id);
+const FIRST_GLOBAL_INDEX: usize = 0;
+
+impl<'tcx> UniversalRegions<'tcx> {
+    /// Creates a new and fully initialized `UniversalRegions` that
+    /// contains indices for all the free regions found in the given
+    /// MIR -- that is, all the regions that appear in the function's
+    /// signature. This will also compute the relationships that are
+    /// known between those regions.
+    pub fn new(
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        mir_def_id: DefId,
+        param_env: ty::ParamEnv<'tcx>,
+    ) -> Self {
+        let tcx = infcx.tcx;
+        let mir_node_id = tcx.hir.as_local_node_id(mir_def_id).unwrap();
+        let mir_hir_id = tcx.hir.node_to_hir_id(mir_node_id);
+        UniversalRegionsBuilder {
+            infcx,
+            mir_def_id,
+            mir_node_id,
+            mir_hir_id,
+            param_env,
+            region_bound_pairs: vec![],
+            relations: UniversalRegionRelations {
+                outlives: TransitiveRelation::new(),
+                inverse_outlives: TransitiveRelation::new(),
+            },
+        }.build()
+    }
+
+    /// Given a reference to a closure type, extracts all the values
+    /// from its free regions and returns a vector with them. This is
+    /// used when the closure's creator checks that the
+    /// `ClosureRegionRequirements` are met. The requirements from
+    /// `ClosureRegionRequirements` are expressed in terms of
+    /// `RegionVid` entries that map into the returned vector `V`: so
+    /// if the `ClosureRegionRequirements` contains something like
+    /// `'1: '2`, then the caller would impose the constraint that
+    /// `V[1]: V[2]`.
+    pub fn closure_mapping(
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        closure_ty: Ty<'tcx>,
+        expected_num_vars: usize,
+    ) -> IndexVec<RegionVid, ty::Region<'tcx>> {
+        let mut region_mapping = IndexVec::with_capacity(expected_num_vars);
+        region_mapping.push(infcx.tcx.types.re_static);
+        infcx.tcx.for_each_free_region(&closure_ty, |fr| {
+            region_mapping.push(fr);
+        });
+
+        assert_eq!(
+            region_mapping.len(),
+            expected_num_vars,
+            "index vec had unexpected number of variables"
+        );
 
-    let mut indices = FxHashMap();
+        region_mapping
+    }
 
-    // `'static` is always free.
-    insert_free_region(&mut indices, infcx.tcx.types.re_static);
+    /// True if `r` is a member of this set of universal regions.
+    pub fn is_universal_region(&self, r: RegionVid) -> bool {
+        (FIRST_GLOBAL_INDEX..self.num_universals).contains(r.index())
+    }
 
-    // Extract the early regions.
-    let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
-    for item_subst in item_substs {
-        if let Some(region) = item_subst.as_region() {
-            insert_free_region(&mut indices, region);
+    /// Classifies `r` as a universal region, returning `None` if this
+    /// is not a member of this set of universal regions.
+    pub fn region_classification(&self, r: RegionVid) -> Option<RegionClassification> {
+        let index = r.index();
+        if (FIRST_GLOBAL_INDEX..self.first_extern_index).contains(index) {
+            Some(RegionClassification::Global)
+        } else if (self.first_extern_index..self.first_local_index).contains(index) {
+            Some(RegionClassification::External)
+        } else if (self.first_local_index..self.num_universals).contains(index) {
+            Some(RegionClassification::Local)
+        } else {
+            None
         }
     }
 
-    // Extract the late-bound regions. Use the liberated fn sigs,
-    // where the late-bound regions will have been converted into free
-    // regions, and add them to the map.
-    let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap();
-    let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
-    let tables = infcx.tcx.typeck_tables_of(item_def_id);
-    let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
-    infcx
-        .tcx
-        .for_each_free_region(&fn_sig.inputs_and_output, |region| {
-            if let ty::ReFree(_) = *region {
-                insert_free_region(&mut indices, region);
+    /// Returns an iterator over all the RegionVids corresponding to
+    /// universally quantified free regions.
+    pub fn universal_regions(&self) -> impl Iterator<Item = RegionVid> {
+        (FIRST_GLOBAL_INDEX..self.num_universals).map(RegionVid::new)
+    }
+
+    /// True if `r` is classied as a global region.
+    pub fn is_global_free_region(&self, r: RegionVid) -> bool {
+        self.region_classification(r) == Some(RegionClassification::Global)
+    }
+
+    /// True if `r` is classied as an external region.
+    pub fn is_extern_free_region(&self, r: RegionVid) -> bool {
+        self.region_classification(r) == Some(RegionClassification::External)
+    }
+
+    /// True if `r` is classied as an local region.
+    pub fn is_local_free_region(&self, r: RegionVid) -> bool {
+        self.region_classification(r) == Some(RegionClassification::Local)
+    }
+
+    /// Returns the number of universal regions created in any category.
+    pub fn len(&self) -> usize {
+        self.num_universals
+    }
+
+    /// Finds an "upper bound" for `fr` that is not local. In other
+    /// words, returns the smallest (*) known region `fr1` that (a)
+    /// outlives `fr` and (b) is not local. This cannot fail, because
+    /// we will always find `'static` at worst.
+    ///
+    /// (*) If there are multiple competing choices, we pick the "postdominating"
+    /// one. See `TransitiveRelation::postdom_upper_bound` for details.
+    pub fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
+        debug!("non_local_upper_bound(fr={:?})", fr);
+        self.non_local_bound(&self.relations.inverse_outlives, fr)
+            .unwrap_or(self.fr_static)
+    }
+
+    /// Finds a "lower bound" for `fr` that is not local. In other
+    /// words, returns the largest (*) known region `fr1` that (a) is
+    /// outlived by `fr` and (b) is not local. This cannot fail,
+    /// because we will always find `'static` at worst.
+    ///
+    /// (*) If there are multiple competing choices, we pick the "postdominating"
+    /// one. See `TransitiveRelation::postdom_upper_bound` for details.
+    pub fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
+        debug!("non_local_lower_bound(fr={:?})", fr);
+        self.non_local_bound(&self.relations.outlives, fr)
+    }
+
+    /// Returns the number of global plus external universal regions.
+    /// For closures, these are the regions that appear free in the
+    /// closure type (versus those bound in the closure
+    /// signature). They are therefore the regions between which the
+    /// closure may impose constraints that its creator must verify.
+    pub fn num_global_and_external_regions(&self) -> usize {
+        self.first_local_index
+    }
+
+    /// Helper for `non_local_upper_bound` and
+    /// `non_local_lower_bound`.  Repeatedly invokes `postdom_parent`
+    /// until we find something that is not local. Returns None if we
+    /// never do so.
+    fn non_local_bound(
+        &self,
+        relation: &TransitiveRelation<RegionVid>,
+        fr0: RegionVid,
+    ) -> Option<RegionVid> {
+        let mut external_parents = vec![];
+        let mut queue = vec![&fr0];
+
+        // Keep expanding `fr` into its parents until we reach
+        // non-local regions.
+        while let Some(fr) = queue.pop() {
+            if !self.is_local_free_region(*fr) {
+                external_parents.push(fr);
+                continue;
             }
-        });
 
-    debug!("universal_regions: indices={:#?}", indices);
+            queue.extend(relation.parents(fr));
+        }
+
+        debug!("non_local_bound: external_parents={:?}", external_parents);
+
+        // In case we find more than one, reduce to one for
+        // convenience.  This is to prevent us from generating more
+        // complex constraints, but it will cause spurious errors.
+        let post_dom = relation
+            .mutual_immediate_postdominator(external_parents)
+            .cloned();
+
+        debug!("non_local_bound: post_dom={:?}", post_dom);
+
+        post_dom.and_then(|post_dom| {
+            // If the mutual immediate postdom is not local, then
+            // there is no non-local result we can return.
+            if !self.is_local_free_region(post_dom) {
+                Some(post_dom)
+            } else {
+                None
+            }
+        })
+    }
+
+    /// True if fr1 is known to outlive fr2.
+    ///
+    /// This will only ever be true for universally quantified regions.
+    pub fn outlives(&self, fr1: RegionVid, fr2: RegionVid) -> bool {
+        self.relations.outlives.contains(&fr1, &fr2)
+    }
+
+    /// Returns a vector of free regions `x` such that `fr1: x` is
+    /// known to hold.
+    pub fn regions_outlived_by(&self, fr1: RegionVid) -> Vec<&RegionVid> {
+        self.relations.outlives.reachable_from(&fr1)
+    }
+
+    /// Get an iterator over all the early-bound regions that have names.
+    pub fn named_universal_regions<'s>(
+        &'s self,
+    ) -> impl Iterator<Item = (ty::Region<'tcx>, ty::RegionVid)> + 's {
+        self.indices.indices.iter().map(|(&r, &v)| (r, v))
+    }
+
+    /// See `UniversalRegionIndices::to_region_vid`.
+    pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+        self.indices.to_region_vid(r)
+    }
+}
+
+struct UniversalRegionsBuilder<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    mir_def_id: DefId,
+    mir_hir_id: HirId,
+    mir_node_id: ast::NodeId,
+    param_env: ty::ParamEnv<'tcx>,
+    region_bound_pairs: Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
+    relations: UniversalRegionRelations,
+}
+
+const FR: NLLRegionVariableOrigin = NLLRegionVariableOrigin::FreeRegion;
+
+impl<'cx, 'gcx, 'tcx> UniversalRegionsBuilder<'cx, 'gcx, 'tcx> {
+    fn build(mut self) -> UniversalRegions<'tcx> {
+        let param_env = self.param_env;
+
+        assert_eq!(FIRST_GLOBAL_INDEX, self.infcx.num_region_vars());
+
+        // Create the "global" region that is always free in all contexts: 'static.
+        let fr_static = self.infcx.next_nll_region_var(FR).to_region_vid();
+
+        // We've now added all the global regions. The next ones we
+        // add will be external.
+        let first_extern_index = self.infcx.num_region_vars();
+
+        let defining_ty = self.defining_ty();
+
+        let indices = self.compute_indices(fr_static, defining_ty);
+
+        let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
+
+        // "Liberate" the late-bound regions. These correspond to
+        // "local" free regions.
+        let first_local_index = self.infcx.num_region_vars();
+        let inputs_and_output = self.infcx
+            .replace_bound_regions_with_nll_infer_vars(FR, &bound_inputs_and_output);
+        let num_universals = self.infcx.num_region_vars();
+
+        // Insert the facts we know from the predicates. Why? Why not.
+        self.add_outlives_bounds(&indices, bounds::explicit_outlives_bounds(param_env));
+
+        // Add the implied bounds from inputs and outputs.
+        for ty in inputs_and_output {
+            self.add_implied_bounds(&indices, ty);
+        }
+
+        // Finally, outlives is reflexive, and static outlives every
+        // other free region.
+        for fr in (FIRST_GLOBAL_INDEX..num_universals).map(RegionVid::new) {
+            self.relations.relate_universal_regions(fr, fr);
+            self.relations.relate_universal_regions(fr_static, fr);
+        }
+
+        let (output_ty, input_tys) = inputs_and_output.split_last().unwrap();
+
+        // we should not have created any more variables
+        assert_eq!(self.infcx.num_region_vars(), num_universals);
+
+        debug!("build: global regions = {}..{}",
+               FIRST_GLOBAL_INDEX,
+               first_extern_index);
+        debug!("build: extern regions = {}..{}",
+               first_extern_index,
+               first_local_index);
+        debug!("build: local regions  = {}..{}",
+               first_local_index,
+               num_universals);
+
+        UniversalRegions {
+            indices,
+            fr_static,
+            first_extern_index,
+            first_local_index,
+            num_universals,
+            defining_ty,
+            output_ty,
+            input_tys,
+            region_bound_pairs: self.region_bound_pairs,
+            relations: self.relations,
+        }
+    }
+
+    fn defining_ty(&self) -> ty::Ty<'tcx> {
+        let tcx = self.infcx.tcx;
+        let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
+
+        let defining_ty = if self.mir_def_id == closure_base_def_id {
+            tcx.type_of(closure_base_def_id)
+        } else {
+            let tables = tcx.typeck_tables_of(self.mir_def_id);
+            tables.node_id_to_type(self.mir_hir_id)
+        };
+
+        self.infcx
+            .replace_free_regions_with_nll_infer_vars(FR, &defining_ty)
+    }
+
+    fn compute_indices(
+        &self,
+        fr_static: RegionVid,
+        defining_ty: Ty<'tcx>,
+    ) -> UniversalRegionIndices<'tcx> {
+        let tcx = self.infcx.tcx;
+        let gcx = tcx.global_tcx();
+        let closure_base_def_id = tcx.closure_base_def_id(self.mir_def_id);
+        let identity_substs = Substs::identity_for_item(gcx, closure_base_def_id);
+        let fr_substs = match defining_ty.sty {
+            ty::TyClosure(_, substs) | ty::TyGenerator(_, substs, ..) => {
+                // In the case of closures, we rely on the fact that
+                // the first N elements in the ClosureSubsts are
+                // inherited from the `closure_base_def_id`.
+                // Therefore, when we zip together (below) with
+                // `identity_substs`, we will get only those regions
+                // that correspond to early-bound regions declared on
+                // the `closure_base_def_id`.
+                assert!(substs.substs.len() >= identity_substs.len());
+                substs.substs
+            }
+            ty::TyFnDef(_, substs) => substs,
+            _ => bug!(),
+        };
+
+        let global_mapping = iter::once((gcx.types.re_static, fr_static));
+        let subst_mapping = identity_substs
+            .regions()
+            .zip(fr_substs.regions().map(|r| r.to_region_vid()));
+
+        UniversalRegionIndices {
+            indices: global_mapping.chain(subst_mapping).collect(),
+        }
+    }
+
+    fn compute_inputs_and_output(
+        &self,
+        indices: &UniversalRegionIndices<'tcx>,
+        defining_ty: Ty<'tcx>,
+    ) -> ty::Binder<&'tcx ty::Slice<Ty<'tcx>>> {
+        let tcx = self.infcx.tcx;
+        match defining_ty.sty {
+            ty::TyClosure(def_id, substs) => {
+                assert_eq!(self.mir_def_id, def_id);
+                let closure_sig = substs.closure_sig_ty(def_id, tcx).fn_sig(tcx);
+                let inputs_and_output = closure_sig.inputs_and_output();
+                let closure_ty = tcx.closure_env_ty(def_id, substs).unwrap();
+                ty::Binder::fuse(
+                    closure_ty,
+                    inputs_and_output,
+                    |closure_ty, inputs_and_output| {
+                        // The "inputs" of the closure in the
+                        // signature appear as a tuple.  The MIR side
+                        // flattens this tuple.
+                        let (&output, tuplized_inputs) = inputs_and_output.split_last().unwrap();
+                        assert_eq!(tuplized_inputs.len(), 1, "multiple closure inputs");
+                        let inputs = match tuplized_inputs[0].sty {
+                            ty::TyTuple(inputs, _) => inputs,
+                            _ => bug!("closure inputs not a tuple: {:?}", tuplized_inputs[0]),
+                        };
 
-    UniversalRegions { indices, free_region_map: &tables.free_region_map }
+                        tcx.mk_type_list(
+                            iter::once(closure_ty)
+                                .chain(inputs.iter().cloned())
+                                .chain(iter::once(output)),
+                        )
+                    },
+                )
+            }
+
+            ty::TyGenerator(def_id, substs, ..) => {
+                assert_eq!(self.mir_def_id, def_id);
+                let output = substs.generator_return_ty(def_id, tcx);
+                let inputs_and_output = self.infcx.tcx.intern_type_list(&[defining_ty, output]);
+                ty::Binder::new_not_binding(inputs_and_output)
+            }
+
+            ty::TyFnDef(def_id, _) => {
+                let sig = tcx.fn_sig(def_id);
+                let sig = indices.fold_to_region_vids(tcx, &sig);
+                return sig.inputs_and_output();
+            }
+
+            _ => span_bug!(
+                tcx.def_span(self.mir_def_id),
+                "unexpected defining type: {:?}",
+                defining_ty
+            ),
+        }
+    }
+
+    /// Update the type of a single local, which should represent
+    /// either the return type of the MIR or one of its arguments. At
+    /// the same time, compute and add any implied bounds that come
+    /// from this local.
+    ///
+    /// Assumes that `universal_regions` indices map is fully constructed.
+    fn add_implied_bounds(&mut self, indices: &UniversalRegionIndices<'tcx>, ty: Ty<'tcx>) {
+        let span = self.infcx.tcx.def_span(self.mir_def_id);
+        let bounds = self.infcx
+            .implied_outlives_bounds(self.param_env, self.mir_node_id, ty, span);
+        self.add_outlives_bounds(indices, bounds);
+    }
+
+    /// Registers the `OutlivesBound` items from `outlives_bounds` in
+    /// the outlives relation as well as the region-bound pairs
+    /// listing.
+    fn add_outlives_bounds<I>(&mut self, indices: &UniversalRegionIndices<'tcx>, outlives_bounds: I)
+    where
+        I: IntoIterator<Item = OutlivesBound<'tcx>>,
+    {
+        for outlives_bound in outlives_bounds {
+            match outlives_bound {
+                OutlivesBound::RegionSubRegion(r1, r2) => {
+                    // The bound says that `r1 <= r2`; we store `r2: r1`.
+                    let r1 = indices.to_region_vid(r1);
+                    let r2 = indices.to_region_vid(r2);
+                    self.relations.relate_universal_regions(r2, r1);
+                }
+
+                OutlivesBound::RegionSubParam(r_a, param_b) => {
+                    self.region_bound_pairs
+                        .push((r_a, GenericKind::Param(param_b)));
+                }
+
+                OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+                    self.region_bound_pairs
+                        .push((r_a, GenericKind::Projection(projection_b)));
+                }
+            }
+        }
+    }
 }
 
-fn insert_free_region<'tcx>(
-    universal_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
-    region: ty::Region<'tcx>,
-) {
-    let next = RegionVid::new(universal_regions.len());
-    universal_regions.entry(region).or_insert(next);
+impl UniversalRegionRelations {
+    /// Records in the `outlives_relation` (and
+    /// `inverse_outlives_relation`) that `fr_a: fr_b`.
+    fn relate_universal_regions(&mut self, fr_a: RegionVid, fr_b: RegionVid) {
+        debug!(
+            "relate_universal_regions: fr_a={:?} outlives fr_b={:?}",
+            fr_a,
+            fr_b
+        );
+        self.outlives.add(fr_a, fr_b);
+        self.inverse_outlives.add(fr_b, fr_a);
+    }
+}
+
+pub(crate) trait InferCtxtExt<'tcx> {
+    fn replace_free_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>;
+
+    fn replace_bound_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &ty::Binder<T>,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>;
+}
+
+impl<'cx, 'gcx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'gcx, 'tcx> {
+    fn replace_free_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.tcx.fold_regions(
+            value,
+            &mut false,
+            |_region, _depth| self.next_nll_region_var(origin),
+        )
+    }
+
+    fn replace_bound_regions_with_nll_infer_vars<T>(
+        &self,
+        origin: NLLRegionVariableOrigin,
+        value: &ty::Binder<T>,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        let (value, _map) = self.tcx
+            .replace_late_bound_regions(value, |_br| self.next_nll_region_var(origin));
+        value
+    }
+}
+
+impl<'tcx> UniversalRegionIndices<'tcx> {
+    /// Converts `r` into a local inference variable: `r` can either
+    /// by a `ReVar` (i.e., already a reference to an inference
+    /// variable) or it can be `'static` or some early-bound
+    /// region. This is useful when taking the results from
+    /// type-checking and trait-matching, which may sometimes
+    /// reference those regions from the `ParamEnv`. It is also used
+    /// during initialization. Relies on the `indices` map having been
+    /// fully initialized.
+    pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
+        match r {
+            ty::ReEarlyBound(..) | ty::ReStatic => *self.indices.get(&r).unwrap(),
+            ty::ReVar(..) => r.to_region_vid(),
+            _ => bug!("cannot convert `{:?}` to a region vid", r),
+        }
+    }
+
+    /// Replace all free regions in `value` with region vids, as
+    /// returned by `to_region_vid`.
+    pub fn fold_to_region_vids<T>(&self, tcx: TyCtxt<'_, '_, 'tcx>, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        tcx.fold_regions(
+            value,
+            &mut false,
+            |region, _| tcx.mk_region(ty::ReVar(self.to_region_vid(region))),
+        )
+    }
 }
index f24aa51eb2584aecd800982b6171e222da3c5c01..1a74f32700151c55e4567839372010801456b14b 100644 (file)
@@ -11,6 +11,7 @@
 //! This pass type-checks the MIR to ensure it is not broken.
 #![allow(unreachable_code)]
 
+use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
 use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
 use rustc::infer::region_constraints::RegionConstraintData;
 use rustc::traits::{self, FulfillmentContext};
@@ -1135,14 +1136,45 @@ 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,