Skip to content

Commit 4a245ae

Browse files
authored
Rollup merge of rust-lang#73534 - estebank:borrowck-suggestions, r=matthewjasper
Provide suggestions for some moved value errors When encountering an used moved value where the previous move happened in a `match` or `if let` pattern, suggest using `ref`. Fix rust-lang#63988. When encountering a `&mut` value that is used in multiple iterations of a loop, suggest reborrowing it with `&mut *`. Fix rust-lang#62112.
2 parents 3f5b8c8 + 520461f commit 4a245ae

17 files changed

+315
-10
lines changed

src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs

+35-4
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
156156
format!("variable moved due to use{}", move_spans.describe()),
157157
);
158158
}
159+
if let UseSpans::PatUse(span) = move_spans {
160+
err.span_suggestion_verbose(
161+
span.shrink_to_lo(),
162+
&format!(
163+
"borrow this field in the pattern to avoid moving {}",
164+
self.describe_place(moved_place.as_ref())
165+
.map(|n| format!("`{}`", n))
166+
.unwrap_or_else(|| "the value".to_string())
167+
),
168+
"ref ".to_string(),
169+
Applicability::MachineApplicable,
170+
);
171+
}
172+
159173
if Some(DesugaringKind::ForLoop) == move_span.desugaring_kind() {
160174
let sess = self.infcx.tcx.sess;
161175
if let Ok(snippet) = sess.source_map().span_to_snippet(move_span) {
@@ -198,11 +212,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
198212
_ => true,
199213
};
200214

201-
if needs_note {
202-
let mpi = self.move_data.moves[move_out_indices[0]].path;
203-
let place = &self.move_data.move_paths[mpi].place;
215+
let mpi = self.move_data.moves[move_out_indices[0]].path;
216+
let place = &self.move_data.move_paths[mpi].place;
217+
let ty = place.ty(self.body, self.infcx.tcx).ty;
218+
219+
if is_loop_move {
220+
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind {
221+
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
222+
err.span_suggestion_verbose(
223+
span.shrink_to_lo(),
224+
&format!(
225+
"consider creating a fresh reborrow of {} here",
226+
self.describe_place(moved_place)
227+
.map(|n| format!("`{}`", n))
228+
.unwrap_or_else(|| "the mutable reference".to_string()),
229+
),
230+
"&mut *".to_string(),
231+
Applicability::MachineApplicable,
232+
);
233+
}
234+
}
204235

205-
let ty = place.ty(self.body, self.infcx.tcx).ty;
236+
if needs_note {
206237
let opt_name =
207238
self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
208239
let note_msg = match opt_name {

src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
509509
// Used in a closure.
510510
(LaterUseKind::ClosureCapture, var_span)
511511
}
512-
UseSpans::OtherUse(span) => {
512+
UseSpans::PatUse(span) | UseSpans::OtherUse(span) => {
513513
let block = &self.body.basic_blocks()[location.block];
514514

515515
let kind = if let Some(&Statement {

src/librustc_mir/borrow_check/diagnostics/mod.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -542,20 +542,26 @@ pub(super) enum UseSpans {
542542
// The span of the first use of the captured variable inside the closure.
543543
var_span: Span,
544544
},
545-
// This access has a single span associated to it: common case.
545+
/// This access is caused by a `match` or `if let` pattern.
546+
PatUse(Span),
547+
/// This access has a single span associated to it: common case.
546548
OtherUse(Span),
547549
}
548550

549551
impl UseSpans {
550552
pub(super) fn args_or_use(self) -> Span {
551553
match self {
552-
UseSpans::ClosureUse { args_span: span, .. } | UseSpans::OtherUse(span) => span,
554+
UseSpans::ClosureUse { args_span: span, .. }
555+
| UseSpans::PatUse(span)
556+
| UseSpans::OtherUse(span) => span,
553557
}
554558
}
555559

556560
pub(super) fn var_or_use(self) -> Span {
557561
match self {
558-
UseSpans::ClosureUse { var_span: span, .. } | UseSpans::OtherUse(span) => span,
562+
UseSpans::ClosureUse { var_span: span, .. }
563+
| UseSpans::PatUse(span)
564+
| UseSpans::OtherUse(span) => span,
559565
}
560566
}
561567

@@ -624,7 +630,7 @@ impl UseSpans {
624630
{
625631
match self {
626632
closure @ UseSpans::ClosureUse { .. } => closure,
627-
UseSpans::OtherUse(_) => if_other(),
633+
UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
628634
}
629635
}
630636
}
@@ -741,7 +747,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
741747
}
742748
}
743749

744-
OtherUse(stmt.source_info.span)
750+
if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
751+
PatUse(stmt.source_info.span)
752+
} else {
753+
OtherUse(stmt.source_info.span)
754+
}
745755
}
746756

747757
/// Finds the span of arguments of a closure (within `maybe_closure_span`)

src/test/ui/borrowck/issue-41962.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ LL | if let Some(thing) = maybe {
55
| ^^^^^ value moved here, in previous iteration of loop
66
|
77
= note: move occurs because value has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
8+
help: borrow this field in the pattern to avoid moving `maybe.0`
9+
|
10+
LL | if let Some(ref thing) = maybe {
11+
| ^^^
812

913
error: aborting due to previous error
1014

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Issue #63988
2+
#[derive(Debug)]
3+
struct S;
4+
fn foo(_: Option<S>) {}
5+
6+
enum E {
7+
V {
8+
s: S,
9+
}
10+
}
11+
fn bar(_: E) {}
12+
13+
fn main() {
14+
let s = Some(S);
15+
if let Some(mut x) = s {
16+
x = S;
17+
}
18+
foo(s); //~ ERROR use of moved value: `s`
19+
let mut e = E::V { s: S };
20+
let E::V { s: mut x } = e;
21+
x = S;
22+
bar(e); //~ ERROR use of moved value: `e`
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0382]: use of moved value: `s`
2+
--> $DIR/move-in-pattern-mut.rs:18:9
3+
|
4+
LL | if let Some(mut x) = s {
5+
| ----- value moved here
6+
...
7+
LL | foo(s);
8+
| ^ value used here after partial move
9+
|
10+
= note: move occurs because value has type `S`, which does not implement the `Copy` trait
11+
help: borrow this field in the pattern to avoid moving `s.0`
12+
|
13+
LL | if let Some(ref mut x) = s {
14+
| ^^^
15+
16+
error[E0382]: use of moved value: `e`
17+
--> $DIR/move-in-pattern-mut.rs:22:9
18+
|
19+
LL | let E::V { s: mut x } = e;
20+
| ----- value moved here
21+
LL | x = S;
22+
LL | bar(e);
23+
| ^ value used here after partial move
24+
|
25+
= note: move occurs because value has type `S`, which does not implement the `Copy` trait
26+
help: borrow this field in the pattern to avoid moving `e.s`
27+
|
28+
LL | let E::V { s: ref mut x } = e;
29+
| ^^^
30+
31+
error: aborting due to 2 previous errors
32+
33+
For more information about this error, try `rustc --explain E0382`.
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
// Issue #63988
3+
#[derive(Debug)]
4+
struct S;
5+
fn foo(_: Option<S>) {}
6+
7+
enum E {
8+
V {
9+
s: S,
10+
}
11+
}
12+
fn bar(_: E) {}
13+
14+
fn main() {
15+
let s = Some(S);
16+
if let Some(ref x) = s {
17+
let _ = x;
18+
}
19+
foo(s); //~ ERROR use of moved value: `s`
20+
let e = E::V { s: S };
21+
let E::V { s: ref x } = e;
22+
let _ = x;
23+
bar(e); //~ ERROR use of moved value: `e`
24+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// run-rustfix
2+
// Issue #63988
3+
#[derive(Debug)]
4+
struct S;
5+
fn foo(_: Option<S>) {}
6+
7+
enum E {
8+
V {
9+
s: S,
10+
}
11+
}
12+
fn bar(_: E) {}
13+
14+
fn main() {
15+
let s = Some(S);
16+
if let Some(x) = s {
17+
let _ = x;
18+
}
19+
foo(s); //~ ERROR use of moved value: `s`
20+
let e = E::V { s: S };
21+
let E::V { s: x } = e;
22+
let _ = x;
23+
bar(e); //~ ERROR use of moved value: `e`
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0382]: use of moved value: `s`
2+
--> $DIR/move-in-pattern.rs:19:9
3+
|
4+
LL | if let Some(x) = s {
5+
| - value moved here
6+
...
7+
LL | foo(s);
8+
| ^ value used here after partial move
9+
|
10+
= note: move occurs because value has type `S`, which does not implement the `Copy` trait
11+
help: borrow this field in the pattern to avoid moving `s.0`
12+
|
13+
LL | if let Some(ref x) = s {
14+
| ^^^
15+
16+
error[E0382]: use of moved value: `e`
17+
--> $DIR/move-in-pattern.rs:23:9
18+
|
19+
LL | let E::V { s: x } = e;
20+
| - value moved here
21+
LL | let _ = x;
22+
LL | bar(e);
23+
| ^ value used here after partial move
24+
|
25+
= note: move occurs because value has type `S`, which does not implement the `Copy` trait
26+
help: borrow this field in the pattern to avoid moving `e.s`
27+
|
28+
LL | let E::V { s: ref x } = e;
29+
| ^^^
30+
31+
error: aborting due to 2 previous errors
32+
33+
For more information about this error, try `rustc --explain E0382`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
3+
4+
struct Events<R>(R);
5+
6+
struct Other;
7+
8+
pub trait Trait<T> {
9+
fn handle(value: T) -> Self;
10+
}
11+
12+
// Blanket impl. (If you comment this out, compiler figures out that it
13+
// is passing an `&mut` to a method that must be expecting an `&mut`,
14+
// and injects an auto-reborrow.)
15+
impl<T, U> Trait<U> for T where T: From<U> {
16+
fn handle(_: U) -> Self { unimplemented!() }
17+
}
18+
19+
impl<'a, R> Trait<&'a mut Events<R>> for Other {
20+
fn handle(_: &'a mut Events<R>) -> Self { unimplemented!() }
21+
}
22+
23+
fn this_compiles<'a, R>(value: &'a mut Events<R>) {
24+
for _ in 0..3 {
25+
Other::handle(&mut *value);
26+
}
27+
}
28+
29+
fn this_does_not<'a, R>(value: &'a mut Events<R>) {
30+
for _ in 0..3 {
31+
Other::handle(&mut *value); //~ ERROR use of moved value: `value`
32+
}
33+
}
34+
35+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
3+
4+
struct Events<R>(R);
5+
6+
struct Other;
7+
8+
pub trait Trait<T> {
9+
fn handle(value: T) -> Self;
10+
}
11+
12+
// Blanket impl. (If you comment this out, compiler figures out that it
13+
// is passing an `&mut` to a method that must be expecting an `&mut`,
14+
// and injects an auto-reborrow.)
15+
impl<T, U> Trait<U> for T where T: From<U> {
16+
fn handle(_: U) -> Self { unimplemented!() }
17+
}
18+
19+
impl<'a, R> Trait<&'a mut Events<R>> for Other {
20+
fn handle(_: &'a mut Events<R>) -> Self { unimplemented!() }
21+
}
22+
23+
fn this_compiles<'a, R>(value: &'a mut Events<R>) {
24+
for _ in 0..3 {
25+
Other::handle(&mut *value);
26+
}
27+
}
28+
29+
fn this_does_not<'a, R>(value: &'a mut Events<R>) {
30+
for _ in 0..3 {
31+
Other::handle(value); //~ ERROR use of moved value: `value`
32+
}
33+
}
34+
35+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0382]: use of moved value: `value`
2+
--> $DIR/mut-borrow-in-loop-2.rs:31:23
3+
|
4+
LL | fn this_does_not<'a, R>(value: &'a mut Events<R>) {
5+
| ----- move occurs because `value` has type `&mut Events<R>`, which does not implement the `Copy` trait
6+
LL | for _ in 0..3 {
7+
LL | Other::handle(value);
8+
| ^^^^^ value moved here, in previous iteration of loop
9+
|
10+
help: consider creating a fresh reborrow of `value` here
11+
|
12+
LL | Other::handle(&mut *value);
13+
| ^^^^^^
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0382`.

src/test/ui/moves/moves-based-on-type-cyclic-types-issue-4821.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | consume(node) + r
88
| ^^^^ value used here after partial move
99
|
1010
= note: move occurs because value has type `std::boxed::Box<List>`, which does not implement the `Copy` trait
11+
help: borrow this field in the pattern to avoid moving `node.next.0`
12+
|
13+
LL | Some(ref right) => consume(right),
14+
| ^^^
1115

1216
error: aborting due to previous error
1317

src/test/ui/nll/issue-53807.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ LL | if let Some(thing) = maybe {
55
| ^^^^^ value moved here, in previous iteration of loop
66
|
77
= note: move occurs because value has type `std::vec::Vec<bool>`, which does not implement the `Copy` trait
8+
help: borrow this field in the pattern to avoid moving `maybe.0`
9+
|
10+
LL | if let Some(ref thing) = maybe {
11+
| ^^^
812

913
error: aborting due to previous error
1014

0 commit comments

Comments
 (0)