Skip to content

Commit bc072ed

Browse files
committed
Auto merge of rust-lang#47144 - estebank:moved-closure-arg, r=nikomatsakis
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; | ----- borrowed data cannot be moved into here... 7 | give_any(|y| x = Some(y)); | --- ^ cannot be moved outside of its closure | | | ...because it cannot outlive this closure ``` 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; | ^^^^^ ``` Fix rust-lang#45983.
2 parents ff2a7c8 + 6f9ecaa commit bc072ed

18 files changed

+352
-21
lines changed

src/librustc/infer/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10671067
sub_region: Region<'tcx>,
10681068
sup_origin: SubregionOrigin<'tcx>,
10691069
sup_region: Region<'tcx>) {
1070+
10701071
let mut err = self.report_inference_failure(var_origin);
10711072

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

src/librustc/infer/error_reporting/nice_region_error/different_lifetimes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
5353
///
5454
/// It will later be extended to trait objects.
5555
pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorReported> {
56-
let NiceRegionError { span, sub, sup, .. } = *self;
56+
let (span, sub, sup) = self.get_regions();
5757

5858
// Determine whether the sub and sup consist of both anonymous (elided) regions.
5959
let anon_reg_sup = self.is_suitable_region(sup)?;

src/librustc/infer/error_reporting/nice_region_error/mod.rs

+29-11
Original file line numberDiff line numberDiff line change
@@ -18,46 +18,64 @@ use util::common::ErrorReported;
1818
mod different_lifetimes;
1919
mod find_anon_type;
2020
mod named_anon_conflict;
21+
mod outlives_closure;
2122
mod util;
2223

2324
impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
2425
pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
25-
let (span, sub, sup) = match *error {
26-
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
27-
SubSupConflict(_, ref origin, sub, _, sup) => (origin.span(), sub, sup),
28-
_ => return false, // inapplicable
29-
};
26+
match *error {
27+
ConcreteFailure(..) | SubSupConflict(..) => {}
28+
_ => return false, // inapplicable
29+
}
3030

3131
if let Some(tables) = self.in_progress_tables {
3232
let tables = tables.borrow();
33-
NiceRegionError::new(self.tcx, span, sub, sup, Some(&tables)).try_report().is_some()
33+
NiceRegionError::new(self.tcx, error.clone(), Some(&tables)).try_report().is_some()
3434
} else {
35-
NiceRegionError::new(self.tcx, span, sub, sup, None).try_report().is_some()
35+
NiceRegionError::new(self.tcx, error.clone(), None).try_report().is_some()
3636
}
3737
}
3838
}
3939

4040
pub struct NiceRegionError<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
4141
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
42-
span: Span,
43-
sub: ty::Region<'tcx>,
44-
sup: ty::Region<'tcx>,
42+
error: Option<RegionResolutionError<'tcx>>,
43+
regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>,
4544
tables: Option<&'cx ty::TypeckTables<'tcx>>,
4645
}
4746

4847
impl<'cx, 'gcx, 'tcx> NiceRegionError<'cx, 'gcx, 'tcx> {
4948
pub fn new(
49+
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
50+
error: RegionResolutionError<'tcx>,
51+
tables: Option<&'cx ty::TypeckTables<'tcx>>,
52+
) -> Self {
53+
Self { tcx, error: Some(error), regions: None, tables }
54+
}
55+
56+
pub fn new_from_span(
5057
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
5158
span: Span,
5259
sub: ty::Region<'tcx>,
5360
sup: ty::Region<'tcx>,
5461
tables: Option<&'cx ty::TypeckTables<'tcx>>,
5562
) -> Self {
56-
Self { tcx, span, sub, sup, tables }
63+
Self { tcx, error: None, regions: Some((span, sub, sup)), tables }
5764
}
5865

5966
pub fn try_report(&self) -> Option<ErrorReported> {
6067
self.try_report_named_anon_conflict()
6168
.or_else(|| self.try_report_anon_anon_conflict())
69+
.or_else(|| self.try_report_outlives_closure())
70+
}
71+
72+
pub fn get_regions(&self) -> (Span, ty::Region<'tcx>, ty::Region<'tcx>) {
73+
match (&self.error, self.regions) {
74+
(&Some(ConcreteFailure(ref origin, sub, sup)), None) => (origin.span(), sub, sup),
75+
(&Some(SubSupConflict(_, ref origin, sub, _, sup)), None) => (origin.span(), sub, sup),
76+
(None, Some((span, sub, sup))) => (span, sub, sup),
77+
(Some(_), Some(_)) => panic!("incorrectly built NiceRegionError"),
78+
_ => panic!("trying to report on an incorrect lifetime failure"),
79+
}
6280
}
6381
}

src/librustc/infer/error_reporting/nice_region_error/named_anon_conflict.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
1818
/// When given a `ConcreteFailure` for a function with arguments containing a named region and
1919
/// an anonymous region, emit an descriptive diagnostic error.
2020
pub(super) fn try_report_named_anon_conflict(&self) -> Option<ErrorReported> {
21-
let NiceRegionError { span, sub, sup, .. } = *self;
21+
let (span, sub, sup) = self.get_regions();
2222

2323
debug!(
2424
"try_report_named_anon_conflict(sub={:?}, sup={:?})",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
//! Error Reporting for Anonymous Region Lifetime Errors
12+
//! where both the regions are anonymous.
13+
14+
use infer::error_reporting::nice_region_error::NiceRegionError;
15+
use infer::SubregionOrigin;
16+
use ty::RegionKind;
17+
use hir::{Expr, ExprClosure};
18+
use hir::map::NodeExpr;
19+
use util::common::ErrorReported;
20+
use infer::lexical_region_resolve::RegionResolutionError::SubSupConflict;
21+
22+
impl<'a, 'gcx, 'tcx> NiceRegionError<'a, 'gcx, 'tcx> {
23+
/// Print the error message for lifetime errors when binding excapes a closure.
24+
///
25+
/// Consider a case where we have
26+
///
27+
/// ```no_run
28+
/// fn with_int<F>(f: F) where F: FnOnce(&isize) {
29+
/// let x = 3;
30+
/// f(&x);
31+
/// }
32+
/// fn main() {
33+
/// let mut x = None;
34+
/// with_int(|y| x = Some(y));
35+
/// }
36+
/// ```
37+
///
38+
/// the output will be
39+
///
40+
/// ```text
41+
/// let mut x = None;
42+
/// ----- borrowed data cannot be stored into here...
43+
/// with_int(|y| x = Some(y));
44+
/// --- ^ cannot be stored outside of its closure
45+
/// |
46+
/// ...because it cannot outlive this closure
47+
/// ```
48+
pub(super) fn try_report_outlives_closure(&self) -> Option<ErrorReported> {
49+
if let Some(SubSupConflict(origin,
50+
ref sub_origin,
51+
_,
52+
ref sup_origin,
53+
sup_region)) = self.error {
54+
55+
// #45983: when trying to assign the contents of an argument to a binding outside of a
56+
// closure, provide a specific message pointing this out.
57+
if let (&SubregionOrigin::BindingTypeIsNotValidAtDecl(ref external_span),
58+
&RegionKind::ReFree(ref free_region)) = (&sub_origin, sup_region) {
59+
let hir = &self.tcx.hir;
60+
if let Some(node_id) = hir.as_local_node_id(free_region.scope) {
61+
match hir.get(node_id) {
62+
NodeExpr(Expr {
63+
node: ExprClosure(_, _, _, closure_span, false),
64+
..
65+
}) => {
66+
let sup_sp = sup_origin.span();
67+
let origin_sp = origin.span();
68+
let mut err = self.tcx.sess.struct_span_err(
69+
sup_sp,
70+
"borrowed data cannot be stored outside of its closure");
71+
err.span_label(sup_sp, "cannot be stored outside of its closure");
72+
if origin_sp == sup_sp || origin_sp.contains(sup_sp) {
73+
// // sup_sp == origin.span():
74+
//
75+
// let mut x = None;
76+
// ----- borrowed data cannot be stored into here...
77+
// with_int(|y| x = Some(y));
78+
// --- ^ cannot be stored outside of its closure
79+
// |
80+
// ...because it cannot outlive this closure
81+
//
82+
// // origin.contains(&sup_sp):
83+
//
84+
// let mut f: Option<&u32> = None;
85+
// ----- borrowed data cannot be stored into here...
86+
// closure_expecting_bound(|x: &'x u32| {
87+
// ------------ ... because it cannot outlive this closure
88+
// f = Some(x);
89+
// ^ cannot be stored outside of its closure
90+
err.span_label(*external_span,
91+
"borrowed data cannot be stored into here...");
92+
err.span_label(*closure_span,
93+
"...because it cannot outlive this closure");
94+
} else {
95+
// FIXME: the wording for this case could be much improved
96+
//
97+
// let mut lines_to_use: Vec<&CrateId> = Vec::new();
98+
// - cannot infer an appropriate lifetime...
99+
// let push_id = |installed_id: &CrateId| {
100+
// ------- ------------------------ borrowed data cannot outlive this closure
101+
// |
102+
// ...so that variable is valid at time of its declaration
103+
// lines_to_use.push(installed_id);
104+
// ^^^^^^^^^^^^ cannot be stored outside of its closure
105+
err.span_label(origin_sp,
106+
"cannot infer an appropriate lifetime...");
107+
err.span_label(*external_span,
108+
"...so that variable is valid at time of its \
109+
declaration");
110+
err.span_label(*closure_span,
111+
"borrowed data cannot outlive this closure");
112+
}
113+
err.emit();
114+
return Some(ErrorReported);
115+
}
116+
_ => {}
117+
}
118+
}
119+
}
120+
}
121+
None
122+
}
123+
}
124+

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
989989

990990
if let (Some(f), Some(o)) = (fr_name, outlived_fr_name) {
991991
let tables = infcx.tcx.typeck_tables_of(mir_def_id);
992-
let nice = NiceRegionError::new(infcx.tcx, blame_span, o, f, Some(tables));
992+
let nice = NiceRegionError::new_from_span(infcx.tcx, blame_span, o, f, Some(tables));
993993
if let Some(ErrorReported) = nice.try_report() {
994994
return;
995995
}

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

+19
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 stored outside of its closure
19+
}
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: borrowed data cannot be stored outside of its closure
2+
--> $DIR/issue-45983.rs:17:27
3+
|
4+
16 | let x = None;
5+
| - borrowed data cannot be stored into here...
6+
17 | give_any(|y| x = Some(y));
7+
| --- ^ cannot be stored outside of its closure
8+
| |
9+
| ...because it cannot outlive this closure
10+
11+
error: aborting due to previous error
12+

src/test/compile-fail/issue-7573.rs renamed to src/test/ui/borrowck/issue-7573.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ impl CrateId {
2424
}
2525

2626
pub fn remove_package_from_database() {
27-
let mut lines_to_use: Vec<&CrateId> = Vec::new(); //~ ERROR E0495
27+
let mut lines_to_use: Vec<&CrateId> = Vec::new();
28+
//~^ NOTE cannot infer an appropriate lifetime
2829
let push_id = |installed_id: &CrateId| {
30+
//~^ NOTE borrowed data cannot outlive this closure
31+
//~| NOTE ...so that variable is valid at time of its declaration
2932
lines_to_use.push(installed_id);
33+
//~^ ERROR borrowed data cannot be stored outside of its closure
34+
//~| NOTE cannot be stored outside of its closure
3035
};
3136
list_database(push_id);
3237

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: borrowed data cannot be stored outside of its closure
2+
--> $DIR/issue-7573.rs:32:27
3+
|
4+
27 | let mut lines_to_use: Vec<&CrateId> = Vec::new();
5+
| - cannot infer an appropriate lifetime...
6+
28 | //~^ NOTE cannot infer an appropriate lifetime
7+
29 | let push_id = |installed_id: &CrateId| {
8+
| ------- ------------------------ borrowed data cannot outlive this closure
9+
| |
10+
| ...so that variable is valid at time of its declaration
11+
...
12+
32 | lines_to_use.push(installed_id);
13+
| ^^^^^^^^^^^^ cannot be stored outside of its closure
14+
15+
error: aborting due to previous error
16+

src/test/compile-fail/regions-escape-bound-fn-2.rs renamed to src/test/ui/borrowck/regions-escape-bound-fn-2.rs

+1-1
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 stored outside of its closure
2020
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: borrowed data cannot be stored outside of its closure
2+
--> $DIR/regions-escape-bound-fn-2.rs:18:27
3+
|
4+
17 | let mut x = None;
5+
| ----- borrowed data cannot be stored into here...
6+
18 | with_int(|y| x = Some(y));
7+
| --- ^ cannot be stored outside of its closure
8+
| |
9+
| ...because it cannot outlive this closure
10+
11+
error: aborting due to previous error
12+

src/test/compile-fail/regions-escape-bound-fn.rs renamed to src/test/ui/borrowck/regions-escape-bound-fn.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ fn with_int<F>(f: F) where F: FnOnce(&isize) {
1515

1616
fn main() {
1717
let mut x: Option<&isize> = None;
18-
with_int(|y| x = Some(y)); //~ ERROR cannot infer
18+
with_int(|y| x = Some(y));
19+
//~^ ERROR borrowed data cannot be stored outside of its closure
1920
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: borrowed data cannot be stored outside of its closure
2+
--> $DIR/regions-escape-bound-fn.rs:18:27
3+
|
4+
17 | let mut x: Option<&isize> = None;
5+
| ----- borrowed data cannot be stored into here...
6+
18 | with_int(|y| x = Some(y));
7+
| --- ^ cannot be stored outside of its closure
8+
| |
9+
| ...because it cannot outlive this closure
10+
11+
error: aborting due to previous error
12+

src/test/compile-fail/regions-escape-unboxed-closure.rs renamed to src/test/ui/borrowck/regions-escape-unboxed-closure.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ fn with_int(f: &mut FnMut(&isize)) {
1313

1414
fn main() {
1515
let mut x: Option<&isize> = None;
16-
with_int(&mut |y| x = Some(y)); //~ ERROR cannot infer
16+
with_int(&mut |y| x = Some(y));
17+
//~^ ERROR borrowed data cannot be stored outside of its closure
1718
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: borrowed data cannot be stored outside of its closure
2+
--> $DIR/regions-escape-unboxed-closure.rs:16:32
3+
|
4+
15 | let mut x: Option<&isize> = None;
5+
| ----- borrowed data cannot be stored into here...
6+
16 | with_int(&mut |y| x = Some(y));
7+
| --- ^ cannot be stored outside of its closure
8+
| |
9+
| ...because it cannot outlive this closure
10+
11+
error: aborting due to previous error
12+

src/test/compile-fail/closure-expected-type/expect-region-supply-region.rs renamed to src/test/ui/closure-expected-type/expect-region-supply-region.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ fn expect_bound_supply_nothing() {
2525
// it to escape into `f`:
2626
let mut f: Option<&u32> = None;
2727
closure_expecting_bound(|x| {
28-
f = Some(x); //~ ERROR E0495
28+
f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
2929
});
3030
}
3131

@@ -35,7 +35,7 @@ fn expect_bound_supply_bound() {
3535
// closure:
3636
let mut f: Option<&u32> = None;
3737
closure_expecting_bound(|x: &u32| {
38-
f = Some(x); //~ ERROR E0495
38+
f = Some(x); //~ ERROR borrowed data cannot be stored outside of its closure
3939
});
4040
}
4141

@@ -50,7 +50,7 @@ fn expect_bound_supply_named<'x>() {
5050

5151
// And we still cannot let `x` escape into `f`.
5252
f = Some(x);
53-
//~^ ERROR cannot infer
53+
//~^ ERROR borrowed data cannot be stored outside of its closure
5454
});
5555
}
5656

0 commit comments

Comments
 (0)