Skip to content

Commit 3a17880

Browse files
committed
start enforcing closure types
1 parent e0871ed commit 3a17880

File tree

3 files changed

+145
-2
lines changed

3 files changed

+145
-2
lines changed

src/librustc_mir/borrow_check/nll/type_check/input_output.rs

+74-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
//! contain revealed `impl Trait` values).
1919
2020
use borrow_check::nll::universal_regions::UniversalRegions;
21+
use rustc::infer::LateBoundRegionConversionTime;
2122
use rustc::mir::*;
2223
use rustc::ty::Ty;
2324

@@ -36,9 +37,47 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
3637
let (&normalized_output_ty, normalized_input_tys) =
3738
normalized_inputs_and_output.split_last().unwrap();
3839

40+
// If the user explicitly annotated the input types, extract
41+
// those.
42+
//
43+
// e.g. `|x: FxHashMap<_, &'static u32>| ...`
44+
let user_provided_sig;
45+
if !self.tcx().is_closure(self.mir_def_id) {
46+
user_provided_sig = None;
47+
} else {
48+
let typeck_tables = self.tcx().typeck_tables_of(self.mir_def_id);
49+
user_provided_sig = match typeck_tables.user_provided_sigs.get(&self.mir_def_id) {
50+
None => None,
51+
Some(user_provided_poly_sig) => {
52+
// Instantiate the canonicalized variables from
53+
// user-provided signature (e.g. the `_` in the code
54+
// above) with fresh variables.
55+
let (poly_sig, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars(
56+
mir.span,
57+
&user_provided_poly_sig,
58+
);
59+
60+
// Replace the bound items in the fn sig with fresh
61+
// variables, so that they represent the view from
62+
// "inside" the closure.
63+
Some(
64+
self.infcx
65+
.replace_late_bound_regions_with_fresh_var(
66+
mir.span,
67+
LateBoundRegionConversionTime::FnCall,
68+
&poly_sig,
69+
)
70+
.0,
71+
)
72+
}
73+
}
74+
};
75+
3976
// Equate expected input tys with those in the MIR.
40-
let argument_locals = (1..).map(Local::new);
41-
for (&normalized_input_ty, local) in normalized_input_tys.iter().zip(argument_locals) {
77+
for (&normalized_input_ty, argument_index) in normalized_input_tys.iter().zip(0..) {
78+
// In MIR, argument N is stored in local N+1.
79+
let local = Local::new(argument_index + 1);
80+
4281
debug!(
4382
"equate_inputs_and_outputs: normalized_input_ty = {:?}",
4483
normalized_input_ty
@@ -53,6 +92,27 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
5392
);
5493
}
5594

95+
if let Some(user_provided_sig) = user_provided_sig {
96+
for (&user_provided_input_ty, argument_index) in
97+
user_provided_sig.inputs().iter().zip(0..)
98+
{
99+
// In MIR, closures begin an implicit `self`, so
100+
// argument N is stored in local N+2.
101+
let local = Local::new(argument_index + 2);
102+
let mir_input_ty = mir.local_decls[local].ty;
103+
let mir_input_span = mir.local_decls[local].source_info.span;
104+
105+
// If the user explicitly annotated the input types, enforce those.
106+
let user_provided_input_ty =
107+
self.normalize(user_provided_input_ty, Locations::All(mir_input_span));
108+
self.equate_normalized_input_or_output(
109+
user_provided_input_ty,
110+
mir_input_ty,
111+
mir_input_span,
112+
);
113+
}
114+
}
115+
56116
assert!(
57117
mir.yield_ty.is_some() && universal_regions.yield_ty.is_some()
58118
|| mir.yield_ty.is_none() && universal_regions.yield_ty.is_none()
@@ -83,6 +143,18 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
83143
terr
84144
);
85145
};
146+
147+
// If the user explicitly annotated the output types, enforce those.
148+
if let Some(user_provided_sig) = user_provided_sig {
149+
let user_provided_output_ty = user_provided_sig.output();
150+
let user_provided_output_ty =
151+
self.normalize(user_provided_output_ty, Locations::All(output_span));
152+
self.equate_normalized_input_or_output(
153+
user_provided_output_ty,
154+
mir_output_ty,
155+
output_span,
156+
);
157+
}
86158
}
87159

88160
fn equate_normalized_input_or_output(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, span: Span) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(nll)]
12+
13+
// Test that we enforce user-provided type annotations on closures.
14+
15+
fn foo<'a>() {
16+
|x: &'a i32| -> &'static i32 {
17+
return x; //~ ERROR
18+
};
19+
}
20+
21+
fn bar<'a>() {
22+
|x: &i32, b: fn(&'static i32)| {
23+
b(x); //~ ERROR
24+
//~^ ERROR borrowed data escapes outside of closure
25+
//~| ERROR unsatisfied lifetime constraints
26+
};
27+
}
28+
29+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error: unsatisfied lifetime constraints
2+
--> $DIR/closure-substs.rs:17:16
3+
|
4+
LL | fn foo<'a>() {
5+
| -- lifetime `'a` defined here
6+
LL | |x: &'a i32| -> &'static i32 {
7+
LL | return x; //~ ERROR
8+
| ^ returning this value requires that `'a` must outlive `'static`
9+
10+
error: borrowed data escapes outside of closure
11+
--> $DIR/closure-substs.rs:23:9
12+
|
13+
LL | |x: &i32, b: fn(&'static i32)| {
14+
| - `x` is a reference that is only valid in the closure body
15+
LL | b(x); //~ ERROR
16+
| ^^^^ `x` escapes the closure body here
17+
18+
error: borrowed data escapes outside of closure
19+
--> $DIR/closure-substs.rs:23:9
20+
|
21+
LL | |x: &i32, b: fn(&'static i32)| {
22+
| - - `b` is declared here, outside of the closure body
23+
| |
24+
| `x` is a reference that is only valid in the closure body
25+
LL | b(x); //~ ERROR
26+
| ^^^^ `x` escapes the closure body here
27+
28+
error: unsatisfied lifetime constraints
29+
--> $DIR/closure-substs.rs:23:9
30+
|
31+
LL | |x: &i32, b: fn(&'static i32)| {
32+
| ------------------------------
33+
| | |
34+
| | let's call the lifetime of this reference `'1`
35+
| lifetime `'2` represents this closure's body
36+
LL | b(x); //~ ERROR
37+
| ^^^^ argument requires that `'1` must outlive `'2`
38+
|
39+
= note: closure implements `Fn`, so references to captured variables can't escape the closure
40+
41+
error: aborting due to 4 previous errors
42+

0 commit comments

Comments
 (0)