| // 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.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 |
| // |
| // FIXME this does not support super traits |
| |
| 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 |