https://gcc.gnu.org/g:9551da9129b152a43657e83fa9c4a992df7863ff

commit r16-6357-g9551da9129b152a43657e83fa9c4a992df7863ff
Author: Harishankar <[email protected]>
Date:   Tue Nov 25 02:11:33 2025 +0530

    gccrs: Fix ICE with continue/break/return in while condition
    
    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 <[email protected]>

Diff:
---
 gcc/rust/backend/rust-compile-expr.cc          | 13 +++++-
 gcc/rust/typecheck/rust-hir-type-check-expr.cc | 16 +++++--
 gcc/testsuite/rust/compile/issue-3977.rs       | 65 ++++++++++++++++++++++++++
 3 files changed, 88 insertions(+), 6 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-expr.cc 
b/gcc/rust/backend/rust-compile-expr.cc
index 9a9c31590483..f2bca2d6d9b0 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 b7374900e437..0188ceae02b1 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 000000000000..ba4de544d44b
--- /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

Reply via email to