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).
This commit is contained in:
bors 2014-12-27 15:28:36 +00:00
commit 0201334439
7 changed files with 96 additions and 3 deletions

View File

@ -19,11 +19,13 @@ use middle::ty::{mod, Ty};
use rscope::RegionScope;
use syntax::abi;
use syntax::ast;
use syntax::ast::CaptureClause::*;
use syntax::ast_util;
use util::ppaux::Repr;
pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &ast::Expr,
capture: ast::CaptureClause,
opt_kind: Option<ast::UnboxedClosureKind>,
decl: &ast::FnDecl,
body: &ast::Block,
@ -48,12 +50,24 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
fcx.infcx(),
expr.span,
&None);
check_boxed_closure(fcx,
expr,
ty::RegionTraitStore(region, ast::MutMutable),
decl,
body,
expected);
match capture {
CaptureByValue => {
fcx.ccx.tcx.sess.span_err(
expr.span,
"boxed closures can't capture by value, \
if you want to use an unboxed closure, \
explicitly annotate its kind: e.g. `move |:|`");
},
CaptureByRef => {}
}
}
Some((sig, kind)) => {
check_unboxed_closure(fcx, expr, kind, decl, body, Some(sig));

View File

@ -3973,8 +3973,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
ast::ExprMatch(ref discrim, ref arms, match_src) => {
_match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src);
}
ast::ExprClosure(_, opt_kind, ref decl, ref body) => {
closure::check_expr_closure(fcx, expr, opt_kind, &**decl, &**body, expected);
ast::ExprClosure(capture, opt_kind, ref decl, ref body) => {
closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected);
}
ast::ExprBlock(ref b) => {
check_block_with_expected(fcx, &**b, expected);

View File

@ -11,6 +11,6 @@
pub fn main() {
let bar = box 3;
let _g = || {
let _h = move|| -> int { *bar }; //~ ERROR cannot move out of captured outer variable
let _h = move |:| -> int { *bar }; //~ ERROR cannot move out of captured outer variable
};
}

View File

@ -0,0 +1,15 @@
// Copyright 2014 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.
fn main() {
let n = 0u;
let f = move || n += 1; //~error boxed closures can't capture by value
}

View File

@ -0,0 +1,24 @@
// Copyright 2014 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.
fn foo(t: &mut int){
println!("{}", t);
}
fn main() {
let test = 10;
let h = move || { //~error boxed closures can't capture by value
let mut r = &mut test.clone();
foo(r);
};
h();
}

View File

@ -0,0 +1,20 @@
// Copyright 2014 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.
struct S;
impl S {
fn foo(&self) {
let _ = move || { self }; //~error boxed closures can't capture by value
}
}
fn main() {
}

View File

@ -0,0 +1,20 @@
// Copyright 2014 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.
struct S;
impl S {
fn foo(&self) {
let _ = move || { self.foo() }; //~error boxed closures can't capture by value
}
}
fn main() {
}