// 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-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 &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:: 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:: 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 &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