Skip to content

Commit 77d1559

Browse files
committed
Auto merge of #85686 - ptrojahn:loop_reinitialize, r=estebank
Add help on reinitialization between move and access Fixes #83760
2 parents eb0b95b + 34ff259 commit 77d1559

File tree

3 files changed

+176
-18
lines changed

3 files changed

+176
-18
lines changed

compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs

+74-18
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::mir::{
1212
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
1313
use rustc_span::source_map::DesugaringKind;
1414
use rustc_span::symbol::sym;
15-
use rustc_span::{Span, DUMMY_SP};
15+
use rustc_span::{MultiSpan, Span, DUMMY_SP};
1616
use rustc_trait_selection::infer::InferCtxtExt;
1717

1818
use crate::dataflow::drop_flag_effects;
@@ -66,7 +66,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
6666
self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));
6767
let span = use_spans.args_or_use();
6868

69-
let move_site_vec = self.get_moved_indexes(location, mpi);
69+
let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);
7070
debug!(
7171
"report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",
7272
move_site_vec, use_spans
@@ -139,6 +139,32 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
139139
self.describe_place_with_options(moved_place, IncludingDowncast(true)),
140140
);
141141

142+
let reinit_spans = maybe_reinitialized_locations
143+
.iter()
144+
.take(3)
145+
.map(|loc| {
146+
self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)
147+
.args_or_use()
148+
})
149+
.collect::<Vec<Span>>();
150+
let reinits = maybe_reinitialized_locations.len();
151+
if reinits == 1 {
152+
err.span_label(reinit_spans[0], "this reinitialization might get skipped");
153+
} else if reinits > 1 {
154+
err.span_note(
155+
MultiSpan::from_spans(reinit_spans),
156+
&if reinits <= 3 {
157+
format!("these {} reinitializations might get skipped", reinits)
158+
} else {
159+
format!(
160+
"these 3 reinitializations and {} other{} might get skipped",
161+
reinits - 3,
162+
if reinits == 4 { "" } else { "s" }
163+
)
164+
},
165+
);
166+
}
167+
142168
self.add_moved_or_invoked_closure_note(location, used_place, &mut err);
143169

144170
let mut is_loop_move = false;
@@ -219,7 +245,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
219245
),
220246
);
221247
}
222-
if is_option_or_result {
248+
if is_option_or_result && maybe_reinitialized_locations.is_empty() {
223249
err.span_suggestion_verbose(
224250
fn_call_span.shrink_to_lo(),
225251
"consider calling `.as_ref()` to borrow the type's contents",
@@ -260,19 +286,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
260286
}
261287
}
262288

263-
if let UseSpans::PatUse(span) = move_spans {
264-
err.span_suggestion_verbose(
265-
span.shrink_to_lo(),
266-
&format!(
267-
"borrow this field in the pattern to avoid moving {}",
268-
self.describe_place(moved_place.as_ref())
269-
.map(|n| format!("`{}`", n))
270-
.unwrap_or_else(|| "the value".to_string())
271-
),
272-
"ref ".to_string(),
273-
Applicability::MachineApplicable,
274-
);
275-
in_pattern = true;
289+
if let (UseSpans::PatUse(span), []) =
290+
(move_spans, &maybe_reinitialized_locations[..])
291+
{
292+
if maybe_reinitialized_locations.is_empty() {
293+
err.span_suggestion_verbose(
294+
span.shrink_to_lo(),
295+
&format!(
296+
"borrow this field in the pattern to avoid moving {}",
297+
self.describe_place(moved_place.as_ref())
298+
.map(|n| format!("`{}`", n))
299+
.unwrap_or_else(|| "the value".to_string())
300+
),
301+
"ref ".to_string(),
302+
Applicability::MachineApplicable,
303+
);
304+
in_pattern = true;
305+
}
276306
}
277307

278308
if let Some(DesugaringKind::ForLoop(_)) = move_span.desugaring_kind() {
@@ -1465,7 +1495,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
14651495
err
14661496
}
14671497

1468-
fn get_moved_indexes(&mut self, location: Location, mpi: MovePathIndex) -> Vec<MoveSite> {
1498+
fn get_moved_indexes(
1499+
&mut self,
1500+
location: Location,
1501+
mpi: MovePathIndex,
1502+
) -> (Vec<MoveSite>, Vec<Location>) {
14691503
fn predecessor_locations(
14701504
body: &'a mir::Body<'tcx>,
14711505
location: Location,
@@ -1488,6 +1522,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
14881522
}));
14891523

14901524
let mut visited = FxHashSet::default();
1525+
let mut move_locations = FxHashSet::default();
1526+
let mut reinits = vec![];
14911527
let mut result = vec![];
14921528

14931529
'dfs: while let Some((location, is_back_edge)) = stack.pop() {
@@ -1529,6 +1565,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15291565
move_paths[path].place
15301566
);
15311567
result.push(MoveSite { moi: *moi, traversed_back_edge: is_back_edge });
1568+
move_locations.insert(location);
15321569

15331570
// Strictly speaking, we could continue our DFS here. There may be
15341571
// other moves that can reach the point of error. But it is kind of
@@ -1565,6 +1602,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15651602
},
15661603
);
15671604
if any_match {
1605+
reinits.push(location);
15681606
continue 'dfs;
15691607
}
15701608

@@ -1574,7 +1612,25 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15741612
}));
15751613
}
15761614

1577-
result
1615+
// Check if we can reach these reinits from a move location.
1616+
let reinits_reachable = reinits
1617+
.into_iter()
1618+
.filter(|reinit| {
1619+
let mut visited = FxHashSet::default();
1620+
let mut stack = vec![*reinit];
1621+
while let Some(location) = stack.pop() {
1622+
if !visited.insert(location) {
1623+
continue;
1624+
}
1625+
if move_locations.contains(&location) {
1626+
return true;
1627+
}
1628+
stack.extend(predecessor_locations(self.body, location));
1629+
}
1630+
false
1631+
})
1632+
.collect::<Vec<Location>>();
1633+
(result, reinits_reachable)
15781634
}
15791635

15801636
pub(in crate::borrow_check) fn report_illegal_mutation_of_borrowed(

src/test/ui/borrowck/issue-83760.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
struct Struct;
2+
3+
fn test1() {
4+
let mut val = Some(Struct);
5+
while let Some(foo) = val { //~ ERROR use of moved value
6+
if true {
7+
val = None;
8+
} else {
9+
10+
}
11+
}
12+
}
13+
14+
fn test2() {
15+
let mut foo = Some(Struct);
16+
let _x = foo.unwrap();
17+
if true {
18+
foo = Some(Struct);
19+
} else {
20+
}
21+
let _y = foo; //~ ERROR use of moved value: `foo`
22+
}
23+
24+
fn test3() {
25+
let mut foo = Some(Struct);
26+
let _x = foo.unwrap();
27+
if true {
28+
foo = Some(Struct);
29+
} else if true {
30+
foo = Some(Struct);
31+
} else if true {
32+
foo = Some(Struct);
33+
} else if true {
34+
foo = Some(Struct);
35+
} else {
36+
}
37+
let _y = foo; //~ ERROR use of moved value: `foo`
38+
}
39+
40+
fn main() {}
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
error[E0382]: use of moved value
2+
--> $DIR/issue-83760.rs:5:20
3+
|
4+
LL | while let Some(foo) = val {
5+
| ^^^ value moved here, in previous iteration of loop
6+
LL | if true {
7+
LL | val = None;
8+
| ---------- this reinitialization might get skipped
9+
|
10+
= note: move occurs because value has type `Struct`, which does not implement the `Copy` trait
11+
12+
error[E0382]: use of moved value: `foo`
13+
--> $DIR/issue-83760.rs:21:14
14+
|
15+
LL | let mut foo = Some(Struct);
16+
| ------- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
17+
LL | let _x = foo.unwrap();
18+
| -------- `foo` moved due to this method call
19+
LL | if true {
20+
LL | foo = Some(Struct);
21+
| ------------------ this reinitialization might get skipped
22+
...
23+
LL | let _y = foo;
24+
| ^^^ value used here after move
25+
|
26+
note: this function takes ownership of the receiver `self`, which moves `foo`
27+
--> $SRC_DIR/core/src/option.rs:LL:COL
28+
|
29+
LL | pub const fn unwrap(self) -> T {
30+
| ^^^^
31+
32+
error[E0382]: use of moved value: `foo`
33+
--> $DIR/issue-83760.rs:37:14
34+
|
35+
LL | let mut foo = Some(Struct);
36+
| ------- move occurs because `foo` has type `Option<Struct>`, which does not implement the `Copy` trait
37+
LL | let _x = foo.unwrap();
38+
| -------- `foo` moved due to this method call
39+
...
40+
LL | let _y = foo;
41+
| ^^^ value used here after move
42+
|
43+
note: these 3 reinitializations and 1 other might get skipped
44+
--> $DIR/issue-83760.rs:30:9
45+
|
46+
LL | foo = Some(Struct);
47+
| ^^^^^^^^^^^^^^^^^^
48+
LL | } else if true {
49+
LL | foo = Some(Struct);
50+
| ^^^^^^^^^^^^^^^^^^
51+
LL | } else if true {
52+
LL | foo = Some(Struct);
53+
| ^^^^^^^^^^^^^^^^^^
54+
note: this function takes ownership of the receiver `self`, which moves `foo`
55+
--> $SRC_DIR/core/src/option.rs:LL:COL
56+
|
57+
LL | pub const fn unwrap(self) -> T {
58+
| ^^^^
59+
60+
error: aborting due to 3 previous errors
61+
62+
For more information about this error, try `rustc --explain E0382`.

0 commit comments

Comments
 (0)