Skip to content

Commit 63dd473

Browse files
committed
Suggest cloning Arc moved into closure
``` error[E0382]: borrow of moved value: `x` --> $DIR/moves-based-on-type-capture-clause-bad.rs:9:20 | LL | let x = "Hello world!".to_string(); | - move occurs because `x` has type `String`, which does not implement the `Copy` trait LL | thread::spawn(move || { | ------- value moved into closure here LL | println!("{}", x); | - variable moved due to use in closure LL | }); LL | println!("{}", x); | ^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value before moving it into the closure | LL ~ let value = x.clone(); LL ~ thread::spawn(move || { LL ~ println!("{}", value); | ``` Fix #104232.
1 parent 36b2163 commit 63dd473

13 files changed

+85
-33
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -518,11 +518,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
518518
} = move_spans
519519
&& can_suggest_clone
520520
{
521-
self.suggest_cloning(err, ty, expr, Some(move_spans));
521+
self.suggest_cloning(err, place.as_ref(), ty, expr, Some(move_spans));
522522
} else if self.suggest_hoisting_call_outside_loop(err, expr) && can_suggest_clone {
523523
// The place where the type moves would be misleading to suggest clone.
524524
// #121466
525-
self.suggest_cloning(err, ty, expr, Some(move_spans));
525+
self.suggest_cloning(err, place.as_ref(), ty, expr, Some(move_spans));
526526
}
527527
}
528528

@@ -1224,6 +1224,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12241224
pub(crate) fn suggest_cloning(
12251225
&self,
12261226
err: &mut Diag<'_>,
1227+
place: PlaceRef<'tcx>,
12271228
ty: Ty<'tcx>,
12281229
expr: &'tcx hir::Expr<'tcx>,
12291230
use_spans: Option<UseSpans<'tcx>>,
@@ -1238,7 +1239,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12381239
}
12391240

12401241
if self.implements_clone(ty) {
1241-
self.suggest_cloning_inner(err, ty, expr);
1242+
if self.in_move_closure(expr) {
1243+
if let Some(name) = self.describe_place(place) {
1244+
self.suggest_clone_of_captured_var_in_move_closure(err, &name, use_spans);
1245+
}
1246+
} else {
1247+
self.suggest_cloning_inner(err, ty, expr);
1248+
}
12421249
} else if let ty::Adt(def, args) = ty.kind()
12431250
&& def.did().as_local().is_some()
12441251
&& def.variants().iter().all(|variant| {
@@ -1505,7 +1512,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
15051512
if let hir::ExprKind::AddrOf(_, _, borrowed_expr) = expr.kind
15061513
&& let Some(ty) = typeck_results.expr_ty_opt(borrowed_expr)
15071514
{
1508-
self.suggest_cloning(&mut err, ty, borrowed_expr, Some(move_spans));
1515+
self.suggest_cloning(&mut err, place.as_ref(), ty, borrowed_expr, Some(move_spans));
15091516
} else if typeck_results.expr_adjustments(expr).first().is_some_and(|adj| {
15101517
matches!(
15111518
adj.kind,
@@ -1518,7 +1525,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
15181525
)
15191526
}) && let Some(ty) = typeck_results.expr_ty_opt(expr)
15201527
{
1521-
self.suggest_cloning(&mut err, ty, expr, Some(move_spans));
1528+
self.suggest_cloning(&mut err, place.as_ref(), ty, expr, Some(move_spans));
15221529
}
15231530
}
15241531
self.buffer_error(err);

compiler/rustc_borrowck/src/diagnostics/move_errors.rs

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -325,25 +325,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
325325
self.cannot_move_out_of(span, &description)
326326
}
327327

328-
fn suggest_clone_of_captured_var_in_move_closure(
328+
pub(in crate::diagnostics) fn suggest_clone_of_captured_var_in_move_closure(
329329
&self,
330330
err: &mut Diag<'_>,
331-
upvar_hir_id: HirId,
332331
upvar_name: &str,
333332
use_spans: Option<UseSpans<'tcx>>,
334333
) {
335334
let tcx = self.infcx.tcx;
336-
let typeck_results = tcx.typeck(self.mir_def_id());
337335
let Some(use_spans) = use_spans else { return };
338336
// We only care about the case where a closure captured a binding.
339337
let UseSpans::ClosureUse { args_span, .. } = use_spans else { return };
340338
let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
341-
// Fetch the type of the expression corresponding to the closure-captured binding.
342-
let Some(captured_ty) = typeck_results.node_type_opt(upvar_hir_id) else { return };
343-
if !self.implements_clone(captured_ty) {
344-
// We only suggest cloning the captured binding if the type can actually be cloned.
345-
return;
346-
};
347339
// Find the closure that captured the binding.
348340
let mut expr_finder = FindExprBySpan::new(args_span, tcx);
349341
expr_finder.include_closures = true;
@@ -396,7 +388,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
396388
.indentation_before(stmt.span)
397389
.unwrap_or_else(|| " ".to_string());
398390
err.multipart_suggestion_verbose(
399-
"clone the value before moving it into the closure",
391+
"consider cloning the value before moving it into the closure",
400392
vec![
401393
(
402394
stmt.span.shrink_to_lo(),
@@ -426,7 +418,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
426418
.indentation_before(closure_expr.span)
427419
.unwrap_or_else(|| " ".to_string());
428420
err.multipart_suggestion_verbose(
429-
"clone the value before moving it into the closure",
421+
"consider cloning the value before moving it into the closure",
430422
vec![
431423
(
432424
closure_expr.span.shrink_to_lo(),
@@ -519,20 +511,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
519511
);
520512

521513
let closure_span = tcx.def_span(def_id);
522-
let mut err = self
523-
.cannot_move_out_of(span, &place_description)
514+
self.cannot_move_out_of(span, &place_description)
524515
.with_span_label(upvar_span, "captured outer variable")
525516
.with_span_label(
526517
closure_span,
527518
format!("captured by this `{closure_kind}` closure"),
528-
);
529-
self.suggest_clone_of_captured_var_in_move_closure(
530-
&mut err,
531-
upvar_hir_id,
532-
&upvar_name,
533-
use_spans,
534-
);
535-
err
519+
)
536520
}
537521
_ => {
538522
let source = self.borrowed_content_source(deref_base);
@@ -593,7 +577,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
593577
};
594578

595579
if let Some(expr) = self.find_expr(span) {
596-
self.suggest_cloning(err, place_ty, expr, None);
580+
self.suggest_cloning(err, move_from.as_ref(), place_ty, expr, None);
597581
}
598582

599583
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
@@ -625,7 +609,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
625609
};
626610

627611
if let Some(expr) = self.find_expr(use_span) {
628-
self.suggest_cloning(err, place_ty, expr, Some(use_spans));
612+
self.suggest_cloning(
613+
err,
614+
original_path.as_ref(),
615+
place_ty,
616+
expr,
617+
Some(use_spans),
618+
);
629619
}
630620

631621
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
@@ -828,7 +818,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
828818
let place_desc = &format!("`{}`", self.local_names[*local].unwrap());
829819

830820
if let Some(expr) = self.find_expr(binding_span) {
831-
self.suggest_cloning(err, bind_to.ty, expr, None);
821+
let local_place: PlaceRef<'tcx> = (*local).into();
822+
self.suggest_cloning(err, local_place, bind_to.ty, expr, None);
832823
}
833824

834825
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ LL | let _h = to_fn_once(move || -> isize { *bar });
1212
| | move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait
1313
| `bar` is moved here
1414
|
15-
help: clone the value before moving it into the closure
15+
help: consider cloning the value before moving it into the closure
1616
|
1717
LL ~ let value = bar.clone();
1818
LL ~ let _h = to_fn_once(move || -> isize { value });

tests/ui/borrowck/borrowck-move-moved-value-into-closure.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ LL | call_f(move|| { *t + 1 });
1212
| ^^^^^^ -- use occurs due to use in closure
1313
| |
1414
| value used here after move
15+
|
16+
help: consider cloning the value before moving it into the closure
17+
|
18+
LL ~ let value = t.clone();
19+
LL ~ call_f(move|| { value + 1 });
20+
|
1521

1622
error: aborting due to 1 previous error
1723

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//@ run-rustfix
2+
use std::thread;
3+
4+
fn main() {
5+
let x = "Hello world!".to_string();
6+
let value = x.clone();
7+
thread::spawn(move || {
8+
println!("{}", value);
9+
});
10+
println!("{}", x); //~ ERROR borrow of moved value
11+
}

tests/ui/moves/moves-based-on-type-capture-clause-bad.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ run-rustfix
12
use std::thread;
23

34
fn main() {

tests/ui/moves/moves-based-on-type-capture-clause-bad.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0382]: borrow of moved value: `x`
2-
--> $DIR/moves-based-on-type-capture-clause-bad.rs:8:20
2+
--> $DIR/moves-based-on-type-capture-clause-bad.rs:9:20
33
|
44
LL | let x = "Hello world!".to_string();
55
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
@@ -12,6 +12,12 @@ LL | println!("{}", x);
1212
| ^ value borrowed here after move
1313
|
1414
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
help: consider cloning the value before moving it into the closure
16+
|
17+
LL ~ let value = x.clone();
18+
LL ~ thread::spawn(move || {
19+
LL ~ println!("{}", value);
20+
|
1521

1622
error: aborting due to 1 previous error
1723

File renamed without changes.

tests/ui/no-capture-arc.stderr renamed to tests/ui/moves/no-capture-arc.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ LL | assert_eq!((*arc_v)[2], 3);
1313
| ^^^^^ value borrowed here after move
1414
|
1515
= note: borrow occurs due to deref coercion to `Vec<i32>`
16+
help: consider cloning the value before moving it into the closure
17+
|
18+
LL ~ let value = arc_v.clone();
19+
LL ~ thread::spawn(move|| {
20+
LL ~ assert_eq!((*value)[3], 4);
21+
|
1622

1723
error: aborting due to 1 previous error
1824

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ run-rustfix
2+
use std::sync::Arc;
3+
use std::thread;
4+
5+
fn main() {
6+
let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
7+
let arc_v = Arc::new(v);
8+
9+
let value = arc_v.clone();
10+
thread::spawn(move|| {
11+
assert_eq!((*value)[3], 4);
12+
});
13+
14+
assert_eq!((*arc_v)[2], 3); //~ ERROR borrow of moved value: `arc_v`
15+
16+
println!("{:?}", *arc_v);
17+
}

tests/ui/no-reuse-move-arc.rs renamed to tests/ui/moves/no-reuse-move-arc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ run-rustfix
12
use std::sync::Arc;
23
use std::thread;
34

tests/ui/no-reuse-move-arc.stderr renamed to tests/ui/moves/no-reuse-move-arc.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0382]: borrow of moved value: `arc_v`
2-
--> $DIR/no-reuse-move-arc.rs:12:18
2+
--> $DIR/no-reuse-move-arc.rs:13:18
33
|
44
LL | let arc_v = Arc::new(v);
55
| ----- move occurs because `arc_v` has type `Arc<Vec<i32>>`, which does not implement the `Copy` trait
@@ -13,6 +13,12 @@ LL | assert_eq!((*arc_v)[2], 3);
1313
| ^^^^^ value borrowed here after move
1414
|
1515
= note: borrow occurs due to deref coercion to `Vec<i32>`
16+
help: you could clone the value before moving it into the closure
17+
|
18+
LL ~ let value = arc_v.clone();
19+
LL ~ thread::spawn(move|| {
20+
LL ~ assert_eq!((*value)[3], 4);
21+
|
1622

1723
error: aborting due to 1 previous error
1824

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ LL | let x = var;
7979
| variable moved due to use in closure
8080
| move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait
8181
|
82-
help: clone the value before moving it into the closure
82+
help: consider cloning the value before moving it into the closure
8383
|
8484
LL ~ {
8585
LL + let value = var.clone();

0 commit comments

Comments
 (0)