Skip to content

Commit dea9b50

Browse files
committed
Better account for more cases involving closures
1 parent 3cdc689 commit dea9b50

14 files changed

+137
-31
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+36-14
Original file line numberDiff line numberDiff line change
@@ -203,13 +203,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
203203

204204
if !seen_spans.contains(&move_span) {
205205
if !closure {
206-
self.suggest_ref_or_clone(
207-
mpi,
208-
move_span,
209-
&mut err,
210-
&mut in_pattern,
211-
move_spans,
212-
);
206+
self.suggest_ref_or_clone(mpi, &mut err, &mut in_pattern, move_spans);
213207
}
214208

215209
let msg_opt = CapturedMessageOpt {
@@ -351,18 +345,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
351345
fn suggest_ref_or_clone(
352346
&self,
353347
mpi: MovePathIndex,
354-
move_span: Span,
355348
err: &mut Diag<'tcx>,
356349
in_pattern: &mut bool,
357350
move_spans: UseSpans<'_>,
358351
) {
352+
let move_span = match move_spans {
353+
UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,
354+
_ => move_spans.args_or_use(),
355+
};
359356
struct ExpressionFinder<'hir> {
360357
expr_span: Span,
361358
expr: Option<&'hir hir::Expr<'hir>>,
362359
pat: Option<&'hir hir::Pat<'hir>>,
363360
parent_pat: Option<&'hir hir::Pat<'hir>>,
361+
hir: rustc_middle::hir::map::Map<'hir>,
364362
}
365363
impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
364+
type NestedFilter = OnlyBodies;
365+
366+
fn nested_visit_map(&mut self) -> Self::Map {
367+
self.hir
368+
}
369+
366370
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
367371
if e.span == self.expr_span {
368372
self.expr = Some(e);
@@ -397,8 +401,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
397401
let expr = hir.body(body_id).value;
398402
let place = &self.move_data.move_paths[mpi].place;
399403
let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span);
400-
let mut finder =
401-
ExpressionFinder { expr_span: move_span, expr: None, pat: None, parent_pat: None };
404+
let mut finder = ExpressionFinder {
405+
expr_span: move_span,
406+
expr: None,
407+
pat: None,
408+
parent_pat: None,
409+
hir,
410+
};
402411
finder.visit_expr(expr);
403412
if let Some(span) = span
404413
&& let Some(expr) = finder.expr
@@ -479,12 +488,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
479488
} else if let UseSpans::ClosureUse {
480489
closure_kind:
481490
ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)),
482-
args_span: _,
483-
capture_kind_span: _,
484-
path_span,
491+
..
485492
} = move_spans
486493
{
487-
self.suggest_cloning(err, ty, expr, path_span);
494+
self.suggest_cloning(err, ty, expr, None);
488495
} else if self.suggest_hoisting_call_outside_loop(err, expr) {
489496
// The place where the the type moves would be misleading to suggest clone.
490497
// #121466
@@ -1233,6 +1240,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
12331240
}
12341241
}
12351242

1243+
fn in_move_closure(&self, expr: &hir::Expr<'_>) -> bool {
1244+
for (_, node) in self.infcx.tcx.hir().parent_iter(expr.hir_id) {
1245+
if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node
1246+
&& let hir::CaptureBy::Value { .. } = closure.capture_clause
1247+
{
1248+
// `move || x.clone()` will not work. FIXME: suggest `let y = x.clone(); move || y`
1249+
return true;
1250+
}
1251+
}
1252+
false
1253+
}
1254+
12361255
fn suggest_cloning_inner(
12371256
&self,
12381257
err: &mut Diag<'_>,
@@ -1245,6 +1264,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
12451264
// See `tests/ui/moves/needs-clone-through-deref.rs`
12461265
return false;
12471266
}
1267+
if self.in_move_closure(expr) {
1268+
return false;
1269+
}
12481270
// Try to find predicates on *generic params* that would allow copying `ty`
12491271
let suggestion =
12501272
if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {

tests/ui/borrowck/borrowck-closures-slice-patterns.stderr

+11
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ LL | let [y, z @ ..] = x;
3838
LL | };
3939
LL | &x;
4040
| ^^ value borrowed here after move
41+
|
42+
help: consider cloning the value if the performance cost is acceptable
43+
|
44+
LL | let [y, z @ ..] = x.clone();
45+
| ++++++++
4146

4247
error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
4348
--> $DIR/borrowck-closures-slice-patterns.rs:33:13
@@ -79,6 +84,12 @@ LL | let [y, z @ ..] = *x;
7984
LL | };
8085
LL | &x;
8186
| ^^ value borrowed here after move
87+
|
88+
help: consider cloning the value if the performance cost is acceptable
89+
|
90+
LL - let [y, z @ ..] = *x;
91+
LL + let [y, z @ ..] = x.clone();
92+
|
8293

8394
error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
8495
--> $DIR/borrowck-closures-slice-patterns.rs:59:13

tests/ui/borrowck/issue-101119.stderr

+14
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,25 @@ error[E0382]: use of moved value: `state`
44
LL | fn fill_memory_blocks_mt(state: &mut State) {
55
| ----- move occurs because `state` has type `&mut State`, which does not implement the `Copy` trait
66
LL | loop {
7+
| ---- inside of this loop
78
LL | once(move || {
89
| ^^^^^^^ value moved into closure here, in previous iteration of loop
910
LL |
1011
LL | fill_segment(state);
1112
| ----- use occurs due to use in closure
13+
|
14+
note: consider changing this parameter type in function `fill_segment` to borrow instead if owning the value isn't necessary
15+
--> $DIR/issue-101119.rs:14:20
16+
|
17+
LL | fn fill_segment(_: &mut State) {}
18+
| ------------ ^^^^^^^^^^ this parameter takes ownership of the value
19+
| |
20+
| in this function
21+
note: if `State` implemented `Clone`, you could clone the value
22+
--> $DIR/issue-101119.rs:1:1
23+
|
24+
LL | struct State;
25+
| ^^^^^^^^^^^^
1226

1327
error: aborting due to 1 previous error
1428

tests/ui/issues/issue-24357.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
struct NoCopy;
1+
struct NoCopy; //~ NOTE if `NoCopy` implemented `Clone`, you could clone the value
22
fn main() {
33
let x = NoCopy;
44
//~^ NOTE move occurs because `x` has type `NoCopy`

tests/ui/issues/issue-24357.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ LL | let f = move || { let y = x; };
1111
...
1212
LL | let z = x;
1313
| ^ value used here after move
14+
|
15+
note: if `NoCopy` implemented `Clone`, you could clone the value
16+
--> $DIR/issue-24357.rs:1:1
17+
|
18+
LL | struct NoCopy;
19+
| ^^^^^^^^^^^^^
1420

1521
error: aborting due to 1 previous error
1622

tests/ui/moves/issue-75904-move-closure-loop.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@ error[E0382]: use of moved value: `a`
44
LL | let mut a = NotCopy;
55
| ----- move occurs because `a` has type `NotCopy`, which does not implement the `Copy` trait
66
LL | loop {
7+
| ---- inside of this loop
78
LL | || {
89
| ^^ value moved into closure here, in previous iteration of loop
910
LL | &mut a;
1011
LL | a;
1112
| - use occurs due to use in closure
13+
|
14+
note: if `NotCopy` implemented `Clone`, you could clone the value
15+
--> $DIR/issue-75904-move-closure-loop.rs:5:1
16+
|
17+
LL | struct NotCopy;
18+
| ^^^^^^^^^^^^^^
1219

1320
error: aborting due to 1 previous error
1421

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::thread;
22

33
fn main() {
44
let x = "Hello world!".to_string();
5-
thread::spawn(move|| {
5+
thread::spawn(move || {
66
println!("{}", x);
77
});
88
println!("{}", x); //~ ERROR borrow of moved value

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ error[E0382]: borrow of moved value: `x`
33
|
44
LL | let x = "Hello world!".to_string();
55
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
6-
LL | thread::spawn(move|| {
7-
| ------ value moved into closure here
6+
LL | thread::spawn(move || {
7+
| ------- value moved into closure here
88
LL | println!("{}", x);
99
| - variable moved due to use in closure
1010
LL | });

tests/ui/nll/closure-move-spans.fixed

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// check that moves due to a closure capture give a special note
2+
//@ run-rustfix
3+
#![allow(unused_variables, unused_must_use, dead_code)]
4+
5+
fn move_after_move(x: String) {
6+
|| x.clone();
7+
let y = x; //~ ERROR
8+
}
9+
10+
fn borrow_after_move(x: String) {
11+
|| x.clone();
12+
let y = &x; //~ ERROR
13+
}
14+
15+
fn borrow_mut_after_move(mut x: String) {
16+
|| x.clone();
17+
let y = &mut x; //~ ERROR
18+
}
19+
20+
fn fn_ref<F: Fn()>(f: F) -> F { f }
21+
fn fn_mut<F: FnMut()>(f: F) -> F { f }
22+
23+
fn main() {}

tests/ui/nll/closure-move-spans.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// check that moves due to a closure capture give a special note
2+
//@ run-rustfix
3+
#![allow(unused_variables, unused_must_use, dead_code)]
24

35
fn move_after_move(x: String) {
46
|| x;

tests/ui/nll/closure-move-spans.stderr

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0382]: use of moved value: `x`
2-
--> $DIR/closure-move-spans.rs:5:13
2+
--> $DIR/closure-move-spans.rs:7:13
33
|
44
LL | fn move_after_move(x: String) {
55
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
@@ -9,9 +9,14 @@ LL | || x;
99
| value moved into closure here
1010
LL | let y = x;
1111
| ^ value used here after move
12+
|
13+
help: consider cloning the value if the performance cost is acceptable
14+
|
15+
LL | || x.clone();
16+
| ++++++++
1217

1318
error[E0382]: borrow of moved value: `x`
14-
--> $DIR/closure-move-spans.rs:10:13
19+
--> $DIR/closure-move-spans.rs:12:13
1520
|
1621
LL | fn borrow_after_move(x: String) {
1722
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
@@ -21,9 +26,14 @@ LL | || x;
2126
| value moved into closure here
2227
LL | let y = &x;
2328
| ^^ value borrowed here after move
29+
|
30+
help: consider cloning the value if the performance cost is acceptable
31+
|
32+
LL | || x.clone();
33+
| ++++++++
2434

2535
error[E0382]: borrow of moved value: `x`
26-
--> $DIR/closure-move-spans.rs:15:13
36+
--> $DIR/closure-move-spans.rs:17:13
2737
|
2838
LL | fn borrow_mut_after_move(mut x: String) {
2939
| ----- move occurs because `x` has type `String`, which does not implement the `Copy` trait
@@ -33,6 +43,11 @@ LL | || x;
3343
| value moved into closure here
3444
LL | let y = &mut x;
3545
| ^^^^^^ value borrowed here after move
46+
|
47+
help: consider cloning the value if the performance cost is acceptable
48+
|
49+
LL | || x.clone();
50+
| ++++++++
3651

3752
error: aborting due to 3 previous errors
3853

tests/ui/nll/closures-in-loops.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ error[E0382]: use of moved value: `x`
44
LL | fn repreated_move(x: String) {
55
| - move occurs because `x` has type `String`, which does not implement the `Copy` trait
66
LL | for i in 0..10 {
7+
| -------------- inside of this loop
78
LL | || x;
89
| ^^ - use occurs due to use in closure
910
| |
1011
| value moved into closure here, in previous iteration of loop
12+
|
13+
help: consider cloning the value if the performance cost is acceptable
14+
|
15+
LL | || x.clone();
16+
| ++++++++
1117

1218
error[E0499]: cannot borrow `x` as mutable more than once at a time
1319
--> $DIR/closures-in-loops.rs:13:16

tests/ui/nll/issue-27282-move-match-input-into-guard.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ LL | _ if { (|| { let bar = b; *bar = false; })();
1010
| -- - variable moved due to use in closure
1111
| |
1212
| value moved into closure here
13+
|
14+
help: consider cloning the value if the performance cost is acceptable
15+
|
16+
LL | _ if { (|| { let bar = b.clone(); *bar = false; })();
17+
| ++++++++
1318

1419
error[E0382]: use of moved value: `b`
1520
--> $DIR/issue-27282-move-match-input-into-guard.rs:24:5
@@ -23,6 +28,11 @@ LL | (|| { let bar = b; *bar = false; })();
2328
| -- - variable moved due to use in closure
2429
| |
2530
| value moved into closure here
31+
|
32+
help: consider cloning the value if the performance cost is acceptable
33+
|
34+
LL | (|| { let bar = b.clone(); *bar = false; })();
35+
| ++++++++
2636

2737
error: aborting due to 2 previous errors
2838

tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr

-10
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,6 @@ LL | let f = to_fn(move || drop(x));
3737
| ------- ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
3838
| |
3939
| captured by this `Fn` closure
40-
|
41-
help: consider cloning the value if the performance cost is acceptable
42-
|
43-
LL | let f = to_fn(move || drop(x.clone()));
44-
| ++++++++
4540

4641
error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure
4742
--> $DIR/unboxed-closure-illegal-move.rs:32:40
@@ -52,11 +47,6 @@ LL | let f = to_fn_mut(move || drop(x));
5247
| ------- ^ move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
5348
| |
5449
| captured by this `FnMut` closure
55-
|
56-
help: consider cloning the value if the performance cost is acceptable
57-
|
58-
LL | let f = to_fn_mut(move || drop(x.clone()));
59-
| ++++++++
6050

6151
error: aborting due to 4 previous errors
6252

0 commit comments

Comments
 (0)