]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gccrs: Support labeled block value breaks in HIR lowering
authorIslam-Imad <islamimad404@gmail.com>
Tue, 21 Apr 2026 13:18:40 +0000 (15:18 +0200)
committerArthur Cohen <arthur.cohen@embecosm.com>
Mon, 1 Jun 2026 13:24:53 +0000 (15:24 +0200)
This change implements backend lowering support for Rust labeled blocks.

Previously, labeled blocks were rejected in "CompileExpr::visit(BlockExpr)"
as unsupported. With this patch, labeled blocks are lowered by introducing
the following :-

1. A backend "LABEL_DECL" used as the jump target for "break 'label".
2. A temporary "Bvariable" used to hold the block’s resulting value.

gcc/rust/ChangeLog:

* backend/rust-compile-expr.cc (CompileExpr::visit): Lower labeled block.
(CompileExpr::construct_block_label): Utility function to construct block label.
(CompileExpr::lookup_label): Utility function to lookup label.
(CompileExpr::lookup_temp_var): Utility function to lookup block temp variables.
(CompileExpr::resolve_util): Utility to resolve NodeId to HirId.
* backend/rust-compile-expr.h: Header functions.
* resolve/rust-late-name-resolver-2.0.cc (Late::visit): Fix label resolution.

gcc/testsuite/ChangeLog:

* rust/execute/cf-labeled-block.rs: New test.

Signed-off-by: Islam-Imad <islamimad404@gmail.com>
gcc/rust/backend/rust-compile-expr.cc
gcc/rust/backend/rust-compile-expr.h
gcc/rust/resolve/rust-late-name-resolver-2.0.cc
gcc/testsuite/rust/execute/cf-labeled-block.rs [new file with mode: 0644]

index 6b21a7745771a182200a394da39be261ff094e35..1c69d511e52fc9f1317dd5459604f6f2363c4192 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "rust-compile-expr.h"
 #include "rust-backend.h"
+#include "rust-compile-context.h"
 #include "rust-compile-type.h"
 #include "rust-compile-struct-field-expr.h"
 #include "rust-compile-pattern.h"
@@ -32,6 +33,7 @@
 #include "realmpfr.h"
 #include "convert.h"
 #include "print-tree.h"
+#include "rust-hir-bound.h"
 #include "rust-hir-expr.h"
 #include "rust-system.h"
 #include "rust-tree.h"
@@ -436,12 +438,6 @@ CompileExpr::visit (HIR::IfExprConseqElse &expr)
 void
 CompileExpr::visit (HIR::BlockExpr &expr)
 {
-  if (expr.has_label ())
-    {
-      rust_error_at (expr.get_locus (), "labeled blocks are not supported");
-      return;
-    }
-
   TyTy::BaseType *block_tyty = nullptr;
   if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
                                       &block_tyty))
@@ -454,18 +450,31 @@ CompileExpr::visit (HIR::BlockExpr &expr)
   fncontext fnctx = ctx->peek_fn ();
   tree enclosing_scope = ctx->peek_enclosing_scope ();
   tree block_type = TyTyResolveCompile::compile (ctx, block_tyty);
+  tree block_label = NULL_TREE;
 
   bool is_address_taken = false;
   tree ret_var_stmt = nullptr;
   tmp = Backend::temporary_variable (fnctx.fndecl, enclosing_scope, block_type,
                                     NULL, is_address_taken, expr.get_locus (),
                                     &ret_var_stmt);
+
   ctx->add_statement (ret_var_stmt);
 
+  if (expr.has_label ())
+    {
+      ctx->insert_var_decl (
+       expr.get_label ().get_lifetime ().get_mappings ().get_hirid (), tmp);
+      block_label = construct_block_label (expr);
+    }
+
   auto block_stmt = CompileBlock::compile (expr, ctx, tmp);
   rust_assert (TREE_CODE (block_stmt) == BIND_EXPR);
   ctx->add_statement (block_stmt);
 
+  if (block_label != NULL_TREE)
+    {
+      ctx->add_statement (block_label);
+    }
   translated = Backend::var_expression (tmp, expr.get_locus ());
 }
 
@@ -853,11 +862,25 @@ CompileExpr::visit (HIR::WhileLoopExpr &expr)
 void
 CompileExpr::visit (HIR::BreakExpr &expr)
 {
+  if (expr.has_break_expr () && expr.has_label ())
+    {
+      HIR::Lifetime &label = expr.get_label ();
+      auto tvar = lookup_temp_var (label.get_mappings ().get_nodeid ());
+      tree value = CompileExpr::Compile (expr.get_expr (), ctx);
+      tree assign
+       = Backend::assignment_statement (tvar->get_tree (label.get_locus ()),
+                                        value, label.get_locus ());
+      tree block_label = lookup_label (label.get_mappings ().get_nodeid ());
+      tree go_to = Backend::goto_statement (block_label, label.get_locus ());
+      ctx->add_statement (assign);
+      ctx->add_statement (go_to);
+      return;
+    }
   if (expr.has_break_expr ())
     {
       tree compiled_expr = CompileExpr::Compile (expr.get_expr (), ctx);
-
       translated = error_mark_node;
+
       if (!ctx->have_loop_context ())
        return;
 
@@ -891,7 +914,6 @@ CompileExpr::visit (HIR::BreakExpr &expr)
            expr.get_label ().get_mappings ().as_string ().c_str ());
          return;
        }
-
       tl::optional<HirId> hid
        = ctx->get_mappings ().lookup_node_to_hir (resolved_node_id);
       if (!hid.has_value ())
@@ -2758,5 +2780,57 @@ CompileExpr::generate_possible_fn_trait_call (HIR::CallExpr &expr,
   return true;
 }
 
+tree
+CompileExpr::construct_block_label (HIR::BlockExpr &expr)
+{
+  if (expr.has_label ())
+    {
+      fncontext fnctx = ctx->peek_fn ();
+      HIR::LoopLabel &label = expr.get_label ();
+      std::string label_name = label.get_lifetime ().get_name ();
+      HirId label_id = label.get_lifetime ().get_mappings ().get_hirid ();
+      tree label_decl
+       = Backend::label (fnctx.fndecl, label_name, label.get_locus ());
+      tree label_expr = Backend::label_definition_statement (label_decl);
+      ctx->insert_label_decl (label_id, label_decl);
+      return label_expr;
+    }
+  return NULL_TREE;
+}
+
+tree
+CompileExpr::lookup_label (NodeId to_be_resolved)
+{
+  HirId ref = resolve_NodeId (to_be_resolved);
+  tree label = NULL_TREE;
+  rust_assert (ctx->lookup_label_decl (ref, &label)
+              && "failed to lookup a label");
+  return label;
+}
+
+Bvariable *
+CompileExpr::lookup_temp_var (NodeId to_be_resolved)
+{
+  HirId ref = resolve_NodeId (to_be_resolved);
+  Bvariable *ltemp = nullptr;
+  rust_assert (ctx->lookup_var_decl (ref, &ltemp)
+              && "failed to lookup a temp var");
+  return ltemp;
+}
+
+HirId
+CompileExpr::resolve_NodeId (NodeId to_be_resolved)
+{
+  auto &nr_ctx
+    = Resolver2_0::ImmutableNameResolutionContext::get ().resolver ();
+
+  NodeId resolved_node_id;
+  resolved_node_id = nr_ctx.lookup (to_be_resolved).value ();
+
+  HirId ref
+    = ctx->get_mappings ().lookup_node_to_hir (resolved_node_id).value ();
+  return ref;
+}
+
 } // namespace Compile
 } // namespace Rust
index 11c99ed024a3b7417e9a3fd6db60e0db26d172b0..7764448d86338e514fc7098607670fbeac2f3f75 100644 (file)
@@ -20,6 +20,8 @@
 #define RUST_COMPILE_EXPR
 
 #include "rust-compile-base.h"
+#include "rust-gcc.h"
+#include "rust-hir-expr.h"
 #include "rust-hir-visitor.h"
 
 namespace Rust {
@@ -151,6 +153,11 @@ protected:
   bool generate_possible_fn_trait_call (HIR::CallExpr &expr, tree receiver,
                                        tree *result);
 
+  tree construct_block_label (HIR::BlockExpr &expr);
+  tree lookup_label (NodeId to_be_resolved);
+  Bvariable *lookup_temp_var (NodeId to_be_resolved);
+  HirId resolve_NodeId (NodeId to_be_resolved);
+
 private:
   CompileExpr (Context *ctx);
 
index 05b2b56b0df6f0043ed326774e6d8da47d60daba..f0db8277598d368bf6938eaea2b0d4875849c063 100644 (file)
@@ -299,6 +299,9 @@ Late::visit (AST::BreakExpr &expr)
 void
 Late::visit (AST::LoopLabel &label)
 {
+  auto resolved = ctx.lookup (label.get_lifetime ().get_node_id ());
+  if (resolved.has_value ())
+    return;
   auto &lifetime = label.get_lifetime ();
   ctx.labels.insert (Identifier (lifetime.as_string (), lifetime.get_locus ()),
                     lifetime.get_node_id ());
diff --git a/gcc/testsuite/rust/execute/cf-labeled-block.rs b/gcc/testsuite/rust/execute/cf-labeled-block.rs
new file mode 100644 (file)
index 0000000..edfc483
--- /dev/null
@@ -0,0 +1,36 @@
+// { dg-output "222\r*\n333\r*\n" }
+#![feature(no_core)]
+#![no_core]
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+fn dump_number(num: i32) {
+    unsafe {
+        let a = "%i\n\0";
+        let c = a as *const str as *const i8;
+        printf(c, num);
+    }
+}
+
+fn main() -> i32 {
+    let a = 'block_a: {
+        if false {
+            break 'block_a 111;
+        }
+        222
+    };
+    dump_number(a);
+    let b = 'block_b: {
+        if true {
+            break 'block_b 333;
+        }
+        444
+    };
+    dump_number(b);
+    // ISSUE : Rust-GCC/gccrs#4538
+    // let c = 'block_c: {
+    //     break 'block_c 555;
+    // };
+    // dump_number(c);
+    0
+}