blob: a1db6ade9fbd10bdb24cc6f5cbbb0ce295b3551a [file] [log] [blame]
// 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
// <http://www.gnu.org/licenses/>.
#include "rust-compile-type.h"
#include "rust-compile-expr.h"
#include "rust-constexpr.h"
#include "rust-gcc.h"
#include "tree.h"
namespace Rust {
namespace Compile {
static const std::string RUST_ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR";
TyTyResolveCompile::TyTyResolveCompile (Context *ctx, bool trait_object_mode)
: ctx (ctx), trait_object_mode (trait_object_mode),
translated (error_mark_node), recurisve_ops (0)
{}
tree
TyTyResolveCompile::compile (Context *ctx, const TyTy::BaseType *ty,
bool trait_object_mode)
{
TyTyResolveCompile compiler (ctx, trait_object_mode);
ty->accept_vis (compiler);
if (compiler.translated != error_mark_node
&& TYPE_NAME (compiler.translated) != NULL)
{
// canonicalize the type
compiler.translated = ctx->insert_compiled_type (compiler.translated);
}
return compiler.translated;
}
// see: gcc/c/c-decl.cc:8230-8241
// https://github.com/Rust-GCC/gccrs/blob/0024bc2f028369b871a65ceb11b2fddfb0f9c3aa/gcc/c/c-decl.c#L8229-L8241
tree
TyTyResolveCompile::get_implicit_enumeral_node_type (Context *ctx)
{
// static tree enum_node = NULL_TREE;
// if (enum_node == NULL_TREE)
// {
// enum_node = make_node (ENUMERAL_TYPE);
// SET_TYPE_MODE (enum_node, TYPE_MODE (unsigned_type_node));
// SET_TYPE_ALIGN (enum_node, TYPE_ALIGN (unsigned_type_node));
// TYPE_USER_ALIGN (enum_node) = 0;
// TYPE_UNSIGNED (enum_node) = 1;
// TYPE_PRECISION (enum_node) = TYPE_PRECISION (unsigned_type_node);
// TYPE_MIN_VALUE (enum_node) = TYPE_MIN_VALUE (unsigned_type_node);
// TYPE_MAX_VALUE (enum_node) = TYPE_MAX_VALUE (unsigned_type_node);
// // tree identifier = ctx->get_backend ()->get_identifier_node
// // ("enumeral"); tree enum_decl
// // = build_decl (BUILTINS_LOCATION, TYPE_DECL, identifier,
// enum_node);
// // TYPE_NAME (enum_node) = enum_decl;
// }
// return enum_node;
static tree enum_node = NULL_TREE;
if (enum_node == NULL_TREE)
{
enum_node = ctx->get_backend ()->named_type (
"enumeral", ctx->get_backend ()->integer_type (false, 64),
Linemap::predeclared_location ());
}
return enum_node;
}
void
TyTyResolveCompile::visit (const TyTy::ErrorType &)
{
translated = error_mark_node;
}
void
TyTyResolveCompile::visit (const TyTy::InferType &)
{
translated = error_mark_node;
}
void
TyTyResolveCompile::visit (const TyTy::ClosureType &type)
{
auto mappings = ctx->get_mappings ();
std::vector<Backend::typed_identifier> fields;
size_t i = 0;
for (const auto &capture : type.get_captures ())
{
// lookup the HirId
HirId ref = UNKNOWN_HIRID;
bool ok = mappings->lookup_node_to_hir (capture, &ref);
rust_assert (ok);
// lookup the var decl type
TyTy::BaseType *lookup = nullptr;
bool found = ctx->get_tyctx ()->lookup_type (ref, &lookup);
rust_assert (found);
// FIXME get the var pattern name
std::string mappings_name = "capture_" + std::to_string (i);
// FIXME
// this should be based on the closure move-ability
tree decl_type = TyTyResolveCompile::compile (ctx, lookup);
tree capture_type = build_reference_type (decl_type);
fields.push_back (Backend::typed_identifier (mappings_name, capture_type,
type.get_ident ().locus));
}
tree type_record = ctx->get_backend ()->struct_type (fields);
RS_CLOSURE_FLAG (type_record) = 1;
std::string named_struct_str
= type.get_ident ().path.get () + "::{{closure}}";
translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
type.get_ident ().locus);
}
void
TyTyResolveCompile::visit (const TyTy::ProjectionType &type)
{
type.get ()->accept_vis (*this);
}
void
TyTyResolveCompile::visit (const TyTy::PlaceholderType &type)
{
type.resolve ()->accept_vis (*this);
}
void
TyTyResolveCompile::visit (const TyTy::ParamType &param)
{
if (recurisve_ops++ >= rust_max_recursion_depth)
{
rust_error_at (Location (),
"%<recursion depth%> count exceeds limit of %i (use "
"%<frust-max-recursion-depth=%> to increase the limit)",
rust_max_recursion_depth);
translated = error_mark_node;
return;
}
param.resolve ()->accept_vis (*this);
}
void
TyTyResolveCompile::visit (const TyTy::FnType &type)
{
Backend::typed_identifier receiver;
std::vector<Backend::typed_identifier> parameters;
std::vector<Backend::typed_identifier> results;
if (!type.get_return_type ()->is_unit ())
{
auto hir_type = type.get_return_type ();
auto ret = TyTyResolveCompile::compile (ctx, hir_type, trait_object_mode);
results.push_back (Backend::typed_identifier (
"_", ret,
ctx->get_mappings ()->lookup_location (hir_type->get_ref ())));
}
for (auto &param_pair : type.get_params ())
{
auto param_tyty = param_pair.second;
auto compiled_param_type
= TyTyResolveCompile::compile (ctx, param_tyty, trait_object_mode);
auto compiled_param = Backend::typed_identifier (
param_pair.first->as_string (), compiled_param_type,
ctx->get_mappings ()->lookup_location (param_tyty->get_ref ()));
parameters.push_back (compiled_param);
}
if (!type.is_varadic ())
translated
= ctx->get_backend ()->function_type (receiver, parameters, results, NULL,
type.get_ident ().locus);
else
translated
= ctx->get_backend ()->function_type_varadic (receiver, parameters,
results, NULL,
type.get_ident ().locus);
}
void
TyTyResolveCompile::visit (const TyTy::FnPtr &type)
{
tree result_type = TyTyResolveCompile::compile (ctx, type.get_return_type ());
std::vector<tree> parameters;
auto &params = type.get_params ();
for (auto &p : params)
{
tree pty = TyTyResolveCompile::compile (ctx, p.get_tyty ());
parameters.push_back (pty);
}
translated = ctx->get_backend ()->function_ptr_type (result_type, parameters,
type.get_ident ().locus);
}
void
TyTyResolveCompile::visit (const TyTy::ADTType &type)
{
tree type_record = error_mark_node;
if (!type.is_enum ())
{
rust_assert (type.number_of_variants () == 1);
TyTy::VariantDef &variant = *type.get_variants ().at (0);
std::vector<Backend::typed_identifier> fields;
for (size_t i = 0; i < variant.num_fields (); i++)
{
const TyTy::StructFieldType *field = variant.get_field_at_index (i);
tree compiled_field_ty
= TyTyResolveCompile::compile (ctx, field->get_field_type ());
Backend::typed_identifier f (field->get_name (), compiled_field_ty,
ctx->get_mappings ()->lookup_location (
type.get_ty_ref ()));
fields.push_back (std::move (f));
}
type_record = type.is_union ()
? ctx->get_backend ()->union_type (fields)
: ctx->get_backend ()->struct_type (fields);
}
else
{
// see:
// https://github.com/bminor/binutils-gdb/blob/527b8861cd472385fa9160a91dd6d65a25c41987/gdb/dwarf2/read.c#L9010-L9241
//
// enums are actually a big union so for example the rust enum:
//
// enum AnEnum {
// A,
// B,
// C (char),
// D { x: i64, y: i64 },
// }
//
// we actually turn this into
//
// union {
// struct A { int RUST$ENUM$DISR; }; <- this is a data-less variant
// struct B { int RUST$ENUM$DISR; }; <- this is a data-less variant
// struct C { int RUST$ENUM$DISR; char __0; };
// struct D { int RUST$ENUM$DISR; i64 x; i64 y; };
// }
//
// Ada, qual_union_types might still work for this but I am not 100% sure.
// I ran into some issues lets reuse our normal union and ask Ada people
// about it.
std::vector<tree> variant_records;
for (auto &variant : type.get_variants ())
{
std::vector<Backend::typed_identifier> fields;
// add in the qualifier field for the variant
tree enumeral_type
= TyTyResolveCompile::get_implicit_enumeral_node_type (ctx);
Backend::typed_identifier f (RUST_ENUM_DISR_FIELD_NAME, enumeral_type,
ctx->get_mappings ()->lookup_location (
variant->get_id ()));
fields.push_back (std::move (f));
// compile the rest of the fields
for (size_t i = 0; i < variant->num_fields (); i++)
{
const TyTy::StructFieldType *field
= variant->get_field_at_index (i);
tree compiled_field_ty
= TyTyResolveCompile::compile (ctx, field->get_field_type ());
std::string field_name = field->get_name ();
if (variant->get_variant_type ()
== TyTy::VariantDef::VariantType::TUPLE)
field_name = "__" + field->get_name ();
Backend::typed_identifier f (
field_name, compiled_field_ty,
ctx->get_mappings ()->lookup_location (type.get_ty_ref ()));
fields.push_back (std::move (f));
}
tree variant_record = ctx->get_backend ()->struct_type (fields);
tree named_variant_record = ctx->get_backend ()->named_type (
variant->get_ident ().path.get (), variant_record,
variant->get_ident ().locus);
// set the qualifier to be a builtin
DECL_ARTIFICIAL (TYPE_FIELDS (variant_record)) = 1;
// add them to the list
variant_records.push_back (named_variant_record);
}
// now we need to make the actual union, but first we need to make
// named_type TYPE_DECL's out of the variants
size_t i = 0;
std::vector<Backend::typed_identifier> enum_fields;
for (auto &variant_record : variant_records)
{
TyTy::VariantDef *variant = type.get_variants ().at (i++);
std::string implicit_variant_name = variant->get_identifier ();
Backend::typed_identifier f (implicit_variant_name, variant_record,
ctx->get_mappings ()->lookup_location (
type.get_ty_ref ()));
enum_fields.push_back (std::move (f));
}
// finally make the union or the enum
type_record = ctx->get_backend ()->union_type (enum_fields);
}
// Handle repr options
// TODO: "packed" should only narrow type alignment and "align" should only
// widen it. Do we need to check and enforce this here, or is it taken care of
// later on in the gcc middle-end?
TyTy::ADTType::ReprOptions repr = type.get_repr_options ();
if (repr.pack)
{
TYPE_PACKED (type_record) = 1;
if (repr.pack > 1)
{
SET_TYPE_ALIGN (type_record, repr.pack * 8);
TYPE_USER_ALIGN (type_record) = 1;
}
}
else if (repr.align)
{
SET_TYPE_ALIGN (type_record, repr.align * 8);
TYPE_USER_ALIGN (type_record) = 1;
}
std::string named_struct_str
= type.get_ident ().path.get () + type.subst_as_string ();
translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
type.get_ident ().locus);
}
void
TyTyResolveCompile::visit (const TyTy::TupleType &type)
{
if (type.num_fields () == 0)
{
translated = ctx->get_backend ()->unit_type ();
return;
}
// create implicit struct
std::vector<Backend::typed_identifier> fields;
for (size_t i = 0; i < type.num_fields (); i++)
{
TyTy::BaseType *field = type.get_field (i);
tree compiled_field_ty = TyTyResolveCompile::compile (ctx, field);
// rustc uses the convention __N, where N is an integer, to
// name the fields of a tuple. We follow this as well,
// because this is used by GDB. One further reason to prefer
// this, rather than simply emitting the integer, is that this
// approach makes it simpler to use a C-only debugger, or
// GDB's C mode, when debugging Rust.
Backend::typed_identifier f ("__" + std::to_string (i), compiled_field_ty,
ctx->get_mappings ()->lookup_location (
type.get_ty_ref ()));
fields.push_back (std::move (f));
}
tree struct_type_record = ctx->get_backend ()->struct_type (fields);
translated
= ctx->get_backend ()->named_type (type.as_string (), struct_type_record,
type.get_ident ().locus);
}
void
TyTyResolveCompile::visit (const TyTy::ArrayType &type)
{
tree element_type
= TyTyResolveCompile::compile (ctx, type.get_element_type ());
ctx->push_const_context ();
tree capacity_expr = CompileExpr::Compile (&type.get_capacity_expr (), ctx);
ctx->pop_const_context ();
tree folded_capacity_expr = fold_expr (capacity_expr);
translated
= ctx->get_backend ()->array_type (element_type, folded_capacity_expr);
}
void
TyTyResolveCompile::visit (const TyTy::SliceType &type)
{
tree type_record = create_slice_type_record (type);
std::string named_struct_str
= std::string ("[") + type.get_element_type ()->get_name () + "]";
translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
type.get_ident ().locus);
}
void
TyTyResolveCompile::visit (const TyTy::BoolType &)
{
translated
= ctx->get_backend ()->named_type ("bool",
ctx->get_backend ()->bool_type (),
Linemap::predeclared_location ());
}
void
TyTyResolveCompile::visit (const TyTy::IntType &type)
{
switch (type.get_int_kind ())
{
case TyTy::IntType::I8:
translated = ctx->get_backend ()->named_type (
"i8", ctx->get_backend ()->integer_type (false, 8),
Linemap::predeclared_location ());
return;
case TyTy::IntType::I16:
translated = ctx->get_backend ()->named_type (
"i16", ctx->get_backend ()->integer_type (false, 16),
Linemap::predeclared_location ());
return;
case TyTy::IntType::I32:
translated = ctx->get_backend ()->named_type (
"i32", ctx->get_backend ()->integer_type (false, 32),
Linemap::predeclared_location ());
return;
case TyTy::IntType::I64:
translated = ctx->get_backend ()->named_type (
"i64", ctx->get_backend ()->integer_type (false, 64),
Linemap::predeclared_location ());
return;
case TyTy::IntType::I128:
translated = ctx->get_backend ()->named_type (
"i128", ctx->get_backend ()->integer_type (false, 128),
Linemap::predeclared_location ());
return;
}
}
void
TyTyResolveCompile::visit (const TyTy::UintType &type)
{
switch (type.get_uint_kind ())
{
case TyTy::UintType::U8:
translated = ctx->get_backend ()->named_type (
"u8", ctx->get_backend ()->integer_type (true, 8),
Linemap::predeclared_location ());
return;
case TyTy::UintType::U16:
translated = ctx->get_backend ()->named_type (
"u16", ctx->get_backend ()->integer_type (true, 16),
Linemap::predeclared_location ());
return;
case TyTy::UintType::U32:
translated = ctx->get_backend ()->named_type (
"u32", ctx->get_backend ()->integer_type (true, 32),
Linemap::predeclared_location ());
return;
case TyTy::UintType::U64:
translated = ctx->get_backend ()->named_type (
"u64", ctx->get_backend ()->integer_type (true, 64),
Linemap::predeclared_location ());
return;
case TyTy::UintType::U128:
translated = ctx->get_backend ()->named_type (
"u128", ctx->get_backend ()->integer_type (true, 128),
Linemap::predeclared_location ());
return;
}
}
void
TyTyResolveCompile::visit (const TyTy::FloatType &type)
{
switch (type.get_float_kind ())
{
case TyTy::FloatType::F32:
translated
= ctx->get_backend ()->named_type ("f32",
ctx->get_backend ()->float_type (32),
Linemap::predeclared_location ());
return;
case TyTy::FloatType::F64:
translated
= ctx->get_backend ()->named_type ("f64",
ctx->get_backend ()->float_type (64),
Linemap::predeclared_location ());
return;
}
}
void
TyTyResolveCompile::visit (const TyTy::USizeType &)
{
translated = ctx->get_backend ()->named_type (
"usize",
ctx->get_backend ()->integer_type (
true, ctx->get_backend ()->get_pointer_size ()),
Linemap::predeclared_location ());
}
void
TyTyResolveCompile::visit (const TyTy::ISizeType &)
{
translated = ctx->get_backend ()->named_type (
"isize",
ctx->get_backend ()->integer_type (
false, ctx->get_backend ()->get_pointer_size ()),
Linemap::predeclared_location ());
}
void
TyTyResolveCompile::visit (const TyTy::CharType &)
{
translated
= ctx->get_backend ()->named_type ("char",
ctx->get_backend ()->wchar_type (),
Linemap::predeclared_location ());
}
void
TyTyResolveCompile::visit (const TyTy::ReferenceType &type)
{
const TyTy::SliceType *slice = nullptr;
const TyTy::StrType *str = nullptr;
if (type.is_dyn_slice_type (&slice))
{
tree type_record = create_slice_type_record (*slice);
std::string dyn_slice_type_str
= std::string (type.is_mutable () ? "&mut " : "&") + "["
+ slice->get_element_type ()->get_name () + "]";
translated
= ctx->get_backend ()->named_type (dyn_slice_type_str, type_record,
slice->get_locus ());
return;
}
else if (type.is_dyn_str_type (&str))
{
tree type_record = create_str_type_record (*str);
std::string dyn_str_type_str
= std::string (type.is_mutable () ? "&mut " : "&") + "str";
translated
= ctx->get_backend ()->named_type (dyn_str_type_str, type_record,
str->get_locus ());
return;
}
tree base_compiled_type
= TyTyResolveCompile::compile (ctx, type.get_base (), trait_object_mode);
if (type.is_mutable ())
{
translated = ctx->get_backend ()->reference_type (base_compiled_type);
}
else
{
auto base = ctx->get_backend ()->immutable_type (base_compiled_type);
translated = ctx->get_backend ()->reference_type (base);
}
}
void
TyTyResolveCompile::visit (const TyTy::PointerType &type)
{
const TyTy::SliceType *slice = nullptr;
const TyTy::StrType *str = nullptr;
if (type.is_dyn_slice_type (&slice))
{
tree type_record = create_slice_type_record (*slice);
std::string dyn_slice_type_str
= std::string (type.is_mutable () ? "*mut " : "*const ") + "["
+ slice->get_element_type ()->get_name () + "]";
translated
= ctx->get_backend ()->named_type (dyn_slice_type_str, type_record,
slice->get_locus ());
return;
}
else if (type.is_dyn_str_type (&str))
{
tree type_record = create_str_type_record (*str);
std::string dyn_str_type_str
= std::string (type.is_mutable () ? "*mut " : "*const ") + "str";
translated
= ctx->get_backend ()->named_type (dyn_str_type_str, type_record,
str->get_locus ());
return;
}
tree base_compiled_type
= TyTyResolveCompile::compile (ctx, type.get_base (), trait_object_mode);
if (type.is_mutable ())
{
translated = ctx->get_backend ()->pointer_type (base_compiled_type);
}
else
{
auto base = ctx->get_backend ()->immutable_type (base_compiled_type);
translated = ctx->get_backend ()->pointer_type (base);
}
}
void
TyTyResolveCompile::visit (const TyTy::StrType &type)
{
tree raw_str = create_str_type_record (type);
translated
= ctx->get_backend ()->named_type ("str", raw_str,
Linemap::predeclared_location ());
}
void
TyTyResolveCompile::visit (const TyTy::NeverType &)
{
translated = ctx->get_backend ()->unit_type ();
}
void
TyTyResolveCompile::visit (const TyTy::DynamicObjectType &type)
{
if (trait_object_mode)
{
translated = ctx->get_backend ()->integer_type (
true, ctx->get_backend ()->get_pointer_size ());
return;
}
// create implicit struct
auto items = type.get_object_items ();
std::vector<Backend::typed_identifier> fields;
tree uint = ctx->get_backend ()->integer_type (
true, ctx->get_backend ()->get_pointer_size ());
tree uintptr_ty = build_pointer_type (uint);
Backend::typed_identifier f ("pointer", uintptr_ty,
ctx->get_mappings ()->lookup_location (
type.get_ty_ref ()));
fields.push_back (std::move (f));
tree vtable_size = build_int_cst (size_type_node, items.size ());
tree vtable_type = ctx->get_backend ()->array_type (uintptr_ty, vtable_size);
Backend::typed_identifier vtf ("vtable", vtable_type,
ctx->get_mappings ()->lookup_location (
type.get_ty_ref ()));
fields.push_back (std::move (vtf));
tree type_record = ctx->get_backend ()->struct_type (fields);
translated = ctx->get_backend ()->named_type (type.get_name (), type_record,
type.get_ident ().locus);
}
tree
TyTyResolveCompile::create_slice_type_record (const TyTy::SliceType &type)
{
// lookup usize
TyTy::BaseType *usize = nullptr;
bool ok = ctx->get_tyctx ()->lookup_builtin ("usize", &usize);
rust_assert (ok);
tree element_type
= TyTyResolveCompile::compile (ctx, type.get_element_type ());
tree data_field_ty = build_pointer_type (element_type);
Backend::typed_identifier data_field ("data", data_field_ty,
type.get_locus ());
tree len_field_ty = TyTyResolveCompile::compile (ctx, usize);
Backend::typed_identifier len_field ("len", len_field_ty, type.get_locus ());
tree record = ctx->get_backend ()->struct_type ({data_field, len_field});
SLICE_FLAG (record) = 1;
TYPE_MAIN_VARIANT (record) = ctx->insert_main_variant (record);
return record;
}
tree
TyTyResolveCompile::create_str_type_record (const TyTy::StrType &type)
{
// lookup usize
TyTy::BaseType *usize = nullptr;
bool ok = ctx->get_tyctx ()->lookup_builtin ("usize", &usize);
rust_assert (ok);
tree char_ptr = build_pointer_type (char_type_node);
tree const_char_type = build_qualified_type (char_ptr, TYPE_QUAL_CONST);
tree element_type = const_char_type;
tree data_field_ty = build_pointer_type (element_type);
Backend::typed_identifier data_field ("data", data_field_ty,
type.get_locus ());
tree len_field_ty = TyTyResolveCompile::compile (ctx, usize);
Backend::typed_identifier len_field ("len", len_field_ty, type.get_locus ());
tree record = ctx->get_backend ()->struct_type ({data_field, len_field});
SLICE_FLAG (record) = 1;
TYPE_MAIN_VARIANT (record) = ctx->insert_main_variant (record);
return record;
}
} // namespace Compile
} // namespace Rust