Skip to content

Commit 33a9e4f

Browse files
authored
Rollup merge of #144200 - estebank:dont-point-at-closure, r=lcnr
Tweak output for non-`Clone` values moved into closures When we encounter a non-`Clone` value being moved into a closure, try to find the corresponding type of the binding being moved, if it is a `let`-binding or a function parameter. If any of those cases, we point at them with the note explaining that the type is not `Copy`, instead of giving that label to the place where it is captured. When it is a `let`-binding with no explicit type, we point at the initializer (if it fits in a single line). ``` error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure --> f111.rs:14:25 | 13 | fn do_stuff(foo: Option<Foo>) { | --- ----------- move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait | | | captured outer variable 14 | require_fn_trait(|| async { | -- ^^^^^ `foo` is moved here | | | captured by this `Fn` closure 15 | if foo.map_or(false, |f| f.foo()) { | --- variable moved due to use in coroutine ``` instead of ``` error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure --> f111.rs:14:25 | 13 | fn do_stuff(foo: Option<Foo>) { | --- captured outer variable 14 | require_fn_trait(|| async { | -- ^^^^^ `foo` is moved here | | | captured by this `Fn` closure 15 | if foo.map_or(false, |f| f.foo()) { | --- | | | variable moved due to use in coroutine | move occurs because `foo` has type `Option<Foo>`, which does not implement the `Copy` trait ```
2 parents dfbd0c4 + 6237e73 commit 33a9e4f

12 files changed

+148
-99
lines changed

compiler/rustc_borrowck/src/diagnostics/move_errors.rs

Lines changed: 83 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
115115
fn append_to_grouped_errors(
116116
&self,
117117
grouped_errors: &mut Vec<GroupedMoveError<'tcx>>,
118-
error: MoveError<'tcx>,
118+
MoveError { place: original_path, location, kind }: MoveError<'tcx>,
119119
) {
120-
let MoveError { place: original_path, location, kind } = error;
121-
122120
// Note: that the only time we assign a place isn't a temporary
123121
// to a user variable is when initializing it.
124122
// If that ever stops being the case, then the ever initialized
@@ -251,54 +249,47 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
251249
}
252250

253251
fn report(&mut self, error: GroupedMoveError<'tcx>) {
254-
let (mut err, err_span) = {
255-
let (span, use_spans, original_path, kind) = match error {
256-
GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
257-
| GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
258-
(span, None, original_path, kind)
259-
}
260-
GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
261-
(use_spans.args_or_use(), Some(use_spans), original_path, kind)
262-
}
263-
};
264-
debug!(
265-
"report: original_path={:?} span={:?}, kind={:?} \
266-
original_path.is_upvar_field_projection={:?}",
267-
original_path,
268-
span,
269-
kind,
270-
self.is_upvar_field_projection(original_path.as_ref())
271-
);
272-
if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
273-
// If the type may implement Copy, skip the error.
274-
// It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
275-
self.dcx().span_delayed_bug(
252+
let (span, use_spans, original_path, kind) = match error {
253+
GroupedMoveError::MovesFromPlace { span, original_path, ref kind, .. }
254+
| GroupedMoveError::MovesFromValue { span, original_path, ref kind, .. } => {
255+
(span, None, original_path, kind)
256+
}
257+
GroupedMoveError::OtherIllegalMove { use_spans, original_path, ref kind } => {
258+
(use_spans.args_or_use(), Some(use_spans), original_path, kind)
259+
}
260+
};
261+
debug!(
262+
"report: original_path={:?} span={:?}, kind={:?} \
263+
original_path.is_upvar_field_projection={:?}",
264+
original_path,
265+
span,
266+
kind,
267+
self.is_upvar_field_projection(original_path.as_ref())
268+
);
269+
if self.has_ambiguous_copy(original_path.ty(self.body, self.infcx.tcx).ty) {
270+
// If the type may implement Copy, skip the error.
271+
// It's an error with the Copy implementation (e.g. duplicate Copy) rather than borrow check
272+
self.dcx()
273+
.span_delayed_bug(span, "Type may implement copy, but there is no other error.");
274+
return;
275+
}
276+
let mut err = match kind {
277+
&IllegalMoveOriginKind::BorrowedContent { target_place } => self
278+
.report_cannot_move_from_borrowed_content(
279+
original_path,
280+
target_place,
276281
span,
277-
"Type may implement copy, but there is no other error.",
278-
);
279-
return;
282+
use_spans,
283+
),
284+
&IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
285+
self.cannot_move_out_of_interior_of_drop(span, ty)
286+
}
287+
&IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
288+
self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
280289
}
281-
(
282-
match kind {
283-
&IllegalMoveOriginKind::BorrowedContent { target_place } => self
284-
.report_cannot_move_from_borrowed_content(
285-
original_path,
286-
target_place,
287-
span,
288-
use_spans,
289-
),
290-
&IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
291-
self.cannot_move_out_of_interior_of_drop(span, ty)
292-
}
293-
&IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
294-
self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
295-
}
296-
},
297-
span,
298-
)
299290
};
300291

301-
self.add_move_hints(error, &mut err, err_span);
292+
self.add_move_hints(error, &mut err, span);
302293
self.buffer_error(err);
303294
}
304295

@@ -483,7 +474,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
483474
self.cannot_move_out_of_interior_noncopy(span, ty, None)
484475
}
485476
ty::Closure(def_id, closure_args)
486-
if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() =>
477+
if def_id.as_local() == Some(self.mir_def_id())
478+
&& let Some(upvar_field) = upvar_field =>
487479
{
488480
let closure_kind_ty = closure_args.as_closure().kind_ty();
489481
let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
@@ -496,7 +488,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
496488
let capture_description =
497489
format!("captured variable in an `{closure_kind}` closure");
498490

499-
let upvar = &self.upvars[upvar_field.unwrap().index()];
491+
let upvar = &self.upvars[upvar_field.index()];
500492
let upvar_hir_id = upvar.get_root_variable();
501493
let upvar_name = upvar.to_string(tcx);
502494
let upvar_span = tcx.hir_span(upvar_hir_id);
@@ -606,7 +598,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
606598
}
607599
// No binding. Nothing to suggest.
608600
GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => {
609-
let use_span = use_spans.var_or_use();
601+
let mut use_span = use_spans.var_or_use();
610602
let place_ty = original_path.ty(self.body, self.infcx.tcx).ty;
611603
let place_desc = match self.describe_place(original_path.as_ref()) {
612604
Some(desc) => format!("`{desc}`"),
@@ -623,19 +615,59 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
623615
);
624616
}
625617

618+
if let Some(upvar_field) = self
619+
.prefixes(original_path.as_ref(), PrefixSet::All)
620+
.find_map(|p| self.is_upvar_field_projection(p))
621+
{
622+
// Look for the introduction of the original binding being moved.
623+
let upvar = &self.upvars[upvar_field.index()];
624+
let upvar_hir_id = upvar.get_root_variable();
625+
use_span = match self.infcx.tcx.parent_hir_node(upvar_hir_id) {
626+
hir::Node::Param(param) => {
627+
// Instead of pointing at the path where we access the value within a
628+
// closure, we point at the type of the outer `fn` argument.
629+
param.ty_span
630+
}
631+
hir::Node::LetStmt(stmt) => match (stmt.ty, stmt.init) {
632+
// We point at the type of the outer let-binding.
633+
(Some(ty), _) => ty.span,
634+
// We point at the initializer of the outer let-binding, but only if it
635+
// isn't something that spans multiple lines, like a closure, as the
636+
// ASCII art gets messy.
637+
(None, Some(init))
638+
if !self.infcx.tcx.sess.source_map().is_multiline(init.span) =>
639+
{
640+
init.span
641+
}
642+
_ => use_span,
643+
},
644+
_ => use_span,
645+
};
646+
}
647+
626648
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
627649
is_partial_move: false,
628650
ty: place_ty,
629651
place: &place_desc,
630652
span: use_span,
631653
});
632654

655+
let mut pointed_at_span = false;
633656
use_spans.args_subdiag(err, |args_span| {
657+
if args_span == span || args_span == use_span {
658+
pointed_at_span = true;
659+
}
634660
crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
635-
place: place_desc,
661+
place: place_desc.clone(),
636662
args_span,
637663
}
638664
});
665+
if !pointed_at_span && use_span != span {
666+
err.subdiagnostic(crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
667+
place: place_desc,
668+
args_span: span,
669+
});
670+
}
639671

640672
self.add_note_for_packed_struct_derive(err, original_path.local);
641673
}

tests/ui/async-await/async-closures/closure-shim-borrowck-error.stderr

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
error[E0507]: cannot move out of `x` which is behind a mutable reference
22
--> $DIR/closure-shim-borrowck-error.rs:11:18
33
|
4+
LL | fn hello(x: Ty) {
5+
| -- move occurs because `x` has type `Ty`, which does not implement the `Copy` trait
46
LL | needs_fn_mut(async || {
57
| ^^^^^^^^ `x` is moved here
68
LL |
79
LL | x.hello();
8-
| -
9-
| |
10-
| variable moved due to use in coroutine
11-
| move occurs because `x` has type `Ty`, which does not implement the `Copy` trait
10+
| - variable moved due to use in coroutine
1211
|
1312
note: if `Ty` implemented `Clone`, you could clone the value
1413
--> $DIR/closure-shim-borrowck-error.rs:17:1

tests/ui/async-await/async-closures/move-out-of-ref.stderr

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
error[E0507]: cannot move out of `*x` which is behind a shared reference
22
--> $DIR/move-out-of-ref.rs:9:9
33
|
4+
LL | fn hello(x: &Ty) {
5+
| --- move occurs because `*x` has type `Ty`, which does not implement the `Copy` trait
6+
LL | let c = async || {
47
LL | *x;
5-
| ^^ move occurs because `*x` has type `Ty`, which does not implement the `Copy` trait
8+
| ^^ `*x` is moved here
69
|
710
note: if `Ty` implemented `Clone`, you could clone the value
811
--> $DIR/move-out-of-ref.rs:5:1

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/issues/issue-4335.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 `*v`, as `v` is a captured variable in an `FnMu
22
--> $DIR/issue-4335.rs:6:20
33
|
44
LL | fn f<'r, T>(v: &'r T) -> Box<dyn FnMut() -> T + 'r> {
5-
| - captured outer variable
5+
| - ----- move occurs because `*v` has type `T`, which does not implement the `Copy` trait
6+
| |
7+
| captured outer variable
68
LL | id(Box::new(|| *v))
7-
| -- ^^ move occurs because `*v` has type `T`, which does not implement the `Copy` trait
9+
| -- ^^ `*v` is moved here
810
| |
911
| captured by this `FnMut` closure
1012
|

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/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)