From e489a694c748ee205e5ac46eaafcb226f87f4538 Mon Sep 17 00:00:00 2001 From: Owen Avery Date: Sun, 22 Oct 2023 21:44:01 -0400 Subject: [PATCH] Compile pattern match statements into conditional statements gcc/rust/ChangeLog: * backend/rust-compile-expr.cc (patterns_mergeable): Remove. (struct PatternMerge): Remove. (sort_tuple_patterns): Remove. (simplify_tuple_match): Remove. (CompileExpr::visit): Modify compilation of MatchExpr. * backend/rust-compile-pattern.cc (CompilePatternCaseLabelExpr::visit): Replace with... (CompilePatternCheckExpr::visit): ...this. * backend/rust-compile-pattern.h (class CompilePatternCaseLabelExpr): Replace with... (class CompilePatternCheckExpr): ...this. * backend/rust-compile-type.cc (TyTyResolveCompile::get_implicit_enumeral_node_type): Make enumeral type equivalent to isize. Signed-off-by: Owen Avery --- gcc/rust/backend/rust-compile-expr.cc | 494 ++--------------------- gcc/rust/backend/rust-compile-pattern.cc | 331 +++++++++++++-- gcc/rust/backend/rust-compile-pattern.h | 45 ++- gcc/rust/backend/rust-compile-type.cc | 7 +- 4 files changed, 357 insertions(+), 520 deletions(-) diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 5f4ce5411cee..71eeb076672a 100644 --- a/gcc/rust/backend/rust-compile-expr.cc +++ b/gcc/rust/backend/rust-compile-expr.cc @@ -929,322 +929,6 @@ CompileExpr::visit (HIR::AssignmentExpr &expr) ctx->add_statement (assignment); } -// Helper for sort_tuple_patterns. -// Determine whether Patterns a and b are really the same pattern. -// FIXME: This is a nasty hack to avoid properly implementing a comparison -// for Patterns, which we really probably do want at some point. -static bool -patterns_mergeable (HIR::Pattern *a, HIR::Pattern *b) -{ - if (!a || !b) - return false; - - HIR::Pattern::PatternType pat_type = a->get_pattern_type (); - if (b->get_pattern_type () != pat_type) - return false; - - switch (pat_type) - { - case HIR::Pattern::PatternType::PATH: { - // FIXME: this is far too naive - HIR::PathPattern &aref = *static_cast (a); - HIR::PathPattern &bref = *static_cast (b); - if (aref.get_num_segments () != bref.get_num_segments ()) - return false; - - const auto &asegs = aref.get_segments (); - const auto &bsegs = bref.get_segments (); - for (size_t i = 0; i < asegs.size (); i++) - { - if (asegs[i].as_string () != bsegs[i].as_string ()) - return false; - } - return true; - } - break; - case HIR::Pattern::PatternType::LITERAL: { - HIR::LiteralPattern &aref = *static_cast (a); - HIR::LiteralPattern &bref = *static_cast (b); - return aref.get_literal ().is_equal (bref.get_literal ()); - } - break; - case HIR::Pattern::PatternType::IDENTIFIER: { - // TODO - } - break; - case HIR::Pattern::PatternType::WILDCARD: - return true; - break; - - // TODO - - default:; - } - return false; -} - -// A little container for rearranging the patterns and cases in a match -// expression while simplifying. -struct PatternMerge -{ - std::unique_ptr wildcard; - std::vector> heads; - std::vector> cases; -}; - -// Helper for simplify_tuple_match. -// For each tuple pattern in a given match, pull out the first elt of the -// tuple and construct a new MatchCase with the remaining tuple elts as the -// pattern. Return a mapping from each _unique_ first tuple element to a -// vec of cases for a new match. -// -// FIXME: This used to be a std::map>, but it doesn't -// actually work like we want - the Pattern includes an HIR ID, which is unique -// per Pattern object. This means we don't have a good means for comparing -// Patterns. It would probably be best to actually implement a means of -// properly comparing patterns, and then use an actual map. -// -static struct PatternMerge -sort_tuple_patterns (HIR::MatchExpr &expr) -{ - rust_assert (expr.get_scrutinee_expr ()->get_expression_type () - == HIR::Expr::ExprType::Tuple); - - struct PatternMerge result; - result.wildcard = nullptr; - result.heads = std::vector> (); - result.cases = std::vector> (); - - for (auto &match_case : expr.get_match_cases ()) - { - HIR::MatchArm &case_arm = match_case.get_arm (); - - // FIXME: Note we are only dealing with the first pattern in the arm. - // The patterns vector in the arm might hold many patterns, which are the - // patterns separated by the '|' token. Rustc abstracts these as "Or" - // patterns, and part of its simplification process is to get rid of them. - // We should get rid of the ORs too, maybe here or earlier than here? - auto pat = case_arm.get_patterns ()[0]->clone_pattern (); - - // Record wildcards so we can add them in inner matches. - if (pat->get_pattern_type () == HIR::Pattern::PatternType::WILDCARD) - { - // The *whole* pattern is a wild card (_). - result.wildcard - = std::unique_ptr (new HIR::MatchCase (match_case)); - continue; - } - - rust_assert (pat->get_pattern_type () - == HIR::Pattern::PatternType::TUPLE); - - auto ref = *static_cast (pat.get ()); - - rust_assert (ref.has_tuple_pattern_items ()); - - auto items - = HIR::TuplePattern (ref).get_items ()->clone_tuple_pattern_items (); - if (items->get_item_type () - == HIR::TuplePatternItems::TuplePatternItemType::MULTIPLE) - { - auto items_ref - = *static_cast (items.get ()); - - // Pop the first pattern out - auto patterns = std::vector> (); - auto first = items_ref.get_patterns ()[0]->clone_pattern (); - for (auto p = items_ref.get_patterns ().begin () + 1; - p != items_ref.get_patterns ().end (); p++) - { - patterns.push_back ((*p)->clone_pattern ()); - } - - // if there is only one pattern left, don't make a tuple out of it - std::unique_ptr result_pattern; - if (patterns.size () == 1) - { - result_pattern = std::move (patterns[0]); - } - else - { - auto new_items = std::unique_ptr ( - new HIR::TuplePatternItemsMultiple (std::move (patterns))); - - // Construct a TuplePattern from the rest of the patterns - result_pattern = std::unique_ptr ( - new HIR::TuplePattern (ref.get_mappings (), - std::move (new_items), - ref.get_locus ())); - } - - // I don't know why we need to make foo separately here but - // using the { new_tuple } syntax in new_arm constructor does not - // compile. - auto foo = std::vector> (); - foo.emplace_back (std::move (result_pattern)); - HIR::MatchArm new_arm (std::move (foo), UNDEF_LOCATION, nullptr, - AST::AttrVec ()); - - HIR::MatchCase new_case (match_case.get_mappings (), new_arm, - match_case.get_expr ()->clone_expr ()); - - bool pushed = false; - for (size_t i = 0; i < result.heads.size (); i++) - { - if (patterns_mergeable (result.heads[i].get (), first.get ())) - { - result.cases[i].push_back (new_case); - pushed = true; - } - } - - if (!pushed) - { - result.heads.push_back (std::move (first)); - result.cases.push_back ({new_case}); - } - } - else /* TuplePatternItemType::RANGED */ - { - // FIXME - rust_unreachable (); - } - } - - return result; -} - -// Helper for CompileExpr::visit (HIR::MatchExpr). -// Given a MatchExpr where the scrutinee is some kind of tuple, build an -// equivalent match where only one element of the tuple is examined at a time. -// This resulting match can then be lowered to a SWITCH_EXPR tree directly. -// -// The approach is as follows: -// 1. Split the scrutinee and each pattern into the first (head) and the -// rest (tail). -// 2. Build a mapping of unique pattern heads to the cases (tail and expr) -// that shared that pattern head in the original match. -// (This is the job of sort_tuple_patterns ()). -// 3. For each unique pattern head, build a new MatchCase where the pattern -// is the unique head, and the expression is a new match where: -// - The scrutinee is the tail of the original scrutinee -// - The cases are are those built by the mapping in step 2, i.e. the -// tails of the patterns and the corresponing expressions from the -// original match expression. -// 4. Do this recursively for each inner match, until there is nothing more -// to simplify. -// 5. Build the resulting match which scrutinizes the head of the original -// scrutinee, using the cases built in step 3. -static HIR::MatchExpr -simplify_tuple_match (HIR::MatchExpr &expr) -{ - if (expr.get_scrutinee_expr ()->get_expression_type () - != HIR::Expr::ExprType::Tuple) - return expr; - - auto ref = *static_cast (expr.get_scrutinee_expr ().get ()); - - auto &tail = ref.get_tuple_elems (); - rust_assert (tail.size () > 1); - - auto head = std::move (tail[0]); - tail.erase (tail.begin (), tail.begin () + 1); - - // e.g. - // match (tupA, tupB, tupC) { - // (a1, b1, c1) => { blk1 }, - // (a2, b2, c2) => { blk2 }, - // (a1, b3, c3) => { blk3 }, - // } - // tail = (tupB, tupC) - // head = tupA - - // Make sure the tail is only a tuple if it consists of at least 2 elements. - std::unique_ptr remaining; - if (tail.size () == 1) - remaining = std::move (tail[0]); - else - remaining = std::unique_ptr ( - new HIR::TupleExpr (ref.get_mappings (), std::move (tail), - AST::AttrVec (), ref.get_outer_attrs (), - ref.get_locus ())); - - // e.g. - // a1 -> [(b1, c1) => { blk1 }, - // (b3, c3) => { blk3 }] - // a2 -> [(b2, c2) => { blk2 }] - struct PatternMerge map = sort_tuple_patterns (expr); - - std::vector cases; - // Construct the inner match for each unique first elt of the tuple - // patterns - for (size_t i = 0; i < map.heads.size (); i++) - { - auto inner_match_cases = map.cases[i]; - - // If there is a wildcard at the outer match level, then need to - // propegate the wildcard case into *every* inner match. - // FIXME: It is probably not correct to add this unconditionally, what if - // we have a pattern like (a, _, c)? Then there is already a wildcard in - // the inner matches, and having two will cause two 'default:' blocks - // which is an error. - if (map.wildcard != nullptr) - { - inner_match_cases.push_back (*(map.wildcard.get ())); - } - - // match (tupB, tupC) { - // (b1, c1) => { blk1 }, - // (b3, c3) => { blk3 } - // } - HIR::MatchExpr inner_match (expr.get_mappings (), - remaining->clone_expr (), inner_match_cases, - AST::AttrVec (), expr.get_outer_attrs (), - expr.get_locus ()); - - inner_match = simplify_tuple_match (inner_match); - - auto outer_arm_pat = std::vector> (); - outer_arm_pat.emplace_back (map.heads[i]->clone_pattern ()); - - HIR::MatchArm outer_arm (std::move (outer_arm_pat), expr.get_locus ()); - - // Need to move the inner match to the heap and put it in a unique_ptr to - // build the actual match case of the outer expression - // auto inner_expr = std::unique_ptr (new HIR::MatchExpr - // (inner_match)); - auto inner_expr = inner_match.clone_expr (); - - // a1 => match (tupB, tupC) { ... } - HIR::MatchCase outer_case (expr.get_mappings (), outer_arm, - std::move (inner_expr)); - - cases.push_back (outer_case); - } - - // If there was a wildcard, make sure to include it at the outer match level - // too. - if (map.wildcard != nullptr) - { - cases.push_back (*(map.wildcard.get ())); - } - - // match tupA { - // a1 => match (tupB, tupC) { - // (b1, c1) => { blk1 }, - // (b3, c3) => { blk3 } - // } - // a2 => match (tupB, tupC) { - // (b2, c2) => { blk2 } - // } - // } - HIR::MatchExpr outer_match (expr.get_mappings (), std::move (head), cases, - AST::AttrVec (), expr.get_outer_attrs (), - expr.get_locus ()); - - return outer_match; -} - // Helper for CompileExpr::visit (HIR::MatchExpr). // Check that the scrutinee of EXPR is a valid kind of expression to match on. // Return the TypeKind of the scrutinee if it is valid, or TyTy::TypeKind::ERROR @@ -1347,111 +1031,6 @@ CompileExpr::visit (HIR::MatchExpr &expr) expr.get_locus ()); ctx->add_statement (assignment); - tree match_scrutinee_expr_qualifier_expr; - if (TyTy::is_primitive_type_kind (scrutinee_kind)) - { - match_scrutinee_expr_qualifier_expr = match_scrutinee_expr; - } - else if (scrutinee_kind == TyTy::TypeKind::ADT) - { - // need to access qualifier the 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 = Backend::struct_field_expression ( - match_scrutinee_expr, 0, expr.get_scrutinee_expr ()->get_locus ()); - match_scrutinee_expr_qualifier_expr = Backend::struct_field_expression ( - scrutinee_first_record_expr, 0, - expr.get_scrutinee_expr ()->get_locus ()); - } - else if (scrutinee_kind == TyTy::TypeKind::REF) - { - tree indirect - = indirect_expression (match_scrutinee_expr, expr.get_locus ()); - match_scrutinee_expr_qualifier_expr = indirect; - } - else if (scrutinee_kind == TyTy::TypeKind::TUPLE) - { - // match on tuple becomes a series of nested switches, with one level - // for each element of the tuple from left to right. - auto exprtype = expr.get_scrutinee_expr ()->get_expression_type (); - switch (exprtype) - { - case HIR::Expr::ExprType::Tuple: { - // Build an equivalent expression which is nicer to lower. - HIR::MatchExpr outer_match = simplify_tuple_match (expr); - - // We've rearranged the match into something that lowers better - // to GENERIC trees. - // For actually doing the lowering we need to compile the match - // we've just made. But we're half-way through compiling the - // original one. - // ... - // For now, let's just replace the original with the rearranged one - // we just made, and compile that instead. What could go wrong? :) - // - // FIXME: What about when we decide a temporary is needed above? - // We might have already pushed a statement for it that - // we no longer need. Probably need to rearrange the order - // of these steps. - expr = outer_match; - - scrutinee_kind = check_match_scrutinee (expr, ctx); - if (scrutinee_kind == TyTy::TypeKind::ERROR) - { - translated = error_mark_node; - return; - } - - // Now compile the scrutinee of the simplified match. - // FIXME: this part is duplicated from above. - match_scrutinee_expr - = CompileExpr::Compile (expr.get_scrutinee_expr ().get (), ctx); - - if (TyTy::is_primitive_type_kind (scrutinee_kind)) - { - match_scrutinee_expr_qualifier_expr = match_scrutinee_expr; - } - else if (scrutinee_kind == TyTy::TypeKind::ADT) - { - // need to access qualifier the 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 - = Backend::struct_field_expression ( - match_scrutinee_expr, 0, - expr.get_scrutinee_expr ()->get_locus ()); - match_scrutinee_expr_qualifier_expr - = Backend::struct_field_expression ( - scrutinee_first_record_expr, 0, - expr.get_scrutinee_expr ()->get_locus ()); - } - else - { - // FIXME: There are other cases, but it better not be a Tuple - rust_unreachable (); - } - } - break; - - case HIR::Expr::ExprType::Path: { - // FIXME - rust_unreachable (); - } - break; - - default: - rust_unreachable (); - } - } - else - { - // FIXME: match on other types of expressions not yet implemented. - rust_unreachable (); - } - // setup the end label so the cases can exit properly tree fndecl = fnctx.fndecl; location_t end_label_locus = expr.get_locus (); // FIXME @@ -1461,58 +1040,57 @@ CompileExpr::visit (HIR::MatchExpr &expr) tree end_label_decl_statement = Backend::label_definition_statement (end_label); - // setup the switch-body-block - location_t start_location = UNKNOWN_LOCATION; // FIXME - location_t end_location = UNKNOWN_LOCATION; // FIXME - tree switch_body_block = 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_t arm_locus = kase_arm.get_locus (); - tree case_label - = Backend::label (fndecl, "" /* empty creates an artificial label */, - arm_locus); - - // 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 (switch_kase_expr); + // setup the match-arm-body-block + location_t start_location = UNKNOWN_LOCATION; // FIXME + location_t end_location = UNKNOWN_LOCATION; // FIXME + tree arm_body_block = Backend::block (fndecl, enclosing_scope, {}, + start_location, end_location); + + ctx->push_block (arm_body_block); + // setup the bindings for the block 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); - tree result_reference = Backend::var_expression (tmp, arm_locus); - tree assignment - = Backend::assignment_statement (result_reference, kase_expr_tree, - arm_locus); - ctx->add_statement (assignment); - - // go to end label - tree goto_end_label - = build1_loc (arm_locus, GOTO_EXPR, void_type_node, end_label); - ctx->add_statement (goto_end_label); + // compile the expr and setup the assignment if required when tmp != + // NULL + location_t arm_locus = kase_arm.get_locus (); + tree kase_expr_tree + = CompileExpr::Compile (kase.get_expr ().get (), ctx); + tree result_reference = Backend::var_expression (tmp, arm_locus); + tree assignment + = Backend::assignment_statement (result_reference, kase_expr_tree, + arm_locus); + ctx->add_statement (assignment); + + // go to end label + tree goto_end_label + = build1_loc (arm_locus, GOTO_EXPR, void_type_node, end_label); + ctx->add_statement (goto_end_label); + + ctx->pop_block (); + + tree check_expr + = CompilePatternCheckExpr::Compile (kase_pattern.get (), + match_scrutinee_expr, ctx); + + tree check_stmt + = Backend::if_statement (NULL_TREE, check_expr, arm_body_block, + NULL_TREE, kase_pattern->get_locus ()); + + ctx->add_statement (check_stmt); + } } // setup the switch expression - tree match_body = ctx->pop_block (); - tree match_expr_stmt - = build2_loc (expr.get_locus (), 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); translated = Backend::var_expression (tmp, expr.get_locus ()); diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc index 5358c8a6dc4e..5e3026996aed 100644 --- a/gcc/rust/backend/rust-compile-pattern.cc +++ b/gcc/rust/backend/rust-compile-pattern.cc @@ -26,7 +26,7 @@ namespace Rust { namespace Compile { void -CompilePatternCaseLabelExpr::visit (HIR::PathInExpression &pattern) +CompilePatternCheckExpr::visit (HIR::PathInExpression &pattern) { // lookup the type TyTy::BaseType *lookup = nullptr; @@ -36,6 +36,7 @@ CompilePatternCaseLabelExpr::visit (HIR::PathInExpression &pattern) rust_assert (ok); // this must be an enum + // TODO: might not be rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT); TyTy::ADTType *adt = static_cast (lookup); rust_assert (adt->is_enum ()); @@ -50,38 +51,28 @@ CompilePatternCaseLabelExpr::visit (HIR::PathInExpression &pattern) ok = adt->lookup_variant_by_id (variant_id, &variant); rust_assert (ok); - HIR::Expr *discrim_expr = variant->get_discriminant (); - tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx); - tree folded_discrim_expr = fold_expr (discrim_expr_node); - tree case_low = folded_discrim_expr; + // find discriminant field of scrutinee + tree scrutinee_record_expr + = Backend::struct_field_expression (match_scrutinee_expr, 0, + pattern.get_locus ()); + tree scrutinee_expr_qualifier_expr + = Backend::struct_field_expression (scrutinee_record_expr, 0, + pattern.get_locus ()); - case_label_expr - = build_case_label (case_low, NULL_TREE, associated_case_label); -} - -void -CompilePatternCaseLabelExpr::visit (HIR::StructPattern &pattern) -{ - CompilePatternCaseLabelExpr::visit (pattern.get_path ()); -} + // must be enum + match_scrutinee_expr = scrutinee_expr_qualifier_expr; -void -CompilePatternCaseLabelExpr::visit (HIR::TupleStructPattern &pattern) -{ - CompilePatternCaseLabelExpr::visit (pattern.get_path ()); -} + HIR::Expr *discrim_expr = variant->get_discriminant (); + tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx); -void -CompilePatternCaseLabelExpr::visit (HIR::WildcardPattern &pattern) -{ - // operand 0 being NULL_TREE signifies this is the default case label see: - // tree.def for documentation for CASE_LABEL_EXPR - case_label_expr - = build_case_label (NULL_TREE, NULL_TREE, associated_case_label); + check_expr + = Backend::comparison_expression (ComparisonOperator::EQUAL, + match_scrutinee_expr, discrim_expr_node, + pattern.get_locus ()); } void -CompilePatternCaseLabelExpr::visit (HIR::LiteralPattern &pattern) +CompilePatternCheckExpr::visit (HIR::LiteralPattern &pattern) { // Compile the literal HIR::LiteralExpr *litexpr @@ -101,18 +92,9 @@ CompilePatternCaseLabelExpr::visit (HIR::LiteralPattern &pattern) tree lit = CompileExpr::Compile (litexpr, ctx); - case_label_expr = build_case_label (lit, NULL_TREE, associated_case_label); -} - -void -CompilePatternCaseLabelExpr::visit (HIR::AltPattern &pattern) -{ - const auto &alts = pattern.get_alts (); - for (auto &alt_pattern : alts) - { - CompilePatternCaseLabelExpr::Compile (alt_pattern.get (), - associated_case_label, ctx); - } + check_expr = Backend::comparison_expression (ComparisonOperator::EQUAL, + match_scrutinee_expr, lit, + pattern.get_locus ()); } static tree @@ -161,7 +143,7 @@ compile_range_pattern_bound (HIR::RangePatternBound *bound, } void -CompilePatternCaseLabelExpr::visit (HIR::RangePattern &pattern) +CompilePatternCheckExpr::visit (HIR::RangePattern &pattern) { tree upper = compile_range_pattern_bound (pattern.get_upper_bound ().get (), pattern.get_mappings (), @@ -170,7 +152,274 @@ CompilePatternCaseLabelExpr::visit (HIR::RangePattern &pattern) pattern.get_mappings (), pattern.get_locus (), ctx); - case_label_expr = build_case_label (lower, upper, associated_case_label); + tree check_lower + = Backend::comparison_expression (ComparisonOperator::GREATER_OR_EQUAL, + match_scrutinee_expr, lower, + pattern.get_locus ()); + tree check_upper + = Backend::comparison_expression (ComparisonOperator::LESS_OR_EQUAL, + match_scrutinee_expr, upper, + pattern.get_locus ()); + check_expr = Backend::arithmetic_or_logical_expression ( + ArithmeticOrLogicalOperator::BITWISE_AND, check_lower, check_upper, + pattern.get_locus ()); +} + +void +CompilePatternCheckExpr::visit (HIR::ReferencePattern &pattern) +{ + match_scrutinee_expr + = indirect_expression (match_scrutinee_expr, pattern.get_locus ()); + pattern.get_referenced_pattern ()->accept_vis (*this); +} + +void +CompilePatternCheckExpr::visit (HIR::AltPattern &pattern) +{ + auto &alts = pattern.get_alts (); + + check_expr = CompilePatternCheckExpr::Compile (alts.at (0).get (), + match_scrutinee_expr, ctx); + auto end = alts.end (); + for (auto i = alts.begin () + 1; i != end; i++) + { + tree next_expr + = CompilePatternCheckExpr::Compile (i->get (), match_scrutinee_expr, + ctx); + check_expr = Backend::arithmetic_or_logical_expression ( + ArithmeticOrLogicalOperator::BITWISE_OR, check_expr, next_expr, + (*i)->get_locus ()); + } +} + +void +CompilePatternCheckExpr::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 might be an enum + rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast (lookup); + + rust_assert (adt->number_of_variants () > 0); + TyTy::VariantDef *variant = nullptr; + if (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 = 0; + ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index); + rust_assert (ok); + + // find expected discriminant + // // need to access qualifier the 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 + HIR::Expr *discrim_expr = variant->get_discriminant (); + tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx); + + // find discriminant field of scrutinee + tree scrutinee_record_expr + = Backend::struct_field_expression (match_scrutinee_expr, variant_index, + pattern.get_path ().get_locus ()); + tree scrutinee_expr_qualifier_expr + = Backend::struct_field_expression (scrutinee_record_expr, 0, + pattern.get_path ().get_locus ()); + + check_expr + = Backend::comparison_expression (ComparisonOperator::EQUAL, + scrutinee_expr_qualifier_expr, + discrim_expr_node, + pattern.get_path ().get_locus ()); + + match_scrutinee_expr = scrutinee_record_expr; + } + else + { + variant = adt->get_variants ().at (0); + check_expr = boolean_true_node; + } + + 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 + rust_unreachable (); + } + break; + + case HIR::StructPatternField::ItemType::IDENT_PAT: { + HIR::StructPatternFieldIdentPat &ident + = static_cast (*field.get ()); + + size_t offs = 0; + ok = variant->lookup_field (ident.get_identifier ().as_string (), + nullptr, &offs); + rust_assert (ok); + + // we may be offsetting by + 1 here since the first field in the + // record is always the discriminator + offs += adt->is_enum (); + tree field_expr + = Backend::struct_field_expression (match_scrutinee_expr, offs, + ident.get_locus ()); + + tree check_expr_sub + = CompilePatternCheckExpr::Compile (ident.get_pattern ().get (), + field_expr, ctx); + check_expr = Backend::arithmetic_or_logical_expression ( + ArithmeticOrLogicalOperator::BITWISE_AND, check_expr, + check_expr_sub, ident.get_pattern ()->get_locus ()); + } + break; + + case HIR::StructPatternField::ItemType::IDENT: { + // ident pattern always matches - do nothing + } + break; + } + } +} + +void +CompilePatternCheckExpr::visit (HIR::TupleStructPattern &pattern) +{ + size_t tuple_field_index; + + // 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 might be an enum + rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast (lookup); + + rust_assert (adt->number_of_variants () > 0); + TyTy::VariantDef *variant = nullptr; + if (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 = 0; + ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index); + rust_assert (ok); + + // find expected discriminant + HIR::Expr *discrim_expr = variant->get_discriminant (); + tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx); + + // find discriminant field of scrutinee + tree scrutinee_record_expr + = Backend::struct_field_expression (match_scrutinee_expr, variant_index, + pattern.get_path ().get_locus ()); + tree scrutinee_expr_qualifier_expr + = Backend::struct_field_expression (scrutinee_record_expr, 0, + pattern.get_path ().get_locus ()); + + check_expr + = Backend::comparison_expression (ComparisonOperator::EQUAL, + scrutinee_expr_qualifier_expr, + discrim_expr_node, + pattern.get_path ().get_locus ()); + + match_scrutinee_expr = scrutinee_record_expr; + // we are offsetting by + 1 here since the first field in the record + // is always the discriminator + tuple_field_index = 1; + } + else + { + variant = adt->get_variants ().at (0); + check_expr = boolean_true_node; + tuple_field_index = 0; + } + + std::unique_ptr &items = pattern.get_items (); + switch (items->get_item_type ()) + { + case HIR::TupleStructItems::RANGE: { + // TODO + rust_unreachable (); + } + break; + + case HIR::TupleStructItems::NO_RANGE: { + HIR::TupleStructItemsNoRange &items_no_range + = static_cast (*items.get ()); + + rust_assert (items_no_range.get_patterns ().size () + == variant->num_fields ()); + + for (auto &pattern : items_no_range.get_patterns ()) + { + tree field_expr + = Backend::struct_field_expression (match_scrutinee_expr, + tuple_field_index++, + pattern->get_locus ()); + + tree check_expr_sub + = CompilePatternCheckExpr::Compile (pattern.get (), field_expr, + ctx); + check_expr = Backend::arithmetic_or_logical_expression ( + ArithmeticOrLogicalOperator::BITWISE_AND, check_expr, + check_expr_sub, pattern->get_locus ()); + } + } + break; + } +} + +void +CompilePatternCheckExpr::visit (HIR::TuplePattern &pattern) +{ + check_expr = boolean_true_node; + + switch (pattern.get_items ()->get_item_type ()) + { + case HIR::TuplePatternItems::RANGED: { + // TODO + gcc_unreachable (); + } + break; + + case HIR::TuplePatternItems::MULTIPLE: { + auto &items = static_cast ( + *pattern.get_items ()); + size_t tuple_field_index = 0; + + for (auto &pat : items.get_patterns ()) + { + tree field_expr + = Backend::struct_field_expression (match_scrutinee_expr, + tuple_field_index++, + pat->get_locus ()); + + tree check_expr_sub + = CompilePatternCheckExpr::Compile (pat.get (), field_expr, ctx); + check_expr = Backend::arithmetic_or_logical_expression ( + ArithmeticOrLogicalOperator::BITWISE_AND, check_expr, + check_expr_sub, pat->get_locus ()); + } + } + } } // setup the bindings diff --git a/gcc/rust/backend/rust-compile-pattern.h b/gcc/rust/backend/rust-compile-pattern.h index 78121e51ddcb..0cf26ccc7554 100644 --- a/gcc/rust/backend/rust-compile-pattern.h +++ b/gcc/rust/backend/rust-compile-pattern.h @@ -22,40 +22,49 @@ namespace Rust { namespace Compile { -class CompilePatternCaseLabelExpr : public HIRCompileBase, - public HIR::HIRPatternVisitor +class CompilePatternCheckExpr : public HIRCompileBase, + public HIR::HIRPatternVisitor { public: - static tree Compile (HIR::Pattern *pattern, tree associated_case_label, + static tree Compile (HIR::Pattern *pattern, tree match_scrutinee_expr, Context *ctx) { - CompilePatternCaseLabelExpr compiler (ctx, associated_case_label); + CompilePatternCheckExpr compiler (ctx, match_scrutinee_expr); pattern->accept_vis (compiler); - return compiler.case_label_expr; + rust_assert (compiler.check_expr); + return compiler.check_expr; } void visit (HIR::PathInExpression &pattern) override; - void visit (HIR::StructPattern &pattern) override; - void visit (HIR::TupleStructPattern &pattern) override; - void visit (HIR::WildcardPattern &pattern) override; + void visit (HIR::LiteralPattern &) override; void visit (HIR::RangePattern &pattern) override; - void visit (HIR::AltPattern &pattern) override; + void visit (HIR::ReferencePattern &) override; + void visit (HIR::AltPattern &) override; + void visit (HIR::StructPattern &) override; + void visit (HIR::TupleStructPattern &) override; + void visit (HIR::TuplePattern &) override; + + // Always succeeds + void visit (HIR::IdentifierPattern &) override + { + check_expr = boolean_true_node; + } + void visit (HIR::WildcardPattern &) override + { + check_expr = boolean_true_node; + } // Empty visit for unused Pattern HIR nodes. - void visit (HIR::IdentifierPattern &) override {} - void visit (HIR::LiteralPattern &) override; void visit (HIR::QualifiedPathInExpression &) override {} - void visit (HIR::ReferencePattern &) override {} void visit (HIR::SlicePattern &) override {} - void visit (HIR::TuplePattern &) override {} - CompilePatternCaseLabelExpr (Context *ctx, tree associated_case_label) - : HIRCompileBase (ctx), case_label_expr (error_mark_node), - associated_case_label (associated_case_label) + CompilePatternCheckExpr (Context *ctx, tree match_scrutinee_expr) + : HIRCompileBase (ctx), match_scrutinee_expr (match_scrutinee_expr), + check_expr (NULL_TREE) {} - tree case_label_expr; - tree associated_case_label; + tree match_scrutinee_expr; + tree check_expr; }; class CompilePatternBindings : public HIRCompileBase, diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc index 232f965bd7a4..9ca65b331267 100644 --- a/gcc/rust/backend/rust-compile-type.cc +++ b/gcc/rust/backend/rust-compile-type.cc @@ -79,9 +79,10 @@ TyTyResolveCompile::get_implicit_enumeral_node_type (Context *ctx) static tree enum_node = NULL_TREE; if (enum_node == NULL_TREE) { - enum_node - = Backend::named_type ("enumeral", Backend::integer_type (false, 64), - BUILTINS_LOCATION); + // equivalent to isize + enum_node = Backend::named_type ( + "enumeral", Backend::integer_type (false, Backend::get_pointer_size ()), + BUILTINS_LOCATION); } return enum_node; }