// Copyright (C) 2020-2023 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
// .
#include "rust-compile-pattern.h"
#include "rust-compile-expr.h"
#include "rust-compile-resolve-path.h"
#include "rust-constexpr.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 (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);
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;
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 ());
}
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);
}
void
CompilePatternCaseLabelExpr::visit (HIR::LiteralPattern &pattern)
{
// Compile the literal
HIR::LiteralExpr *litexpr
= new HIR::LiteralExpr (pattern.get_pattern_mappings (),
pattern.get_literal (), pattern.get_locus (),
std::vector ());
// Note: Floating point literals are currently accepted but will likely be
// forbidden in LiteralPatterns in a future version of Rust.
// See: https://github.com/rust-lang/rust/issues/41620
// For now, we cannot compile them anyway as CASE_LABEL_EXPR does not support
// floating point types.
if (pattern.get_literal ().get_lit_type () == HIR::Literal::LitType::FLOAT)
{
rust_sorry_at (pattern.get_locus (), "floating-point literal in pattern");
}
tree lit = CompileExpr::Compile (litexpr, ctx);
case_label_expr = build_case_label (lit, NULL_TREE, associated_case_label);
}
static tree
compile_range_pattern_bound (HIR::RangePatternBound *bound,
Analysis::NodeMapping mappings, Location locus,
Context *ctx)
{
tree result = NULL_TREE;
switch (bound->get_bound_type ())
{
case HIR::RangePatternBound::RangePatternBoundType::LITERAL: {
HIR::RangePatternBoundLiteral &ref
= *static_cast (bound);
HIR::LiteralExpr *litexpr
= new HIR::LiteralExpr (mappings, ref.get_literal (), locus,
std::vector ());
result = CompileExpr::Compile (litexpr, ctx);
}
break;
case HIR::RangePatternBound::RangePatternBoundType::PATH: {
HIR::RangePatternBoundPath &ref
= *static_cast (bound);
result = ResolvePathRef::Compile (ref.get_path (), ctx);
// If the path resolves to a const expression, fold it.
result = fold_expr (result);
}
break;
case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: {
HIR::RangePatternBoundQualPath &ref
= *static_cast (bound);
result = ResolvePathRef::Compile (ref.get_qualified_path (), ctx);
// If the path resolves to a const expression, fold it.
result = fold_expr (result);
}
}
return result;
}
void
CompilePatternCaseLabelExpr::visit (HIR::RangePattern &pattern)
{
tree upper = compile_range_pattern_bound (pattern.get_upper_bound ().get (),
pattern.get_pattern_mappings (),
pattern.get_locus (), ctx);
tree lower = compile_range_pattern_bound (pattern.get_lower_bound ().get (),
pattern.get_pattern_mappings (),
pattern.get_locus (), ctx);
case_label_expr = build_case_label (lower, upper, associated_case_label);
}
// 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 (lookup);
rust_assert (adt->number_of_variants () > 0);
int variant_index = 0;
TyTy::VariantDef *variant = adt->get_variants ().at (0);
if (adt->is_enum ())
{
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);
}
rust_assert (variant->get_variant_type ()
== TyTy::VariantDef::VariantType::TUPLE);
std::unique_ptr &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 (*items.get ());
rust_assert (items_no_range.get_patterns ().size ()
== variant->num_fields ());
if (adt->is_enum ())
{
// 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);
}
}
else
{
size_t tuple_field_index = 0;
for (auto &pattern : items_no_range.get_patterns ())
{
tree variant_accessor = match_scrutinee_expr;
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 (lookup);
rust_assert (adt->number_of_variants () > 0);
int variant_index = 0;
TyTy::VariantDef *variant = adt->get_variants ().at (0);
if (adt->is_enum ())
{
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);
}
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 (*field.get ());
size_t offs = 0;
ok
= variant->lookup_field (ident.get_identifier (), nullptr, &offs);
rust_assert (ok);
tree binding = error_mark_node;
if (adt->is_enum ())
{
tree variant_accessor
= ctx->get_backend ()->struct_field_expression (
match_scrutinee_expr, variant_index, ident.get_locus ());
// we are offsetting by + 1 here since the first field in the
// record is always the discriminator
binding = ctx->get_backend ()->struct_field_expression (
variant_accessor, offs + 1, ident.get_locus ());
}
else
{
tree variant_accessor = match_scrutinee_expr;
binding = ctx->get_backend ()->struct_field_expression (
variant_accessor, offs, ident.get_locus ());
}
ctx->insert_pattern_binding (ident.get_mappings ().get_hirid (),
binding);
}
break;
}
}
}
void
CompilePatternLet::visit (HIR::IdentifierPattern &pattern)
{
Bvariable *var = nullptr;
rust_assert (
ctx->lookup_var_decl (pattern.get_pattern_mappings ().get_hirid (), &var));
auto fnctx = ctx->peek_fn ();
if (ty->is_unit ())
{
ctx->add_statement (init_expr);
tree stmt_type = TyTyResolveCompile::compile (ctx, ty);
auto unit_type_init_expr
= ctx->get_backend ()->constructor_expression (stmt_type, false, {}, -1,
rval_locus);
auto s = ctx->get_backend ()->init_statement (fnctx.fndecl, var,
unit_type_init_expr);
ctx->add_statement (s);
}
else
{
auto s
= ctx->get_backend ()->init_statement (fnctx.fndecl, var, init_expr);
ctx->add_statement (s);
}
}
void
CompilePatternLet::visit (HIR::WildcardPattern &pattern)
{
tree init_stmt = NULL;
tree stmt_type = TyTyResolveCompile::compile (ctx, ty);
ctx->get_backend ()->temporary_variable (ctx->peek_fn ().fndecl, NULL_TREE,
stmt_type, init_expr, false,
pattern.get_locus (), &init_stmt);
ctx->add_statement (init_stmt);
}
void
CompilePatternLet::visit (HIR::TuplePattern &pattern)
{
rust_assert (pattern.has_tuple_pattern_items ());
tree tuple_type = TyTyResolveCompile::compile (ctx, ty);
tree init_stmt;
Bvariable *tmp_var
= ctx->get_backend ()->temporary_variable (ctx->peek_fn ().fndecl,
NULL_TREE, tuple_type, init_expr,
false, pattern.get_locus (),
&init_stmt);
tree access_expr
= ctx->get_backend ()->var_expression (tmp_var, pattern.get_locus ());
ctx->add_statement (init_stmt);
switch (pattern.get_items ()->get_pattern_type ())
{
case HIR::TuplePatternItems::TuplePatternItemType::RANGED: {
size_t tuple_idx = 0;
auto &items
= static_cast (*pattern.get_items ());
auto &items_lower = items.get_lower_patterns ();
auto &items_upper = items.get_upper_patterns ();
for (auto &sub : items_lower)
{
TyTy::BaseType *ty_sub = nullptr;
HirId pattern_id = pattern.get_pattern_mappings ().get_hirid ();
bool ok = ctx->get_tyctx ()->lookup_type (pattern_id, &ty_sub);
rust_assert (ok);
tree sub_init = ctx->get_backend ()->struct_field_expression (
access_expr, tuple_idx, sub->get_locus ());
CompilePatternLet::Compile (sub.get (), sub_init, ty_sub,
rval_locus, ctx);
tuple_idx++;
}
rust_assert (ty->get_kind () == TyTy::TypeKind::TUPLE);
tuple_idx = static_cast (*ty).num_fields ()
- items_upper.size ();
for (auto &sub : items_upper)
{
TyTy::BaseType *ty_sub = nullptr;
HirId pattern_id = pattern.get_pattern_mappings ().get_hirid ();
bool ok = ctx->get_tyctx ()->lookup_type (pattern_id, &ty_sub);
rust_assert (ok);
tree sub_init = ctx->get_backend ()->struct_field_expression (
access_expr, tuple_idx, sub->get_locus ());
CompilePatternLet::Compile (sub.get (), sub_init, ty_sub,
rval_locus, ctx);
tuple_idx++;
}
return;
}
case HIR::TuplePatternItems::TuplePatternItemType::MULTIPLE: {
size_t tuple_idx = 0;
auto &items = static_cast (
*pattern.get_items ());
for (auto &sub : items.get_patterns ())
{
TyTy::BaseType *ty_sub = nullptr;
HirId pattern_id = pattern.get_pattern_mappings ().get_hirid ();
bool ok = ctx->get_tyctx ()->lookup_type (pattern_id, &ty_sub);
rust_assert (ok);
tree sub_init = ctx->get_backend ()->struct_field_expression (
access_expr, tuple_idx, sub->get_locus ());
CompilePatternLet::Compile (sub.get (), sub_init, ty_sub,
rval_locus, ctx);
tuple_idx++;
}
return;
}
default: {
gcc_unreachable ();
}
}
}
} // namespace Compile
} // namespace Rust