Skip to content

Commit a3f6725

Browse files
committed
Generalize logic pointing at binding moved into closure
Account not only for `fn` parameters when moving non-`Copy` values into closure, but also for let bindings. ``` error[E0507]: cannot move out of `bar`, a captured variable in an `FnMut` closure --> $DIR/borrowck-move-by-capture.rs:9:29 | LL | let bar: Box<_> = Box::new(3); | --- ------ move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait | | | captured outer variable LL | let _g = to_fn_mut(|| { | -- captured by this `FnMut` closure LL | let _h = to_fn_once(move || -> isize { *bar }); | ^^^^^^^^^^^^^^^^ ---- variable moved due to use in closure | | | `bar` is moved here | help: consider cloning the value before moving it into the closure | LL ~ let value = bar.clone(); LL ~ let _h = to_fn_once(move || -> isize { value }); | ``` ``` error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9 | LL | let y = vec![format!("World")]; | - ---------------------- move occurs because `y` has type `Vec<String>`, which does not implement the `Copy` trait | | | captured outer variable LL | call(|| { | -- captured by this `Fn` closure LL | y.into_iter(); | ^ ----------- `y` moved due to this method call | | | `y` is moved here | note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior | LL | <Vec<String> as Clone>::clone(&y).into_iter(); | +++++++++++++++++++++++++++++++ + help: consider cloning the value if the performance cost is acceptable | LL | y.clone().into_iter(); | ++++++++ ```
1 parent 80cfebe commit a3f6725

12 files changed

+125
-75
lines changed

compiler/rustc_borrowck/src/diagnostics/move_errors.rs

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -623,30 +623,51 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
623623
&& let Some(upvar_field) = self
624624
.prefixes(original_path.as_ref(), PrefixSet::All)
625625
.find_map(|p| self.is_upvar_field_projection(p))
626-
&& let upvar = &self.upvars[upvar_field.index()]
627-
&& let upvar_hir_id = upvar.get_root_variable()
628-
&& let hir::Node::Param(param) = self.infcx.tcx.parent_hir_node(upvar_hir_id)
629626
{
630-
// Instead of pointing at the path where we access the value within a closure,
631-
// we point at the type on the parameter from the definition of the outer
632-
// function:
633-
//
634-
// error[E0507]: cannot move out of `foo`, a captured
635-
// variable in an `Fn` closure
636-
// --> file.rs:14:25
637-
// |
638-
// 13 | fn do_stuff(foo: Option<Foo>) {
639-
// | --- ----------- move occurs because `foo` has type
640-
// | | `Option<Foo>`, which does not implement
641-
// | | the `Copy` trait
642-
// | captured outer variable
643-
// 14 | require_fn_trait(|| async {
644-
// | -- ^^^^^ `foo` is moved here
645-
// | |
646-
// | captured by this `Fn` closure
647-
// 15 | if foo.map_or(false, |f| f.foo()) {
648-
// | --- variable moved due to use in coroutine
649-
use_span = param.ty_span;
627+
let upvar = &self.upvars[upvar_field.index()];
628+
let upvar_hir_id = upvar.get_root_variable();
629+
use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
630+
hir::Node::Param(param) => {
631+
// Instead of pointing at the path where we access the value within a
632+
// closure, we point at the type on the parameter from the definition
633+
// of the outer function:
634+
//
635+
// error[E0507]: cannot move out of `foo`, a captured
636+
// variable in an `Fn` closure
637+
// --> file.rs:14:25
638+
// |
639+
// 13 | fn do_stuff(foo: Option<Foo>) {
640+
// | --- ----------- move occurs because `foo` has type
641+
// | | `Option<Foo>`, which does not
642+
// | | implement the `Copy` trait
643+
// | captured outer variable
644+
// 14 | require_fn_trait(|| async {
645+
// | -- ^^^^^ `foo` is moved here
646+
// | |
647+
// | captured by this `Fn` closure
648+
// 15 | if foo.map_or(false, |f| f.foo()) {
649+
// | --- variable moved due to use in coroutine
650+
param.ty_span
651+
}
652+
hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
653+
// 13 | fn do_stuff(foo: Option<Foo>) {
654+
// 14 | let foo: Option<Foo> = foo;
655+
// | --- ----------- move occurs because `foo` has type
656+
// | | `Option<Foo>`, which does not implement
657+
// | | the `Copy` trait
658+
// | captured outer variable
659+
(Some(ty), _) => ty.span,
660+
// 13 | fn do_stuff(bar: Option<Foo>) {
661+
// 14 | let foo = bar;
662+
// | --- --- move occurs because `foo` has type
663+
// | | `Option<Foo>`, which does not implement the
664+
// | | `Copy` trait
665+
// | captured outer variable
666+
(None, Some(init)) => init.span,
667+
(None, None) => use_span,
668+
},
669+
_ => use_span,
670+
};
650671
}
651672

652673
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
@@ -656,12 +677,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
656677
span: use_span,
657678
});
658679

680+
let mut pointed_at_span = false;
659681
use_spans.args_subdiag(err, |args_span| {
682+
if args_span == span || args_span == use_span {
683+
pointed_at_span = true;
684+
}
660685
crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
661-
place: place_desc,
686+
place: place_desc.clone(),
662687
args_span,
663688
}
664689
});
690+
if !pointed_at_span && use_span != span {
691+
err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
692+
place: place_desc,
693+
args_span: span,
694+
});
695+
}
665696

666697
self.add_note_for_packed_struct_derive(err, original_path.local);
667698
}

tests/ui/borrowck/borrowck-in-static.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ error[E0507]: cannot move out of `x`, a captured variable in an `Fn` closure
22
--> $DIR/borrowck-in-static.rs:5:17
33
|
44
LL | let x = Box::new(0);
5-
| - captured outer variable
5+
| - ----------- move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
6+
| |
7+
| captured outer variable
68
LL | Box::new(|| x)
7-
| -- ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
9+
| -- ^ `x` is moved here
810
| |
911
| captured by this `Fn` closure
1012
|

tests/ui/borrowck/borrowck-move-by-capture.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ error[E0507]: cannot move out of `bar`, a captured variable in an `FnMut` closur
22
--> $DIR/borrowck-move-by-capture.rs:9:29
33
|
44
LL | let bar: Box<_> = Box::new(3);
5-
| --- captured outer variable
5+
| --- ------ move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait
6+
| |
7+
| captured outer variable
68
LL | let _g = to_fn_mut(|| {
79
| -- captured by this `FnMut` closure
810
LL | let _h = to_fn_once(move || -> isize { *bar });
9-
| ^^^^^^^^^^^^^^^^ ----
10-
| | |
11-
| | variable moved due to use in closure
12-
| | move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait
11+
| ^^^^^^^^^^^^^^^^ ---- variable moved due to use in closure
12+
| |
1313
| `bar` is moved here
1414
|
1515
help: consider cloning the value before moving it into the closure

tests/ui/borrowck/issue-103624.stderr

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ error[E0507]: cannot move out of `self.b`, as `self` is a captured variable in a
22
--> $DIR/issue-103624.rs:16:13
33
|
44
LL | async fn foo(&self) {
5-
| ----- captured outer variable
5+
| -----
6+
| |
7+
| captured outer variable
8+
| move occurs because `self.b` has type `StructB`, which does not implement the `Copy` trait
69
LL | let bar = self.b.bar().await;
710
LL | spawn_blocking(move || {
811
| ------- captured by this `Fn` closure
912
LL |
1013
LL | self.b;
11-
| ^^^^^^ move occurs because `self.b` has type `StructB`, which does not implement the `Copy` trait
14+
| ^^^^^^ `self.b` is moved here
1215
|
1316
note: if `StructB` implemented `Clone`, you could clone the value
1417
--> $DIR/issue-103624.rs:23:1

tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ error[E0507]: cannot move out of `y`, a captured variable in an `Fn` closure
22
--> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:12:9
33
|
44
LL | let y = vec![format!("World")];
5-
| - captured outer variable
5+
| - ---------------------- move occurs because `y` has type `Vec<String>`, which does not implement the `Copy` trait
6+
| |
7+
| captured outer variable
68
LL | call(|| {
79
| -- captured by this `Fn` closure
810
LL | y.into_iter();
911
| ^ ----------- `y` moved due to this method call
1012
| |
11-
| move occurs because `y` has type `Vec<String>`, which does not implement the `Copy` trait
13+
| `y` is moved here
1214
|
1315
note: `into_iter` takes ownership of the receiver `self`, which moves `y`
1416
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL

tests/ui/issues/issue-4335.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | fn f<'r, T>(v: &'r T) -> Box<dyn FnMut() -> T + 'r> {
66
| |
77
| captured outer variable
88
LL | id(Box::new(|| *v))
9-
| -- ^^
9+
| -- ^^ `*v` is moved here
1010
| |
1111
| captured by this `FnMut` closure
1212
|

tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ error[E0507]: cannot move out of `i`, a captured variable in an `Fn` closure
22
--> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:9:28
33
|
44
LL | let i = Box::new(3);
5-
| - captured outer variable
5+
| - ----------- move occurs because `i` has type `Box<usize>`, which does not implement the `Copy` trait
6+
| |
7+
| captured outer variable
68
LL | let _f = to_fn(|| test(i));
7-
| -- ^ move occurs because `i` has type `Box<usize>`, which does not implement the `Copy` trait
9+
| -- ^ `i` is moved here
810
| |
911
| captured by this `Fn` closure
1012
|

tests/ui/nll/issue-52663-span-decl-captured-variable.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn`
22
--> $DIR/issue-52663-span-decl-captured-variable.rs:8:26
33
|
44
LL | let x = (vec![22], vec![44]);
5-
| - captured outer variable
5+
| - -------------------- move occurs because `x.0` has type `Vec<i32>`, which does not implement the `Copy` trait
6+
| |
7+
| captured outer variable
68
LL | expect_fn(|| drop(x.0));
7-
| -- ^^^ move occurs because `x.0` has type `Vec<i32>`, which does not implement the `Copy` trait
9+
| -- ^^^ `x.0` is moved here
810
| |
911
| captured by this `Fn` closure
1012
|

tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,18 @@ LL | fn test4(f: &mut Test) {
3535
error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure
3636
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:57:13
3737
|
38-
LL | let mut f = move |g: Box<dyn FnMut(isize)>, b: isize| {
39-
| ----- captured outer variable
40-
...
41-
LL | f(Box::new(|a| {
42-
| --- captured by this `FnMut` closure
38+
LL | let mut f = move |g: Box<dyn FnMut(isize)>, b: isize| {
39+
| _________-----___-
40+
| | |
41+
| | captured outer variable
42+
LL | | let _ = s.len();
43+
LL | | };
44+
| |_____- move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait
45+
LL | f(Box::new(|a| {
46+
| --- captured by this `FnMut` closure
4347
LL |
44-
LL | foo(f);
45-
| ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait
48+
LL | foo(f);
49+
| ^ `f` is moved here
4650
|
4751
help: consider cloning the value if the performance cost is acceptable
4852
|

tests/ui/suggestions/option-content-move2.stderr

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,33 @@ error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closur
22
--> $DIR/option-content-move2.rs:11:9
33
|
44
LL | let mut var = None;
5-
| ------- captured outer variable
5+
| ------- ---- move occurs because `var` has type `Option<NotCopyable>`, which does not implement the `Copy` trait
6+
| |
7+
| captured outer variable
68
LL | func(|| {
79
| -- captured by this `FnMut` closure
810
LL | // Shouldn't suggest `move ||.as_ref()` here
911
LL | move || {
1012
| ^^^^^^^ `var` is moved here
1113
LL |
1214
LL | var = Some(NotCopyable);
13-
| ---
14-
| |
15-
| variable moved due to use in closure
16-
| move occurs because `var` has type `Option<NotCopyable>`, which does not implement the `Copy` trait
15+
| --- variable moved due to use in closure
1716

1817
error[E0507]: cannot move out of `var`, a captured variable in an `FnMut` closure
1918
--> $DIR/option-content-move2.rs:21:9
2019
|
2120
LL | let mut var = None;
22-
| ------- captured outer variable
21+
| ------- ---- move occurs because `var` has type `Option<NotCopyableButCloneable>`, which does not implement the `Copy` trait
22+
| |
23+
| captured outer variable
2324
LL | func(|| {
2425
| -- captured by this `FnMut` closure
2526
LL | // Shouldn't suggest `move ||.as_ref()` nor to `clone()` here
2627
LL | move || {
2728
| ^^^^^^^ `var` is moved here
2829
LL |
2930
LL | var = Some(NotCopyableButCloneable);
30-
| ---
31-
| |
32-
| variable moved due to use in closure
33-
| move occurs because `var` has type `Option<NotCopyableButCloneable>`, which does not implement the `Copy` trait
31+
| --- variable moved due to use in closure
3432

3533
error: aborting due to 2 previous errors
3634

0 commit comments

Comments
 (0)