about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-10-20 08:38:16 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-10-22 10:04:47 -0400
commit3a178805397e07be4cd8506f34dbdb17abc8426d (patch)
tree8e803ffc2ef9fc5c85c51509cc3b872f71387ca3
parente0871ed318e2e6706f81c98cc348bcb99e78b4bf (diff)
downloadrust-3a178805397e07be4cd8506f34dbdb17abc8426d.tar.gz
rust-3a178805397e07be4cd8506f34dbdb17abc8426d.zip
start enforcing closure types
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/input_output.rs76
-rw-r--r--src/test/ui/nll/user-annotations/closure-substs.rs29
-rw-r--r--src/test/ui/nll/user-annotations/closure-substs.stderr42
3 files changed, 145 insertions, 2 deletions
diff --git a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
index a4665984d3e..ab4ee3a4ad0 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/input_output.rs
@@ -18,6 +18,7 @@
 //! contain revealed `impl Trait` values).
 
 use borrow_check::nll::universal_regions::UniversalRegions;
+use rustc::infer::LateBoundRegionConversionTime;
 use rustc::mir::*;
 use rustc::ty::Ty;
 
@@ -36,9 +37,47 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         let (&normalized_output_ty, normalized_input_tys) =
             normalized_inputs_and_output.split_last().unwrap();
 
+        // If the user explicitly annotated the input types, extract
+        // those.
+        //
+        // e.g. `|x: FxHashMap<_, &'static u32>| ...`
+        let user_provided_sig;
+        if !self.tcx().is_closure(self.mir_def_id) {
+            user_provided_sig = None;
+        } else {
+            let typeck_tables = self.tcx().typeck_tables_of(self.mir_def_id);
+            user_provided_sig = match typeck_tables.user_provided_sigs.get(&self.mir_def_id) {
+                None => None,
+                Some(user_provided_poly_sig) => {
+                    // Instantiate the canonicalized variables from
+                    // user-provided signature (e.g. the `_` in the code
+                    // above) with fresh variables.
+                    let (poly_sig, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(
+                        mir.span,
+                        &user_provided_poly_sig,
+                    );
+
+                    // Replace the bound items in the fn sig with fresh
+                    // variables, so that they represent the view from
+                    // "inside" the closure.
+                    Some(
+                        self.infcx
+                            .replace_late_bound_regions_with_fresh_var(
+                                mir.span,
+                                LateBoundRegionConversionTime::FnCall,
+                                &poly_sig,
+                            )
+                            .0,
+                    )
+                }
+            }
+        };
+
         // Equate expected input tys with those in the MIR.
-        let argument_locals = (1..).map(Local::new);
-        for (&normalized_input_ty, local) in normalized_input_tys.iter().zip(argument_locals) {
+        for (&normalized_input_ty, argument_index) in normalized_input_tys.iter().zip(0..) {
+            // In MIR, argument N is stored in local N+1.
+            let local = Local::new(argument_index + 1);
+
             debug!(
                 "equate_inputs_and_outputs: normalized_input_ty = {:?}",
                 normalized_input_ty
@@ -53,6 +92,27 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             );
         }
 
+        if let Some(user_provided_sig) = user_provided_sig {
+            for (&user_provided_input_ty, argument_index) in
+                user_provided_sig.inputs().iter().zip(0..)
+            {
+                // In MIR, closures begin an implicit `self`, so
+                // argument N is stored in local N+2.
+                let local = Local::new(argument_index + 2);
+                let mir_input_ty = mir.local_decls[local].ty;
+                let mir_input_span = mir.local_decls[local].source_info.span;
+
+                // If the user explicitly annotated the input types, enforce those.
+                let user_provided_input_ty =
+                    self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
+                self.equate_normalized_input_or_output(
+                    user_provided_input_ty,
+                    mir_input_ty,
+                    mir_input_span,
+                );
+            }
+        }
+
         assert!(
             mir.yield_ty.is_some() && universal_regions.yield_ty.is_some()
                 || mir.yield_ty.is_none() && universal_regions.yield_ty.is_none()
@@ -83,6 +143,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 terr
             );
         };
+
+        // If the user explicitly annotated the output types, enforce those.
+        if let Some(user_provided_sig) = user_provided_sig {
+            let user_provided_output_ty = user_provided_sig.output();
+            let user_provided_output_ty =
+                self.normalize(user_provided_output_ty, Locations::All(output_span));
+            self.equate_normalized_input_or_output(
+                user_provided_output_ty,
+                mir_output_ty,
+                output_span,
+            );
+        }
     }
 
     fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
diff --git a/src/test/ui/nll/user-annotations/closure-substs.rs b/src/test/ui/nll/user-annotations/closure-substs.rs
new file mode 100644
index 00000000000..0a22390fc71
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/closure-substs.rs
@@ -0,0 +1,29 @@
+// 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.
+
+#![feature(nll)]
+
+// Test that we enforce user-provided type annotations on closures.
+
+fn foo<'a>() {
+    |x: &'a i32| -> &'static i32 {
+        return x; //~ ERROR
+    };
+}
+
+fn bar<'a>() {
+    |x: &i32, b: fn(&'static i32)| {
+        b(x); //~ ERROR
+        //~^ ERROR borrowed data escapes outside of closure
+        //~| ERROR unsatisfied lifetime constraints
+    };
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/user-annotations/closure-substs.stderr b/src/test/ui/nll/user-annotations/closure-substs.stderr
new file mode 100644
index 00000000000..97f6e594de5
--- /dev/null
+++ b/src/test/ui/nll/user-annotations/closure-substs.stderr
@@ -0,0 +1,42 @@
+error: unsatisfied lifetime constraints
+  --> $DIR/closure-substs.rs:17:16
+   |
+LL | fn foo<'a>() {
+   |        -- lifetime `'a` defined here
+LL |     |x: &'a i32| -> &'static i32 {
+LL |         return x; //~ ERROR
+   |                ^ returning this value requires that `'a` must outlive `'static`
+
+error: borrowed data escapes outside of closure
+  --> $DIR/closure-substs.rs:23:9
+   |
+LL |     |x: &i32, b: fn(&'static i32)| {
+   |      - `x` is a reference that is only valid in the closure body
+LL |         b(x); //~ ERROR
+   |         ^^^^ `x` escapes the closure body here
+
+error: borrowed data escapes outside of closure
+  --> $DIR/closure-substs.rs:23:9
+   |
+LL |     |x: &i32, b: fn(&'static i32)| {
+   |      -        - `b` is declared here, outside of the closure body
+   |      |
+   |      `x` is a reference that is only valid in the closure body
+LL |         b(x); //~ ERROR
+   |         ^^^^ `x` escapes the closure body here
+
+error: unsatisfied lifetime constraints
+  --> $DIR/closure-substs.rs:23:9
+   |
+LL |     |x: &i32, b: fn(&'static i32)| {
+   |     ------------------------------
+   |     |   |
+   |     |   let's call the lifetime of this reference `'1`
+   |     lifetime `'2` represents this closure's body
+LL |         b(x); //~ ERROR
+   |         ^^^^ argument requires that `'1` must outlive `'2`
+   |
+   = note: closure implements `Fn`, so references to captured variables can't escape the closure
+
+error: aborting due to 4 previous errors
+