aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/backend/rust-compile.cc
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2022-10-21 14:29:50 +0200
committerArthur Cohen <arthur.cohen@embecosm.com>2022-12-13 14:00:07 +0100
commitcfbda2f78baac4f329efe1838401b4ae2ed5b6a5 (patch)
tree63d04c0e9b01fe40f25380329f5801707583db1b /gcc/rust/backend/rust-compile.cc
parent019b2f15581948806ee14a6d05b09ec94f04c966 (diff)
downloadgcc-cfbda2f78baac4f329efe1838401b4ae2ed5b6a5.zip
gcc-cfbda2f78baac4f329efe1838401b4ae2ed5b6a5.tar.gz
gcc-cfbda2f78baac4f329efe1838401b4ae2ed5b6a5.tar.bz2
gccrs: Add HIR to GCC GENERIC lowering entry point
This patch contains the entry point and utilities used for the lowering of HIR nodes to `tree`s. It also contains a constant evaluator, ported over from the C++ frontend. gcc/rust/ * backend/rust-compile-context.cc: New. * backend/rust-compile-context.h: New. * backend/rust-compile.cc: New. * backend/rust-compile.h: New. * backend/rust-constexpr.cc: New. * backend/rust-constexpr.h: New. Co-authored-by: David Faust <david.faust@oracle.com> Co-authored-by: Faisal Abbas <90.abbasfaisal@gmail.com> Signed-off-by: Faisal Abbas <90.abbasfaisal@gmail.com>
Diffstat (limited to 'gcc/rust/backend/rust-compile.cc')
-rw-r--r--gcc/rust/backend/rust-compile.cc414
1 files changed, 414 insertions, 0 deletions
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
new file mode 100644
index 0000000..0ccb98d
--- /dev/null
+++ b/gcc/rust/backend/rust-compile.cc
@@ -0,0 +1,414 @@
+// Copyright (C) 2020-2022 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.h"
+#include "rust-compile-item.h"
+#include "rust-compile-implitem.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-struct-field-expr.h"
+#include "rust-compile-stmt.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-type-bounds.h"
+#include "rust-hir-dot-operator.h"
+#include "rust-compile-block.h"
+
+namespace Rust {
+namespace Compile {
+
+CompileCrate::CompileCrate (HIR::Crate &crate, Context *ctx)
+ : crate (crate), ctx (ctx)
+{}
+
+CompileCrate::~CompileCrate () {}
+
+void
+CompileCrate::Compile (HIR::Crate &crate, Context *ctx)
+{
+ CompileCrate c (crate, ctx);
+ c.go ();
+}
+
+void
+CompileCrate::go ()
+{
+ for (auto &item : crate.items)
+ CompileItem::compile (item.get (), ctx);
+}
+
+// Shared methods in compilation
+
+tree
+HIRCompileBase::coercion_site (HirId id, tree rvalue,
+ const TyTy::BaseType *rval,
+ const TyTy::BaseType *lval,
+ Location lvalue_locus, Location rvalue_locus)
+{
+ std::vector<Resolver::Adjustment> *adjustments = nullptr;
+ bool ok = ctx->get_tyctx ()->lookup_autoderef_mappings (id, &adjustments);
+ if (ok)
+ {
+ rvalue = resolve_adjustements (*adjustments, rvalue, rvalue_locus);
+ }
+
+ return coercion_site1 (rvalue, rval, lval, lvalue_locus, rvalue_locus);
+}
+
+tree
+HIRCompileBase::coercion_site1 (tree rvalue, const TyTy::BaseType *rval,
+ const TyTy::BaseType *lval,
+ Location lvalue_locus, Location rvalue_locus)
+{
+ if (rvalue == error_mark_node)
+ return error_mark_node;
+
+ const TyTy::BaseType *actual = rval->destructure ();
+ const TyTy::BaseType *expected = lval->destructure ();
+
+ if (expected->get_kind () == TyTy::TypeKind::REF)
+ {
+ // this is a dyn object
+ if (SLICE_TYPE_P (TREE_TYPE (rvalue)))
+ {
+ return rvalue;
+ }
+
+ // bad coercion... of something to a reference
+ if (actual->get_kind () != TyTy::TypeKind::REF)
+ return error_mark_node;
+
+ const TyTy::ReferenceType *exp
+ = static_cast<const TyTy::ReferenceType *> (expected);
+ const TyTy::ReferenceType *act
+ = static_cast<const TyTy::ReferenceType *> (actual);
+
+ tree deref_rvalue = indirect_expression (rvalue, rvalue_locus);
+ tree coerced
+ = coercion_site1 (deref_rvalue, act->get_base (), exp->get_base (),
+ lvalue_locus, rvalue_locus);
+ if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced)))
+ return coerced;
+
+ return address_expression (coerced, rvalue_locus);
+ }
+ else if (expected->get_kind () == TyTy::TypeKind::POINTER)
+ {
+ // this is a dyn object
+ if (SLICE_TYPE_P (TREE_TYPE (rvalue)))
+ {
+ return rvalue;
+ }
+
+ // bad coercion... of something to a reference
+ bool valid_coercion = actual->get_kind () == TyTy::TypeKind::REF
+ || actual->get_kind () == TyTy::TypeKind::POINTER;
+ if (!valid_coercion)
+ return error_mark_node;
+
+ const TyTy::ReferenceType *exp
+ = static_cast<const TyTy::ReferenceType *> (expected);
+
+ TyTy::BaseType *actual_base = nullptr;
+ if (actual->get_kind () == TyTy::TypeKind::REF)
+ {
+ const TyTy::ReferenceType *act
+ = static_cast<const TyTy::ReferenceType *> (actual);
+
+ actual_base = act->get_base ();
+ }
+ else if (actual->get_kind () == TyTy::TypeKind::POINTER)
+ {
+ const TyTy::PointerType *act
+ = static_cast<const TyTy::PointerType *> (actual);
+
+ actual_base = act->get_base ();
+ }
+ rust_assert (actual_base != nullptr);
+
+ tree deref_rvalue = indirect_expression (rvalue, rvalue_locus);
+ tree coerced
+ = coercion_site1 (deref_rvalue, actual_base, exp->get_base (),
+ lvalue_locus, rvalue_locus);
+
+ if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced)))
+ return coerced;
+
+ return address_expression (coerced, rvalue_locus);
+ }
+ else if (expected->get_kind () == TyTy::TypeKind::ARRAY)
+ {
+ if (actual->get_kind () != TyTy::TypeKind::ARRAY)
+ return error_mark_node;
+
+ tree tree_rval_type = TyTyResolveCompile::compile (ctx, actual);
+ tree tree_lval_type = TyTyResolveCompile::compile (ctx, expected);
+ if (!verify_array_capacities (tree_lval_type, tree_rval_type,
+ lvalue_locus, rvalue_locus))
+ return error_mark_node;
+ }
+ else if (expected->get_kind () == TyTy::TypeKind::SLICE)
+ {
+ // bad coercion
+ bool valid_coercion = actual->get_kind () == TyTy::TypeKind::SLICE
+ || actual->get_kind () == TyTy::TypeKind::ARRAY;
+ if (!valid_coercion)
+ return error_mark_node;
+
+ // nothing to do here
+ if (actual->get_kind () == TyTy::TypeKind::SLICE)
+ return rvalue;
+
+ // return an unsized coercion
+ Resolver::Adjustment unsize_adj (
+ Resolver::Adjustment::AdjustmentType::UNSIZE, actual, expected);
+ return resolve_unsized_adjustment (unsize_adj, rvalue, rvalue_locus);
+ }
+
+ return rvalue;
+}
+
+tree
+HIRCompileBase::coerce_to_dyn_object (tree compiled_ref,
+ const TyTy::BaseType *actual,
+ const TyTy::DynamicObjectType *ty,
+ Location locus)
+{
+ tree dynamic_object = TyTyResolveCompile::compile (ctx, ty);
+ tree dynamic_object_fields = TYPE_FIELDS (dynamic_object);
+ tree vtable_field = DECL_CHAIN (dynamic_object_fields);
+ rust_assert (TREE_CODE (TREE_TYPE (vtable_field)) == ARRAY_TYPE);
+
+ //' this assumes ordering and current the structure is
+ // __trait_object_ptr
+ // [list of function ptrs]
+
+ std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+ probed_bounds_for_receiver = Resolver::TypeBoundsProbe::Probe (actual);
+
+ tree address_of_compiled_ref = null_pointer_node;
+ if (!actual->is_unit ())
+ address_of_compiled_ref = address_expression (compiled_ref, locus);
+
+ std::vector<tree> vtable_ctor_elems;
+ std::vector<unsigned long> vtable_ctor_idx;
+ unsigned long i = 0;
+ for (auto &bound : ty->get_object_items ())
+ {
+ const Resolver::TraitItemReference *item = bound.first;
+ const TyTy::TypeBoundPredicate *predicate = bound.second;
+
+ auto address = compute_address_for_trait_item (item, predicate,
+ probed_bounds_for_receiver,
+ actual, actual, locus);
+ vtable_ctor_elems.push_back (address);
+ vtable_ctor_idx.push_back (i++);
+ }
+
+ tree vtable_ctor = ctx->get_backend ()->array_constructor_expression (
+ TREE_TYPE (vtable_field), vtable_ctor_idx, vtable_ctor_elems, locus);
+
+ std::vector<tree> dyn_ctor = {address_of_compiled_ref, vtable_ctor};
+ return ctx->get_backend ()->constructor_expression (dynamic_object, false,
+ dyn_ctor, -1, locus);
+}
+
+tree
+HIRCompileBase::compute_address_for_trait_item (
+ const Resolver::TraitItemReference *ref,
+ const TyTy::TypeBoundPredicate *predicate,
+ std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+ &receiver_bounds,
+ const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus)
+{
+ // There are two cases here one where its an item which has an implementation
+ // within a trait-impl-block. Then there is the case where there is a default
+ // implementation for this within the trait.
+ //
+ // The awkward part here is that this might be a generic trait and we need to
+ // figure out the correct monomorphized type for this so we can resolve the
+ // address of the function , this is stored as part of the
+ // type-bound-predicate
+ //
+ // Algo:
+ // check if there is an impl-item for this trait-item-ref first
+ // else assert that the trait-item-ref has an implementation
+
+ TyTy::TypeBoundPredicateItem predicate_item
+ = predicate->lookup_associated_item (ref->get_identifier ());
+ rust_assert (!predicate_item.is_error ());
+
+ // this is the expected end type
+ TyTy::BaseType *trait_item_type = predicate_item.get_tyty_for_receiver (root);
+ rust_assert (trait_item_type->get_kind () == TyTy::TypeKind::FNDEF);
+ TyTy::FnType *trait_item_fntype
+ = static_cast<TyTy::FnType *> (trait_item_type);
+
+ // find impl-block for this trait-item-ref
+ HIR::ImplBlock *associated_impl_block = nullptr;
+ const Resolver::TraitReference *predicate_trait_ref = predicate->get ();
+ for (auto &item : receiver_bounds)
+ {
+ Resolver::TraitReference *trait_ref = item.first;
+ HIR::ImplBlock *impl_block = item.second;
+ if (predicate_trait_ref->is_equal (*trait_ref))
+ {
+ associated_impl_block = impl_block;
+ break;
+ }
+ }
+
+ // FIXME this probably should just return error_mark_node but this helps
+ // debug for now since we are wrongly returning early on type-resolution
+ // failures, until we take advantage of more error types and error_mark_node
+ rust_assert (associated_impl_block != nullptr);
+
+ // lookup self for the associated impl
+ std::unique_ptr<HIR::Type> &self_type_path
+ = associated_impl_block->get_type ();
+ TyTy::BaseType *self = nullptr;
+ bool ok = ctx->get_tyctx ()->lookup_type (
+ self_type_path->get_mappings ().get_hirid (), &self);
+ rust_assert (ok);
+
+ // lookup the predicate item from the self
+ TyTy::TypeBoundPredicate *self_bound = nullptr;
+ for (auto &bound : self->get_specified_bounds ())
+ {
+ const Resolver::TraitReference *bound_ref = bound.get ();
+ const Resolver::TraitReference *specified_ref = predicate->get ();
+ if (bound_ref->is_equal (*specified_ref))
+ {
+ self_bound = &bound;
+ break;
+ }
+ }
+ rust_assert (self_bound != nullptr);
+
+ // lookup the associated item from the associated impl block
+ TyTy::TypeBoundPredicateItem associated_self_item
+ = self_bound->lookup_associated_item (ref->get_identifier ());
+ rust_assert (!associated_self_item.is_error ());
+
+ TyTy::BaseType *mono1 = associated_self_item.get_tyty_for_receiver (self);
+ rust_assert (mono1 != nullptr);
+ rust_assert (mono1->get_kind () == TyTy::TypeKind::FNDEF);
+ TyTy::FnType *assocated_item_ty1 = static_cast<TyTy::FnType *> (mono1);
+
+ // Lookup the impl-block for the associated impl_item if it exists
+ HIR::Function *associated_function = nullptr;
+ for (auto &impl_item : associated_impl_block->get_impl_items ())
+ {
+ bool is_function = impl_item->get_impl_item_type ()
+ == HIR::ImplItem::ImplItemType::FUNCTION;
+ if (!is_function)
+ continue;
+
+ HIR::Function *fn = static_cast<HIR::Function *> (impl_item.get ());
+ bool found_associated_item
+ = fn->get_function_name ().compare (ref->get_identifier ()) == 0;
+ if (found_associated_item)
+ associated_function = fn;
+ }
+
+ // we found an impl_item for this
+ if (associated_function != nullptr)
+ {
+ // lookup the associated type for this item
+ TyTy::BaseType *lookup = nullptr;
+ bool ok = ctx->get_tyctx ()->lookup_type (
+ associated_function->get_mappings ().get_hirid (), &lookup);
+ rust_assert (ok);
+ rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+ TyTy::FnType *lookup_fntype = static_cast<TyTy::FnType *> (lookup);
+
+ if (lookup_fntype->needs_substitution ())
+ {
+ TyTy::SubstitutionArgumentMappings mappings
+ = assocated_item_ty1->solve_missing_mappings_from_this (
+ *trait_item_fntype, *lookup_fntype);
+ lookup_fntype = lookup_fntype->handle_substitions (mappings);
+ }
+
+ return CompileInherentImplItem::Compile (associated_function, ctx,
+ lookup_fntype, true, locus);
+ }
+
+ // we can only compile trait-items with a body
+ bool trait_item_has_definition = ref->is_optional ();
+ rust_assert (trait_item_has_definition);
+
+ HIR::TraitItem *trait_item = ref->get_hir_trait_item ();
+ return CompileTraitItem::Compile (trait_item, ctx, trait_item_fntype, true,
+ locus);
+}
+
+bool
+HIRCompileBase::verify_array_capacities (tree ltype, tree rtype,
+ Location lvalue_locus,
+ Location rvalue_locus)
+{
+ rust_assert (ltype != NULL_TREE);
+ rust_assert (rtype != NULL_TREE);
+
+ // lets just return ok as other errors have already occurred
+ if (ltype == error_mark_node || rtype == error_mark_node)
+ return true;
+
+ tree ltype_domain = TYPE_DOMAIN (ltype);
+ if (!ltype_domain)
+ return false;
+
+ if (!TREE_CONSTANT (TYPE_MAX_VALUE (ltype_domain)))
+ return false;
+
+ unsigned HOST_WIDE_INT ltype_length
+ = wi::ext (wi::to_offset (TYPE_MAX_VALUE (ltype_domain))
+ - wi::to_offset (TYPE_MIN_VALUE (ltype_domain)) + 1,
+ TYPE_PRECISION (TREE_TYPE (ltype_domain)),
+ TYPE_SIGN (TREE_TYPE (ltype_domain)))
+ .to_uhwi ();
+
+ tree rtype_domain = TYPE_DOMAIN (rtype);
+ if (!rtype_domain)
+ return false;
+
+ if (!TREE_CONSTANT (TYPE_MAX_VALUE (rtype_domain)))
+ return false;
+
+ unsigned HOST_WIDE_INT rtype_length
+ = wi::ext (wi::to_offset (TYPE_MAX_VALUE (rtype_domain))
+ - wi::to_offset (TYPE_MIN_VALUE (rtype_domain)) + 1,
+ TYPE_PRECISION (TREE_TYPE (rtype_domain)),
+ TYPE_SIGN (TREE_TYPE (rtype_domain)))
+ .to_uhwi ();
+
+ if (ltype_length != rtype_length)
+ {
+ rust_error_at (
+ rvalue_locus,
+ "expected an array with a fixed size of " HOST_WIDE_INT_PRINT_UNSIGNED
+ " elements, found one with " HOST_WIDE_INT_PRINT_UNSIGNED " elements",
+ ltype_length, rtype_length);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace Compile
+} // namespace Rust