diff options
author | Philip Herron <philip.herron@embecosm.com> | 2022-10-21 14:29:50 +0200 |
---|---|---|
committer | Arthur Cohen <arthur.cohen@embecosm.com> | 2022-12-13 14:00:07 +0100 |
commit | cfbda2f78baac4f329efe1838401b4ae2ed5b6a5 (patch) | |
tree | 63d04c0e9b01fe40f25380329f5801707583db1b /gcc/rust/backend/rust-compile.cc | |
parent | 019b2f15581948806ee14a6d05b09ec94f04c966 (diff) | |
download | gcc-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.cc | 414 |
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 |