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 a4665984d3e0..ab4ee3a4ad0e 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 000000000000..0a22390fc714 --- /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 or the MIT license +// , 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 000000000000..97f6e594de5e --- /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 +