// 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 // . #include "rust-hir-type-check-type.h" #include "rust-hir-trait-resolve.h" #include "rust-hir-type-check-expr.h" namespace Rust { namespace Resolver { HIR::GenericArgs TypeCheckResolveGenericArguments::resolve (HIR::TypePathSegment *segment) { TypeCheckResolveGenericArguments resolver (segment->get_locus ()); switch (segment->get_type ()) { case HIR::TypePathSegment::SegmentType::GENERIC: resolver.visit (static_cast (*segment)); break; default: break; } return resolver.args; } void TypeCheckResolveGenericArguments::visit (HIR::TypePathSegmentGeneric &generic) { args = generic.get_generic_args (); } TyTy::BaseType * TypeCheckType::Resolve (HIR::Type *type) { // is it already resolved? auto context = TypeCheckContext::get (); TyTy::BaseType *resolved = nullptr; bool already_resolved = context->lookup_type (type->get_mappings ().get_hirid (), &resolved); if (already_resolved) return resolved; TypeCheckType resolver (type->get_mappings ().get_hirid ()); type->accept_vis (resolver); rust_assert (resolver.translated != nullptr); resolver.context->insert_type (type->get_mappings (), resolver.translated); return resolver.translated; } void TypeCheckType::visit (HIR::BareFunctionType &fntype) { TyTy::BaseType *return_type = fntype.has_return_type () ? TypeCheckType::Resolve (fntype.get_return_type ().get ()) : TyTy::TupleType::get_unit_type (fntype.get_mappings ().get_hirid ()); std::vector params; for (auto ¶m : fntype.get_function_params ()) { TyTy::BaseType *ptype = TypeCheckType::Resolve (param.get_type ().get ()); params.push_back (TyTy::TyVar (ptype->get_ref ())); } translated = new TyTy::FnPtr (fntype.get_mappings ().get_hirid (), fntype.get_locus (), std::move (params), TyTy::TyVar (return_type->get_ref ())); } void TypeCheckType::visit (HIR::TupleType &tuple) { if (tuple.is_unit_type ()) { auto unit_node_id = resolver->get_unit_type_node_id (); if (!context->lookup_builtin (unit_node_id, &translated)) { rust_error_at (tuple.get_locus (), "failed to lookup builtin unit type"); } return; } std::vector fields; for (auto &elem : tuple.get_elems ()) { auto field_ty = TypeCheckType::Resolve (elem.get ()); fields.push_back (TyTy::TyVar (field_ty->get_ref ())); } translated = new TyTy::TupleType (tuple.get_mappings ().get_hirid (), tuple.get_locus (), fields); } void TypeCheckType::visit (HIR::TypePath &path) { // this can happen so we need to look up the root then resolve the // remaining segments if possible size_t offset = 0; NodeId resolved_node_id = UNKNOWN_NODEID; TyTy::BaseType *root = resolve_root_path (path, &offset, &resolved_node_id); if (root->get_kind () == TyTy::TypeKind::ERROR) return; TyTy::BaseType *path_type = root->clone (); path_type->set_ref (path.get_mappings ().get_hirid ()); context->insert_implicit_type (path.get_mappings ().get_hirid (), path_type); bool fully_resolved = offset >= path.get_segments ().size (); if (fully_resolved) { translated = path_type; return; } translated = resolve_segments (resolved_node_id, path.get_mappings ().get_hirid (), path.get_segments (), offset, path_type, path.get_mappings (), path.get_locus ()); } void TypeCheckType::visit (HIR::QualifiedPathInType &path) { HIR::QualifiedPathType qual_path_type = path.get_path_type (); TyTy::BaseType *root = TypeCheckType::Resolve (qual_path_type.get_type ().get ()); if (root->get_kind () == TyTy::TypeKind::ERROR) { rust_debug_loc (path.get_locus (), "failed to resolve the root"); return; } if (!qual_path_type.has_as_clause ()) { // then this is just a normal path-in-expression NodeId root_resolved_node_id = UNKNOWN_NODEID; bool ok = resolver->lookup_resolved_type ( qual_path_type.get_type ()->get_mappings ().get_nodeid (), &root_resolved_node_id); rust_assert (ok); translated = resolve_segments (root_resolved_node_id, path.get_mappings ().get_hirid (), path.get_segments (), 0, translated, path.get_mappings (), path.get_locus ()); return; } // Resolve the trait now TraitReference *trait_ref = TraitResolver::Resolve (*qual_path_type.get_trait ().get ()); if (trait_ref->is_error ()) return; // does this type actually implement this type-bound? if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref)) { rust_error_at (qual_path_type.get_locus (), "root does not satisfy specified trait-bound"); return; } // get the predicate for the bound auto specified_bound = get_predicate_from_bound (*qual_path_type.get_trait ().get ()); if (specified_bound.is_error ()) return; // inherit the bound root->inherit_bounds ({specified_bound}); // setup the associated types const TraitReference *specified_bound_ref = specified_bound.get (); auto candidates = TypeBoundsProbe::Probe (root); AssociatedImplTrait *associated_impl_trait = nullptr; for (auto &probed_bound : candidates) { const TraitReference *bound_trait_ref = probed_bound.first; const HIR::ImplBlock *associated_impl = probed_bound.second; HirId impl_block_id = associated_impl->get_mappings ().get_hirid (); AssociatedImplTrait *associated = nullptr; bool found_impl_trait = context->lookup_associated_trait_impl (impl_block_id, &associated); if (found_impl_trait) { bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref); bool found_self = associated->get_self ()->can_eq (root, false); if (found_trait && found_self) { associated_impl_trait = associated; break; } } } if (associated_impl_trait != nullptr) { associated_impl_trait->setup_associated_types (root, specified_bound); } // lookup the associated item from the specified bound std::unique_ptr &item_seg = path.get_associated_segment (); HIR::PathIdentSegment item_seg_identifier = item_seg->get_ident_segment (); TyTy::TypeBoundPredicateItem item = specified_bound.lookup_associated_item (item_seg_identifier.as_string ()); if (item.is_error ()) { rust_error_at (item_seg->get_locus (), "unknown associated item"); return; } // infer the root type translated = item.get_tyty_for_receiver (root); // turbo-fish segment path:: if (item_seg->get_type () == HIR::TypePathSegment::SegmentType::GENERIC) { HIR::TypePathSegmentGeneric &generic_seg = static_cast (*item_seg.get ()); // turbo-fish segment path:: if (generic_seg.has_generic_args ()) { if (!translated->can_substitute ()) { rust_error_at (item_seg->get_locus (), "substitutions not supported for %s", translated->as_string ().c_str ()); translated = new TyTy::ErrorType (path.get_mappings ().get_hirid ()); return; } translated = SubstMapper::Resolve (translated, path.get_locus (), &generic_seg.get_generic_args ()); } } // continue on as a path-in-expression const TraitItemReference *trait_item_ref = item.get_raw_item (); NodeId root_resolved_node_id = trait_item_ref->get_mappings ().get_nodeid (); bool fully_resolved = path.get_segments ().empty (); if (fully_resolved) { resolver->insert_resolved_type (path.get_mappings ().get_nodeid (), root_resolved_node_id); context->insert_receiver (path.get_mappings ().get_hirid (), root); return; } translated = resolve_segments (root_resolved_node_id, path.get_mappings ().get_hirid (), path.get_segments (), 0, translated, path.get_mappings (), path.get_locus ()); } TyTy::BaseType * TypeCheckType::resolve_root_path (HIR::TypePath &path, size_t *offset, NodeId *root_resolved_node_id) { TyTy::BaseType *root_tyty = nullptr; *offset = 0; for (size_t i = 0; i < path.get_num_segments (); i++) { std::unique_ptr &seg = path.get_segments ().at (i); bool have_more_segments = (path.get_num_segments () - 1 != i); bool is_root = *offset == 0; NodeId ast_node_id = seg->get_mappings ().get_nodeid (); // then lookup the reference_node_id NodeId ref_node_id = UNKNOWN_NODEID; if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id)) { resolver->lookup_resolved_type (ast_node_id, &ref_node_id); } // ref_node_id is the NodeId that the segments refers to. if (ref_node_id == UNKNOWN_NODEID) { if (is_root) { rust_error_at (seg->get_locus (), "unknown reference for resolved name: %<%s%>", seg->get_ident_segment ().as_string ().c_str ()); return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); } return root_tyty; } // node back to HIR HirId ref = UNKNOWN_HIRID; if (!mappings->lookup_node_to_hir (ref_node_id, &ref)) { if (is_root) { rust_error_at (seg->get_locus (), "789 reverse lookup failure"); rust_debug_loc ( seg->get_locus (), "failure with [%s] mappings [%s] ref_node_id [%u]", seg->as_string ().c_str (), seg->get_mappings ().as_string ().c_str (), ref_node_id); return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); } return root_tyty; } auto seg_is_module = (nullptr != mappings->lookup_module (ref)); auto seg_is_crate = mappings->is_local_hirid_crate (ref); if (seg_is_module || seg_is_crate) { // A::B::C::this_is_a_module::D::E::F // ^^^^^^^^^^^^^^^^ // Currently handling this. if (have_more_segments) { (*offset)++; continue; } // In the case of : // A::B::C::this_is_a_module // ^^^^^^^^^^^^^^^^ // This is an error, we are not expecting a module. rust_error_at (seg->get_locus (), "expected value"); return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); } TyTy::BaseType *lookup = nullptr; if (!query_type (ref, &lookup)) { if (is_root) { rust_error_at (seg->get_locus (), "failed to resolve root segment"); return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); } return root_tyty; } // if we have a previous segment type if (root_tyty != nullptr) { // if this next segment needs substitution we must apply the // previous type arguments // // such as: GenericStruct::<_>::new(123, 456) if (lookup->needs_generic_substitutions ()) { if (!root_tyty->needs_generic_substitutions ()) { auto used_args_in_prev_segment = GetUsedSubstArgs::From (root_tyty); lookup = SubstMapperInternal::Resolve (lookup, used_args_in_prev_segment); } } } // turbo-fish segment path:: if (seg->is_generic_segment ()) { HIR::TypePathSegmentGeneric *generic_segment = static_cast (seg.get ()); if (!lookup->can_substitute ()) { rust_error_at (path.get_locus (), "TypePath %s declares generic arguments but the " "type %s does not have any", path.as_string ().c_str (), lookup->as_string ().c_str ()); return new TyTy::ErrorType (lookup->get_ref ()); } lookup = SubstMapper::Resolve (lookup, path.get_locus (), &generic_segment->get_generic_args ()); } else if (lookup->needs_generic_substitutions ()) { HIR::GenericArgs empty = HIR::GenericArgs::create_empty (path.get_locus ()); lookup = SubstMapper::Resolve (lookup, path.get_locus (), &empty); } *root_resolved_node_id = ref_node_id; *offset = *offset + 1; root_tyty = lookup; } return root_tyty; } TyTy::BaseType * TypeCheckType::resolve_segments ( NodeId root_resolved_node_id, HirId expr_id, std::vector> &segments, size_t offset, TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings, Location expr_locus) { NodeId resolved_node_id = root_resolved_node_id; TyTy::BaseType *prev_segment = tyseg; for (size_t i = offset; i < segments.size (); i++) { std::unique_ptr &seg = segments.at (i); bool reciever_is_generic = prev_segment->get_kind () == TyTy::TypeKind::PARAM; bool probe_bounds = true; bool probe_impls = !reciever_is_generic; bool ignore_mandatory_trait_items = !reciever_is_generic; // probe the path is done in two parts one where we search impls if no // candidate is found then we search extensions from traits auto candidates = PathProbeType::Probe (prev_segment, seg->get_ident_segment (), probe_impls, false, ignore_mandatory_trait_items); if (candidates.size () == 0) { candidates = PathProbeType::Probe (prev_segment, seg->get_ident_segment (), false, probe_bounds, ignore_mandatory_trait_items); if (candidates.size () == 0) { rust_error_at ( seg->get_locus (), "failed to resolve path segment using an impl Probe"); return new TyTy::ErrorType (expr_id); } } if (candidates.size () > 1) { ReportMultipleCandidateError::Report (candidates, seg->get_ident_segment (), seg->get_locus ()); return new TyTy::ErrorType (expr_id); } auto &candidate = *candidates.begin (); prev_segment = tyseg; tyseg = candidate.ty; if (candidate.is_impl_candidate ()) { resolved_node_id = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid (); } else { resolved_node_id = candidate.item.trait.item_ref->get_mappings ().get_nodeid (); } if (seg->is_generic_segment ()) { HIR::TypePathSegmentGeneric *generic_segment = static_cast (seg.get ()); if (!tyseg->can_substitute ()) { rust_error_at (expr_locus, "substitutions not supported for %s", tyseg->as_string ().c_str ()); return new TyTy::ErrorType (expr_id); } tyseg = SubstMapper::Resolve (tyseg, expr_locus, &generic_segment->get_generic_args ()); if (tyseg->get_kind () == TyTy::TypeKind::ERROR) return new TyTy::ErrorType (expr_id); } } context->insert_receiver (expr_mappings.get_hirid (), prev_segment); if (tyseg->needs_generic_substitutions ()) { // Location locus = segments.back ()->get_locus (); if (!prev_segment->needs_generic_substitutions ()) { auto used_args_in_prev_segment = GetUsedSubstArgs::From (prev_segment); if (!used_args_in_prev_segment.is_error ()) tyseg = SubstMapperInternal::Resolve (tyseg, used_args_in_prev_segment); } if (tyseg->get_kind () == TyTy::TypeKind::ERROR) return new TyTy::ErrorType (expr_id); } rust_assert (resolved_node_id != UNKNOWN_NODEID); // lookup if the name resolver was able to canonically resolve this or not NodeId path_resolved_id = UNKNOWN_NODEID; if (resolver->lookup_resolved_name (expr_mappings.get_nodeid (), &path_resolved_id)) { rust_assert (path_resolved_id == resolved_node_id); } // check the type scope else if (resolver->lookup_resolved_type (expr_mappings.get_nodeid (), &path_resolved_id)) { rust_assert (path_resolved_id == resolved_node_id); } else { // name scope first if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) { resolver->insert_resolved_name (expr_mappings.get_nodeid (), resolved_node_id); } // check the type scope else if (resolver->get_type_scope ().decl_was_declared_here ( resolved_node_id)) { resolver->insert_resolved_type (expr_mappings.get_nodeid (), resolved_node_id); } } return tyseg; } void TypeCheckType::visit (HIR::TraitObjectType &type) { std::vector specified_bounds; for (auto &bound : type.get_type_param_bounds ()) { if (bound->get_bound_type () != HIR::TypeParamBound::BoundType::TRAITBOUND) continue; HIR::TypeParamBound &b = *bound.get (); HIR::TraitBound &trait_bound = static_cast (b); TyTy::TypeBoundPredicate predicate = get_predicate_from_bound (trait_bound.get_path ()); if (!predicate.is_error () && predicate.is_object_safe (true, type.get_locus ())) specified_bounds.push_back (std::move (predicate)); } RustIdent ident{CanonicalPath::create_empty (), type.get_locus ()}; translated = new TyTy::DynamicObjectType (type.get_mappings ().get_hirid (), ident, std::move (specified_bounds)); } void TypeCheckType::visit (HIR::ArrayType &type) { auto capacity_type = TypeCheckExpr::Resolve (type.get_size_expr ()); if (capacity_type->get_kind () == TyTy::TypeKind::ERROR) return; TyTy::BaseType *expected_ty = nullptr; bool ok = context->lookup_builtin ("usize", &expected_ty); rust_assert (ok); context->insert_type (type.get_size_expr ()->get_mappings (), expected_ty); unify_site (type.get_size_expr ()->get_mappings ().get_hirid (), TyTy::TyWithLocation (expected_ty), TyTy::TyWithLocation (capacity_type, type.get_size_expr ()->get_locus ()), type.get_size_expr ()->get_locus ()); TyTy::BaseType *base = TypeCheckType::Resolve (type.get_element_type ()); translated = new TyTy::ArrayType (type.get_mappings ().get_hirid (), type.get_locus (), *type.get_size_expr (), TyTy::TyVar (base->get_ref ())); } void TypeCheckType::visit (HIR::SliceType &type) { TyTy::BaseType *base = TypeCheckType::Resolve (type.get_element_type ().get ()); translated = new TyTy::SliceType (type.get_mappings ().get_hirid (), type.get_locus (), TyTy::TyVar (base->get_ref ())); } void TypeCheckType::visit (HIR::ReferenceType &type) { TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ()); translated = new TyTy::ReferenceType (type.get_mappings ().get_hirid (), TyTy::TyVar (base->get_ref ()), type.get_mut ()); } void TypeCheckType::visit (HIR::RawPointerType &type) { TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ()); translated = new TyTy::PointerType (type.get_mappings ().get_hirid (), TyTy::TyVar (base->get_ref ()), type.get_mut ()); } void TypeCheckType::visit (HIR::InferredType &type) { translated = new TyTy::InferType (type.get_mappings ().get_hirid (), TyTy::InferType::InferTypeKind::GENERAL, type.get_locus ()); } void TypeCheckType::visit (HIR::NeverType &type) { TyTy::BaseType *lookup = nullptr; bool ok = context->lookup_builtin ("!", &lookup); rust_assert (ok); translated = lookup->clone (); } TyTy::ParamType * TypeResolveGenericParam::Resolve (HIR::GenericParam *param) { TypeResolveGenericParam resolver; switch (param->get_kind ()) { case HIR::GenericParam::GenericKind::TYPE: resolver.visit (static_cast (*param)); break; case HIR::GenericParam::GenericKind::CONST: resolver.visit (static_cast (*param)); break; case HIR::GenericParam::GenericKind::LIFETIME: resolver.visit (static_cast (*param)); break; } return resolver.resolved; } void TypeResolveGenericParam::visit (HIR::LifetimeParam ¶m) { // nothing to do } void TypeResolveGenericParam::visit (HIR::ConstGenericParam ¶m) { // TODO } void TypeResolveGenericParam::visit (HIR::TypeParam ¶m) { if (param.has_type ()) TypeCheckType::Resolve (param.get_type ().get ()); std::vector specified_bounds; if (param.has_type_param_bounds ()) { for (auto &bound : param.get_type_param_bounds ()) { switch (bound->get_bound_type ()) { case HIR::TypeParamBound::BoundType::TRAITBOUND: { HIR::TraitBound *b = static_cast (bound.get ()); TyTy::TypeBoundPredicate predicate = get_predicate_from_bound (b->get_path ()); if (!predicate.is_error ()) specified_bounds.push_back (std::move (predicate)); } break; default: break; } } } resolved = new TyTy::ParamType (param.get_type_representation (), param.get_locus (), param.get_mappings ().get_hirid (), param, specified_bounds); } void ResolveWhereClauseItem::Resolve (HIR::WhereClauseItem &item) { ResolveWhereClauseItem resolver; switch (item.get_item_type ()) { case HIR::WhereClauseItem::LIFETIME: resolver.visit (static_cast (item)); break; case HIR::WhereClauseItem::TYPE_BOUND: resolver.visit (static_cast (item)); break; } } void ResolveWhereClauseItem::visit (HIR::LifetimeWhereClauseItem &item) {} void ResolveWhereClauseItem::visit (HIR::TypeBoundWhereClauseItem &item) { auto &binding_type_path = item.get_bound_type (); TyTy::BaseType *binding = TypeCheckType::Resolve (binding_type_path.get ()); std::vector specified_bounds; for (auto &bound : item.get_type_param_bounds ()) { switch (bound->get_bound_type ()) { case HIR::TypeParamBound::BoundType::TRAITBOUND: { HIR::TraitBound *b = static_cast (bound.get ()); TyTy::TypeBoundPredicate predicate = get_predicate_from_bound (b->get_path ()); if (!predicate.is_error ()) specified_bounds.push_back (std::move (predicate)); } break; default: break; } } binding->inherit_bounds (specified_bounds); // When we apply these bounds we must lookup which type this binding // resolves to, as this is the type which will be used during resolution // of the block. NodeId ast_node_id = binding_type_path->get_mappings ().get_nodeid (); // then lookup the reference_node_id NodeId ref_node_id = UNKNOWN_NODEID; if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id)) { // FIXME rust_error_at (Location (), "Failed to lookup type reference for node: %s", binding_type_path->as_string ().c_str ()); return; } // node back to HIR HirId ref; if (!mappings->lookup_node_to_hir (ref_node_id, &ref)) { // FIXME rust_error_at (Location (), "where-clause reverse lookup failure"); return; } // the base reference for this name _must_ have a type set TyTy::BaseType *lookup; if (!context->lookup_type (ref, &lookup)) { rust_error_at (mappings->lookup_location (ref), "Failed to resolve where-clause binding type: %s", binding_type_path->as_string ().c_str ()); return; } // FIXME // rust_assert (binding->is_equal (*lookup)); lookup->inherit_bounds (specified_bounds); } } // namespace Resolver } // namespace Rust