Change:
// Because we stop the DFS here, we only highlight `let c = a`, // and not `let b = a`. We will of course also report an error at // `let c = a` which highlights `let b = a` as the move. <del> continue 'dfs; </del> <ins> return true; </ins> } } } // check for inits let mut any_match = false; <del> drop_flag_effects::for_location_inits( self.infcx.tcx, &self.body, self.move_data, location, |m| { if m == mpi { any_match = true; </del> <ins> for ii in &self.move_data.init_loc_map[location] { let init = self.move_data.inits[*ii]; match init.kind { InitKind::Deep | InitKind::NonPanicPathOnly => { if mpis.contains(&init.path) { any_match = true; } </ins> } <del> }, ); </del> <ins> InitKind::Shallow => { if mpi == init.path { any_match = true; } } } } </ins> if any_match { reinits.push(location); <del> continue 'dfs; </del> <ins> return true; </ins> } <ins> return false; }; </ins> <del> stack.extend(predecessor_locations(self.body, location).map(|predecessor| { let back_edge = location.dominates(predecessor, &self.dominators); (predecessor, is_back_edge || back_edge) })); </del> <ins> while let Some(location) = stack.pop() { if dfs_iter(&mut result, location, false) { continue; } let mut has_predecessor = false; predecessor_locations(self.body, location).for_each(|predecessor| { if location.dominates(predecessor, &self.dominators) { back_edge_stack.push(predecessor) } else { stack.push(predecessor); } has_predecessor = true; }); if !has_predecessor { reached_start = true; } } if (is_argument || !reached_start) && result.is_empty() { /* Process back edges (moves in future loop iterations) only if the move path is definitely initialized upon loop entry, to avoid spurious "in previous iteration" errors. During DFS, if there's a path from the error back to the start of the function with no intervening init or move, then the move path may be uninitialized at loop entry. */ while let Some(location) = back_edge_stack.pop() { if dfs_iter(&mut result, location, true) { continue; } predecessor_locations(self.body, location) .for_each(|predecessor| back_edge_stack.push(predecessor)); } </ins> } // Check if we can reach these reinits from a move location.

Discussion:
Forgetting to initialize a non-Copy variable in a loop and then referring to it gives a 'borrowed after move' error, instead of a 'possibly-uninitialized variable' error: Using a type like does result in the right error: Tested with 1.43.0 (stable) and 1.45.0-nightly.
In both Ada and D languages (in different ways, in Ada it's more refined) there's a way to tell the type system that the input variable to that bla function is not initialized and that bla must initialize it, and inside bla you can't read it before it's initialized. This means you have a way to propagate the not initialized state in function arguments, and safety is preserved. In practice this is not a big problem for Rust because in Rust you can return tuples and now Rustc is getting logic to optimize this usage pattern.
Uh, this issue is just about the error message. That this code gives an error is fine.
I'd be interested in fixing this myself, but I'm not sure where to start.
This is an issue for a command line program I'm writing. At the beginning of a function, I declare some uninitialized variables, and then I have a loop for each one. In this loop, it will read user input, and if it is valid, assign the variable that value. The loop will never complete without the variable being initialized, but rustc refuses to compile because it thinks the variable could be uninitialized after the loop.
That sounds like a different issue. This issue is about a confusing error message. The example is supposed to not compile. If the compiler rejects correct code, that's another issue.
To add context, I find this example illustrates the problem more clearly for me: the same code with the same types, just outside a loop context: