diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2018-01-02 15:25:53 -0800 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2018-01-15 07:04:38 -0800 |
| commit | 846042161c17bcc696715b3dfe2e6c4970da0b34 (patch) | |
| tree | 75069a38c6b18a3a7d1e8d7a2ad41c18529ff527 | |
| parent | bb345a0be3ba3fa1970fe02789791c5c72788d8f (diff) | |
| download | rust-846042161c17bcc696715b3dfe2e6c4970da0b34.tar.gz rust-846042161c17bcc696715b3dfe2e6c4970da0b34.zip | |
Custom error when moving arg outside of its closure
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;
| ^^^^^
```
| -rw-r--r-- | src/librustc/infer/error_reporting/mod.rs | 36 | ||||
| -rw-r--r-- | src/test/compile-fail/regions-escape-bound-fn-2.rs | 2 | ||||
| -rw-r--r-- | src/test/ui/borrowck/issue-45983.rs | 19 | ||||
| -rw-r--r-- | src/test/ui/borrowck/issue-45983.stderr | 12 |
4 files changed, 67 insertions, 2 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index c477a0d383e..3cd7c5e0af3 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -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}; @@ -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, diff --git a/src/test/compile-fail/regions-escape-bound-fn-2.rs b/src/test/compile-fail/regions-escape-bound-fn-2.rs index 1329d05a0f6..042c55eed86 100644 --- a/src/test/compile-fail/regions-escape-bound-fn-2.rs +++ b/src/test/compile-fail/regions-escape-bound-fn-2.rs @@ -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 } diff --git a/src/test/ui/borrowck/issue-45983.rs b/src/test/ui/borrowck/issue-45983.rs new file mode 100644 index 00000000000..b2316a6b61c --- /dev/null +++ b/src/test/ui/borrowck/issue-45983.rs @@ -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 +} diff --git a/src/test/ui/borrowck/issue-45983.stderr b/src/test/ui/borrowck/issue-45983.stderr new file mode 100644 index 00000000000..689fe6053c9 --- /dev/null +++ b/src/test/ui/borrowck/issue-45983.stderr @@ -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 + |
