From: Yap Zhi Heng <[email protected]>

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 <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/d198b238fc377de3d0016f203d9435bd0e0b3dc3

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4773

 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 19f5bc4ed..9d722358b 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 35e152fe3..9fa898970 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 92108aea0..c7a358aa1 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 000000000..07cf55009
--- /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 000000000..2352e9fb5
--- /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)
+}

base-commit: 00a99e715512791f52ad68522e38d149c6373310
-- 
2.55.0

Reply via email to