Skip to content

Commit

Permalink
Add support for enums on the match expression
Browse files Browse the repository at this point in the history
Add initial support for MatchExpr there is an issue once LTO is enabled
where it hits and ICE:

/home/../rust/execute/torture/match1.rs:48:1: internal compiler error: in find_taken_edge_switch_expr, at tree-cfg.c:2481
0x121bc38 find_taken_edge_switch_expr(gswitch const*, tree_node*)
        ../../gccrs/gcc/tree-cfg.c:2481
0x121b925 find_taken_edge(basic_block_def*, tree_node*)
        ../../gccrs/gcc/tree-cfg.c:2377
0x147e7bb process_bb
        ../../gccrs/gcc/tree-ssa-sccvn.c:7300
0x14809a6 do_rpo_vn
        ../../gccrs/gcc/tree-ssa-sccvn.c:7777
0x1481a7c execute
        ../../gccrs/gcc/tree-ssa-sccvn.c:8045

I believe this might be due to the qualifier type used within the enum type
creating an enumeral type with all variants of it might fix this and or
moving to the qual_union_type instead of a big union.

Fixes #190
  • Loading branch information
philberty committed Dec 17, 2021
1 parent a8a3456 commit 26792ec
Show file tree
Hide file tree
Showing 7 changed files with 543 additions and 0 deletions.
1 change: 1 addition & 0 deletions gcc/rust/Make-lang.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
16 changes: 16 additions & 0 deletions gcc/rust/backend/rust-compile-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,21 @@ class Context
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});
Expand Down Expand Up @@ -326,6 +341,7 @@ class Context
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;
Expand Down
164 changes: 164 additions & 0 deletions gcc/rust/backend/rust-compile-expr.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -154,6 +157,167 @@ CompileExpr::visit (HIR::DereferenceExpr &expr)
known_valid, expr.get_locus ());
}

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 = create_artificial_label (arm_locus.gcc_location ());

// 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);

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)
{
Expand Down
6 changes: 6 additions & 0 deletions gcc/rust/backend/rust-compile-expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ class CompileExpr : public HIRCompileBase
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 (),
Expand Down Expand Up @@ -1043,6 +1047,8 @@ class CompileExpr : public HIRCompileBase

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,
Expand Down
Loading

0 comments on commit 26792ec

Please sign in to comment.