| // 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-hir-type-check.h" |
| |
| namespace Rust { |
| namespace Resolver { |
| |
| TypeCheckContext * |
| TypeCheckContext::get () |
| { |
| static TypeCheckContext *instance; |
| if (instance == nullptr) |
| instance = new TypeCheckContext (); |
| |
| return instance; |
| } |
| |
| TypeCheckContext::TypeCheckContext () {} |
| |
| TypeCheckContext::~TypeCheckContext () {} |
| |
| bool |
| TypeCheckContext::lookup_builtin (NodeId id, TyTy::BaseType **type) |
| { |
| auto ref_it = node_id_refs.find (id); |
| if (ref_it == node_id_refs.end ()) |
| return false; |
| |
| auto it = resolved.find (ref_it->second); |
| if (it == resolved.end ()) |
| return false; |
| |
| *type = it->second; |
| return true; |
| } |
| |
| bool |
| TypeCheckContext::lookup_builtin (std::string name, TyTy::BaseType **type) |
| { |
| for (auto &builtin : builtins) |
| { |
| if (name.compare (builtin->as_string ()) == 0) |
| { |
| *type = builtin.get (); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void |
| TypeCheckContext::insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type) |
| { |
| node_id_refs[ref] = id; |
| resolved[id] = type; |
| builtins.push_back (std::unique_ptr<TyTy::BaseType> (type)); |
| } |
| |
| void |
| TypeCheckContext::insert_type (const Analysis::NodeMapping &mappings, |
| TyTy::BaseType *type) |
| { |
| rust_assert (type != nullptr); |
| NodeId ref = mappings.get_nodeid (); |
| HirId id = mappings.get_hirid (); |
| node_id_refs[ref] = id; |
| resolved[id] = type; |
| } |
| |
| void |
| TypeCheckContext::insert_implicit_type (TyTy::BaseType *type) |
| { |
| rust_assert (type != nullptr); |
| resolved[type->get_ref ()] = type; |
| } |
| |
| void |
| TypeCheckContext::insert_implicit_type (HirId id, TyTy::BaseType *type) |
| { |
| rust_assert (type != nullptr); |
| resolved[id] = type; |
| } |
| |
| bool |
| TypeCheckContext::lookup_type (HirId id, TyTy::BaseType **type) const |
| { |
| auto it = resolved.find (id); |
| if (it == resolved.end ()) |
| return false; |
| |
| *type = it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_type_by_node_id (NodeId ref, HirId id) |
| { |
| rust_assert (node_id_refs.find (ref) == node_id_refs.end ()); |
| node_id_refs[ref] = id; |
| } |
| |
| bool |
| TypeCheckContext::lookup_type_by_node_id (NodeId ref, HirId *id) |
| { |
| auto it = node_id_refs.find (ref); |
| if (it == node_id_refs.end ()) |
| return false; |
| |
| *id = it->second; |
| return true; |
| } |
| |
| TyTy::BaseType * |
| TypeCheckContext::peek_return_type () |
| { |
| rust_assert (!return_type_stack.empty ()); |
| return return_type_stack.back ().second; |
| } |
| |
| void |
| TypeCheckContext::push_return_type (TypeCheckContextItem item, |
| TyTy::BaseType *return_type) |
| { |
| return_type_stack.push_back ({std::move (item), return_type}); |
| } |
| |
| void |
| TypeCheckContext::pop_return_type () |
| { |
| rust_assert (!return_type_stack.empty ()); |
| return_type_stack.pop_back (); |
| } |
| |
| TypeCheckContextItem & |
| TypeCheckContext::peek_context () |
| { |
| rust_assert (!return_type_stack.empty ()); |
| return return_type_stack.back ().first; |
| } |
| |
| void |
| TypeCheckContext::iterate (std::function<bool (HirId, TyTy::BaseType *)> cb) |
| { |
| for (auto it = resolved.begin (); it != resolved.end (); it++) |
| { |
| if (!cb (it->first, it->second)) |
| return; |
| } |
| } |
| |
| bool |
| TypeCheckContext::have_loop_context () const |
| { |
| return !loop_type_stack.empty (); |
| } |
| |
| void |
| TypeCheckContext::push_new_loop_context (HirId id, Location locus) |
| { |
| TyTy::BaseType *infer_var |
| = new TyTy::InferType (id, TyTy::InferType::InferTypeKind::GENERAL, locus); |
| loop_type_stack.push_back (infer_var); |
| } |
| |
| void |
| TypeCheckContext::push_new_while_loop_context (HirId id) |
| { |
| TyTy::BaseType *infer_var = new TyTy::ErrorType (id); |
| loop_type_stack.push_back (infer_var); |
| } |
| |
| TyTy::BaseType * |
| TypeCheckContext::peek_loop_context () |
| { |
| return loop_type_stack.back (); |
| } |
| |
| TyTy::BaseType * |
| TypeCheckContext::pop_loop_context () |
| { |
| auto back = peek_loop_context (); |
| loop_type_stack.pop_back (); |
| return back; |
| } |
| |
| void |
| TypeCheckContext::swap_head_loop_context (TyTy::BaseType *val) |
| { |
| loop_type_stack.pop_back (); |
| loop_type_stack.push_back (val); |
| } |
| |
| void |
| TypeCheckContext::insert_trait_reference (DefId id, TraitReference &&ref) |
| { |
| rust_assert (trait_context.find (id) == trait_context.end ()); |
| trait_context.emplace (id, std::move (ref)); |
| } |
| |
| bool |
| TypeCheckContext::lookup_trait_reference (DefId id, TraitReference **ref) |
| { |
| auto it = trait_context.find (id); |
| if (it == trait_context.end ()) |
| return false; |
| |
| *ref = &it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_receiver (HirId id, TyTy::BaseType *t) |
| { |
| receiver_context[id] = t; |
| } |
| |
| bool |
| TypeCheckContext::lookup_receiver (HirId id, TyTy::BaseType **ref) |
| { |
| auto it = receiver_context.find (id); |
| if (it == receiver_context.end ()) |
| return false; |
| |
| *ref = it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_associated_trait_impl ( |
| HirId id, AssociatedImplTrait &&associated) |
| { |
| rust_assert (associated_impl_traits.find (id) |
| == associated_impl_traits.end ()); |
| associated_impl_traits.emplace (id, std::move (associated)); |
| } |
| |
| bool |
| TypeCheckContext::lookup_associated_trait_impl ( |
| HirId id, AssociatedImplTrait **associated) |
| { |
| auto it = associated_impl_traits.find (id); |
| if (it == associated_impl_traits.end ()) |
| return false; |
| |
| *associated = &it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_associated_type_mapping (HirId id, HirId mapping) |
| { |
| associated_type_mappings[id] = mapping; |
| } |
| |
| void |
| TypeCheckContext::clear_associated_type_mapping (HirId id) |
| { |
| auto it = associated_type_mappings.find (id); |
| if (it != associated_type_mappings.end ()) |
| associated_type_mappings.erase (it); |
| } |
| |
| // lookup any associated type mappings, the out parameter of mapping is |
| // allowed to be nullptr which allows this interface to do a simple does exist |
| // check |
| bool |
| TypeCheckContext::lookup_associated_type_mapping (HirId id, HirId *mapping) |
| { |
| auto it = associated_type_mappings.find (id); |
| if (it == associated_type_mappings.end ()) |
| return false; |
| |
| if (mapping != nullptr) |
| *mapping = it->second; |
| |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_associated_impl_mapping ( |
| HirId trait_id, const TyTy::BaseType *impl_type, HirId impl_id) |
| { |
| auto it = associated_traits_to_impls.find (trait_id); |
| if (it == associated_traits_to_impls.end ()) |
| { |
| associated_traits_to_impls[trait_id] = {}; |
| } |
| |
| associated_traits_to_impls[trait_id].push_back ({impl_type, impl_id}); |
| } |
| |
| bool |
| TypeCheckContext::lookup_associated_impl_mapping_for_self ( |
| HirId trait_id, const TyTy::BaseType *self, HirId *mapping) |
| { |
| auto it = associated_traits_to_impls.find (trait_id); |
| if (it == associated_traits_to_impls.end ()) |
| return false; |
| |
| for (auto &item : it->second) |
| { |
| if (item.first->can_eq (self, false)) |
| { |
| *mapping = item.second; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void |
| TypeCheckContext::insert_autoderef_mappings ( |
| HirId id, std::vector<Adjustment> &&adjustments) |
| { |
| rust_assert (autoderef_mappings.find (id) == autoderef_mappings.end ()); |
| autoderef_mappings.emplace (id, std::move (adjustments)); |
| } |
| |
| bool |
| TypeCheckContext::lookup_autoderef_mappings ( |
| HirId id, std::vector<Adjustment> **adjustments) |
| { |
| auto it = autoderef_mappings.find (id); |
| if (it == autoderef_mappings.end ()) |
| return false; |
| |
| *adjustments = &it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_cast_autoderef_mappings ( |
| HirId id, std::vector<Adjustment> &&adjustments) |
| { |
| rust_assert (cast_autoderef_mappings.find (id) |
| == cast_autoderef_mappings.end ()); |
| cast_autoderef_mappings.emplace (id, std::move (adjustments)); |
| } |
| |
| bool |
| TypeCheckContext::lookup_cast_autoderef_mappings ( |
| HirId id, std::vector<Adjustment> **adjustments) |
| { |
| auto it = cast_autoderef_mappings.find (id); |
| if (it == cast_autoderef_mappings.end ()) |
| return false; |
| |
| *adjustments = &it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_variant_definition (HirId id, HirId variant) |
| { |
| auto it = variants.find (id); |
| rust_assert (it == variants.end ()); |
| |
| variants[id] = variant; |
| } |
| |
| bool |
| TypeCheckContext::lookup_variant_definition (HirId id, HirId *variant) |
| { |
| auto it = variants.find (id); |
| if (it == variants.end ()) |
| return false; |
| |
| *variant = it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_operator_overload (HirId id, TyTy::FnType *call_site) |
| { |
| auto it = operator_overloads.find (id); |
| rust_assert (it == operator_overloads.end ()); |
| |
| operator_overloads[id] = call_site; |
| } |
| |
| bool |
| TypeCheckContext::lookup_operator_overload (HirId id, TyTy::FnType **call) |
| { |
| auto it = operator_overloads.find (id); |
| if (it == operator_overloads.end ()) |
| return false; |
| |
| *call = it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_unconstrained_check_marker (HirId id, bool status) |
| { |
| unconstrained[id] = status; |
| } |
| |
| bool |
| TypeCheckContext::have_checked_for_unconstrained (HirId id, bool *result) |
| { |
| auto it = unconstrained.find (id); |
| bool found = it != unconstrained.end (); |
| if (!found) |
| return false; |
| |
| *result = it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_resolved_predicate (HirId id, |
| TyTy::TypeBoundPredicate predicate) |
| { |
| auto it = predicates.find (id); |
| rust_assert (it == predicates.end ()); |
| |
| predicates.insert ({id, predicate}); |
| } |
| |
| bool |
| TypeCheckContext::lookup_predicate (HirId id, TyTy::TypeBoundPredicate *result) |
| { |
| auto it = predicates.find (id); |
| bool found = it != predicates.end (); |
| if (!found) |
| return false; |
| |
| *result = it->second; |
| return true; |
| } |
| |
| void |
| TypeCheckContext::insert_query (HirId id) |
| { |
| querys_in_progress.insert (id); |
| } |
| |
| void |
| TypeCheckContext::query_completed (HirId id) |
| { |
| querys_in_progress.erase (id); |
| } |
| |
| bool |
| TypeCheckContext::query_in_progress (HirId id) const |
| { |
| return querys_in_progress.find (id) != querys_in_progress.end (); |
| } |
| |
| void |
| TypeCheckContext::insert_trait_query (DefId id) |
| { |
| trait_queries_in_progress.insert (id); |
| } |
| |
| void |
| TypeCheckContext::trait_query_completed (DefId id) |
| { |
| trait_queries_in_progress.erase (id); |
| } |
| |
| bool |
| TypeCheckContext::trait_query_in_progress (DefId id) const |
| { |
| return trait_queries_in_progress.find (id) |
| != trait_queries_in_progress.end (); |
| } |
| |
| // TypeCheckContextItem |
| |
| TypeCheckContextItem::Item::Item (HIR::Function *item) : item (item) {} |
| |
| TypeCheckContextItem::Item::Item (HIR::ImplBlock *impl_block, |
| HIR::Function *item) |
| : impl_item ({impl_block, item}) |
| {} |
| |
| TypeCheckContextItem::Item::Item (HIR::TraitItemFunc *trait_item) |
| : trait_item (trait_item) |
| {} |
| |
| TypeCheckContextItem::TypeCheckContextItem (HIR::Function *item) |
| : type (ItemType::ITEM), item (item) |
| {} |
| |
| TypeCheckContextItem::TypeCheckContextItem (HIR::ImplBlock *impl_block, |
| HIR::Function *item) |
| : type (ItemType::IMPL_ITEM), item (impl_block, item) |
| {} |
| |
| TypeCheckContextItem::TypeCheckContextItem (HIR::TraitItemFunc *trait_item) |
| : type (ItemType::TRAIT_ITEM), item (trait_item) |
| {} |
| |
| HIR::Function * |
| TypeCheckContextItem::get_item () |
| { |
| rust_assert (get_type () == ItemType::ITEM); |
| return item.item; |
| } |
| |
| std::pair<HIR::ImplBlock *, HIR::Function *> & |
| TypeCheckContextItem::get_impl_item () |
| { |
| rust_assert (get_type () == ItemType::IMPL_ITEM); |
| return item.impl_item; |
| } |
| |
| HIR::TraitItemFunc * |
| TypeCheckContextItem::get_trait_item () |
| { |
| rust_assert (get_type () == ItemType::TRAIT_ITEM); |
| return item.trait_item; |
| } |
| |
| TypeCheckContextItem::ItemType |
| TypeCheckContextItem::get_type () const |
| { |
| return type; |
| } |
| |
| TyTy::FnType * |
| TypeCheckContextItem::get_context_type () |
| { |
| auto &context = *TypeCheckContext::get (); |
| |
| HirId reference = UNKNOWN_HIRID; |
| switch (get_type ()) |
| { |
| case ITEM: |
| reference = get_item ()->get_mappings ().get_hirid (); |
| break; |
| |
| case IMPL_ITEM: |
| reference = get_impl_item ().second->get_mappings ().get_hirid (); |
| break; |
| |
| case TRAIT_ITEM: |
| reference = get_trait_item ()->get_mappings ().get_hirid (); |
| break; |
| } |
| |
| rust_assert (reference != UNKNOWN_HIRID); |
| |
| TyTy::BaseType *lookup = nullptr; |
| bool ok = context.lookup_type (reference, &lookup); |
| rust_assert (ok); |
| rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); |
| return static_cast<TyTy::FnType *> (lookup); |
| } |
| |
| } // namespace Resolver |
| } // namespace Rust |