Skip to content

Commit 06051e0

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 06051e0

File tree

3 files changed

+55
-66
lines changed

3 files changed

+55
-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: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,25 +1543,12 @@ 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 ();
1551+
infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
15651552
context->pop_loop_context ();
15661553
return;
15671554
}
@@ -1571,13 +1558,6 @@ TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
15711558
rust_error_at (expr.get_loop_block ().get_locus (),
15721559
"expected %<()%> got %s",
15731560
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 ();
15811561
context->pop_loop_context ();
15821562
return;
15831563
}
@@ -1628,9 +1608,9 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr)
16281608
{
16291609
rust_error_at (expr.get_locus (), ErrorCode::E0268,
16301610
"%<continue%> outside of a loop");
1611+
infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
16311612
return;
16321613
}
1633-
16341614
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
16351615
}
16361616

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)