Skip to content

Commit 6dc08b9

Browse files
committed
Auto merge of rust-lang#87998 - nneonneo:master, r=oli-obk
Avoid spurious "previous iteration of loop" errors Only follow backwards edges during `get_moved_indexes` if the move path is definitely initialized at loop entry. Otherwise, the error occurred prior to the loop, so we ignore the backwards edges to avoid generating misleading "value moved here, in previous iteration of loop" errors. This patch also slightly improves the analysis of inits, including `NonPanicPathOnly` initializations (which are ignored by `drop_flag_effects::for_location_inits`). This is required for the definite initialization analysis, but may also help find certain skipped reinits in rare cases. Patch passes all non-ignored src/test/ui testcases. Fixes rust-lang#72649.
2 parents c6007fd + 6ff5b47 commit 6dc08b9

File tree

3 files changed

+212
-28
lines changed

3 files changed

+212
-28
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+80-28
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ use rustc_middle::mir::{
1010
ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
1111
};
1212
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
13-
use rustc_mir_dataflow::drop_flag_effects;
14-
use rustc_mir_dataflow::move_paths::{MoveOutIndex, MovePathIndex};
13+
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
1514
use rustc_span::source_map::DesugaringKind;
1615
use rustc_span::symbol::sym;
1716
use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
@@ -1531,25 +1530,45 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15311530
}
15321531
}
15331532

1533+
let mut mpis = vec![mpi];
1534+
let move_paths = &self.move_data.move_paths;
1535+
mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1536+
15341537
let mut stack = Vec::new();
1535-
stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
1536-
let is_back_edge = location.dominates(predecessor, &self.dominators);
1537-
(predecessor, is_back_edge)
1538-
}));
1538+
let mut back_edge_stack = Vec::new();
1539+
1540+
predecessor_locations(self.body, location).for_each(|predecessor| {
1541+
if location.dominates(predecessor, &self.dominators) {
1542+
back_edge_stack.push(predecessor)
1543+
} else {
1544+
stack.push(predecessor);
1545+
}
1546+
});
1547+
1548+
let mut reached_start = false;
1549+
1550+
/* Check if the mpi is initialized as an argument */
1551+
let mut is_argument = false;
1552+
for arg in self.body.args_iter() {
1553+
let path = self.move_data.rev_lookup.find_local(arg);
1554+
if mpis.contains(&path) {
1555+
is_argument = true;
1556+
}
1557+
}
15391558

15401559
let mut visited = FxHashSet::default();
15411560
let mut move_locations = FxHashSet::default();
15421561
let mut reinits = vec![];
15431562
let mut result = vec![];
15441563

1545-
'dfs: while let Some((location, is_back_edge)) = stack.pop() {
1564+
let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
15461565
debug!(
15471566
"report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
15481567
location, is_back_edge
15491568
);
15501569

15511570
if !visited.insert(location) {
1552-
continue;
1571+
return true;
15531572
}
15541573

15551574
// check for moves
@@ -1568,10 +1587,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15681587
// worry about the other case: that is, if there is a move of a.b.c, it is already
15691588
// marked as a move of a.b and a as well, so we will generate the correct errors
15701589
// there.
1571-
let mut mpis = vec![mpi];
1572-
let move_paths = &self.move_data.move_paths;
1573-
mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1574-
15751590
for moi in &self.move_data.loc_map[location] {
15761591
debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
15771592
let path = self.move_data.moves[*moi].path;
@@ -1599,33 +1614,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15991614
// Because we stop the DFS here, we only highlight `let c = a`,
16001615
// and not `let b = a`. We will of course also report an error at
16011616
// `let c = a` which highlights `let b = a` as the move.
1602-
continue 'dfs;
1617+
return true;
16031618
}
16041619
}
16051620
}
16061621

16071622
// check for inits
16081623
let mut any_match = false;
1609-
drop_flag_effects::for_location_inits(
1610-
self.infcx.tcx,
1611-
&self.body,
1612-
self.move_data,
1613-
location,
1614-
|m| {
1615-
if m == mpi {
1616-
any_match = true;
1624+
for ii in &self.move_data.init_loc_map[location] {
1625+
let init = self.move_data.inits[*ii];
1626+
match init.kind {
1627+
InitKind::Deep | InitKind::NonPanicPathOnly => {
1628+
if mpis.contains(&init.path) {
1629+
any_match = true;
1630+
}
16171631
}
1618-
},
1619-
);
1632+
InitKind::Shallow => {
1633+
if mpi == init.path {
1634+
any_match = true;
1635+
}
1636+
}
1637+
}
1638+
}
16201639
if any_match {
16211640
reinits.push(location);
1622-
continue 'dfs;
1641+
return true;
16231642
}
1643+
return false;
1644+
};
16241645

1625-
stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
1626-
let back_edge = location.dominates(predecessor, &self.dominators);
1627-
(predecessor, is_back_edge || back_edge)
1628-
}));
1646+
while let Some(location) = stack.pop() {
1647+
if dfs_iter(&mut result, location, false) {
1648+
continue;
1649+
}
1650+
1651+
let mut has_predecessor = false;
1652+
predecessor_locations(self.body, location).for_each(|predecessor| {
1653+
if location.dominates(predecessor, &self.dominators) {
1654+
back_edge_stack.push(predecessor)
1655+
} else {
1656+
stack.push(predecessor);
1657+
}
1658+
has_predecessor = true;
1659+
});
1660+
1661+
if !has_predecessor {
1662+
reached_start = true;
1663+
}
1664+
}
1665+
if (is_argument || !reached_start) && result.is_empty() {
1666+
/* Process back edges (moves in future loop iterations) only if
1667+
the move path is definitely initialized upon loop entry,
1668+
to avoid spurious "in previous iteration" errors.
1669+
During DFS, if there's a path from the error back to the start
1670+
of the function with no intervening init or move, then the
1671+
move path may be uninitialized at loop entry.
1672+
*/
1673+
while let Some(location) = back_edge_stack.pop() {
1674+
if dfs_iter(&mut result, location, true) {
1675+
continue;
1676+
}
1677+
1678+
predecessor_locations(self.body, location)
1679+
.for_each(|predecessor| back_edge_stack.push(predecessor));
1680+
}
16291681
}
16301682

16311683
// Check if we can reach these reinits from a move location.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Regression test for issue #72649
2+
// Tests that we don't emit spurious
3+
// 'value moved in previous iteration of loop' message
4+
5+
struct NonCopy;
6+
7+
fn good() {
8+
loop {
9+
let value = NonCopy{};
10+
let _used = value;
11+
}
12+
}
13+
14+
fn moved_here_1() {
15+
loop {
16+
let value = NonCopy{};
17+
//~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
18+
let _used = value;
19+
//~^ NOTE value moved here
20+
let _used2 = value; //~ ERROR use of moved value: `value`
21+
//~^ NOTE value used here after move
22+
}
23+
}
24+
25+
fn moved_here_2() {
26+
let value = NonCopy{};
27+
//~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
28+
loop {
29+
let _used = value;
30+
//~^ NOTE value moved here
31+
loop {
32+
let _used2 = value; //~ ERROR use of moved value: `value`
33+
//~^ NOTE value used here after move
34+
}
35+
}
36+
}
37+
38+
fn moved_loop_1() {
39+
let value = NonCopy{};
40+
//~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
41+
loop {
42+
let _used = value; //~ ERROR use of moved value: `value`
43+
//~^ NOTE value moved here, in previous iteration of loop
44+
}
45+
}
46+
47+
fn moved_loop_2() {
48+
let mut value = NonCopy{};
49+
//~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
50+
let _used = value;
51+
value = NonCopy{};
52+
loop {
53+
let _used2 = value; //~ ERROR use of moved value: `value`
54+
//~^ NOTE value moved here, in previous iteration of loop
55+
}
56+
}
57+
58+
fn uninit_1() {
59+
loop {
60+
let value: NonCopy;
61+
let _used = value; //~ ERROR use of possibly-uninitialized variable: `value`
62+
//~^ NOTE use of possibly-uninitialized `value`
63+
}
64+
}
65+
66+
fn uninit_2() {
67+
let mut value: NonCopy;
68+
loop {
69+
let _used = value; //~ ERROR use of possibly-uninitialized variable: `value`
70+
//~^ NOTE use of possibly-uninitialized `value`
71+
}
72+
}
73+
74+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
error[E0382]: use of moved value: `value`
2+
--> $DIR/issue-72649-uninit-in-loop.rs:20:22
3+
|
4+
LL | let value = NonCopy{};
5+
| ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
6+
LL |
7+
LL | let _used = value;
8+
| ----- value moved here
9+
LL |
10+
LL | let _used2 = value;
11+
| ^^^^^ value used here after move
12+
13+
error[E0382]: use of moved value: `value`
14+
--> $DIR/issue-72649-uninit-in-loop.rs:32:26
15+
|
16+
LL | let value = NonCopy{};
17+
| ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
18+
...
19+
LL | let _used = value;
20+
| ----- value moved here
21+
...
22+
LL | let _used2 = value;
23+
| ^^^^^ value used here after move
24+
25+
error[E0382]: use of moved value: `value`
26+
--> $DIR/issue-72649-uninit-in-loop.rs:42:21
27+
|
28+
LL | let value = NonCopy{};
29+
| ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
30+
...
31+
LL | let _used = value;
32+
| ^^^^^ value moved here, in previous iteration of loop
33+
34+
error[E0382]: use of moved value: `value`
35+
--> $DIR/issue-72649-uninit-in-loop.rs:53:22
36+
|
37+
LL | let mut value = NonCopy{};
38+
| --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait
39+
...
40+
LL | let _used2 = value;
41+
| ^^^^^ value moved here, in previous iteration of loop
42+
43+
error[E0381]: use of possibly-uninitialized variable: `value`
44+
--> $DIR/issue-72649-uninit-in-loop.rs:61:21
45+
|
46+
LL | let _used = value;
47+
| ^^^^^ use of possibly-uninitialized `value`
48+
49+
error[E0381]: use of possibly-uninitialized variable: `value`
50+
--> $DIR/issue-72649-uninit-in-loop.rs:69:21
51+
|
52+
LL | let _used = value;
53+
| ^^^^^ use of possibly-uninitialized `value`
54+
55+
error: aborting due to 6 previous errors
56+
57+
Some errors have detailed explanations: E0381, E0382.
58+
For more information about an error, try `rustc --explain E0381`.

0 commit comments

Comments
 (0)