474 lines
14 KiB
C++
474 lines
14 KiB
C++
// Copyright (C) 2020-2022 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 ())
|
|
{
|
|
// 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);
|
|
|
|
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 (!context->lookup_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.at (0);
|
|
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)
|
|
{
|
|
// get the type of the parent Self
|
|
HirId impl_ty_id
|
|
= associated_impl_block->get_type ()->get_mappings ().get_hirid ();
|
|
TyTy::BaseType *impl_block_ty = nullptr;
|
|
bool ok = context->lookup_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 = prev_segment->unify (impl_block_ty);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
infered = tyseg;
|
|
}
|
|
|
|
} // namespace Resolver
|
|
} // namespace Rust
|