Skip to content

Commit e14f017

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 32ac8d8 commit e14f017

File tree

3 files changed

+53
-66
lines changed

3 files changed

+53
-66
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,23 @@ 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;
813+
condition = CompileExpr::Compile (predicate, ctx);
814+
if (condition == error_mark_node)
815+
{
816+
condition = boolean_true_node;
817+
}
818+
else if (predicate_type->get_kind () == TyTy::TypeKind::NEVER)
819+
{
820+
ctx->add_statement (condition);
821+
condition = boolean_true_node;
822+
}
807823
tree exit_condition = fold_build1_loc (expr.get_locus (), TRUTH_NOT_EXPR,
808824
boolean_type_node, condition);
809825
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
@@ -1552,25 +1552,11 @@ TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
15521552
context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
15531553
TyTy::BaseType *predicate_type
15541554
= TypeCheckExpr::Resolve (expr.get_predicate_expr ());
1555-
if (predicate_type->get_kind () == TyTy::TypeKind::ERROR)
1556-
{
1557-
infered = TyTy::TupleType::get_unit_type ();
1558-
context->pop_loop_context ();
1559-
return;
1560-
}
1561-
if (predicate_type->get_kind () == TyTy::TypeKind::NEVER)
1562-
{
1563-
rust_error_at (expr.get_predicate_expr ().get_locus (),
1564-
"expected boolean expression in %<while%> condition");
1565-
infered = TyTy::TupleType::get_unit_type ();
1566-
context->pop_loop_context ();
1567-
return;
1568-
}
1569-
if (predicate_type->get_kind () != TyTy::TypeKind::BOOL)
1555+
if (predicate_type->get_kind () != TyTy::TypeKind::BOOL
1556+
&& predicate_type->get_kind () != TyTy::TypeKind::NEVER)
15701557
{
15711558
rust_error_at (expr.get_predicate_expr ().get_locus (),
15721559
"expected boolean expression in %<while%> condition");
1573-
infered = TyTy::TupleType::get_unit_type ();
15741560
context->pop_loop_context ();
15751561
return;
15761562
}
@@ -1580,13 +1566,6 @@ TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
15801566
rust_error_at (expr.get_loop_block ().get_locus (),
15811567
"expected %<()%> got %s",
15821568
block_expr->as_string ().c_str ());
1583-
infered = TyTy::TupleType::get_unit_type ();
1584-
context->pop_loop_context ();
1585-
return;
1586-
}
1587-
if (block_expr->get_kind () == TyTy::TypeKind::ERROR)
1588-
{
1589-
infered = TyTy::TupleType::get_unit_type ();
15901569
context->pop_loop_context ();
15911570
return;
15921571
}
@@ -1639,7 +1618,6 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr)
16391618
"%<continue%> outside of a loop");
16401619
return;
16411620
}
1642-
16431621
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
16441622
}
16451623

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)