[COMMITTED 05/12] gccrs: Fix ICE with continue/break/return in while condition

arthur.cohen@embecosm.com arthur.cohen@embecosm.com
Tue Dec 23 13:10:27 GMT 2025


From: Harishankar <harishankarpp7@gmail.com>

Fixes Rust-GCC/gccrs#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 <harishankarpp7@gmail.com>
---
 gcc/rust/backend/rust-compile-expr.cc         | 13 +++-
 .../typecheck/rust-hir-type-check-expr.cc     | 16 +++--
 gcc/testsuite/rust/compile/issue-3977.rs      | 65 +++++++++++++++++++
 3 files changed, 88 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/issue-3977.rs

diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 9a9c3159048..f2bca2d6d9b 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -803,7 +803,18 @@ CompileExpr::visit (HIR::WhileLoopExpr &expr)
   ctx->add_statement (loop_begin_label_decl);
   ctx->push_loop_begin_label (loop_begin_label);
 
-  tree condition = CompileExpr::Compile (expr.get_predicate_expr (), ctx);
+  HIR::Expr &predicate = expr.get_predicate_expr ();
+  TyTy::BaseType *predicate_type = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (predicate.get_mappings ().get_hirid (),
+				      &predicate_type);
+  rust_assert (ok && predicate_type != nullptr);
+  tree condition = CompileExpr::Compile (predicate, ctx);
+  if (predicate_type->get_kind () == TyTy::TypeKind::NEVER)
+    {
+      ctx->add_statement (condition);
+      condition = boolean_true_node;
+    }
   tree exit_condition = fold_build1_loc (expr.get_locus (), TRUTH_NOT_EXPR,
 					 boolean_type_node, condition);
   tree exit_expr = Backend::exit_expression (exit_condition, expr.get_locus ());
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index b7374900e43..0188ceae02b 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -1550,18 +1550,25 @@ void
 TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
 {
   context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
-
-  TypeCheckExpr::Resolve (expr.get_predicate_expr ());
+  TyTy::BaseType *predicate_type
+    = TypeCheckExpr::Resolve (expr.get_predicate_expr ());
+  if (predicate_type->get_kind () != TyTy::TypeKind::BOOL
+      && predicate_type->get_kind () != TyTy::TypeKind::NEVER)
+    {
+      rust_error_at (expr.get_predicate_expr ().get_locus (),
+		     "expected boolean expression in %<while%> condition");
+      context->pop_loop_context ();
+      return;
+    }
   TyTy::BaseType *block_expr = TypeCheckExpr::Resolve (expr.get_loop_block ());
-
   if (!block_expr->is_unit ())
     {
       rust_error_at (expr.get_loop_block ().get_locus (),
 		     "expected %<()%> got %s",
 		     block_expr->as_string ().c_str ());
+      context->pop_loop_context ();
       return;
     }
-
   context->pop_loop_context ();
   infered = TyTy::TupleType::get_unit_type ();
 }
@@ -1611,7 +1618,6 @@ TypeCheckExpr::visit (HIR::ContinueExpr &expr)
 		     "%<continue%> outside of a loop");
       return;
     }
-
   infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
 }
 
diff --git a/gcc/testsuite/rust/compile/issue-3977.rs b/gcc/testsuite/rust/compile/issue-3977.rs
new file mode 100644
index 00000000000..ba4de544d44
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-3977.rs
@@ -0,0 +1,65 @@
+// Test for issue #3977 - ICE with continue/break/return in while condition
+
+fn diverge() -> ! {
+    loop {}
+}
+
+fn test_continue() {
+    loop {
+        while continue {}
+    }
+}
+
+fn test_break() {      
+    loop {
+        while break {}
+    }
+}
+
+fn test_return() {
+    loop {
+        while return {}
+    }
+}
+
+fn test_labeled_break() {
+    'outer: loop {
+        loop {
+            while break 'outer {}
+        }
+    }
+}
+
+fn test_labeled_continue() {
+    'outer: loop {
+        loop {
+            while continue 'outer {}
+        }
+    }
+}
+
+fn test_complex_if_else() {
+    loop {
+        while if true { continue } else { break } {}
+    }
+}
+
+fn foo() {
+    while diverge() {
+        break
+    }
+    let _x = 5;
+}
+
+fn main() {
+    // Just reference them so they're "used"
+    if false {
+        test_continue();
+        test_break();
+        test_return();
+        test_labeled_break();
+        test_labeled_continue();
+        test_complex_if_else();
+        foo();
+    }
+}
\ No newline at end of file
-- 
2.50.1



More information about the Gcc-rust mailing list