diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/rust/Make-lang.in | 1 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-context.h | 16 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-expr.cc | 172 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-expr.h | 6 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-pattern.cc | 218 | ||||
-rw-r--r-- | gcc/rust/backend/rust-compile-pattern.h | 78 | ||||
-rw-r--r-- | gcc/testsuite/rust/execute/torture/match1.rs | 58 | ||||
-rw-r--r-- | gcc/testsuite/rust/execute/torture/match2.rs | 42 |
8 files changed, 591 insertions, 0 deletions
diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index 391151f..3554982 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -94,6 +94,7 @@ GRS_OBJS = \ rust/rust-lint-marklive.o \ rust/rust-hir-type-check-path.o \ rust/rust-compile-intrinsic.o \ + rust/rust-compile-pattern.o \ rust/rust-base62.o \ rust/rust-compile-expr.o \ rust/rust-compile-type.o \ diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h index 6347e76..896d42e 100644 --- a/gcc/rust/backend/rust-compile-context.h +++ b/gcc/rust/backend/rust-compile-context.h @@ -233,6 +233,21 @@ public: return true; } + void insert_pattern_binding (HirId id, tree binding) + { + implicit_pattern_bindings[id] = binding; + } + + bool lookup_pattern_binding (HirId id, tree *binding) + { + auto it = implicit_pattern_bindings.find (id); + if (it == implicit_pattern_bindings.end ()) + return false; + + *binding = it->second; + return true; + } + void push_fn (tree fn, ::Bvariable *ret_addr) { fn_stack.push_back (fncontext{fn, ret_addr}); @@ -326,6 +341,7 @@ private: std::map<const TyTy::BaseType *, std::pair<HirId, tree>> mono; std::map<DefId, std::vector<std::pair<const TyTy::BaseType *, tree>>> mono_fns; + std::map<HirId, tree> implicit_pattern_bindings; // To GCC middle-end std::vector<tree> type_decls; diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index fa88e48..b77a4d5 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -24,6 +24,9 @@ #include "rust-hir-path-probe.h" #include "rust-hir-type-bounds.h" #include "rust-hir-dot-operator.h" +#include "rust-compile-pattern.h" + +#include "fold-const.h" namespace Rust { namespace Compile { @@ -155,6 +158,175 @@ CompileExpr::visit (HIR::DereferenceExpr &expr) } void +CompileExpr::visit (HIR::MatchExpr &expr) +{ + // https://gcc.gnu.org/onlinedocs/gccint/Basic-Statements.html#Basic-Statements + // TODO + // SWITCH_ALL_CASES_P is true if the switch includes a default label or the + // case label ranges cover all possible values of the condition expression + + /* Switch expression. + + TREE_TYPE is the original type of the condition, before any + language required type conversions. It may be NULL, in which case + the original type and final types are assumed to be the same. + + Operand 0 is the expression used to perform the branch, + Operand 1 is the body of the switch, which probably contains + CASE_LABEL_EXPRs. It may also be NULL, in which case operand 2 + must not be NULL. */ + // DEFTREECODE (SWITCH_EXPR, "switch_expr", tcc_statement, 2) + + /* Used to represent a case label. + + Operand 0 is CASE_LOW. It may be NULL_TREE, in which case the label + is a 'default' label. + Operand 1 is CASE_HIGH. If it is NULL_TREE, the label is a simple + (one-value) case label. If it is non-NULL_TREE, the case is a range. + Operand 2 is CASE_LABEL, which has the corresponding LABEL_DECL. + Operand 3 is CASE_CHAIN. This operand is only used in tree-cfg.c to + speed up the lookup of case labels which use a particular edge in + the control flow graph. */ + // DEFTREECODE (CASE_LABEL_EXPR, "case_label_expr", tcc_statement, 4) + + TyTy::BaseType *scrutinee_expr_tyty = nullptr; + if (!ctx->get_tyctx ()->lookup_type ( + expr.get_scrutinee_expr ()->get_mappings ().get_hirid (), + &scrutinee_expr_tyty)) + { + translated = ctx->get_backend ()->error_expression (); + return; + } + + rust_assert (scrutinee_expr_tyty->get_kind () == TyTy::TypeKind::ADT); + + // this will need to change but for now the first pass implementation, lets + // assert this is the case + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (scrutinee_expr_tyty); + rust_assert (adt->is_enum ()); + rust_assert (adt->number_of_variants () > 0); + + TyTy::BaseType *expr_tyty = nullptr; + if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), + &expr_tyty)) + { + translated = ctx->get_backend ()->error_expression (); + return; + } + + fncontext fnctx = ctx->peek_fn (); + Bvariable *tmp = NULL; + bool needs_temp = !expr_tyty->is_unit (); + if (needs_temp) + { + tree enclosing_scope = ctx->peek_enclosing_scope (); + tree block_type = TyTyResolveCompile::compile (ctx, expr_tyty); + + bool is_address_taken = false; + tree ret_var_stmt = nullptr; + tmp = ctx->get_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); + } + + // lets compile the scrutinee expression + tree match_scrutinee_expr + = CompileExpr::Compile (expr.get_scrutinee_expr ().get (), ctx); + + // need to access the qualifier field, if we use QUAL_UNION_TYPE this would be + // DECL_QUALIFIER i think. For now this will just access the first record + // field and its respective qualifier because it will always be set because + // this is all a big special union + tree scrutinee_first_record_expr + = ctx->get_backend ()->struct_field_expression ( + match_scrutinee_expr, 0, expr.get_scrutinee_expr ()->get_locus ()); + tree match_scrutinee_expr_qualifier_expr + = ctx->get_backend ()->struct_field_expression ( + scrutinee_first_record_expr, 0, expr.get_scrutinee_expr ()->get_locus ()); + + // setup the end label so the cases can exit properly + tree fndecl = fnctx.fndecl; + Location end_label_locus = expr.get_locus (); // FIXME + tree end_label + = ctx->get_backend ()->label (fndecl, + "" /* empty creates an artificial label */, + end_label_locus); + tree end_label_decl_statement + = ctx->get_backend ()->label_definition_statement (end_label); + + // setup the switch-body-block + Location start_location; // FIXME + Location end_location; // FIXME + tree enclosing_scope = ctx->peek_enclosing_scope (); + tree switch_body_block + = ctx->get_backend ()->block (fndecl, enclosing_scope, {}, start_location, + end_location); + ctx->push_block (switch_body_block); + + for (auto &kase : expr.get_match_cases ()) + { + // for now lets just get single pattern's working + HIR::MatchArm &kase_arm = kase.get_arm (); + rust_assert (kase_arm.get_patterns ().size () > 0); + + // generate implicit label + Location arm_locus = kase_arm.get_patterns ().at (0)->get_locus (); + tree case_label = ctx->get_backend ()->label ( + fndecl, "" /* empty creates an artificial label */, arm_locus); + + // not sure if we need to add this to the block or if the CASE_LABEL_EXPR + // does this implicitly + // + // tree case_label_decl_statement + // = ctx->get_backend ()->label_definition_statement (case_label); + + // setup the bindings for the block + for (auto &kase_pattern : kase_arm.get_patterns ()) + { + tree switch_kase_expr + = CompilePatternCaseLabelExpr::Compile (kase_pattern.get (), + case_label, ctx); + // ctx->add_statement (case_label_decl_statement); + ctx->add_statement (switch_kase_expr); + + CompilePatternBindings::Compile (kase_pattern.get (), + match_scrutinee_expr, ctx); + } + + // compile the expr and setup the assignment if required when tmp != NULL + tree kase_expr_tree = CompileExpr::Compile (kase.get_expr ().get (), ctx); + if (tmp != NULL) + { + tree result_reference + = ctx->get_backend ()->var_expression (tmp, arm_locus); + tree assignment = ctx->get_backend ()->assignment_statement ( + fnctx.fndecl, result_reference, kase_expr_tree, arm_locus); + ctx->add_statement (assignment); + } + + // go to end label + tree goto_end_label = build1_loc (arm_locus.gcc_location (), GOTO_EXPR, + void_type_node, end_label); + ctx->add_statement (goto_end_label); + } + + // setup the switch expression + tree match_body = ctx->pop_block (); + tree match_expr_stmt + = build2_loc (expr.get_locus ().gcc_location (), SWITCH_EXPR, + TREE_TYPE (match_scrutinee_expr_qualifier_expr), + match_scrutinee_expr_qualifier_expr, match_body); + ctx->add_statement (match_expr_stmt); + ctx->add_statement (end_label_decl_statement); + + if (tmp != NULL) + { + translated = ctx->get_backend ()->var_expression (tmp, expr.get_locus ()); + } +} + +void CompileExpr::visit (HIR::CallExpr &expr) { TyTy::BaseType *tyty = nullptr; diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h index 9895540..dc4f909 100644 --- a/gcc/rust/backend/rust-compile-expr.h +++ b/gcc/rust/backend/rust-compile-expr.h @@ -221,6 +221,10 @@ public: translated = ctx->get_backend ()->var_expression (var, expr.get_locus ()); } + else if (ctx->lookup_pattern_binding (ref, &translated)) + { + return; + } else { rust_fatal_error (expr.get_locus (), @@ -1043,6 +1047,8 @@ public: void visit (HIR::DereferenceExpr &expr) override; + void visit (HIR::MatchExpr &expr) override; + protected: tree compile_dyn_dispatch_call (const TyTy::DynamicObjectType *dyn, TyTy::BaseType *receiver, diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc new file mode 100644 index 0000000..e634dbd --- /dev/null +++ b/gcc/rust/backend/rust-compile-pattern.cc @@ -0,0 +1,218 @@ +// Copyright (C) 2020-2021 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include "rust-compile-pattern.h" + +namespace Rust { +namespace Compile { + +void +CompilePatternCaseLabelExpr::visit (HIR::PathInExpression &pattern) +{ + // lookup the type + TyTy::BaseType *lookup = nullptr; + bool ok + = ctx->get_tyctx ()->lookup_type (pattern.get_mappings ().get_hirid (), + &lookup); + rust_assert (ok); + + // this must be an enum + rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup); + rust_assert (adt->is_enum ()); + + // lookup the variant + HirId variant_id; + ok = ctx->get_tyctx ()->lookup_variant_definition ( + pattern.get_mappings ().get_hirid (), &variant_id); + rust_assert (ok); + + TyTy::VariantDef *variant = nullptr; + ok = adt->lookup_variant_by_id (variant_id, &variant); + rust_assert (ok); + + mpz_t disciminantl; + if (variant->get_variant_type () == TyTy::VariantDef::VariantType::NUM) + { + mpz_init_set_ui (disciminantl, variant->get_discriminant ()); + } + else + { + HirId variant_id = variant->get_id (); + mpz_init_set_ui (disciminantl, variant_id); + } + + tree t = TyTyResolveCompile::get_implicit_enumeral_node_type (ctx); + tree case_low + = double_int_to_tree (t, mpz_get_double_int (t, disciminantl, true)); + + case_label_expr + = build_case_label (case_low, NULL_TREE, associated_case_label); +} + +void +CompilePatternCaseLabelExpr::visit (HIR::StructPattern &pattern) +{ + CompilePatternCaseLabelExpr::visit (pattern.get_path ()); +} + +void +CompilePatternCaseLabelExpr::visit (HIR::TupleStructPattern &pattern) +{ + CompilePatternCaseLabelExpr::visit (pattern.get_path ()); +} + +// setup the bindings + +void +CompilePatternBindings::visit (HIR::TupleStructPattern &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); + + // this must be an enum + rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup); + rust_assert (adt->is_enum ()); + + // lookup the variant + HirId variant_id; + ok = ctx->get_tyctx ()->lookup_variant_definition ( + pattern.get_path ().get_mappings ().get_hirid (), &variant_id); + rust_assert (ok); + + int variant_index = -1; + TyTy::VariantDef *variant = nullptr; + ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index); + rust_assert (ok); + + rust_assert (variant->get_variant_type () + == TyTy::VariantDef::VariantType::TUPLE); + + std::unique_ptr<HIR::TupleStructItems> &items = pattern.get_items (); + switch (items->get_item_type ()) + { + case HIR::TupleStructItems::RANGE: { + // TODO + gcc_unreachable (); + } + break; + + case HIR::TupleStructItems::NO_RANGE: { + HIR::TupleStructItemsNoRange &items_no_range + = static_cast<HIR::TupleStructItemsNoRange &> (*items.get ()); + + rust_assert (items_no_range.get_patterns ().size () + == variant->num_fields ()); + + // we are offsetting by + 1 here since the first field in the record + // is always the discriminator + size_t tuple_field_index = 1; + for (auto &pattern : items_no_range.get_patterns ()) + { + tree variant_accessor + = ctx->get_backend ()->struct_field_expression ( + match_scrutinee_expr, variant_index, pattern->get_locus ()); + + tree binding = ctx->get_backend ()->struct_field_expression ( + variant_accessor, tuple_field_index++, pattern->get_locus ()); + + ctx->insert_pattern_binding ( + pattern->get_pattern_mappings ().get_hirid (), binding); + } + } + break; + } +} + +void +CompilePatternBindings::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); + + // this must be an enum + rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup); + rust_assert (adt->is_enum ()); + + // lookup the variant + HirId variant_id; + ok = ctx->get_tyctx ()->lookup_variant_definition ( + pattern.get_path ().get_mappings ().get_hirid (), &variant_id); + rust_assert (ok); + + int variant_index = -1; + TyTy::VariantDef *variant = nullptr; + ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index); + rust_assert (ok); + + rust_assert (variant->get_variant_type () + == TyTy::VariantDef::VariantType::STRUCT); + + 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: { + // TODO + gcc_unreachable (); + } + break; + + case HIR::StructPatternField::ItemType::IDENT_PAT: { + // TODO + gcc_unreachable (); + } + break; + + case HIR::StructPatternField::ItemType::IDENT: { + HIR::StructPatternFieldIdent &ident + = static_cast<HIR::StructPatternFieldIdent &> (*field.get ()); + + tree variant_accessor + = ctx->get_backend ()->struct_field_expression ( + match_scrutinee_expr, variant_index, ident.get_locus ()); + + size_t offs = 0; + ok + = variant->lookup_field (ident.get_identifier (), nullptr, &offs); + rust_assert (ok); + + // we are offsetting by + 1 here since the first field in the record + // is always the discriminator + tree binding = ctx->get_backend ()->struct_field_expression ( + variant_accessor, offs + 1, ident.get_locus ()); + + ctx->insert_pattern_binding (ident.get_mappings ().get_hirid (), + binding); + } + break; + } + } +} + +} // namespace Compile +} // namespace Rust diff --git a/gcc/rust/backend/rust-compile-pattern.h b/gcc/rust/backend/rust-compile-pattern.h new file mode 100644 index 0000000..e49f75c --- /dev/null +++ b/gcc/rust/backend/rust-compile-pattern.h @@ -0,0 +1,78 @@ +// Copyright (C) 2020-2021 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include "rust-compile-base.h" + +namespace Rust { +namespace Compile { + +class CompilePatternCaseLabelExpr : public HIRCompileBase +{ + using Rust::Compile::HIRCompileBase::visit; + +public: + static tree Compile (HIR::Pattern *pattern, tree associated_case_label, + Context *ctx) + { + CompilePatternCaseLabelExpr compiler (ctx, associated_case_label); + pattern->accept_vis (compiler); + return compiler.case_label_expr; + } + + void visit (HIR::PathInExpression &pattern) override; + + void visit (HIR::StructPattern &pattern) override; + + void visit (HIR::TupleStructPattern &pattern) override; + +private: + CompilePatternCaseLabelExpr (Context *ctx, tree associated_case_label) + : HIRCompileBase (ctx), case_label_expr (error_mark_node), + associated_case_label (associated_case_label) + {} + + tree case_label_expr; + tree associated_case_label; +}; + +class CompilePatternBindings : public HIRCompileBase +{ + using Rust::Compile::HIRCompileBase::visit; + +public: + static void Compile (HIR::Pattern *pattern, tree match_scrutinee_expr, + Context *ctx) + { + CompilePatternBindings compiler (ctx, match_scrutinee_expr); + pattern->accept_vis (compiler); + } + + void visit (HIR::StructPattern &pattern) override; + + void visit (HIR::TupleStructPattern &pattern) override; + +private: + CompilePatternBindings (Context *ctx, tree match_scrutinee_expr) + : HIRCompileBase (ctx), match_scrutinee_expr (match_scrutinee_expr) + {} + + tree match_scrutinee_expr; +}; + +} // namespace Compile +} // namespace Rust diff --git a/gcc/testsuite/rust/execute/torture/match1.rs b/gcc/testsuite/rust/execute/torture/match1.rs new file mode 100644 index 0000000..2393b15 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/match1.rs @@ -0,0 +1,58 @@ +// { dg-output "Foo::A\nFoo::B\nFoo::C x\nFoo::D 20 80\n" } +extern "C" { + fn printf(s: *const i8, ...); +} + +enum Foo { + A, + B, + C(char), + D { x: i64, y: i64 }, +} + +fn inspect(f: Foo) { + match f { + Foo::A => unsafe { + let a = "Foo::A\n\0"; + let b = a as *const str; + let c = b as *const i8; + + printf(c); + }, + Foo::B => unsafe { + let a = "Foo::B\n\0"; + let b = a as *const str; + let c = b as *const i8; + + printf(c); + }, + Foo::C(x) => unsafe { + let a = "Foo::C %c\n\0"; + let b = a as *const str; + let c = b as *const i8; + + printf(c, x); + }, + Foo::D { x, y } => unsafe { + let a = "Foo::D %i %i\n\0"; + let b = a as *const str; + let c = b as *const i8; + + printf(c, x, y); + }, + } +} + +fn main() -> i32 { + let a = Foo::A; + let b = Foo::B; + let c = Foo::C('x'); + let d = Foo::D { x: 20, y: 80 }; + + inspect(a); + inspect(b); + inspect(c); + inspect(d); + + 0 +} diff --git a/gcc/testsuite/rust/execute/torture/match2.rs b/gcc/testsuite/rust/execute/torture/match2.rs new file mode 100644 index 0000000..4a018c1 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/match2.rs @@ -0,0 +1,42 @@ +// { dg-output "123\n80\n" } +extern "C" { + fn printf(s: *const i8, ...); +} + +enum Foo { + C(i32), + D { x: i32, y: i32 }, +} + +fn inspect(f: Foo) -> i32 { + match f { + Foo::C(x) => x, + Foo::D { x, y } => y, + // { dg-warning "unused name .x." "" { target *-*-* } .-1 } + } +} + +fn main() -> i32 { + let a = Foo::C(123); + let b = Foo::D { x: 20, y: 80 }; + + let result = inspect(a); + unsafe { + let a = "%i\n\0"; + let b = a as *const str; + let c = b as *const i8; + + printf(c, result); + } + + let result = inspect(b); + unsafe { + let a = "%i\n\0"; + let b = a as *const str; + let c = b as *const i8; + + printf(c, result); + } + + 0 +} |