Skip to content

Commit f947b54

Browse files
Harishankar14P-E-P
authored andcommitted
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. Proof of fix using -fdump-tree-gimple: __attribute__((cdecl)) struct () test::main () { struct () D.107; <D.101>: <D.103>: { <D.104>: { <D.102>: goto <D.102>; // Side-effect correctly preserved if (0 != 0) goto <D.105>; else goto <D.106>; <D.106>: { } } goto <D.104>; <D.105>: } goto <D.103>; return D.107; } 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 09de0d0 commit f947b54

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
@@ -1550,18 +1550,25 @@ void
15501550
TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
15511551
{
15521552
context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
1553-
1554-
TypeCheckExpr::Resolve (expr.get_predicate_expr ());
1553+
TyTy::BaseType *predicate_type
1554+
= TypeCheckExpr::Resolve (expr.get_predicate_expr ());
1555+
if (predicate_type->get_kind () != TyTy::TypeKind::BOOL
1556+
&& predicate_type->get_kind () != TyTy::TypeKind::NEVER)
1557+
{
1558+
rust_error_at (expr.get_predicate_expr ().get_locus (),
1559+
"expected boolean expression in %<while%> condition");
1560+
context->pop_loop_context ();
1561+
return;
1562+
}
15551563
TyTy::BaseType *block_expr = TypeCheckExpr::Resolve (expr.get_loop_block ());
1556-
15571564
if (!block_expr->is_unit ())
15581565
{
15591566
rust_error_at (expr.get_loop_block ().get_locus (),
15601567
"expected %<()%> got %s",
15611568
block_expr->as_string ().c_str ());
1569+
context->pop_loop_context ();
15621570
return;
15631571
}
1564-
15651572
context->pop_loop_context ();
15661573
infered = TyTy::TupleType::get_unit_type ();
15671574
}
@@ -1611,7 +1618,6 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr)
16111618
"%<continue%> outside of a loop");
16121619
return;
16131620
}
1614-
16151621
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
16161622
}
16171623

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)