// Copyright (C) 2020-2025 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.h" #include "rust-compile-item.h" #include "rust-compile-implitem.h" #include "rust-hir-type-bounds.h" #include "rust-compile-type.h" #include "rust-substitution-mapper.h" #include "rust-type-util.h" #include "rust-session-manager.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.get_items ()) CompileItem::compile (item.get (), ctx); auto crate_type = Rust::Session::get_instance ().options.target_data.get_crate_type (); if (crate_type == TargetOptions::CrateType::PROC_MACRO) add_proc_macro_symbols (); } // Shared methods in compilation tree HIRCompileBase::coercion_site (HirId id, tree rvalue, TyTy::BaseType *rval, TyTy::BaseType *lval, location_t lvalue_locus, location_t rvalue_locus) { std::vector *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, TyTy::BaseType *rval, TyTy::BaseType *lval, location_t lvalue_locus, location_t rvalue_locus) { if (rvalue == error_mark_node) return error_mark_node; TyTy::BaseType *actual = rval->destructure (); TyTy::BaseType *expected = lval->destructure (); if (expected->get_kind () == TyTy::TypeKind::REF) { // this is a dyn object if (RS_DST_FLAG_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 (expected); const TyTy::ReferenceType *act = static_cast (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 () && RS_DST_FLAG_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 (RS_DST_FLAG_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::PointerType *exp = static_cast (expected); TyTy::BaseType *actual_base = nullptr; if (actual->get_kind () == TyTy::TypeKind::REF) { const TyTy::ReferenceType *act = static_cast (actual); actual_base = act->get_base (); } else if (actual->get_kind () == TyTy::TypeKind::POINTER) { const TyTy::PointerType *act = static_cast (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 () && RS_DST_FLAG_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_t locus) { // DST's get wrapped in a pseudo reference that doesnt exist... const TyTy::ReferenceType r (ctx->get_mappings ().get_next_hir_id (), TyTy::TyVar (ty->get_ref ()), Mutability::Imm); tree dynamic_object = TyTyResolveCompile::compile (ctx, &r); 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> 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 vtable_ctor_elems; std::vector 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 = Backend::array_constructor_expression (TREE_TYPE (vtable_field), vtable_ctor_idx, vtable_ctor_elems, locus); std::vector dyn_ctor = {address_of_compiled_ref, vtable_ctor}; return 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> &receiver_bounds, const TyTy::BaseType *receiver, const TyTy::BaseType *root, location_t locus) { 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 (trait_item_type); // Loop through the list of trait references and impls that we satisfy. // We are looking for one that has an implementation for "ref", a trait // item. for (auto &item : receiver_bounds) { HIR::ImplBlock *impl_block = item.second; rust_assert (impl_block != nullptr); // Checks for empty impl blocks, triggered by Sized trait. if (!impl_block->has_type ()) continue; // Lookup type for potentially associated impl. HIR::Type &self_type_path = impl_block->get_type (); // Convert HIR::Type to TyTy::BaseType TyTy::BaseType *self = nullptr; bool ok = ctx->get_tyctx ()->lookup_type ( self_type_path.get_mappings ().get_hirid (), &self); rust_assert (ok); // Look through the relevant bounds on our type, and find which one our // impl block satisfies 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 this impl is for one of our types or supertypes if (specified_ref->satisfies_bound (*bound_ref)) { self_bound = &bound; break; } } // This impl block doesn't help us if (self_bound == nullptr) continue; // Find the specific function in the impl block that matches "ref". // This is the one we want to compute the address for. HIR::Function *associated_function = nullptr; for (auto &impl_item : 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 (impl_item.get ()); bool found_associated_item = fn->get_function_name ().as_string ().compare ( ref->get_identifier ()) == 0; if (found_associated_item) associated_function = fn; } // This impl block satisfies the bound, but doesn't contain the relevant // function. This could happen because of supertraits. if (associated_function == nullptr) continue; // lookup the associated type for this item TyTy::BaseType *lookup = nullptr; 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 (lookup); if (lookup_fntype->needs_substitution ()) { TyTy::BaseType *infer = Resolver::SubstMapper::InferSubst (lookup_fntype, UNDEF_LOCATION); infer = Resolver::unify_site (infer->get_ref (), TyTy::TyWithLocation (trait_item_fntype), TyTy::TyWithLocation (infer), UNDEF_LOCATION); rust_assert (infer->get_kind () == TyTy::TypeKind::FNDEF); lookup_fntype = static_cast (infer); } 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_t lvalue_locus, location_t 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, ErrorCode::E0308, "mismatched types, 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