about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2018-01-02 15:25:53 -0800
committerEsteban Küber <esteban@kuber.com.ar>2018-01-15 07:04:38 -0800
commit846042161c17bcc696715b3dfe2e6c4970da0b34 (patch)
tree75069a38c6b18a3a7d1e8d7a2ad41c18529ff527
parentbb345a0be3ba3fa1970fe02789791c5c72788d8f (diff)
downloadrust-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.rs36
-rw-r--r--src/test/compile-fail/regions-escape-bound-fn-2.rs2
-rw-r--r--src/test/ui/borrowck/issue-45983.rs19
-rw-r--r--src/test/ui/borrowck/issue-45983.stderr12
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
+