Skip to content

Commit

Permalink
Custom error when moving arg outside of its closure
Browse files Browse the repository at this point in the history
When given the following code:

```rust
fn give_any<F: for<'r> FnOnce(&'r ())>(f: F) {
    f(&());
}

fn main() {
    let mut x = None;
    give_any(|y| x = Some(y));
}
```

provide a custom error:

```
error: borrowed data cannot be moved outside of its closure
 --> file.rs:7:27
  |
6 |     let mut x = None;
  |         ----- binding declared outside of closure
7 |     give_any(|y| x = Some(y));
  |              ---          ^ cannot be assigned to binding outside of its closure
  |              |
  |              closure you can't escape
```

instead of the generic lifetime error:

```
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> file.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 7:14...
 --> file.rs:7:14
  |
7 |     give_any(|y| x = Some(y));
  |              ^^^^^^^^^^^^^^^
note: ...so that expression is assignable (expected &(), found &())
 --> file.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
note: but, the lifetime must be valid for the block suffix following statement 0 at 6:5...
 --> file.rs:6:5
  |
6 | /     let mut x = None;
7 | |     give_any(|y| x = Some(y));
8 | | }
  | |_^
note: ...so that variable is valid at time of its declaration
 --> file.rs:6:9
  |
6 |     let mut x = None;
  |         ^^^^^
```
  • Loading branch information
estebank committed Jan 15, 2018
1 parent bb345a0 commit 8460421
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 2 deletions.
36 changes: 35 additions & 1 deletion src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use hir::map as hir_map;
use hir::def_id::DefId;
use middle::region;
use traits::{ObligationCause, ObligationCauseCode};
use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::{self, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVariants};
use ty::error::TypeError;
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
Expand Down Expand Up @@ -1067,6 +1067,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
sub_region: Region<'tcx>,
sup_origin: SubregionOrigin<'tcx>,
sup_region: Region<'tcx>) {

// #45983: when trying to assign the contents of an argument to a binding outside of a
// closure, provide a specific message pointing this out.
if let (&SubregionOrigin::BindingTypeIsNotValidAtDecl(ref external_span),
&SubregionOrigin::Subtype(TypeTrace { ref cause, .. }),
&RegionKind::ReFree(ref free_region)) = (&sub_origin, &sup_origin, sup_region) {
let hir = &self.tcx.hir;
if let Some(node_id) = hir.as_local_node_id(free_region.scope) {
match hir.get(node_id) {
hir_map::NodeExpr(hir::Expr {
node: hir::ExprClosure(_, _, _, closure_span, false),
..
}) => {
let sp = var_origin.span();
let mut err = self.tcx.sess.struct_span_err(
sp,
"borrowed data cannot be moved outside of its closure");
let label = match cause.code {
ObligationCauseCode::ExprAssignable => {
"cannot be assigned to binding outside of its closure"
}
_ => "cannot be moved outside of its closure",
};
err.span_label(sp, label);
err.span_label(*closure_span, "closure you can't escape");
err.span_label(*external_span, "binding declared outside of closure");
err.emit();
return;
}
_ => {}
}
}
}

let mut err = self.report_inference_failure(var_origin);

self.tcx.note_and_explain_region(region_scope_tree, &mut err,
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/regions-escape-bound-fn-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ fn with_int<F>(f: F) where F: FnOnce(&isize) {
fn main() {
let mut x = None;
with_int(|y| x = Some(y));
//~^ ERROR cannot infer
//~^ ERROR borrowed data cannot be moved outside of its closure
}
19 changes: 19 additions & 0 deletions src/test/ui/borrowck/issue-45983.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2018 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 give_any<F: for<'r> FnOnce(&'r ())>(f: F) {
f(&());
}

fn main() {
let x = None;
give_any(|y| x = Some(y));
//~^ ERROR borrowed data cannot be moved outside of its closure
}
12 changes: 12 additions & 0 deletions src/test/ui/borrowck/issue-45983.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error: borrowed data cannot be moved outside of its closure
--> $DIR/issue-45983.rs:17:27
|
16 | let x = None;
| - binding declared outside of closure
17 | give_any(|y| x = Some(y));
| --- ^ cannot be assigned to binding outside of its closure
| |
| closure you can't escape

error: aborting due to previous error

0 comments on commit 8460421

Please sign in to comment.