[COMMITTED 12/83] gccrs: Compile `let <StructPattern>`

arthur.cohen@opensrcsec.com arthur.cohen@opensrcsec.com
Wed Sep 16 12:29:31 GMT 2026


From: Yap Zhi Heng <yapzhhg@gmail.com>

gcc/rust/ChangeLog:

	* backend/rust-compile-var-decl.h (CompileVarDecl::visit (StructPattern)): Compile
	variable declarations for use within CompilePatternLet::visit(StructPattern).
	* backend/rust-compile-pattern.h (CompilePatternLet::visit (StructPattern)):
	Remove rust_sorry_at.
	* backend/rust-compile-pattern.cc (CompilePatternLet::visit (StructPattern)):
	Compile bindings for struct fields.

Signed-off-by: Yap Zhi Heng <yapzhhg@gmail.com>
---
 gcc/rust/backend/rust-compile-pattern.cc      | 167 ++++++++++++++++++
 gcc/rust/backend/rust-compile-pattern.h       |   7 +-
 gcc/rust/backend/rust-compile-var-decl.h      | 117 +++++++++++-
 .../rust/compile/let-structpattern.rs         |  19 ++
 .../rust/execute/torture/let-structpattern.rs |  19 ++
 5 files changed, 322 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/let-structpattern.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/let-structpattern.rs

diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc
index 19f5bc4ed88..9d722358bb2 100644
--- a/gcc/rust/backend/rust-compile-pattern.cc
+++ b/gcc/rust/backend/rust-compile-pattern.cc
@@ -1522,5 +1522,172 @@ CompilePatternLet::visit (HIR::TuplePattern &pattern)
     }
 }
 
+void
+CompilePatternLet::visit (HIR::StructPattern &pattern)
+{
+  // lookup the type
+  TyTy::BaseType *lookup = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    pattern.get_path ().get_mappings ().get_hirid (), &lookup);
+  rust_assert (ok);
+
+  rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+
+  // only structs and single-variant enums are irrefutable, this check should
+  // already be handled by type check
+  rust_assert (adt->number_of_variants () == 1);
+
+  int variant_index = 0;
+  TyTy::VariantDef *variant = nullptr;
+  if (adt->is_enum ())
+    {
+      // lookup the variant
+      HirId variant_id = UNKNOWN_HIRID;
+      bool ok = ctx->get_tyctx ()->lookup_variant_definition (
+	pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+      rust_assert (ok);
+
+      ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index);
+      rust_assert (ok);
+    }
+  else
+    {
+      variant = adt->get_variants ().at (0);
+    }
+
+  bool has_by_ref = false;
+  auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
+  for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+    {
+      if (field->get_item_type () == HIR::StructPatternField::ItemType::IDENT)
+	{
+	  HIR::StructPatternFieldIdent &ident
+	    = static_cast<HIR::StructPatternFieldIdent &> (*field);
+	  if (ident.get_has_ref ())
+	    has_by_ref = true;
+	}
+    }
+
+  tree rhs_type = TYPE_MAIN_VARIANT (TREE_TYPE (init_expr));
+  tree init_stmt;
+  Bvariable *tmp_var
+    = Backend::temporary_variable (ctx->peek_fn ().fndecl, NULL_TREE, rhs_type,
+				   init_expr, has_by_ref, pattern.get_locus (),
+				   &init_stmt);
+  ctx->add_statement (init_stmt);
+  tree access_expr = Backend::var_expression (tmp_var, pattern.get_locus ());
+
+  auto make_ident_field_access = [&] (const Identifier &ident, location_t loc) {
+    size_t offs = 0;
+    bool ok = variant->lookup_field (ident.as_string (), nullptr, &offs);
+    rust_assert (ok);
+
+    if (adt->is_enum ())
+      {
+	tree payload_accessor_union
+	  = Backend::struct_field_expression (access_expr, 1, loc);
+	tree variant_accessor
+	  = Backend::struct_field_expression (payload_accessor_union,
+					      variant_index, loc);
+	return Backend::struct_field_expression (variant_accessor, offs, loc);
+      }
+    else
+      {
+	return Backend::struct_field_expression (access_expr, offs, loc);
+      }
+  };
+
+  for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+    {
+      switch (field->get_item_type ())
+	{
+	case HIR::StructPatternField::ItemType::TUPLE_PAT:
+	  {
+	    HIR::StructPatternFieldTuplePat &tuple_pat
+	      = static_cast<HIR::StructPatternFieldTuplePat &> (*field);
+
+	    size_t tuple_pat_index = tuple_pat.get_index ();
+	    tree field_expr = NULL_TREE;
+	    if (adt->is_enum ())
+	      {
+		tree payload_accessor_union
+		  = Backend::struct_field_expression (access_expr, 1,
+						      tuple_pat.get_locus ());
+		tree variant_accessor
+		  = Backend::struct_field_expression (payload_accessor_union,
+						      variant_index,
+						      tuple_pat.get_locus ());
+		field_expr
+		  = Backend::struct_field_expression (variant_accessor,
+						      tuple_pat_index,
+						      tuple_pat.get_locus ());
+	      }
+	    else
+	      {
+		field_expr
+		  = Backend::struct_field_expression (access_expr,
+						      tuple_pat_index,
+						      tuple_pat.get_locus ());
+	      }
+
+	    TyTy::BaseType *ty_sub = nullptr;
+	    HirId sub_id
+	      = tuple_pat.get_tuple_pattern ().get_mappings ().get_hirid ();
+	    bool ok = ctx->get_tyctx ()->lookup_type (sub_id, &ty_sub);
+	    rust_assert (ok);
+
+	    CompilePatternLet::Compile (&tuple_pat.get_tuple_pattern (),
+					field_expr, ty_sub, rval_locus, ctx);
+	  }
+	  break;
+	case HIR::StructPatternField::ItemType::IDENT_PAT:
+	  {
+	    HIR::StructPatternFieldIdentPat &ident_pat
+	      = static_cast<HIR::StructPatternFieldIdentPat &> (*field);
+
+	    tree field_expr
+	      = make_ident_field_access (ident_pat.get_identifier (),
+					 ident_pat.get_locus ());
+
+	    TyTy::BaseType *ty_sub = nullptr;
+	    HirId sub_id
+	      = ident_pat.get_pattern ().get_mappings ().get_hirid ();
+	    bool ok = ctx->get_tyctx ()->lookup_type (sub_id, &ty_sub);
+	    rust_assert (ok);
+
+	    CompilePatternLet::Compile (&ident_pat.get_pattern (), field_expr,
+					ty_sub, rval_locus, ctx);
+	  }
+	  break;
+
+	case HIR::StructPatternField::ItemType::IDENT:
+	  {
+	    HIR::StructPatternFieldIdent &ident
+	      = static_cast<HIR::StructPatternFieldIdent &> (*field);
+
+	    tree field_expr = make_ident_field_access (ident.get_identifier (),
+						       ident.get_locus ());
+
+	    Bvariable *var = nullptr;
+	    ok
+	      = ctx->lookup_var_decl (ident.get_mappings ().get_hirid (), &var);
+	    rust_assert (ok);
+
+	    if (ident.get_has_ref ())
+	      {
+		field_expr
+		  = address_expression (field_expr, EXPR_LOCATION (field_expr));
+	      }
+
+	    auto fnctx = ctx->peek_fn ();
+	    auto s = Backend::init_statement (fnctx.fndecl, var, field_expr);
+	    ctx->add_statement (s);
+	  }
+	  break;
+	}
+    }
+}
+
 } // namespace Compile
 } // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-pattern.h b/gcc/rust/backend/rust-compile-pattern.h
index 35e152fe3b1..9fa89897093 100644
--- a/gcc/rust/backend/rust-compile-pattern.h
+++ b/gcc/rust/backend/rust-compile-pattern.h
@@ -129,6 +129,7 @@ public:
   void visit (HIR::IdentifierPattern &) override;
   void visit (HIR::WildcardPattern &) override;
   void visit (HIR::TuplePattern &) override;
+  void visit (HIR::StructPattern &) override;
 
   // check for unimplemented Pattern HIR nodes.
   void visit (HIR::AltPattern &pattern) override
@@ -174,12 +175,6 @@ public:
 		   "slice pattern let statements not supported");
   }
 
-  void visit (HIR::StructPattern &pattern) override
-  {
-    rust_sorry_at (pattern.get_locus (),
-		   "struct pattern let statements not supported");
-  }
-
   void visit (HIR::TupleStructPattern &pattern) override
   {
     rust_sorry_at (pattern.get_locus (),
diff --git a/gcc/rust/backend/rust-compile-var-decl.h b/gcc/rust/backend/rust-compile-var-decl.h
index 92108aea0ad..c7a358aa181 100644
--- a/gcc/rust/backend/rust-compile-var-decl.h
+++ b/gcc/rust/backend/rust-compile-var-decl.h
@@ -20,6 +20,7 @@
 #define RUST_COMPILE_VAR_DECL
 
 #include "rust-compile-base.h"
+#include "rust-compile-type.h"
 #include "rust-hir-visitor.h"
 
 namespace Rust {
@@ -146,6 +147,121 @@ public:
       }
   }
 
+  void visit (HIR::StructPattern &pattern) override
+  {
+    // lookup the type
+    TyTy::BaseType *lookup = nullptr;
+    bool ok = ctx->get_tyctx ()->lookup_type (
+      pattern.get_path ().get_mappings ().get_hirid (), &lookup);
+    rust_assert (ok);
+
+    rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+    TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+
+    // only structs and single-variant enums are irrefutable, this check should
+    // already be handled by type check
+    rust_assert (adt->number_of_variants () == 1);
+
+    int variant_index = 0;
+    TyTy::VariantDef *variant = nullptr;
+    if (adt->is_enum ())
+      {
+	// lookup the variant
+	HirId variant_id = UNKNOWN_HIRID;
+	bool ok = ctx->get_tyctx ()->lookup_variant_definition (
+	  pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+	rust_assert (ok);
+
+	ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index);
+	rust_assert (ok);
+      }
+    else
+      {
+	variant = adt->get_variants ().at (0);
+      }
+
+    auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
+    for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+      {
+	switch (field->get_item_type ())
+	  {
+	  case HIR::StructPatternField::ItemType::TUPLE_PAT:
+	    {
+	      HIR::StructPatternFieldTuplePat &tuple_pat
+		= static_cast<HIR::StructPatternFieldTuplePat &> (*field);
+	      TyTy::StructFieldType *field_ty = nullptr;
+	      ok = variant->lookup_field (std::to_string (
+					    tuple_pat.get_index ()),
+					  &field_ty, nullptr);
+	      rust_assert (ok);
+	      tree sub_ty
+		= TyTyResolveCompile::compile (ctx,
+					       field_ty->get_field_type ());
+	      auto sub_vars
+		= CompileVarDecl::compile (fndecl, sub_ty,
+					   &tuple_pat.get_tuple_pattern (),
+					   ctx);
+	      vars.insert (vars.end (), sub_vars.begin (), sub_vars.end ());
+	    }
+	    break;
+	  case HIR::StructPatternField::ItemType::IDENT_PAT:
+	    {
+	      HIR::StructPatternFieldIdentPat &ident_pat
+		= static_cast<HIR::StructPatternFieldIdentPat &> (*field);
+	      TyTy::StructFieldType *field_ty = nullptr;
+	      ok = variant->lookup_field (
+		ident_pat.get_identifier ().as_string (), &field_ty, nullptr);
+	      rust_assert (ok);
+	      tree sub_ty
+		= TyTyResolveCompile::compile (ctx,
+					       field_ty->get_field_type ());
+	      auto sub_vars
+		= CompileVarDecl::compile (fndecl, sub_ty,
+					   &ident_pat.get_pattern (), ctx);
+	      vars.insert (vars.end (), sub_vars.begin (), sub_vars.end ());
+	    }
+	    break;
+	  case HIR::StructPatternField::ItemType::IDENT:
+	    {
+	      HIR::StructPatternFieldIdent &ident
+		= static_cast<HIR::StructPatternFieldIdent &> (*field);
+	      TyTy::StructFieldType *field_ty = nullptr;
+	      ok = variant->lookup_field (ident.get_identifier ().as_string (),
+					  &field_ty, nullptr);
+	      rust_assert (ok);
+	      tree sub_ty
+		= TyTyResolveCompile::compile (ctx,
+					       field_ty->get_field_type ());
+
+	      // code below is pretty much copied from
+	      // visit(IdentifierPattern) above
+	      if (!ident.is_mut ())
+		sub_ty = Backend::immutable_type (sub_ty);
+
+	      tree bind_tree = ctx->peek_enclosing_scope ();
+	      std::string identifier = ident.get_identifier ().as_string ();
+	      tree decl = build_decl (ident.get_locus (), VAR_DECL,
+				      Backend::get_identifier_node (identifier),
+				      sub_ty);
+	      DECL_CONTEXT (decl) = fndecl;
+	      gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+	      tree block_tree = BIND_EXPR_BLOCK (bind_tree);
+	      gcc_assert (TREE_CODE (block_tree) == BLOCK);
+	      DECL_CHAIN (decl) = BLOCK_VARS (block_tree);
+	      BLOCK_VARS (block_tree) = decl;
+	      BIND_EXPR_VARS (bind_tree) = BLOCK_VARS (block_tree);
+	      rust_preserve_from_gc (decl);
+	      Bvariable *var = new Bvariable (decl);
+
+	      HirId stmt_id = ident.get_mappings ().get_hirid ();
+	      ctx->insert_var_decl (stmt_id, var);
+	      vars.push_back (var);
+	    }
+	    break;
+	  }
+      }
+  }
+
   // Empty visit for unused Pattern HIR nodes.
   void visit (HIR::AltPattern &) override {}
   void visit (HIR::LiteralPattern &) override {}
@@ -154,7 +270,6 @@ public:
   void visit (HIR::RangePattern &) override {}
   void visit (HIR::ReferencePattern &) override {}
   void visit (HIR::SlicePattern &) override {}
-  void visit (HIR::StructPattern &) override {}
   void visit (HIR::TupleStructPattern &) override {}
   void visit (HIR::WildcardPattern &) override {}
 
diff --git a/gcc/testsuite/rust/compile/let-structpattern.rs b/gcc/testsuite/rust/compile/let-structpattern.rs
new file mode 100644
index 00000000000..07cf55009ae
--- /dev/null
+++ b/gcc/testsuite/rust/compile/let-structpattern.rs
@@ -0,0 +1,19 @@
+#![feature(no_core)]
+#![no_core]
+
+// TODO the warnings below shouldn't be emitted...?
+struct Foo {
+    x: i32, // { dg-bogus "field is never read: .x." TODO { xfail *-*-* } }
+    y: i32 // { dg-bogus "field is never read: .y." TODO { xfail *-*-* } }
+}
+
+struct Bar (i32, i32);
+
+fn main() {
+    let mut my_foo = Foo{x: 32, y: 64};
+    let Foo {x, y: _renamed_y} = my_foo; // { dg-warning "unused name .x." }
+    let Foo {x: _another_x, ..} = my_foo;
+    let Foo {y: _yet_another_y, ..} = my_foo;
+    let my_bar = Bar(32, 64);
+    let Bar{1: _w, ..} = my_bar;
+}
diff --git a/gcc/testsuite/rust/execute/torture/let-structpattern.rs b/gcc/testsuite/rust/execute/torture/let-structpattern.rs
new file mode 100644
index 00000000000..2352e9fb560
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/let-structpattern.rs
@@ -0,0 +1,19 @@
+#![feature(no_core)]
+#![no_core]
+
+struct Foo {
+    x: i32
+}
+
+struct Bar (i32, i32);
+
+fn main() -> i32 {
+    let mut my_foo = Foo{x: 32};
+    let mut my_bar = Bar(32, 64);
+    let Foo {x} = my_foo;
+    let Foo {x: _x @ test} = my_foo;
+    my_foo.x = 16;
+    let Foo {x: yet_another_x } = my_foo;
+    let Bar {0: w, ..} = my_bar;
+    (x - test) | (x - w) | (yet_another_x - 16)
+}
-- 
2.50.1



More information about the Gcc-rust mailing list