Skip to content

Commit 5e5d63d

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 2f752b0 + 5faf657 commit 5e5d63d

15 files changed

+306
-8
lines changed

src/librustc_mir/borrow_check/diagnostics/conflict_errors.rs

+34-4
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
212212
&mut err,
213213
format!("variable moved due to use{}", move_spans.describe()),
214214
);
215+
if let UseSpans::PatUse(span) = move_spans {
216+
err.span_suggestion_verbose(
217+
span.shrink_to_lo(),
218+
&format!(
219+
"borrow this field in the pattern to avoid moving {}",
220+
self.describe_place(moved_place.as_ref())
221+
.map(|n| format!("`{}`", n))
222+
.unwrap_or_else(|| "the value".to_string())
223+
),
224+
"ref ".to_string(),
225+
Applicability::MachineApplicable,
226+
);
227+
}
215228
}
216229
}
217230
if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() {
@@ -256,11 +269,28 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
256269
_ => true,
257270
};
258271

259-
if needs_note {
260-
let mpi = self.move_data.moves[move_out_indices[0]].path;
261-
let place = &self.move_data.move_paths[mpi].place;
272+
let mpi = self.move_data.moves[move_out_indices[0]].path;
273+
let place = &self.move_data.move_paths[mpi].place;
274+
let ty = place.ty(self.body, self.infcx.tcx).ty;
275+
276+
if is_loop_move {
277+
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind {
278+
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
279+
err.span_suggestion_verbose(
280+
span.shrink_to_lo(),
281+
&format!(
282+
"consider creating a fresh reborrow of {} here",
283+
self.describe_place(moved_place)
284+
.map(|n| format!("`{}`", n))
285+
.unwrap_or_else(|| "the mutable reference".to_string()),
286+
),
287+
"&mut *".to_string(),
288+
Applicability::MachineApplicable,
289+
);
290+
}
291+
}
262292

263-
let ty = place.ty(self.body, self.infcx.tcx).ty;
293+
if needs_note {
264294
let opt_name =
265295
self.describe_place_with_options(place.as_ref(), IncludingDowncast(true));
266296
let note_msg = match opt_name {

src/librustc_mir/borrow_check/diagnostics/explain_borrow.rs

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

515517
let kind = if let Some(&Statement {

src/librustc_mir/borrow_check/diagnostics/mod.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,9 @@ pub(super) enum UseSpans {
558558
fn_span: Span,
559559
kind: FnSelfUseKind,
560560
},
561-
// This access has a single span associated to it: common case.
561+
/// This access is caused by a `match` or `if let` pattern.
562+
PatUse(Span),
563+
/// This access has a single span associated to it: common case.
562564
OtherUse(Span),
563565
}
564566

@@ -577,6 +579,7 @@ impl UseSpans {
577579
match self {
578580
UseSpans::ClosureUse { args_span: span, .. }
579581
| UseSpans::FnSelfUse { var_span: span, .. }
582+
| UseSpans::PatUse(span)
580583
| UseSpans::OtherUse(span) => span,
581584
}
582585
}
@@ -585,6 +588,7 @@ impl UseSpans {
585588
match self {
586589
UseSpans::ClosureUse { var_span: span, .. }
587590
| UseSpans::FnSelfUse { var_span: span, .. }
591+
| UseSpans::PatUse(span)
588592
| UseSpans::OtherUse(span) => span,
589593
}
590594
}
@@ -655,7 +659,7 @@ impl UseSpans {
655659
match self {
656660
closure @ UseSpans::ClosureUse { .. } => closure,
657661
fn_self @ UseSpans::FnSelfUse { .. } => fn_self,
658-
UseSpans::OtherUse(_) => if_other(),
662+
UseSpans::PatUse(_) | UseSpans::OtherUse(_) => if_other(),
659663
}
660664
}
661665
}
@@ -772,7 +776,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
772776
}
773777
}
774778

775-
let normal_ret = OtherUse(stmt.source_info.span);
779+
let normal_ret =
780+
if moved_place.projection.iter().any(|p| matches!(p, ProjectionElem::Downcast(..))) {
781+
PatUse(stmt.source_info.span)
782+
} else {
783+
OtherUse(stmt.source_info.span)
784+
};
776785

777786
// We are trying to find MIR of the form:
778787
// ```
@@ -792,6 +801,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
792801
_ => return normal_ret,
793802
};
794803

804+
debug!("move_spans: normal_ret = {:?}", normal_ret);
795805
debug!("move_spans: target_temp = {:?}", target_temp);
796806

797807
if let Some(Terminator { kind: TerminatorKind::Call { func, args, fn_span, .. }, .. }) =
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/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ LL | Some(_z @ ref _y) => {}
4646
| value moved here
4747
|
4848
= note: move occurs because value has type `X`, which does not implement the `Copy` trait
49+
help: borrow this field in the pattern to avoid moving `x.0`
50+
|
51+
LL | Some(ref _z @ ref _y) => {}
52+
| ^^^
4953

5054
error[E0382]: borrow of moved value
5155
--> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:35:19
@@ -57,6 +61,10 @@ LL | Some(_z @ ref mut _y) => {}
5761
| value moved here
5862
|
5963
= note: move occurs because value has type `X`, which does not implement the `Copy` trait
64+
help: borrow this field in the pattern to avoid moving `x.0`
65+
|
66+
LL | Some(ref _z @ ref mut _y) => {}
67+
| ^^^
6068

6169
error: aborting due to 6 previous errors
6270

0 commit comments

Comments
 (0)