Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 520461f

Browse files
committedJun 25, 2020
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.
1 parent 67100f6 commit 520461f

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

There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.