]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Fix ICE with continue/break/return in while condition
authorHarishankar <harishankarpp7@gmail.com>
Mon, 24 Nov 2025 20:41:33 +0000 (02:11 +0530)
committerArthur Cohen <arthur.cohen@embecosm.com>
Tue, 23 Dec 2025 11:16:48 +0000 (12:16 +0100)
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
gcc/rust/typecheck/rust-hir-type-check-expr.cc
gcc/testsuite/rust/compile/issue-3977.rs [new file with mode: 0644]

index 9a9c315904839cd9385b1b7ba2aa27a87715c621..f2bca2d6d9b0928992e6ad928f39d2edb6e8ad8a 100644 (file)
@@ -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 ());
index b7374900e43759a441e99401c8ca64b2f44a6daf..0188ceae02b18fc9a3c6f95e3317a164031855e7 100644 (file)
@@ -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 (file)
index 0000000..ba4de54
--- /dev/null
@@ -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