]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #45668 - nikomatsakis:nll-free-region, r=arielb1
authorbors <bors@rust-lang.org>
Mon, 6 Nov 2017 23:30:57 +0000 (23:30 +0000)
committerbors <bors@rust-lang.org>
Mon, 6 Nov 2017 23:30:57 +0000 (23:30 +0000)
extend NLL with preliminary support for free regions on functions

This PR extends https://github.com/rust-lang/rust/pull/45538 with support for free regions. This is pretty preliminary and will no doubt want to change in various ways, particularly as we add support for closures, but it's enough to get the basic idea in place:

- We now create specific regions to represent each named lifetime declared on the function.
- Region values can contain references to these regions (represented for now as a `BTreeSet<RegionIndex>`).
- If we wind up trying to infer that `'a: 'b` must hold, but no such relationship was declared, we report an error.

It also does a number of drive-by refactorings.

r? @arielb1

cc @spastorino

19 files changed:
src/librustc/infer/error_reporting/mod.rs
src/librustc/middle/free_region.rs
src/librustc/mir/visit.rs
src/librustc_data_structures/bitvec.rs
src/librustc_data_structures/transitive_relation.rs
src/librustc_mir/build/mod.rs
src/librustc_mir/dataflow/impls/borrows.rs
src/librustc_mir/transform/clean_end_regions.rs
src/librustc_mir/transform/erase_regions.rs
src/librustc_mir/transform/nll/constraint_generation.rs
src/librustc_mir/transform/nll/free_regions.rs [new file with mode: 0644]
src/librustc_mir/transform/nll/mod.rs
src/librustc_mir/transform/nll/region_infer.rs
src/librustc_mir/transform/nll/renumber.rs
src/librustc_mir/transform/type_check.rs
src/librustc_passes/mir_stats.rs
src/test/mir-opt/nll/named-lifetimes-basic.rs [new file with mode: 0644]
src/test/ui/nll/named-region-basic.rs [new file with mode: 0644]
src/test/ui/nll/named-region-basic.stderr [new file with mode: 0644]

index aac67b528c2d08d3b6a2b28d21d8a38c4bc407e1..e9916bd77e7582ea59880046bd41e4a0fe468653 100644 (file)
@@ -262,6 +262,27 @@ pub fn report_region_errors(&self,
                                 errors: &Vec<RegionResolutionError<'tcx>>) {
         debug!("report_region_errors(): {} errors to start", errors.len());
 
+        if self.tcx.sess.opts.debugging_opts.nll {
+            for error in errors {
+                match *error {
+                    RegionResolutionError::ConcreteFailure(ref origin, ..) |
+                    RegionResolutionError::GenericBoundFailure(ref origin, ..) => {
+                        self.tcx.sess.span_warn(
+                            origin.span(),
+                            "not reporting region error due to -Znll");
+                    }
+
+                    RegionResolutionError::SubSupConflict(ref rvo, ..) => {
+                        self.tcx.sess.span_warn(
+                            rvo.span(),
+                            "not reporting region error due to -Znll");
+                    }
+                }
+            }
+
+            return;
+        }
+
         // try to pre-process the errors, which will group some of them
         // together into a `ProcessedErrors` group:
         let errors = self.process_errors(errors);
index 49a241b86e015046eb318be88fdca0226d101f0d..3bcdc4f7e2c630a860a483c733a9adbe69f95b2d 100644 (file)
@@ -182,6 +182,19 @@ pub fn lub_free_regions<'a, 'gcx>(&self,
         debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
         result
     }
+
+    /// Returns all regions that are known to outlive `r_a`. For
+    /// example, in a function:
+    ///
+    /// ```
+    /// fn foo<'a, 'b: 'a, 'c: 'b>() { .. }
+    /// ```
+    ///
+    /// if `r_a` represents `'a`, this function would return `{'b, 'c}`.
+    pub fn regions_that_outlive<'a, 'gcx>(&self, r_a: Region<'tcx>) -> Vec<&Region<'tcx>> {
+        assert!(is_free(r_a));
+        self.relation.greater_than(&r_a)
+    }
 }
 
 fn is_free(r: Region) -> bool {
index 47dbcee6394253b167b353541ce51b4d97bc1d9e..00863abc84deeb61b4e5652aadbdb399b34895fc 100644 (file)
@@ -209,7 +209,7 @@ fn visit_source_info(&mut self,
 
             fn visit_ty(&mut self,
                         ty: & $($mutability)* Ty<'tcx>,
-                        _: Lookup) {
+                        _: TyContext) {
                 self.super_ty(ty);
             }
 
@@ -256,8 +256,9 @@ fn visit_const_usize(&mut self,
             }
 
             fn visit_local_decl(&mut self,
+                                local: Local,
                                 local_decl: & $($mutability)* LocalDecl<'tcx>) {
-                self.super_local_decl(local_decl);
+                self.super_local_decl(local, local_decl);
             }
 
             fn visit_local(&mut self,
@@ -291,14 +292,14 @@ macro_rules! basic_blocks {
                     self.visit_visibility_scope_data(scope);
                 }
 
-                let lookup = Lookup::Src(SourceInfo {
+                let lookup = TyContext::SourceInfo(SourceInfo {
                     span: mir.span,
                     scope: ARGUMENT_VISIBILITY_SCOPE,
                 });
                 self.visit_ty(&$($mutability)* mir.return_ty, lookup);
 
-                for local_decl in &$($mutability)* mir.local_decls {
-                    self.visit_local_decl(local_decl);
+                for local in mir.local_decls.indices() {
+                    self.visit_local_decl(local, & $($mutability)* mir.local_decls[local]);
                 }
 
                 self.visit_span(&$($mutability)* mir.span);
@@ -359,7 +360,8 @@ fn super_statement(&mut self,
                         for operand in lvalues {
                             self.visit_lvalue(& $($mutability)* operand.lval,
                                               LvalueContext::Validate, location);
-                            self.visit_ty(& $($mutability)* operand.ty, Lookup::Loc(location));
+                            self.visit_ty(& $($mutability)* operand.ty,
+                                          TyContext::Location(location));
                         }
                     }
                     StatementKind::SetDiscriminant{ ref $($mutability)* lvalue, .. } => {
@@ -421,7 +423,7 @@ fn super_terminator_kind(&mut self,
                                                 ref values,
                                                 ref targets } => {
                         self.visit_operand(discr, source_location);
-                        self.visit_ty(switch_ty, Lookup::Loc(source_location));
+                        self.visit_ty(switch_ty, TyContext::Location(source_location));
                         for value in &values[..] {
                             self.visit_const_int(value, source_location);
                         }
@@ -545,7 +547,7 @@ fn super_rvalue(&mut self,
                                  ref $($mutability)* operand,
                                  ref $($mutability)* ty) => {
                         self.visit_operand(operand, location);
-                        self.visit_ty(ty, Lookup::Loc(location));
+                        self.visit_ty(ty, TyContext::Location(location));
                     }
 
                     Rvalue::BinaryOp(_bin_op,
@@ -567,7 +569,7 @@ fn super_rvalue(&mut self,
                     }
 
                     Rvalue::NullaryOp(_op, ref $($mutability)* ty) => {
-                        self.visit_ty(ty, Lookup::Loc(location));
+                        self.visit_ty(ty, TyContext::Location(location));
                     }
 
                     Rvalue::Aggregate(ref $($mutability)* kind,
@@ -575,7 +577,7 @@ fn super_rvalue(&mut self,
                         let kind = &$($mutability)* **kind;
                         match *kind {
                             AggregateKind::Array(ref $($mutability)* ty) => {
-                                self.visit_ty(ty, Lookup::Loc(location));
+                                self.visit_ty(ty, TyContext::Location(location));
                             }
                             AggregateKind::Tuple => {
                             }
@@ -645,7 +647,7 @@ fn super_static(&mut self,
                     ref $($mutability)* ty,
                 } = *static_;
                 self.visit_def_id(def_id, location);
-                self.visit_ty(ty, Lookup::Loc(location));
+                self.visit_ty(ty, TyContext::Location(location));
             }
 
             fn super_projection(&mut self,
@@ -675,7 +677,7 @@ fn super_projection_elem(&mut self,
                     ProjectionElem::Subslice { from: _, to: _ } => {
                     }
                     ProjectionElem::Field(_field, ref $($mutability)* ty) => {
-                        self.visit_ty(ty, Lookup::Loc(location));
+                        self.visit_ty(ty, TyContext::Location(location));
                     }
                     ProjectionElem::Index(ref $($mutability)* local) => {
                         self.visit_local(local, LvalueContext::Consume, location);
@@ -690,6 +692,7 @@ fn super_projection_elem(&mut self,
             }
 
             fn super_local_decl(&mut self,
+                                local: Local,
                                 local_decl: & $($mutability)* LocalDecl<'tcx>) {
                 let LocalDecl {
                     mutability: _,
@@ -701,7 +704,10 @@ fn super_local_decl(&mut self,
                     is_user_variable: _,
                 } = *local_decl;
 
-                self.visit_ty(ty, Lookup::Src(*source_info));
+                self.visit_ty(ty, TyContext::LocalDecl {
+                    local,
+                    source_info: *source_info,
+                });
                 self.visit_source_info(source_info);
                 self.visit_visibility_scope(lexical_scope);
             }
@@ -725,7 +731,7 @@ fn super_constant(&mut self,
                 } = *constant;
 
                 self.visit_span(span);
-                self.visit_ty(ty, Lookup::Loc(location));
+                self.visit_ty(ty, TyContext::Location(location));
                 self.visit_literal(literal, location);
             }
 
@@ -803,10 +809,21 @@ fn visit_location(&mut self, mir: & $($mutability)* Mir<'tcx>, location: Locatio
 make_mir_visitor!(Visitor,);
 make_mir_visitor!(MutVisitor,mut);
 
+/// Extra information passed to `visit_ty` and friends to give context
+/// about where the type etc appears.
 #[derive(Copy, Clone, Debug)]
-pub enum Lookup {
-    Loc(Location),
-    Src(SourceInfo),
+pub enum TyContext {
+    LocalDecl {
+        /// The index of the local variable we are visiting.
+        local: Local,
+
+        /// The source location where this local variable was declared.
+        source_info: SourceInfo,
+    },
+
+    Location(Location),
+
+    SourceInfo(SourceInfo),
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
index e8f9a6720872d5ec21374e0b8fbba97865400ee4..94edaa746f915b08a6d2f83f77888288e4affd72 100644 (file)
@@ -145,7 +145,7 @@ pub struct BitMatrix {
 }
 
 impl BitMatrix {
-    // Create a new `rows x columns` matrix, initially empty.
+    /// Create a new `rows x columns` matrix, initially empty.
     pub fn new(rows: usize, columns: usize) -> BitMatrix {
         // For every element, we need one bit for every other
         // element. Round up to an even number of u64s.
@@ -163,9 +163,13 @@ fn range(&self, row: usize) -> (usize, usize) {
         (start, start + u64s_per_row)
     }
 
-    pub fn add(&mut self, source: usize, target: usize) -> bool {
-        let (start, _) = self.range(source);
-        let (word, mask) = word_mask(target);
+    /// Sets the cell at `(row, column)` to true. Put another way, add
+    /// `column` to the bitset for `row`.
+    ///
+    /// Returns true if this changed the matrix, and false otherwies.
+    pub fn add(&mut self, row: usize, column: usize) -> bool {
+        let (start, _) = self.range(row);
+        let (word, mask) = word_mask(column);
         let vector = &mut self.vector[..];
         let v1 = vector[start + word];
         let v2 = v1 | mask;
@@ -173,19 +177,19 @@ pub fn add(&mut self, source: usize, target: usize) -> bool {
         v1 != v2
     }
 
-    /// Do the bits from `source` contain `target`?
-    ///
-    /// Put another way, if the matrix represents (transitive)
-    /// reachability, can `source` reach `target`?
-    pub fn contains(&self, source: usize, target: usize) -> bool {
-        let (start, _) = self.range(source);
-        let (word, mask) = word_mask(target);
+    /// Do the bits from `row` contain `column`? Put another way, is
+    /// the matrix cell at `(row, column)` true?  Put yet another way,
+    /// if the matrix represents (transitive) reachability, can
+    /// `row` reach `column`?
+    pub fn contains(&self, row: usize, column: usize) -> bool {
+        let (start, _) = self.range(row);
+        let (word, mask) = word_mask(column);
         (self.vector[start + word] & mask) != 0
     }
 
-    /// Returns those indices that are reachable from both `a` and
-    /// `b`. This is an O(n) operation where `n` is the number of
-    /// elements (somewhat independent from the actual size of the
+    /// Returns those indices that are true in rows `a` and `b`.  This
+    /// is an O(n) operation where `n` is the number of elements
+    /// (somewhat independent from the actual size of the
     /// intersection, in particular).
     pub fn intersection(&self, a: usize, b: usize) -> Vec<usize> {
         let (a_start, a_end) = self.range(a);
@@ -206,7 +210,7 @@ pub fn intersection(&self, a: usize, b: usize) -> Vec<usize> {
         result
     }
 
-    /// Add the bits from `read` to the bits from `write`,
+    /// Add the bits from row `read` to the bits from row `write`,
     /// return true if anything changed.
     ///
     /// This is used when computing transitive reachability because if
@@ -227,6 +231,8 @@ pub fn merge(&mut self, read: usize, write: usize) -> bool {
         changed
     }
 
+    /// Iterates through all the columns set to true in a given row of
+    /// the matrix.
     pub fn iter<'a>(&'a self, row: usize) -> BitVectorIter<'a> {
         let (start, end) = self.range(row);
         BitVectorIter {
index 7cb386b019798d3ecce2a1f015d4a867d3fb1d79..933e08811ce5d6a647de68f4f8e08e6b855a9d15 100644 (file)
@@ -134,12 +134,12 @@ pub fn contains(&self, a: &T, b: &T) -> bool {
         }
     }
 
-    /// Returns a vector of all things less than `a`.
+    /// Returns a vector of all things greater than `a`.
     ///
     /// Really this probably ought to be `impl Iterator<Item=&T>`, but
     /// I'm too lazy to make that work, and -- given the caching
     /// strategy -- it'd be a touch tricky anyhow.
-    pub fn less_than(&self, a: &T) -> Vec<&T> {
+    pub fn greater_than(&self, a: &T) -> Vec<&T> {
         match self.index(a) {
             Some(a) => self.with_closure(|closure| {
                 closure.iter(a.0).map(|i| &self.elements[i]).collect()
index 77496c7b8f218b922e9991ea3cc540cf8034c311..2073d49530061fc728d97a44cec2e4409a97c5d5 100644 (file)
@@ -17,7 +17,7 @@
 use rustc::middle::region;
 use rustc::mir::*;
 use rustc::mir::transform::MirSource;
-use rustc::mir::visit::{MutVisitor, Lookup};
+use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::subst::Substs;
 use rustc::util::nodemap::NodeMap;
@@ -165,7 +165,7 @@ struct GlobalizeMir<'a, 'gcx: 'a> {
 }
 
 impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> {
-    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: Lookup) {
+    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) {
         if let Some(lifted) = self.tcx.lift(ty) {
             *ty = lifted;
         } else {
index 17aa8c054181e4a904a0ca7566cbccf9633a243f..e8bf543b70b6a1d443151888d81290cef66722ee 100644 (file)
@@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
     location_map: FxHashMap<Location, BorrowIndex>,
     region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
     region_span_map: FxHashMap<RegionKind, Span>,
-    nonlexical_regioncx: Option<&'a RegionInferenceContext>,
+    nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>,
 }
 
 // temporarily allow some dead fields: `kind` and `region` will be
@@ -69,7 +69,7 @@ fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
 impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                mir: &'a Mir<'tcx>,
-               nonlexical_regioncx: Option<&'a RegionInferenceContext>)
+               nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
                -> Self {
         let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
                                           location_map: FxHashMap(),
@@ -139,11 +139,21 @@ fn kill_loans_out_of_scope_at_location(&self,
                                            location: Location) {
         if let Some(regioncx) = self.nonlexical_regioncx {
             for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
-                let borrow_region = regioncx.region_value(borrow_data.region.to_region_index());
-                if !borrow_region.may_contain(location) && location != borrow_data.location {
-                    debug!("kill_loans_out_of_scope_at_location: kill{:?} \
-                           location={:?} borrow_data={:?}", borrow_index, location, borrow_data);
-                    sets.kill(&borrow_index);
+                let borrow_region = borrow_data.region.to_region_index();
+                if !regioncx.region_contains_point(borrow_region, location) {
+                    // The region checker really considers the borrow
+                    // to start at the point **after** the location of
+                    // the borrow, but the borrow checker puts the gen
+                    // directly **on** the location of the
+                    // borrow. This results in a gen/kill both being
+                    // generated for same point if we are not
+                    // careful. Probably we should change the point of
+                    // the gen, but for now we hackily account for the
+                    // mismatch here by not generating a kill for the
+                    // location on the borrow itself.
+                    if location != borrow_data.location {
+                        sets.kill(&borrow_index);
+                    }
                 }
             }
         }
index a6750f400ba939ca4d1e9d903a1aa533ce40104d..d356d3b5a850874da1acd393a1968b37d19b680d 100644 (file)
@@ -24,7 +24,7 @@
 use rustc::middle::region;
 use rustc::mir::transform::{MirPass, MirSource};
 use rustc::mir::{BasicBlock, Location, Mir, Rvalue, Statement, StatementKind};
-use rustc::mir::visit::{MutVisitor, Visitor, Lookup};
+use rustc::mir::visit::{MutVisitor, Visitor, TyContext};
 use rustc::ty::{Ty, RegionKind, TyCtxt};
 
 pub struct CleanEndRegions;
@@ -67,7 +67,7 @@ fn visit_rvalue(&mut self,
         self.super_rvalue(rvalue, location);
     }
 
-    fn visit_ty(&mut self, ty: &Ty<'tcx>, _: Lookup) {
+    fn visit_ty(&mut self, ty: &Ty<'tcx>, _: TyContext) {
         // Gather regions that occur in types
         for re in ty.walk().flat_map(|t| t.regions()) {
             match *re {
index dc18cdd8f0dd6006e2270974f8cae06240319d0e..08ca20e50eb7f8eef5f021ad82678a234bb10aec 100644 (file)
@@ -17,7 +17,7 @@
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::mir::*;
-use rustc::mir::visit::{MutVisitor, Lookup};
+use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::mir::transform::{MirPass, MirSource};
 
 struct EraseRegionsVisitor<'a, 'tcx: 'a> {
@@ -35,7 +35,7 @@ pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self {
 }
 
 impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
-    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: Lookup) {
+    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, _: TyContext) {
         if !self.in_validation_statement {
             *ty = self.tcx.erase_regions(ty);
         }
index a7570c610d8d34a1b07f0f6e897bd9f1d2c6612f..a2f9bbb174eb737b2b6a91e8a51615b350650c0e 100644 (file)
@@ -29,7 +29,7 @@
 
 pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    regioncx: &mut RegionInferenceContext,
+    regioncx: &mut RegionInferenceContext<'tcx>,
     mir: &Mir<'tcx>,
     mir_source: MirSource,
     liveness: &LivenessResults,
@@ -45,7 +45,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
 
 struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
     infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
-    regioncx: &'cx mut RegionInferenceContext,
+    regioncx: &'cx mut RegionInferenceContext<'tcx>,
     mir: &'cx Mir<'tcx>,
     liveness: &'cx LivenessResults,
     mir_source: MirSource,
@@ -191,6 +191,7 @@ fn add_borrow_constraint(
         _borrowed_lv: &Lvalue<'tcx>,
     ) {
         let tcx = self.infcx.tcx;
+        let span = self.mir.source_info(location).span;
         let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
 
         let destination_region = match destination_ty.sty {
@@ -198,7 +199,8 @@ fn add_borrow_constraint(
             _ => bug!()
         };
 
-        self.regioncx.add_outlives(borrow_region.to_region_index(),
+        self.regioncx.add_outlives(span,
+                                   borrow_region.to_region_index(),
                                    destination_region.to_region_index(),
                                    location.successor_within_block());
     }
@@ -226,7 +228,9 @@ fn add_reborrow_constraint(
                         },
                     }
 
-                    self.regioncx.add_outlives(base_region.to_region_index(),
+                    let span = self.mir.source_info(location).span;
+                    self.regioncx.add_outlives(span,
+                                               base_region.to_region_index(),
                                                borrow_region.to_region_index(),
                                                location.successor_within_block());
                 }
@@ -259,8 +263,9 @@ fn visit_statement(&mut self,
             let destination_ty = destination_lv.ty(self.mir, tcx).to_ty(tcx);
             let rv_ty = rv.ty(self.mir, tcx);
 
+            let span = self.mir.source_info(location).span;
             for (a, b) in subtype::outlives_pairs(tcx, rv_ty, destination_ty) {
-                self.regioncx.add_outlives(a, b, location.successor_within_block());
+                self.regioncx.add_outlives(span, a, b, location.successor_within_block());
             }
         }
 
diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs
new file mode 100644 (file)
index 0000000..006a2f9
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Code to extract the free regions declared on a function and the
+//! relationships between them. For example:
+//!
+//! ```
+//! fn foo<'a, 'b, 'c: 'b>() { }
+//! ```
+//!
+//! here we would be returning a map assigning each of `{'a, 'b, 'c}`
+//! to an index, as well as the `FreeRegionMap` which can compute
+//! relationships between them.
+//!
+//! The code in this file doesn't *do anything* with those results; it
+//! just returns them for other code to use.
+
+use rustc::infer::InferCtxt;
+use rustc::middle::free_region::FreeRegionMap;
+use rustc::mir::transform::MirSource;
+use rustc::ty;
+use rustc::ty::subst::Substs;
+use rustc::util::nodemap::FxHashMap;
+
+#[derive(Debug)]
+pub struct FreeRegions<'tcx> {
+    /// Given a free region defined on this function (either early- or
+    /// late-bound), this maps it to its internal region index. The
+    /// corresponding variable will be "capped" so that it cannot
+    /// grow.
+    pub indices: FxHashMap<ty::Region<'tcx>, usize>,
+
+    /// The map from the typeck tables telling us how to relate free regions.
+    pub free_region_map: &'tcx FreeRegionMap<'tcx>,
+}
+
+pub fn free_regions<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    source: MirSource,
+) -> FreeRegions<'tcx> {
+    debug!("free_regions(source={:?})", source);
+
+    let item_id = source.item_id();
+    let item_def_id = infcx.tcx.hir.local_def_id(item_id);
+
+    let mut indices = FxHashMap();
+
+    // 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);
+        }
+    }
+
+    // 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 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);
+            }
+        });
+
+    debug!("free_regions: indices={:#?}", indices);
+
+    FreeRegions { indices, free_region_map: &tables.free_region_map }
+}
+
+fn insert_free_region<'tcx>(
+    free_regions: &mut FxHashMap<ty::Region<'tcx>, usize>,
+    region: ty::Region<'tcx>,
+) {
+    let len = free_regions.len();
+    free_regions.entry(region).or_insert(len);
+}
index d4938dc40bf25689463cedc6c22541944c29adb3..e24def2292ec6e0656f328696cb10c65e79fdb11 100644 (file)
@@ -9,19 +9,19 @@
 // except according to those terms.
 
 use rustc::ty::{self, RegionKind};
-use rustc::mir::{Location, Mir};
+use rustc::mir::Mir;
 use rustc::mir::transform::MirSource;
 use rustc::infer::InferCtxt;
 use rustc::util::nodemap::FxHashMap;
 use rustc_data_structures::indexed_vec::Idx;
 use std::collections::BTreeSet;
-use std::fmt;
 use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
 
 use util as mir_util;
 use self::mir_util::PassWhere;
 
 mod constraint_generation;
+mod free_regions;
 mod subtype;
 
 pub(crate) mod region_infer;
@@ -36,9 +36,12 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
     infcx: &InferCtxt<'a, 'gcx, 'tcx>,
     source: MirSource,
     mir: &mut Mir<'tcx>,
-) -> RegionInferenceContext {
+) -> RegionInferenceContext<'tcx> {
+    // Compute named region information.
+    let free_regions = &free_regions::free_regions(infcx, source);
+
     // Replace all regions with fresh inference variables.
-    let num_region_variables = renumber::renumber_mir(infcx, mir);
+    let num_region_variables = renumber::renumber_mir(infcx, free_regions, mir);
 
     // Compute what is live where.
     let liveness = &LivenessResults {
@@ -61,11 +64,9 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
 
     // Create the region inference context, generate the constraints,
     // and then solve them.
-    let mut regioncx = RegionInferenceContext::new(num_region_variables);
+    let mut regioncx = RegionInferenceContext::new(free_regions, num_region_variables, mir);
     constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness);
-    let errors = regioncx.solve(infcx, &mir);
-
-    assert!(errors.is_empty(), "FIXME: report region inference failures");
+    regioncx.solve(infcx, &mir);
 
     // Dump MIR results into a file, if that is enabled. This let us
     // write unit-tests.
@@ -149,27 +150,6 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
     });
 }
 
-#[derive(Clone, Default, PartialEq, Eq)]
-pub struct Region {
-    points: BTreeSet<Location>,
-}
-
-impl fmt::Debug for Region {
-    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(formatter, "{:?}", self.points)
-    }
-}
-
-impl Region {
-    pub fn add_point(&mut self, point: Location) -> bool {
-        self.points.insert(point)
-    }
-
-    pub fn may_contain(&self, point: Location) -> bool {
-        self.points.contains(&point)
-    }
-}
-
 newtype_index!(RegionIndex {
     DEBUG_FORMAT = "'_#{}r",
 });
index c23d73e784ae9d04b4325e244feab9f66d95b5ef..553d5ad4a320ab687aea1c9d8f6a115b2914f7a2 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use super::{Region, RegionIndex};
-use std::mem;
+use super::RegionIndex;
+use super::free_regions::FreeRegions;
 use rustc::infer::InferCtxt;
 use rustc::mir::{Location, Mir};
-use rustc_data_structures::indexed_vec::IndexVec;
+use rustc::ty;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc_data_structures::fx::FxHashSet;
+use std::collections::BTreeSet;
+use std::fmt;
+use syntax_pos::Span;
 
-pub struct RegionInferenceContext {
+pub struct RegionInferenceContext<'tcx> {
     /// Contains the definition for every region variable.  Region
     /// variables are identified by their index (`RegionIndex`). The
     /// definition contains information about where the region came
     /// from as well as its final inferred value.
-    definitions: IndexVec<RegionIndex, RegionDefinition>,
+    definitions: IndexVec<RegionIndex, RegionDefinition<'tcx>>,
+
+    /// The indices of all "free regions" in scope. These are the
+    /// lifetime parameters (anonymous and named) declared in the
+    /// function signature:
+    ///
+    ///     fn foo<'a, 'b>(x: &Foo<'a, 'b>)
+    ///            ^^  ^^     ^
+    ///
+    /// These indices will be from 0..N, as it happens, but we collect
+    /// them into a vector for convenience.
+    free_regions: Vec<RegionIndex>,
 
     /// The constraints we have accumulated and used during solving.
     constraints: Vec<Constraint>,
+}
+
+#[derive(Default)]
+struct RegionDefinition<'tcx> {
+    /// If this is a free-region, then this is `Some(X)` where `X` is
+    /// the name of the region.
+    name: Option<ty::Region<'tcx>>,
+
+    /// If true, this is a constant region which cannot grow larger.
+    /// This is used for named regions as well as `'static`.
+    constant: bool,
+
+    /// The current value of this inference variable. This starts out
+    /// empty, but grows as we add constraints. The final value is
+    /// determined when `solve()` is executed.
+    value: Region,
+}
 
-    /// List of errors we have accumulated as we add constraints.
-    /// After solving is done, this is replaced with an empty vector.
-    errors: Vec<InferenceError>,
+/// The value of an individual region variable. Region variables
+/// consist of a set of points in the CFG as well as a set of "free
+/// regions", which are sometimes written as `end(R)`. These
+/// correspond to the named lifetimes and refer to portions of the
+/// caller's control-flow graph -- specifically some portion that can
+/// be reached after we return.
+#[derive(Clone, Default, PartialEq, Eq)]
+struct Region {
+    points: BTreeSet<Location>,
+    free_regions: BTreeSet<RegionIndex>,
 }
 
-pub struct InferenceError {
-    pub constraint_point: Location,
-    pub name: (), // FIXME(nashenas88) RegionName
+impl fmt::Debug for Region {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        formatter
+            .debug_set()
+            .entries(&self.points)
+            .entries(&self.free_regions)
+            .finish()
+    }
 }
 
-#[derive(Default)]
-struct RegionDefinition {
-    name: (), // FIXME(nashenas88) RegionName
-    value: Region,
-    capped: bool,
+impl Region {
+    fn add_point(&mut self, point: Location) -> bool {
+        self.points.insert(point)
+    }
+
+    fn add_free_region(&mut self, region: RegionIndex) -> bool {
+        self.free_regions.insert(region)
+    }
+
+    fn contains_point(&self, point: Location) -> bool {
+        self.points.contains(&point)
+    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub struct Constraint {
-    sub: RegionIndex,
+    /// Where did this constraint arise?
+    span: Span,
+
+    /// The region SUP must outlive SUB...
     sup: RegionIndex,
+
+    /// Region that must be outlived.
+    sub: RegionIndex,
+
+    /// At this location.
     point: Location,
 }
 
-impl RegionInferenceContext {
-    pub fn new(num_region_variables: usize) -> Self {
-        Self {
+impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
+    /// Creates a new region inference context with a total of
+    /// `num_region_variables` valid inference variables; the first N
+    /// of those will be constant regions representing the free
+    /// regions defined in `free_regions`.
+    pub fn new(
+        free_regions: &FreeRegions<'tcx>,
+        num_region_variables: usize,
+        mir: &Mir<'tcx>,
+    ) -> Self {
+        let mut result = Self {
             definitions: (0..num_region_variables)
                 .map(|_| RegionDefinition::default())
                 .collect(),
             constraints: Vec::new(),
-            errors: Vec::new(),
-        }
+            free_regions: Vec::new(),
+        };
+
+        result.init_free_regions(free_regions, mir);
+
+        result
     }
 
+    /// Initializes the region variables for each free region
+    /// (lifetime parameter). The first N variables always correspond
+    /// to the free regions appearing in the function signature (both
+    /// named and anonymous) and where clauses. This function iterates
+    /// over those regions and initializes them with minimum values.
+    ///
+    /// For example:
+    ///
+    ///     fn foo<'a, 'b>(..) where 'a: 'b
+    ///
+    /// would initialize two variables like so:
+    ///
+    ///     R0 = { CFG, R0 } // 'a
+    ///     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_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) {
+        let &FreeRegions {
+            ref indices,
+            ref free_region_map,
+        } = free_regions;
+
+        // For each free region X:
+        for (free_region, index) in indices {
+            let variable = RegionIndex::new(*index);
+
+            self.free_regions.push(variable);
+
+            // Initialize the name and a few other details.
+            self.definitions[variable].name = Some(free_region);
+            self.definitions[variable].constant = true;
+
+            // Add all nodes in the CFG to `definition.value`.
+            for (block, block_data) in mir.basic_blocks().iter_enumerated() {
+                let definition = &mut self.definitions[variable];
+                for statement_index in 0..block_data.statements.len() + 1 {
+                    let location = Location {
+                        block,
+                        statement_index,
+                    };
+                    definition.value.add_point(location);
+                }
+            }
+
+            // Add `end(X)` into the set for X.
+            self.definitions[variable].value.add_free_region(variable);
+
+            // Go through each region Y that outlives X (i.e., where
+            // Y: X is true). Add `end(X)` into the set for `Y`.
+            for superregion in free_region_map.regions_that_outlive(&free_region) {
+                let superregion_index = RegionIndex::new(indices[superregion]);
+                self.definitions[superregion_index]
+                    .value
+                    .add_free_region(variable);
+            }
+
+            debug!(
+                "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`",
+                free_region,
+                variable,
+                self.definitions[variable].value
+            );
+        }
+    }
 
     /// Returns an iterator over all the region indices.
     pub fn regions(&self) -> impl Iterator<Item = RegionIndex> {
         self.definitions.indices()
     }
 
-    /// Returns the inferred value for the region `r`.
+    /// Returns true if the region `r` contains the point `p`.
     ///
     /// Until `solve()` executes, this value is not particularly meaningful.
-    pub fn region_value(&self, r: RegionIndex) -> &Region {
-        &self.definitions[r].value
+    pub fn region_contains_point(&self, r: RegionIndex, p: Location) -> bool {
+        self.definitions[r].value.contains_point(p)
     }
 
-    /// Flags a region as being "capped" -- this means that if its
-    /// value is required to grow as a result of some constraint
-    /// (e.g., `add_live_point` or `add_outlives`), that indicates an
-    /// error. This is used for the regions representing named
-    /// lifetime parameters on a function: they get initialized to
-    /// their complete value, and then "capped" so that they can no
-    /// longer grow.
-    #[allow(dead_code)]
-    pub(super) fn cap_var(&mut self, v: RegionIndex) {
-        self.definitions[v].capped = true;
+    /// Returns access to the value of `r` for debugging purposes.
+    pub(super) fn region_value(&self, r: RegionIndex) -> &fmt::Debug {
+        &self.definitions[r].value
     }
 
+    /// Indicates that the region variable `v` is live at the point `point`.
     pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) {
         debug!("add_live_point({:?}, {:?})", v, point);
         let definition = &mut self.definitions[v];
-        if definition.value.add_point(point) {
-            if definition.capped {
-                self.errors.push(InferenceError {
-                    constraint_point: point,
-                    name: definition.name,
-                });
-            }
+        if !definition.constant {
+            definition.value.add_point(point);
+        } else {
+            // Constants are used for free regions, which already
+            // contain all the points in the control-flow graph.
+            assert!(definition.value.contains_point(point));
         }
     }
 
-    pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
+    /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
+    pub(super) fn add_outlives(
+        &mut self,
+        span: Span,
+        sup: RegionIndex,
+        sub: RegionIndex,
+        point: Location,
+    ) {
         debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
-        self.constraints.push(Constraint { sup, sub, point });
+        self.constraints.push(Constraint {
+            span,
+            sup,
+            sub,
+            point,
+        });
     }
 
     /// Perform region inference.
-    pub(super) fn solve<'a, 'gcx, 'tcx>(
+    pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>) {
+        let errors = self.propagate_constraints(mir);
+
+        // worst error msg ever
+        for (fr1, span, fr2) in errors {
+            infcx.tcx.sess.span_err(
+                span,
+                &format!(
+                    "free region `{}` does not outlive `{}`",
+                    self.definitions[fr1].name.unwrap(),
+                    self.definitions[fr2].name.unwrap()
+                ),
+            );
+        }
+    }
+
+    /// 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
+    /// feasible, but we check this later.
+    fn propagate_constraints(
         &mut self,
-        infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-        mir: &'a Mir<'tcx>,
-    ) -> Vec<InferenceError>
-    where
-        'gcx: 'tcx + 'a,
-        'tcx: 'a,
-    {
+        mir: &Mir<'tcx>,
+    ) -> Vec<(RegionIndex, Span, RegionIndex)> {
         let mut changed = true;
-        let mut dfs = Dfs::new(infcx, mir);
+        let mut dfs = Dfs::new(mir);
+        let mut error_regions = FxHashSet();
+        let mut errors = vec![];
         while changed {
             changed = false;
             for constraint in &self.constraints {
+                debug!("constraint: {:?}", constraint);
                 let sub = &self.definitions[constraint.sub].value.clone();
                 let sup_def = &mut self.definitions[constraint.sup];
-                debug!("constraint: {:?}", constraint);
+
                 debug!("    sub (before): {:?}", sub);
                 debug!("    sup (before): {:?}", sup_def.value);
 
-                if dfs.copy(sub, &mut sup_def.value, constraint.point) {
-                    changed = true;
-                    if sup_def.capped {
-                        // This is kind of a hack, but when we add a
-                        // constraint, the "point" is always the point
-                        // AFTER the action that induced the
-                        // constraint. So report the error on the
-                        // action BEFORE that.
-                        assert!(constraint.point.statement_index > 0);
-                        let p = Location {
-                            block: constraint.point.block,
-                            statement_index: constraint.point.statement_index - 1,
-                        };
-
-                        self.errors.push(InferenceError {
-                            constraint_point: p,
-                            name: sup_def.name,
-                        });
+                if !sup_def.constant {
+                    // If this is not a constant, then grow the value as needed to
+                    // accommodate the outlives constraint.
+
+                    if dfs.copy(sub, &mut sup_def.value, constraint.point) {
+                        changed = true;
                     }
-                }
 
-                debug!("    sup (after) : {:?}", sup_def.value);
-                debug!("    changed     : {:?}", changed);
+                    debug!("    sup (after) : {:?}", sup_def.value);
+                    debug!("    changed     : {:?}", changed);
+                } else {
+                    // If this is a constant, check whether it *would
+                    // have* to grow in order for the constraint to be
+                    // satisfied. If so, create an error.
+
+                    let mut sup_value = sup_def.value.clone();
+                    if dfs.copy(sub, &mut sup_value, constraint.point) {
+                        // Constant values start out with the entire
+                        // CFG, so it must be some new free region
+                        // that was added. Find one.
+                        let &new_region = sup_value
+                            .free_regions
+                            .difference(&sup_def.value.free_regions)
+                            .next()
+                            .unwrap();
+                        debug!("    new_region : {:?}", new_region);
+                        if error_regions.insert(constraint.sup) {
+                            errors.push((constraint.sup, constraint.span, new_region));
+                        }
+                    }
+                }
             }
             debug!("\n");
         }
-
-        mem::replace(&mut self.errors, Vec::new())
+        errors
     }
 }
 
-struct Dfs<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> {
-    #[allow(dead_code)] infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+struct Dfs<'a, 'tcx: 'a> {
     mir: &'a Mir<'tcx>,
 }
 
-impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
-    fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
-        Self { infcx, mir }
+impl<'a, 'tcx> Dfs<'a, 'tcx> {
+    fn new(mir: &'a Mir<'tcx>) -> Self {
+        Self { mir }
     }
 
     fn copy(
@@ -180,7 +347,7 @@ fn copy(
         while let Some(p) = stack.pop() {
             debug!("        dfs: p={:?}", p);
 
-            if !from_region.may_contain(p) {
+            if !from_region.contains_point(p) {
                 debug!("            not in from-region");
                 continue;
             }
@@ -215,19 +382,14 @@ fn copy(
             };
 
             if successor_points.is_empty() {
-                // FIXME handle free regions
                 // If we reach the END point in the graph, then copy
                 // over any skolemized end points in the `from_region`
                 // and make sure they are included in the `to_region`.
-                // for region_decl in self.infcx.tcx.tables.borrow().free_region_map() {
-                //     // FIXME(nashenas88) figure out skolemized_end points
-                //     let block = self.env.graph.skolemized_end(region_decl.name);
-                //     let skolemized_end_point = Location {
-                //         block,
-                //         statement_index: 0,
-                //     };
-                //     changed |= to_region.add_point(skolemized_end_point);
-                // }
+
+                debug!("        dfs: free_regions={:?}", from_region.free_regions);
+                for &fr in &from_region.free_regions {
+                    changed |= to_region.free_regions.insert(fr);
+                }
             } else {
                 stack.extend(successor_points);
             }
index 589179c2066854ef05e451a6967acb333eec0454..a3ff7a041ca0714bd2d963274d377cd4ab03f18b 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use rustc::ty::TypeFoldable;
+use rustc_data_structures::indexed_vec::Idx;
 use rustc::ty::subst::{Kind, Substs};
-use rustc::ty::{Ty, ClosureSubsts, RegionVid, RegionKind};
-use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
-use rustc::mir::visit::{MutVisitor, Lookup};
+use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable};
+use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind};
+use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::infer::{self as rustc_infer, InferCtxt};
 use syntax_pos::DUMMY_SP;
 use std::collections::HashMap;
 
+use super::free_regions::FreeRegions;
+
 /// Replaces all free regions appearing in the MIR with fresh
 /// inference variables, returning the number of variables created.
-pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-                                    mir: &mut Mir<'tcx>)
-                                    -> usize
-{
-    let mut visitor = NLLVisitor::new(infcx);
+pub fn renumber_mir<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    free_regions: &FreeRegions<'tcx>,
+    mir: &mut Mir<'tcx>,
+) -> usize {
+    // Create inference variables for each of the free regions
+    // declared on the function signature.
+    let free_region_inference_vars = (0..free_regions.indices.len())
+        .map(|_| {
+            infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
+        })
+        .collect();
+
+    let mut visitor = NLLVisitor {
+        infcx,
+        lookup_map: HashMap::new(),
+        num_region_variables: free_regions.indices.len(),
+        free_regions,
+        free_region_inference_vars,
+        arg_count: mir.arg_count,
+    };
     visitor.visit_mir(mir);
     visitor.num_region_variables
 }
 
 struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
-    lookup_map: HashMap<RegionVid, Lookup>,
+    lookup_map: HashMap<RegionVid, TyContext>,
     num_region_variables: usize,
     infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    free_regions: &'a FreeRegions<'tcx>,
+    free_region_inference_vars: Vec<ty::Region<'tcx>>,
+    arg_count: usize,
 }
 
 impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
-    pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
-        NLLVisitor {
-            infcx,
-            lookup_map: HashMap::new(),
-            num_region_variables: 0
-        }
+    /// Replaces all regions appearing in `value` with fresh inference
+    /// variables. This is what we do for almost the entire MIR, with
+    /// the exception of the declared types of our arguments.
+    fn renumber_regions<T>(&mut self, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.infcx
+            .tcx
+            .fold_regions(value, &mut false, |_region, _depth| {
+                self.num_region_variables += 1;
+                self.infcx
+                    .next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
+            })
     }
 
-    fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> {
-        self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| {
-            self.num_region_variables += 1;
-            self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
-        })
+    /// Renumbers the regions appearing in `value`, but those regions
+    /// are expected to be free regions from the function signature.
+    fn renumber_free_regions<T>(&mut self, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        self.infcx
+            .tcx
+            .fold_regions(value, &mut false, |region, _depth| {
+                let index = self.free_regions.indices[&region];
+                self.free_region_inference_vars[index]
+            })
     }
 
-    fn store_region(&mut self, region: &RegionKind, lookup: Lookup) {
+    fn store_region(&mut self, region: &RegionKind, lookup: TyContext) {
         if let RegionKind::ReVar(rid) = *region {
             self.lookup_map.entry(rid).or_insert(lookup);
         }
     }
 
-    fn store_ty_regions(&mut self, ty: &Ty<'tcx>, lookup: Lookup) {
+    fn store_ty_regions(&mut self, ty: &Ty<'tcx>, ty_context: TyContext) {
         for region in ty.regions() {
-            self.store_region(region, lookup);
+            self.store_region(region, ty_context);
         }
     }
 
-    fn store_kind_regions(&mut self, kind: &'tcx Kind, lookup: Lookup) {
+    fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) {
         if let Some(ty) = kind.as_type() {
-            self.store_ty_regions(&ty, lookup);
+            self.store_ty_regions(&ty, ty_context);
         } else if let Some(region) = kind.as_region() {
-            self.store_region(region, lookup);
+            self.store_region(region, ty_context);
         }
     }
+
+    fn is_argument_or_return_slot(&self, local: Local) -> bool {
+        // The first argument is return slot, next N are arguments.
+        local.index() <= self.arg_count
+    }
 }
 
 impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
-    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, lookup: Lookup) {
+    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
+        let is_arg = match ty_context {
+            TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
+            _ => false,
+        };
+
         let old_ty = *ty;
-        *ty = self.renumber_regions(&old_ty);
-        self.store_ty_regions(ty, lookup);
+        *ty = if is_arg {
+            self.renumber_free_regions(&old_ty)
+        } else {
+            self.renumber_regions(&old_ty)
+        };
+        self.store_ty_regions(ty, ty_context);
     }
 
     fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
-        *substs = self.renumber_regions(&{*substs});
-        let lookup = Lookup::Loc(location);
+        *substs = self.renumber_regions(&{ *substs });
+        let ty_context = TyContext::Location(location);
         for kind in *substs {
-            self.store_kind_regions(kind, lookup);
+            self.store_kind_regions(kind, ty_context);
         }
     }
 
@@ -91,8 +141,8 @@ fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
             Rvalue::Ref(ref mut r, _, _) => {
                 let old_r = *r;
                 *r = self.renumber_regions(&old_r);
-                let lookup = Lookup::Loc(location);
-                self.store_region(r, lookup);
+                let ty_context = TyContext::Location(location);
+                self.store_region(r, ty_context);
             }
             Rvalue::Use(..) |
             Rvalue::Repeat(..) |
@@ -110,20 +160,20 @@ fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
         self.super_rvalue(rvalue, location);
     }
 
-    fn visit_closure_substs(&mut self,
-                            substs: &mut ClosureSubsts<'tcx>,
-                            location: Location) {
+    fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
         *substs = self.renumber_regions(substs);
-        let lookup = Lookup::Loc(location);
+        let ty_context = TyContext::Location(location);
         for kind in substs.substs {
-            self.store_kind_regions(kind, lookup);
+            self.store_kind_regions(kind, ty_context);
         }
     }
 
-    fn visit_statement(&mut self,
-                       block: BasicBlock,
-                       statement: &mut Statement<'tcx>,
-                       location: Location) {
+    fn visit_statement(
+        &mut self,
+        block: BasicBlock,
+        statement: &mut Statement<'tcx>,
+        location: Location,
+    ) {
         if let StatementKind::EndRegion(_) = statement.kind {
             statement.kind = StatementKind::Nop;
         }
index d8dc7a8d5cc7d3e57f377828295f4bfd900a61a7..b07e818ee87525045e6d237d575d74cb66117cef 100644 (file)
@@ -92,8 +92,8 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         self.sanitize_type(rvalue, rval_ty);
     }
 
-    fn visit_local_decl(&mut self, local_decl: &LocalDecl<'tcx>) {
-        self.super_local_decl(local_decl);
+    fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
+        self.super_local_decl(local, local_decl);
         self.sanitize_type(local_decl, local_decl.ty);
     }
 
index 30e2ba61dbda7ef90b50b4c6fd5605562f517a78..ab41ad1e0995056c46635618869377fffad65af6 100644 (file)
@@ -14,7 +14,7 @@
 
 use rustc_const_math::{ConstUsize};
 use rustc::mir::{AggregateKind, AssertMessage, BasicBlock, BasicBlockData};
-use rustc::mir::{Constant, Literal, Location, LocalDecl};
+use rustc::mir::{Constant, Literal, Location, Local, LocalDecl};
 use rustc::mir::{Lvalue, LvalueElem, LvalueProjection};
 use rustc::mir::{Mir, Operand, ProjectionElem};
 use rustc::mir::{Rvalue, SourceInfo, Statement, StatementKind};
@@ -270,9 +270,10 @@ fn visit_const_usize(&mut self,
     }
 
     fn visit_local_decl(&mut self,
+                        local: Local,
                         local_decl: &LocalDecl<'tcx>) {
         self.record("LocalDecl", local_decl);
-        self.super_local_decl(local_decl);
+        self.super_local_decl(local, local_decl);
     }
 
     fn visit_visibility_scope(&mut self,
diff --git a/src/test/mir-opt/nll/named-lifetimes-basic.rs b/src/test/mir-opt/nll/named-lifetimes-basic.rs
new file mode 100644 (file)
index 0000000..c4f3a6f
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for named lifetime translation. Check that we
+// instantiate the types that appear in function arguments with
+// suitable variables and that we setup the outlives relationship
+// between R0 and R1 properly.
+
+// compile-flags:-Znll -Zverbose
+//                     ^^^^^^^^^ force compiler to dump more region information
+// ignore-tidy-linelength
+
+#![allow(warnings)]
+
+fn use_x<'a, 'b: 'a, 'c>(w: &'a mut i32, x: &'b u32, y: &'a u32, z: &'c u32) -> bool { true }
+
+fn main() {
+}
+
+// END RUST SOURCE
+// START rustc.node4.nll.0.mir
+// | '_#0r: {bb0[0], bb0[1], '_#0r}
+// | '_#1r: {bb0[0], bb0[1], '_#0r, '_#1r}
+// | '_#2r: {bb0[0], bb0[1], '_#2r}
+// ...
+// fn use_x(_1: &'_#0r mut i32, _2: &'_#1r u32, _3: &'_#0r u32, _4: &'_#2r u32) -> bool {
+// END rustc.node4.nll.0.mir
diff --git a/src/test/ui/nll/named-region-basic.rs b/src/test/ui/nll/named-region-basic.rs
new file mode 100644 (file)
index 0000000..539c201
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Basic test for free regions in the NLL code. This test ought to
+// report an error due to a reborrowing constraint. Right now, we get
+// a variety of errors from the older, AST-based machinery (notably
+// borrowck), and then we get the NLL error at the end.
+
+// compile-flags:-Znll
+
+fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
+    &*x
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/named-region-basic.stderr b/src/test/ui/nll/named-region-basic.stderr
new file mode 100644 (file)
index 0000000..42b2aea
--- /dev/null
@@ -0,0 +1,29 @@
+warning: not reporting region error due to -Znll
+  --> $DIR/named-region-basic.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error[E0597]: `*x` does not live long enough
+  --> $DIR/named-region-basic.rs:19:6
+   |
+19 |     &*x
+   |      ^^ does not live long enough
+   |
+   = note: borrowed value must be valid for the static lifetime...
+note: ...but borrowed value is only valid for the lifetime 'a as defined on the function body at 18:1
+  --> $DIR/named-region-basic.rs:18:1
+   |
+18 | / fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
+19 | |     &*x
+20 | | }
+   | |_^
+
+error: free region `'a` does not outlive `'b`
+  --> $DIR/named-region-basic.rs:19:5
+   |
+19 |     &*x
+   |     ^^^
+
+error: aborting due to 2 previous errors
+