| // 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-expr.h" |
| #include "rust-hir-type-check-type.h" |
| #include "rust-hir-trait-resolve.h" |
| |
| namespace Rust { |
| namespace Resolver { |
| |
| void |
| TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr) |
| { |
| HIR::QualifiedPathType qual_path_type = expr.get_path_type (); |
| TyTy::BaseType *root |
| = TypeCheckType::Resolve (qual_path_type.get_type ().get ()); |
| if (root->get_kind () == TyTy::TypeKind::ERROR) |
| return; |
| |
| if (!qual_path_type.has_as_clause ()) |
| { |
| NodeId root_resolved_node_id = UNKNOWN_NODEID; |
| resolve_segments (root_resolved_node_id, expr.get_segments (), 0, root, |
| expr.get_mappings (), expr.get_locus ()); |
| return; |
| } |
| |
| // Resolve the trait now |
| std::unique_ptr<HIR::TypePath> &trait_path_ref = qual_path_type.get_trait (); |
| TraitReference *trait_ref = TraitResolver::Resolve (*trait_path_ref.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)) |
| return; |
| |
| // then we need to look at the next segment to create perform the correct |
| // projection type |
| if (expr.get_segments ().empty ()) |
| return; |
| |
| // get the predicate for the bound |
| auto specified_bound = get_predicate_from_bound (*trait_path_ref.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 |
| HIR::PathExprSegment &item_seg = expr.get_segments ().at (0); |
| HIR::PathIdentSegment item_seg_identifier = item_seg.get_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 |
| infered = item.get_tyty_for_receiver (root); |
| |
| // turbo-fish segment path::<ty> |
| if (item_seg.has_generic_args ()) |
| { |
| if (!infered->can_substitute ()) |
| { |
| rust_error_at (item_seg.get_locus (), |
| "substitutions not supported for %s", |
| infered->as_string ().c_str ()); |
| infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); |
| return; |
| } |
| infered = SubstMapper::Resolve (infered, expr.get_locus (), |
| &item_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 = expr.get_segments ().size () <= 1; |
| |
| if (fully_resolved) |
| { |
| resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (), |
| root_resolved_node_id); |
| context->insert_receiver (expr.get_mappings ().get_hirid (), root); |
| return; |
| } |
| |
| resolve_segments (root_resolved_node_id, expr.get_segments (), 1, infered, |
| expr.get_mappings (), expr.get_locus ()); |
| } |
| |
| void |
| TypeCheckExpr::visit (HIR::PathInExpression &expr) |
| { |
| NodeId resolved_node_id = UNKNOWN_NODEID; |
| size_t offset = -1; |
| TyTy::BaseType *tyseg = resolve_root_path (expr, &offset, &resolved_node_id); |
| if (tyseg->get_kind () == TyTy::TypeKind::ERROR) |
| return; |
| |
| if (tyseg->needs_generic_substitutions ()) |
| { |
| tyseg = SubstMapper::InferSubst (tyseg, expr.get_locus ()); |
| } |
| |
| bool fully_resolved = offset == expr.get_segments ().size (); |
| if (fully_resolved) |
| { |
| infered = tyseg; |
| return; |
| } |
| |
| resolve_segments (resolved_node_id, expr.get_segments (), offset, tyseg, |
| expr.get_mappings (), expr.get_locus ()); |
| } |
| |
| TyTy::BaseType * |
| TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset, |
| NodeId *root_resolved_node_id) |
| { |
| TyTy::BaseType *root_tyty = nullptr; |
| *offset = 0; |
| for (size_t i = 0; i < expr.get_num_segments (); i++) |
| { |
| HIR::PathExprSegment &seg = expr.get_segments ().at (i); |
| |
| bool have_more_segments = (expr.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 (root_tyty != nullptr && *offset > 0) |
| { |
| // then we can let the impl path probe take over now |
| return root_tyty; |
| } |
| |
| rust_error_at (seg.get_locus (), |
| "failed to type resolve root segment"); |
| return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); |
| } |
| |
| // node back to HIR |
| HirId ref; |
| if (!mappings->lookup_node_to_hir (ref_node_id, &ref)) |
| { |
| rust_error_at (seg.get_locus (), "456 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 (expr.get_mappings ().get_hirid ()); |
| } |
| |
| 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 (expr.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 (expr.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::<ty> |
| if (seg.has_generic_args ()) |
| { |
| if (!lookup->can_substitute ()) |
| { |
| rust_error_at (expr.get_locus (), |
| "substitutions not supported for %s", |
| root_tyty->as_string ().c_str ()); |
| return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); |
| } |
| |
| lookup = SubstMapper::Resolve (lookup, expr.get_locus (), |
| &seg.get_generic_args ()); |
| if (lookup->get_kind () == TyTy::TypeKind::ERROR) |
| return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); |
| } |
| |
| *root_resolved_node_id = ref_node_id; |
| *offset = *offset + 1; |
| root_tyty = lookup; |
| } |
| |
| return root_tyty; |
| } |
| |
| void |
| TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id, |
| std::vector<HIR::PathExprSegment> &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; |
| bool reciever_is_generic = prev_segment->get_kind () == TyTy::TypeKind::PARAM; |
| |
| for (size_t i = offset; i < segments.size (); i++) |
| { |
| HIR::PathExprSegment &seg = segments.at (i); |
| |
| 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_segment (), probe_impls, |
| false, ignore_mandatory_trait_items); |
| if (candidates.size () == 0) |
| { |
| candidates |
| = PathProbeType::Probe (prev_segment, seg.get_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; |
| } |
| } |
| |
| if (candidates.size () > 1) |
| { |
| ReportMultipleCandidateError::Report (candidates, seg.get_segment (), |
| seg.get_locus ()); |
| return; |
| } |
| |
| auto &candidate = *candidates.begin (); |
| prev_segment = tyseg; |
| tyseg = candidate.ty; |
| |
| HIR::ImplBlock *associated_impl_block = nullptr; |
| if (candidate.is_enum_candidate ()) |
| { |
| const TyTy::VariantDef *variant = candidate.item.enum_field.variant; |
| |
| HirId variant_id = variant->get_id (); |
| HIR::Item *enum_item = mappings->lookup_hir_item (variant_id); |
| rust_assert (enum_item != nullptr); |
| |
| resolved_node_id = enum_item->get_mappings ().get_nodeid (); |
| |
| // insert the id of the variant we are resolved to |
| context->insert_variant_definition (expr_mappings.get_hirid (), |
| variant_id); |
| } |
| else if (candidate.is_impl_candidate ()) |
| { |
| resolved_node_id |
| = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid (); |
| |
| associated_impl_block = candidate.item.impl.parent; |
| } |
| else |
| { |
| resolved_node_id |
| = candidate.item.trait.item_ref->get_mappings ().get_nodeid (); |
| |
| // lookup the associated-impl-trait |
| HIR::ImplBlock *impl = candidate.item.trait.impl; |
| if (impl != nullptr) |
| { |
| // get the associated impl block |
| associated_impl_block = impl; |
| } |
| } |
| |
| if (associated_impl_block != nullptr) |
| { |
| // associated types |
| HirId impl_block_id |
| = associated_impl_block->get_mappings ().get_hirid (); |
| |
| AssociatedImplTrait *associated = nullptr; |
| bool found_impl_trait |
| = context->lookup_associated_trait_impl (impl_block_id, |
| &associated); |
| TyTy::BaseType *impl_block_ty = nullptr; |
| if (found_impl_trait) |
| { |
| TyTy::TypeBoundPredicate predicate (*associated->get_trait (), |
| seg.get_locus ()); |
| impl_block_ty |
| = associated->setup_associated_types (prev_segment, predicate); |
| } |
| else |
| { |
| // get the type of the parent Self |
| HirId impl_ty_id = associated_impl_block->get_type () |
| ->get_mappings () |
| .get_hirid (); |
| |
| bool ok = query_type (impl_ty_id, &impl_block_ty); |
| rust_assert (ok); |
| |
| if (impl_block_ty->needs_generic_substitutions ()) |
| impl_block_ty |
| = SubstMapper::InferSubst (impl_block_ty, seg.get_locus ()); |
| } |
| |
| prev_segment = unify_site (seg.get_mappings ().get_hirid (), |
| TyTy::TyWithLocation (prev_segment), |
| TyTy::TyWithLocation (impl_block_ty), |
| seg.get_locus ()); |
| } |
| |
| if (tyseg->needs_generic_substitutions ()) |
| { |
| if (!prev_segment->needs_generic_substitutions ()) |
| { |
| auto used_args_in_prev_segment |
| = GetUsedSubstArgs::From (prev_segment); |
| |
| if (!used_args_in_prev_segment.is_error ()) |
| { |
| if (SubstMapperInternal::mappings_are_bound ( |
| tyseg, used_args_in_prev_segment)) |
| { |
| tyseg = SubstMapperInternal::Resolve ( |
| tyseg, used_args_in_prev_segment); |
| } |
| } |
| } |
| } |
| |
| if (seg.has_generic_args ()) |
| { |
| if (!tyseg->can_substitute ()) |
| { |
| rust_error_at (expr_locus, "substitutions not supported for %s", |
| tyseg->as_string ().c_str ()); |
| return; |
| } |
| |
| tyseg = SubstMapper::Resolve (tyseg, expr_locus, |
| &seg.get_generic_args ()); |
| if (tyseg->get_kind () == TyTy::TypeKind::ERROR) |
| return; |
| } |
| else if (tyseg->needs_generic_substitutions () && !reciever_is_generic) |
| { |
| Location locus = seg.get_locus (); |
| tyseg = SubstMapper::InferSubst (tyseg, locus); |
| if (tyseg->get_kind () == TyTy::TypeKind::ERROR) |
| return; |
| } |
| } |
| |
| rust_assert (resolved_node_id != UNKNOWN_NODEID); |
| if (tyseg->needs_generic_substitutions () && !reciever_is_generic) |
| { |
| Location locus = segments.back ().get_locus (); |
| tyseg = SubstMapper::InferSubst (tyseg, locus); |
| if (tyseg->get_kind () == TyTy::TypeKind::ERROR) |
| return; |
| } |
| |
| context->insert_receiver (expr_mappings.get_hirid (), prev_segment); |
| |
| // 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); |
| } |
| else |
| { |
| resolver->insert_resolved_misc (expr_mappings.get_nodeid (), |
| resolved_node_id); |
| } |
| |
| infered = tyseg; |
| } |
| |
| } // namespace Resolver |
| } // namespace Rust |