https://gcc.gnu.org/g:625c60a9a032274cd1bc0dd8f01e094f1c454f04
commit r17-4289-g625c60a9a032274cd1bc0dd8f01e094f1c454f04 Author: Yap Zhi Heng <[email protected]> Date: Sat Aug 8 22:58:45 2026 +0800 gccrs: Compile `let <StructPattern>` 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]> Diff: --- 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 ++++++++++++++- gcc/testsuite/rust/compile/let-structpattern.rs | 19 +++ .../rust/execute/torture/let-structpattern.rs | 19 +++ 5 files changed, 322 insertions(+), 7 deletions(-) diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc index 19f5bc4ed887..9d722358bb2c 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 35e152fe3b18..9fa89897093e 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 92108aea0ad9..c7a358aa181b 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 000000000000..07cf55009aef --- /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 000000000000..2352e9fb560c --- /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) +}
