Skip to content

Commit c2a70d2

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. gcc/testsuite/ChangeLog: * rust/compile/issue-3977.rs: New test. Signed-off-by: Harishankar <harishankarpp7@gmail.com>
1 parent e90c207 commit c2a70d2

File tree

3 files changed

+48
-66
lines changed

3 files changed

+48
-66
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: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,25 +1543,11 @@ TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
15431543
context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
15441544
TyTy::BaseType *predicate_type
15451545
= TypeCheckExpr::Resolve (expr.get_predicate_expr ());
1546-
if (predicate_type->get_kind () == TyTy::TypeKind::ERROR)
1547-
{
1548-
infered = TyTy::TupleType::get_unit_type ();
1549-
context->pop_loop_context ();
1550-
return;
1551-
}
1552-
if (predicate_type->get_kind () == TyTy::TypeKind::NEVER)
1553-
{
1554-
rust_error_at (expr.get_predicate_expr ().get_locus (),
1555-
"expected boolean expression in %<while%> condition");
1556-
infered = TyTy::TupleType::get_unit_type ();
1557-
context->pop_loop_context ();
1558-
return;
1559-
}
1560-
if (predicate_type->get_kind () != TyTy::TypeKind::BOOL)
1546+
if (predicate_type->get_kind () != TyTy::TypeKind::BOOL
1547+
&& predicate_type->get_kind () != TyTy::TypeKind::NEVER)
15611548
{
15621549
rust_error_at (expr.get_predicate_expr ().get_locus (),
15631550
"expected boolean expression in %<while%> condition");
1564-
infered = TyTy::TupleType::get_unit_type ();
15651551
context->pop_loop_context ();
15661552
return;
15671553
}
@@ -1571,13 +1557,6 @@ TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
15711557
rust_error_at (expr.get_loop_block ().get_locus (),
15721558
"expected %<()%> got %s",
15731559
block_expr->as_string ().c_str ());
1574-
infered = TyTy::TupleType::get_unit_type ();
1575-
context->pop_loop_context ();
1576-
return;
1577-
}
1578-
if (block_expr->get_kind () == TyTy::TypeKind::ERROR)
1579-
{
1580-
infered = TyTy::TupleType::get_unit_type ();
15811560
context->pop_loop_context ();
15821561
return;
15831562
}
@@ -1630,7 +1609,6 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr)
16301609
"%<continue%> outside of a loop");
16311610
return;
16321611
}
1633-
16341612
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
16351613
}
16361614

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,65 @@
1-
// Test for issue #3977: ICE in fold_convert_loc with diverging expressions in while conditions
2-
// { dg-excess-errors "expected boolean expression" }
1+
// Test for issue #3977 - ICE with continue/break/return in while condition
2+
3+
fn diverge() -> ! {
4+
loop {}
5+
}
36

47
fn test_continue() {
58
loop {
6-
while continue {} // { dg-error "expected boolean expression in 'while' condition" }
9+
while continue {}
710
}
811
}
912

10-
fn test_break() {
13+
fn test_break() {
1114
loop {
12-
while break {} // { dg-error "expected boolean expression in 'while' condition" }
15+
while break {}
1316
}
1417
}
1518

1619
fn test_return() {
17-
while return {} // { dg-error "expected boolean expression in 'while' condition" }
18-
}
19-
20-
fn test_return_with_value() {
21-
while return 42 {} // { dg-error "expected boolean expression in 'while' condition" }
22-
}
23-
24-
fn test_infinite_loop() {
25-
while loop {} {} // { dg-error "expected boolean expression in 'while' condition" }
26-
}
27-
28-
fn test_nested_continue() {
2920
loop {
30-
loop {
31-
while continue {} // { dg-error "expected boolean expression in 'while' condition" }
32-
}
21+
while return {}
3322
}
3423
}
3524

3625
fn test_labeled_break() {
3726
'outer: loop {
38-
while break 'outer {} // { dg-error "expected boolean expression in 'while' condition" }
27+
loop {
28+
while break 'outer {}
29+
}
3930
}
4031
}
4132

4233
fn test_labeled_continue() {
4334
'outer: loop {
44-
while continue 'outer {} // { dg-error "expected boolean expression in 'while' condition" }
35+
loop {
36+
while continue 'outer {}
37+
}
4538
}
4639
}
4740

48-
// Valid cases that should NOT error
49-
fn test_valid_boolean() {
50-
while true {}
51-
while false {}
52-
}
53-
54-
fn test_valid_expression() {
55-
let x = 5;
56-
while x > 0 {}
41+
fn test_complex_if_else() {
42+
loop {
43+
while if true { continue } else { break } {}
44+
}
5745
}
5846

59-
fn test_valid_function_call() -> bool {
60-
fn condition() -> bool { true }
61-
while condition() {}
62-
true
47+
fn foo() {
48+
while diverge() {
49+
break
50+
}
51+
let _x = 5;
6352
}
6453

6554
fn main() {
66-
test_valid_boolean();
67-
test_valid_expression();
68-
test_valid_function_call();
69-
70-
// The error cases would cause compilation to fail
71-
// so they're tested separately
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+
}
7265
}

0 commit comments

Comments
 (0)