Skip to content

Commit c999c67

Browse files
committed
Fix ICE with continue/break/return in while condition
Fixes #3977 The predicate expression must be evaluated before type checking to ensure side effects occur even when the predicate has never type. This prevents skipping function calls, panics, or other side effects in diverging predicates. gcc/rust/ChangeLog: * backend/rust-compile-expr.cc (CompileExpr::visit): Always evaluate predicate expression before checking for never type to preserve side effects in while loop conditions. * typecheck/rust-hir-type-check-expr.cc: Update handling of break/continue. gcc/testsuite/ChangeLog: * rust/compile/issue-3977.rs: New test. Signed-off-by: Harishankar <[email protected]>
1 parent 7699f7f commit c999c67

File tree

3 files changed

+88
-6
lines changed

3 files changed

+88
-6
lines changed

gcc/rust/backend/rust-compile-expr.cc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,18 @@ CompileExpr::visit (HIR::WhileLoopExpr &expr)
803803
ctx->add_statement (loop_begin_label_decl);
804804
ctx->push_loop_begin_label (loop_begin_label);
805805

806-
tree condition = CompileExpr::Compile (expr.get_predicate_expr (), ctx);
806+
HIR::Expr &predicate = expr.get_predicate_expr ();
807+
TyTy::BaseType *predicate_type = nullptr;
808+
bool ok
809+
= ctx->get_tyctx ()->lookup_type (predicate.get_mappings ().get_hirid (),
810+
&predicate_type);
811+
rust_assert (ok && predicate_type != nullptr);
812+
tree condition = CompileExpr::Compile (predicate, ctx);
813+
if (predicate_type->get_kind () == TyTy::TypeKind::NEVER)
814+
{
815+
ctx->add_statement (condition);
816+
condition = boolean_true_node;
817+
}
807818
tree exit_condition = fold_build1_loc (expr.get_locus (), TRUTH_NOT_EXPR,
808819
boolean_type_node, condition);
809820
tree exit_expr = Backend::exit_expression (exit_condition, expr.get_locus ());

gcc/rust/typecheck/rust-hir-type-check-expr.cc

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,18 +1541,25 @@ void
15411541
TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
15421542
{
15431543
context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
1544-
1545-
TypeCheckExpr::Resolve (expr.get_predicate_expr ());
1544+
TyTy::BaseType *predicate_type
1545+
= TypeCheckExpr::Resolve (expr.get_predicate_expr ());
1546+
if (predicate_type->get_kind () != TyTy::TypeKind::BOOL
1547+
&& predicate_type->get_kind () != TyTy::TypeKind::NEVER)
1548+
{
1549+
rust_error_at (expr.get_predicate_expr ().get_locus (),
1550+
"expected boolean expression in %<while%> condition");
1551+
context->pop_loop_context ();
1552+
return;
1553+
}
15461554
TyTy::BaseType *block_expr = TypeCheckExpr::Resolve (expr.get_loop_block ());
1547-
15481555
if (!block_expr->is_unit ())
15491556
{
15501557
rust_error_at (expr.get_loop_block ().get_locus (),
15511558
"expected %<()%> got %s",
15521559
block_expr->as_string ().c_str ());
1560+
context->pop_loop_context ();
15531561
return;
15541562
}
1555-
15561563
context->pop_loop_context ();
15571564
infered = TyTy::TupleType::get_unit_type ();
15581565
}
@@ -1602,7 +1609,6 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr)
16021609
"%<continue%> outside of a loop");
16031610
return;
16041611
}
1605-
16061612
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
16071613
}
16081614

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Test for issue #3977 - ICE with continue/break/return in while condition
2+
3+
fn diverge() -> ! {
4+
loop {}
5+
}
6+
7+
fn test_continue() {
8+
loop {
9+
while continue {}
10+
}
11+
}
12+
13+
fn test_break() {
14+
loop {
15+
while break {}
16+
}
17+
}
18+
19+
fn test_return() {
20+
loop {
21+
while return {}
22+
}
23+
}
24+
25+
fn test_labeled_break() {
26+
'outer: loop {
27+
loop {
28+
while break 'outer {}
29+
}
30+
}
31+
}
32+
33+
fn test_labeled_continue() {
34+
'outer: loop {
35+
loop {
36+
while continue 'outer {}
37+
}
38+
}
39+
}
40+
41+
fn test_complex_if_else() {
42+
loop {
43+
while if true { continue } else { break } {}
44+
}
45+
}
46+
47+
fn foo() {
48+
while diverge() {
49+
break
50+
}
51+
let _x = 5;
52+
}
53+
54+
fn main() {
55+
// Just reference them so they're "used"
56+
if false {
57+
test_continue();
58+
test_break();
59+
test_return();
60+
test_labeled_break();
61+
test_labeled_continue();
62+
test_complex_if_else();
63+
foo();
64+
}
65+
}

0 commit comments

Comments
 (0)