Skip to content

Commit 025b7c4

Browse files
fix ICE when passing empty block to while-loop condition
1 parent d973b35 commit 025b7c4

File tree

4 files changed

+111
-71
lines changed

4 files changed

+111
-71
lines changed

compiler/rustc_typeck/src/check/expr.rs

+24-11
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
842842
);
843843
err.span_label(lhs.span, "cannot assign to this expression");
844844

845-
let mut parent = self.tcx.hir().get_parent_node(lhs.hir_id);
845+
self.comes_from_while_condition(lhs.hir_id, |expr| {
846+
err.span_suggestion_verbose(
847+
expr.span.shrink_to_lo(),
848+
"you might have meant to use pattern destructuring",
849+
"let ".to_string(),
850+
Applicability::MachineApplicable,
851+
);
852+
});
853+
854+
err.emit();
855+
}
856+
857+
// Check if an expression `original_expr_id` comes from the condition of a while loop,
858+
// as opposed from the body of a while loop, which we can naively check by iterating
859+
// parents until we find a loop...
860+
pub(super) fn comes_from_while_condition(
861+
&self,
862+
original_expr_id: HirId,
863+
then: impl FnOnce(&hir::Expr<'_>),
864+
) {
865+
let mut parent = self.tcx.hir().get_parent_node(original_expr_id);
846866
while let Some(node) = self.tcx.hir().find(parent) {
847867
match node {
848868
hir::Node::Expr(hir::Expr {
@@ -863,21 +883,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
863883
),
864884
..
865885
}) => {
866-
// Check if our lhs is a child of the condition of a while loop
867-
let expr_is_ancestor = std::iter::successors(Some(lhs.hir_id), |id| {
886+
// Check if our original expression is a child of the condition of a while loop
887+
let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| {
868888
self.tcx.hir().find_parent_node(*id)
869889
})
870890
.take_while(|id| *id != parent)
871891
.any(|id| id == expr.hir_id);
872892
// if it is, then we have a situation like `while Some(0) = value.get(0) {`,
873893
// where `while let` was more likely intended.
874894
if expr_is_ancestor {
875-
err.span_suggestion_verbose(
876-
expr.span.shrink_to_lo(),
877-
"you might have meant to use pattern destructuring",
878-
"let ".to_string(),
879-
Applicability::MachineApplicable,
880-
);
895+
then(expr);
881896
}
882897
break;
883898
}
@@ -890,8 +905,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
890905
}
891906
}
892907
}
893-
894-
err.emit();
895908
}
896909

897910
// A generic function for checking the 'then' and 'else' clauses in an 'if'

compiler/rustc_typeck/src/check/fn_ctxt/checks.rs

+74-60
Original file line numberDiff line numberDiff line change
@@ -770,55 +770,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
770770
let prev_diverges = self.diverges.get();
771771
let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
772772

773-
let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
774-
for (pos, s) in blk.stmts.iter().enumerate() {
775-
self.check_stmt(s, blk.stmts.len() - 1 == pos);
776-
}
773+
let (ctxt, ()) =
774+
self.with_breakable_ctxt(blk.hir_id, ctxt, || {
775+
for (pos, s) in blk.stmts.iter().enumerate() {
776+
self.check_stmt(s, blk.stmts.len() - 1 == pos);
777+
}
777778

778-
// check the tail expression **without** holding the
779-
// `enclosing_breakables` lock below.
780-
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
781-
782-
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
783-
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
784-
let coerce = ctxt.coerce.as_mut().unwrap();
785-
if let Some(tail_expr_ty) = tail_expr_ty {
786-
let tail_expr = tail_expr.unwrap();
787-
let span = self.get_expr_coercion_span(tail_expr);
788-
let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
789-
coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
790-
} else {
791-
// Subtle: if there is no explicit tail expression,
792-
// that is typically equivalent to a tail expression
793-
// of `()` -- except if the block diverges. In that
794-
// case, there is no value supplied from the tail
795-
// expression (assuming there are no other breaks,
796-
// this implies that the type of the block will be
797-
// `!`).
798-
//
799-
// #41425 -- label the implicit `()` as being the
800-
// "found type" here, rather than the "expected type".
801-
if !self.diverges.get().is_always() {
802-
// #50009 -- Do not point at the entire fn block span, point at the return type
803-
// span, as it is the cause of the requirement, and
804-
// `consider_hint_about_removing_semicolon` will point at the last expression
805-
// if it were a relevant part of the error. This improves usability in editors
806-
// that highlight errors inline.
807-
let mut sp = blk.span;
808-
let mut fn_span = None;
809-
if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
810-
let ret_sp = decl.output.span();
811-
if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
812-
// HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
813-
// output would otherwise be incorrect and even misleading. Make sure
814-
// the span we're aiming at correspond to a `fn` body.
815-
if block_sp == blk.span {
816-
sp = ret_sp;
817-
fn_span = Some(ident.span);
779+
// check the tail expression **without** holding the
780+
// `enclosing_breakables` lock below.
781+
let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected));
782+
783+
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
784+
let ctxt = enclosing_breakables.find_breakable(blk.hir_id);
785+
let coerce = ctxt.coerce.as_mut().unwrap();
786+
if let Some(tail_expr_ty) = tail_expr_ty {
787+
let tail_expr = tail_expr.unwrap();
788+
let span = self.get_expr_coercion_span(tail_expr);
789+
let cause =
790+
self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
791+
coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
792+
} else {
793+
// Subtle: if there is no explicit tail expression,
794+
// that is typically equivalent to a tail expression
795+
// of `()` -- except if the block diverges. In that
796+
// case, there is no value supplied from the tail
797+
// expression (assuming there are no other breaks,
798+
// this implies that the type of the block will be
799+
// `!`).
800+
//
801+
// #41425 -- label the implicit `()` as being the
802+
// "found type" here, rather than the "expected type".
803+
if !self.diverges.get().is_always() {
804+
// #50009 -- Do not point at the entire fn block span, point at the return type
805+
// span, as it is the cause of the requirement, and
806+
// `consider_hint_about_removing_semicolon` will point at the last expression
807+
// if it were a relevant part of the error. This improves usability in editors
808+
// that highlight errors inline.
809+
let mut sp = blk.span;
810+
let mut fn_span = None;
811+
if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) {
812+
let ret_sp = decl.output.span();
813+
if let Some(block_sp) = self.parent_item_span(blk.hir_id) {
814+
// HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the
815+
// output would otherwise be incorrect and even misleading. Make sure
816+
// the span we're aiming at correspond to a `fn` body.
817+
if block_sp == blk.span {
818+
sp = ret_sp;
819+
fn_span = Some(ident.span);
820+
}
818821
}
819822
}
820-
}
821-
coerce.coerce_forced_unit(
823+
coerce.coerce_forced_unit(
822824
self,
823825
&self.misc(sp),
824826
&mut |err| {
@@ -827,19 +829,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
827829
if expected_ty == self.tcx.types.bool {
828830
// If this is caused by a missing `let` in a `while let`,
829831
// silence this redundant error, as we already emit E0070.
830-
let parent = self.tcx.hir().get_parent_node(blk.hir_id);
831-
let parent = self.tcx.hir().get_parent_node(parent);
832-
let parent = self.tcx.hir().get_parent_node(parent);
833-
let parent = self.tcx.hir().get_parent_node(parent);
834-
let parent = self.tcx.hir().get_parent_node(parent);
835-
match self.tcx.hir().find(parent) {
836-
Some(hir::Node::Expr(hir::Expr {
837-
kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
838-
..
839-
})) => {
832+
833+
// Our block must be a `assign desugar local; assignment`
834+
if let Some(hir::Node::Block(hir::Block {
835+
stmts:
836+
[hir::Stmt {
837+
kind:
838+
hir::StmtKind::Local(hir::Local {
839+
source: hir::LocalSource::AssignDesugar(_),
840+
..
841+
}),
842+
..
843+
}, hir::Stmt {
844+
kind:
845+
hir::StmtKind::Expr(hir::Expr {
846+
kind: hir::ExprKind::Assign(..),
847+
..
848+
}),
849+
..
850+
}],
851+
..
852+
})) = self.tcx.hir().find(blk.hir_id)
853+
{
854+
self.comes_from_while_condition(blk.hir_id, |_| {
840855
err.downgrade_to_delayed_bug();
841-
}
842-
_ => {}
856+
})
843857
}
844858
}
845859
}
@@ -853,9 +867,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
853867
},
854868
false,
855869
);
870+
}
856871
}
857-
}
858-
});
872+
});
859873

860874
if ctxt.may_break {
861875
// If we can break from the block, then the block's exit is always reachable
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
while {} {}
3+
//~^ ERROR mismatched types [E0308]
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/while-loop-block-cond.rs:2:11
3+
|
4+
LL | while {} {}
5+
| ^^ expected `bool`, found `()`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)