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