Skip to content

Commit 0201334

Browse files
committed
auto merge of #20244 : japaric/rust/bc-no-move, r=nikomatsakis
closes #19141 closes #20193 closes #20228 --- Currently whenever we encounter `let f = || {/* */}`, we *always* type check the RHS as a *boxed* closure. This is wrong when the RHS is `move || {/* */}` (because boxed closures can't capture by value) and generates all sort of badness during trans (see issues above). What we *should* do is always type check `move || {/* */}` as an *unboxed* closure, but ~~I *think* (haven't tried)~~ (2) this is not feasible right now because we have a limited form of kind (`Fn` vs `FnMut` vs `FnOnce`) inference that only works when there is an expected type (1). In this PR, I've chosen to generate a type error whenever `let f = move || {/* */}` is encountered. The error asks the user to annotate the kind of the unboxed closure (e.g. `move |:| {/* */}`). Once annotated, the compiler will type check the RHS as an unboxed closure which is what the user wants. r? @nikomatsakis (1) AIUI it only triggers in this scenario: ``` rust fn is_uc<F>(_: F) where F: FnOnce() {} fn main() { is_uc(|| {}); // type checked as unboxed closure with kind `FnOnce` } ``` (2) I checked, and it's not possible because `check_unboxed_closure` expects a `kind` argument, but we can't supply that argument in this case (i.e. `let f = || {}`, what's the kind?). We could force the `FnOnce` kind in that case, but that's ad hoc. We should try to infer the kind depending on how the closure is used afterwards, but there is no inference mechanism to do that (at least, not right now).
2 parents 4a4c89c + 5b0c8ac commit 0201334

File tree

7 files changed

+96
-3
lines changed

7 files changed

+96
-3
lines changed

src/librustc_typeck/check/closure.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ use middle::ty::{mod, Ty};
1919
use rscope::RegionScope;
2020
use syntax::abi;
2121
use syntax::ast;
22+
use syntax::ast::CaptureClause::*;
2223
use syntax::ast_util;
2324
use util::ppaux::Repr;
2425

2526
pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
2627
expr: &ast::Expr,
28+
capture: ast::CaptureClause,
2729
opt_kind: Option<ast::UnboxedClosureKind>,
2830
decl: &ast::FnDecl,
2931
body: &ast::Block,
@@ -48,12 +50,24 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
4850
fcx.infcx(),
4951
expr.span,
5052
&None);
53+
5154
check_boxed_closure(fcx,
5255
expr,
5356
ty::RegionTraitStore(region, ast::MutMutable),
5457
decl,
5558
body,
5659
expected);
60+
61+
match capture {
62+
CaptureByValue => {
63+
fcx.ccx.tcx.sess.span_err(
64+
expr.span,
65+
"boxed closures can't capture by value, \
66+
if you want to use an unboxed closure, \
67+
explicitly annotate its kind: e.g. `move |:|`");
68+
},
69+
CaptureByRef => {}
70+
}
5771
}
5872
Some((sig, kind)) => {
5973
check_unboxed_closure(fcx, expr, kind, decl, body, Some(sig));

src/librustc_typeck/check/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3973,8 +3973,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
39733973
ast::ExprMatch(ref discrim, ref arms, match_src) => {
39743974
_match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src);
39753975
}
3976-
ast::ExprClosure(_, opt_kind, ref decl, ref body) => {
3977-
closure::check_expr_closure(fcx, expr, opt_kind, &**decl, &**body, expected);
3976+
ast::ExprClosure(capture, opt_kind, ref decl, ref body) => {
3977+
closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected);
39783978
}
39793979
ast::ExprBlock(ref b) => {
39803980
check_block_with_expected(fcx, &**b, expected);

src/test/compile-fail/borrowck-move-by-capture.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
pub fn main() {
1212
let bar = box 3;
1313
let _g = || {
14-
let _h = move|| -> int { *bar }; //~ ERROR cannot move out of captured outer variable
14+
let _h = move |:| -> int { *bar }; //~ ERROR cannot move out of captured outer variable
1515
};
1616
}

src/test/compile-fail/issue-19141.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2014 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+
fn main() {
12+
let n = 0u;
13+
14+
let f = move || n += 1; //~error boxed closures can't capture by value
15+
}

src/test/compile-fail/issue-20193.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2014 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+
fn foo(t: &mut int){
12+
println!("{}", t);
13+
}
14+
15+
fn main() {
16+
let test = 10;
17+
18+
let h = move || { //~error boxed closures can't capture by value
19+
let mut r = &mut test.clone();
20+
foo(r);
21+
};
22+
23+
h();
24+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2014 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+
struct S;
12+
13+
impl S {
14+
fn foo(&self) {
15+
let _ = move || { self }; //~error boxed closures can't capture by value
16+
}
17+
}
18+
19+
fn main() {
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2014 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+
struct S;
12+
13+
impl S {
14+
fn foo(&self) {
15+
let _ = move || { self.foo() }; //~error boxed closures can't capture by value
16+
}
17+
}
18+
19+
fn main() {
20+
}

0 commit comments

Comments
 (0)