Skip to content

Commit 8460421

Browse files
committed
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; | ^^^^^ ```
1 parent bb345a0 commit 8460421

File tree

4 files changed

+67
-2
lines changed

4 files changed

+67
-2
lines changed

src/librustc/infer/error_reporting/mod.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ use hir::map as hir_map;
6666
use hir::def_id::DefId;
6767
use middle::region;
6868
use traits::{ObligationCause, ObligationCauseCode};
69-
use ty::{self, Region, Ty, TyCtxt, TypeFoldable, TypeVariants};
69+
use ty::{self, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVariants};
7070
use ty::error::TypeError;
7171
use syntax::ast::DUMMY_NODE_ID;
7272
use syntax_pos::{Pos, Span};
@@ -1067,6 +1067,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10671067
sub_region: Region<'tcx>,
10681068
sup_origin: SubregionOrigin<'tcx>,
10691069
sup_region: Region<'tcx>) {
1070+
1071+
// #45983: when trying to assign the contents of an argument to a binding outside of a
1072+
// closure, provide a specific message pointing this out.
1073+
if let (&SubregionOrigin::BindingTypeIsNotValidAtDecl(ref external_span),
1074+
&SubregionOrigin::Subtype(TypeTrace { ref cause, .. }),
1075+
&RegionKind::ReFree(ref free_region)) = (&sub_origin, &sup_origin, sup_region) {
1076+
let hir = &self.tcx.hir;
1077+
if let Some(node_id) = hir.as_local_node_id(free_region.scope) {
1078+
match hir.get(node_id) {
1079+
hir_map::NodeExpr(hir::Expr {
1080+
node: hir::ExprClosure(_, _, _, closure_span, false),
1081+
..
1082+
}) => {
1083+
let sp = var_origin.span();
1084+
let mut err = self.tcx.sess.struct_span_err(
1085+
sp,
1086+
"borrowed data cannot be moved outside of its closure");
1087+
let label = match cause.code {
1088+
ObligationCauseCode::ExprAssignable => {
1089+
"cannot be assigned to binding outside of its closure"
1090+
}
1091+
_ => "cannot be moved outside of its closure",
1092+
};
1093+
err.span_label(sp, label);
1094+
err.span_label(*closure_span, "closure you can't escape");
1095+
err.span_label(*external_span, "binding declared outside of closure");
1096+
err.emit();
1097+
return;
1098+
}
1099+
_ => {}
1100+
}
1101+
}
1102+
}
1103+
10701104
let mut err = self.report_inference_failure(var_origin);
10711105

10721106
self.tcx.note_and_explain_region(region_scope_tree, &mut err,

src/test/compile-fail/regions-escape-bound-fn-2.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ fn with_int<F>(f: F) where F: FnOnce(&isize) {
1616
fn main() {
1717
let mut x = None;
1818
with_int(|y| x = Some(y));
19-
//~^ ERROR cannot infer
19+
//~^ ERROR borrowed data cannot be moved outside of its closure
2020
}

src/test/ui/borrowck/issue-45983.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2018 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 give_any<F: for<'r> FnOnce(&'r ())>(f: F) {
12+
f(&());
13+
}
14+
15+
fn main() {
16+
let x = None;
17+
give_any(|y| x = Some(y));
18+
//~^ ERROR borrowed data cannot be moved outside of its closure
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: borrowed data cannot be moved outside of its closure
2+
--> $DIR/issue-45983.rs:17:27
3+
|
4+
16 | let x = None;
5+
| - binding declared outside of closure
6+
17 | give_any(|y| x = Some(y));
7+
| --- ^ cannot be assigned to binding outside of its closure
8+
| |
9+
| closure you can't escape
10+
11+
error: aborting due to previous error
12+

0 commit comments

Comments
 (0)