about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2017-10-30 04:51:10 -0400
committerNiko Matsakis <niko@alum.mit.edu>2017-11-02 04:40:50 -0400
commitcafbd99c3822f24ca53c3cce79ffe2471e05dbc3 (patch)
tree799402bab481bff6136b465702fbe9003f825ee8
parent7b30e8d6681b10d6b7ecf92aa52d3085c5218a85 (diff)
downloadrust-cafbd99c3822f24ca53c3cce79ffe2471e05dbc3.tar.gz
rust-cafbd99c3822f24ca53c3cce79ffe2471e05dbc3.zip
extend NLL regions to include free region indices and add outlives
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs6
-rw-r--r--src/librustc_mir/transform/nll/constraint_generation.rs4
-rw-r--r--src/librustc_mir/transform/nll/free_regions.rs88
-rw-r--r--src/librustc_mir/transform/nll/mod.rs30
-rw-r--r--src/librustc_mir/transform/nll/region_infer.rs171
-rw-r--r--src/librustc_mir/transform/nll/renumber.rs106
-rw-r--r--src/test/compile-fail/nll/region-error.rs5
7 files changed, 297 insertions, 113 deletions
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 17aa8c05418..85f05a10cd9 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -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 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
 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(),
@@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
         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 {
+                if !borrow_region.may_contain_point(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);
diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs
index a7570c610d8..f077dd4d124 100644
--- a/src/librustc_mir/transform/nll/constraint_generation.rs
+++ b/src/librustc_mir/transform/nll/constraint_generation.rs
@@ -29,7 +29,7 @@ use super::region_infer::RegionInferenceContext;
 
 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,
diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs
new file mode 100644
index 00000000000..006a2f9047a
--- /dev/null
+++ b/src/librustc_mir/transform/nll/free_regions.rs
@@ -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);
+}
diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs
index d4938dc40bf..2dd9b85c294 100644
--- a/src/librustc_mir/transform/nll/mod.rs
+++ b/src/librustc_mir/transform/nll/mod.rs
@@ -22,6 +22,7 @@ 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 +37,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 +65,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.
@@ -152,11 +154,15 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
 #[derive(Clone, Default, PartialEq, Eq)]
 pub struct Region {
     points: BTreeSet<Location>,
+    free_regions: BTreeSet<RegionIndex>,
 }
 
 impl fmt::Debug for Region {
     fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(formatter, "{:?}", self.points)
+        formatter.debug_set()
+                 .entries(&self.points)
+                 .entries(&self.free_regions)
+                 .finish()
     }
 }
 
@@ -165,9 +171,17 @@ impl Region {
         self.points.insert(point)
     }
 
-    pub fn may_contain(&self, point: Location) -> bool {
+    pub fn add_free_region(&mut self, region: RegionIndex) -> bool {
+        self.free_regions.insert(region)
+    }
+
+    pub fn may_contain_point(&self, point: Location) -> bool {
         self.points.contains(&point)
     }
+
+    pub fn may_contain_free_region(&self, region: RegionIndex) -> bool {
+        self.free_regions.contains(&region)
+    }
 }
 
 newtype_index!(RegionIndex {
diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs
index b825528b1bc..1027cf85090 100644
--- a/src/librustc_mir/transform/nll/region_infer.rs
+++ b/src/librustc_mir/transform/nll/region_infer.rs
@@ -9,37 +9,49 @@
 // except according to those terms.
 
 use super::{Region, RegionIndex};
-use std::mem;
+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;
 
-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>,
-
-    /// List of errors we have accumulated as we add constraints.
-    /// After solving is done, this is replaced with an empty vector.
-    errors: Vec<InferenceError>,
-}
-
-pub struct InferenceError {
-    pub constraint_point: Location,
-    pub name: (), // FIXME(nashenas88) RegionName
 }
 
 #[derive(Default)]
-struct RegionDefinition {
-    name: (), // FIXME(nashenas88) RegionName
+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,
-    capped: bool,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
@@ -49,14 +61,73 @@ pub struct Constraint {
     point: Location,
 }
 
-impl RegionInferenceContext {
-    pub fn new(num_region_variables: usize) -> Self {
-        Self {
+impl<'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
+    }
+
+    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 variable X, it should contain:
+        //
+        // (a) the entire CFG
+        // (b) `end(Y)` for all regions Y such that X: Y (or Y <= X)
+        //
+        // we add however the regions for clause (b) somewhat in
+        // reverse, because of how the data structure in
+        // `free_regions` is organized.
+        for (free_region, index) in indices {
+            let variable = RegionIndex::new(*index);
+
+            self.free_regions.push(variable);
+
+            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);
         }
     }
 
@@ -72,42 +143,25 @@ impl RegionInferenceContext {
         &self.definitions[r].value
     }
 
-    /// 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;
-    }
-
+    /// 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,
-                });
-            }
-        }
+        definition.value.add_point(point);
     }
 
+    /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
     pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
         debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
         self.constraints.push(Constraint { sup, sub, point });
     }
 
     /// Perform region inference.
-    pub(super) fn solve<'a, 'gcx, 'tcx>(
+    pub(super) fn solve<'a, 'gcx>(
         &mut self,
         infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
         mir: &'a Mir<'tcx>,
-    ) -> Vec<InferenceError>
+    )
     where
         'gcx: 'tcx + 'a,
         'tcx: 'a,
@@ -125,23 +179,6 @@ impl RegionInferenceContext {
 
                 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,
-                        });
-                    }
                 }
 
                 debug!("    sup (after) : {:?}", sup_def.value);
@@ -149,8 +186,6 @@ impl RegionInferenceContext {
             }
             debug!("\n");
         }
-
-        mem::replace(&mut self.errors, Vec::new())
     }
 }
 
@@ -179,7 +214,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
         while let Some(p) = stack.pop() {
             debug!("        dfs: p={:?}", p);
 
-            if !from_region.may_contain(p) {
+            if !from_region.may_contain_point(p) {
                 debug!("            not in from-region");
                 continue;
             }
@@ -214,19 +249,11 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
             };
 
             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);
-                // }
+
+                to_region.free_regions.extend(&from_region.free_regions);
             } else {
                 stack.extend(successor_points);
             }
diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs
index 40ce44bae9d..a3ff7a041ca 100644
--- a/src/librustc_mir/transform/nll/renumber.rs
+++ b/src/librustc_mir/transform/nll/renumber.rs
@@ -8,22 +8,40 @@
 // 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::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
 }
@@ -32,22 +50,40 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     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: TyContext) {
@@ -69,17 +105,31 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
             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>, 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);
+        *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});
+        *substs = self.renumber_regions(&{ *substs });
         let ty_context = TyContext::Location(location);
         for kind in *substs {
             self.store_kind_regions(kind, ty_context);
@@ -110,9 +160,7 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
         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 ty_context = TyContext::Location(location);
         for kind in substs.substs {
@@ -120,10 +168,12 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
         }
     }
 
-    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;
         }
diff --git a/src/test/compile-fail/nll/region-error.rs b/src/test/compile-fail/nll/region-error.rs
new file mode 100644
index 00000000000..4a3e838beb3
--- /dev/null
+++ b/src/test/compile-fail/nll/region-error.rs
@@ -0,0 +1,5 @@
+fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
+    &*x
+}
+
+fn main() { }