Skip to content

Commit 6ff5b47

Browse files
committed
Fix issue #72649: 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.
1 parent 626649f commit 6ff5b47

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};
@@ -1516,25 +1515,45 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15161515
}
15171516
}
15181517

1518+
let mut mpis = vec![mpi];
1519+
let move_paths = &self.move_data.move_paths;
1520+
mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1521+
15191522
let mut stack = Vec::new();
1520-
stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
1521-
let is_back_edge = location.dominates(predecessor, &self.dominators);
1522-
(predecessor, is_back_edge)
1523-
}));
1523+
let mut back_edge_stack = Vec::new();
1524+
1525+
predecessor_locations(self.body, location).for_each(|predecessor| {
1526+
if location.dominates(predecessor, &self.dominators) {
1527+
back_edge_stack.push(predecessor)
1528+
} else {
1529+
stack.push(predecessor);
1530+
}
1531+
});
1532+
1533+
let mut reached_start = false;
1534+
1535+
/* Check if the mpi is initialized as an argument */
1536+
let mut is_argument = false;
1537+
for arg in self.body.args_iter() {
1538+
let path = self.move_data.rev_lookup.find_local(arg);
1539+
if mpis.contains(&path) {
1540+
is_argument = true;
1541+
}
1542+
}
15241543

15251544
let mut visited = FxHashSet::default();
15261545
let mut move_locations = FxHashSet::default();
15271546
let mut reinits = vec![];
15281547
let mut result = vec![];
15291548

1530-
'dfs: while let Some((location, is_back_edge)) = stack.pop() {
1549+
let mut dfs_iter = |result: &mut Vec<MoveSite>, location: Location, is_back_edge: bool| {
15311550
debug!(
15321551
"report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})",
15331552
location, is_back_edge
15341553
);
15351554

15361555
if !visited.insert(location) {
1537-
continue;
1556+
return true;
15381557
}
15391558

15401559
// check for moves
@@ -1553,10 +1572,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15531572
// worry about the other case: that is, if there is a move of a.b.c, it is already
15541573
// marked as a move of a.b and a as well, so we will generate the correct errors
15551574
// there.
1556-
let mut mpis = vec![mpi];
1557-
let move_paths = &self.move_data.move_paths;
1558-
mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi));
1559-
15601575
for moi in &self.move_data.loc_map[location] {
15611576
debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi);
15621577
let path = self.move_data.moves[*moi].path;
@@ -1584,33 +1599,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15841599
// Because we stop the DFS here, we only highlight `let c = a`,
15851600
// and not `let b = a`. We will of course also report an error at
15861601
// `let c = a` which highlights `let b = a` as the move.
1587-
continue 'dfs;
1602+
return true;
15881603
}
15891604
}
15901605
}
15911606

15921607
// check for inits
15931608
let mut any_match = false;
1594-
drop_flag_effects::for_location_inits(
1595-
self.infcx.tcx,
1596-
&self.body,
1597-
self.move_data,
1598-
location,
1599-
|m| {
1600-
if m == mpi {
1601-
any_match = true;
1609+
for ii in &self.move_data.init_loc_map[location] {
1610+
let init = self.move_data.inits[*ii];
1611+
match init.kind {
1612+
InitKind::Deep | InitKind::NonPanicPathOnly => {
1613+
if mpis.contains(&init.path) {
1614+
any_match = true;
1615+
}
16021616
}
1603-
},
1604-
);
1617+
InitKind::Shallow => {
1618+
if mpi == init.path {
1619+
any_match = true;
1620+
}
1621+
}
1622+
}
1623+
}
16051624
if any_match {
16061625
reinits.push(location);
1607-
continue 'dfs;
1626+
return true;
16081627
}
1628+
return false;
1629+
};
16091630

1610-
stack.extend(predecessor_locations(self.body, location).map(|predecessor| {
1611-
let back_edge = location.dominates(predecessor, &self.dominators);
1612-
(predecessor, is_back_edge || back_edge)
1613-
}));
1631+
while let Some(location) = stack.pop() {
1632+
if dfs_iter(&mut result, location, false) {
1633+
continue;
1634+
}
1635+
1636+
let mut has_predecessor = false;
1637+
predecessor_locations(self.body, location).for_each(|predecessor| {
1638+
if location.dominates(predecessor, &self.dominators) {
1639+
back_edge_stack.push(predecessor)
1640+
} else {
1641+
stack.push(predecessor);
1642+
}
1643+
has_predecessor = true;
1644+
});
1645+
1646+
if !has_predecessor {
1647+
reached_start = true;
1648+
}
1649+
}
1650+
if (is_argument || !reached_start) && result.is_empty() {
1651+
/* Process back edges (moves in future loop iterations) only if
1652+
the move path is definitely initialized upon loop entry,
1653+
to avoid spurious "in previous iteration" errors.
1654+
During DFS, if there's a path from the error back to the start
1655+
of the function with no intervening init or move, then the
1656+
move path may be uninitialized at loop entry.
1657+
*/
1658+
while let Some(location) = back_edge_stack.pop() {
1659+
if dfs_iter(&mut result, location, true) {
1660+
continue;
1661+
}
1662+
1663+
predecessor_locations(self.body, location)
1664+
.for_each(|predecessor| back_edge_stack.push(predecessor));
1665+
}
16141666
}
16151667

16161668
// 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)