gcc/gcc/rust/typecheck/rust-hir-type-check-expr.cc

1615 lines
50 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-full.h"
#include "rust-tyty-call.h"
#include "rust-hir-type-check-struct-field.h"
#include "rust-hir-path-probe.h"
#include "rust-substitution-mapper.h"
#include "rust-hir-trait-resolve.h"
#include "rust-hir-type-bounds.h"
#include "rust-hir-dot-operator.h"
#include "rust-hir-type-check-pattern.h"
#include "rust-hir-type-check-expr.h"
#include "rust-hir-type-check-stmt.h"
namespace Rust {
namespace Resolver {
TypeCheckExpr::TypeCheckExpr () : TypeCheckBase (), infered (nullptr) {}
// Perform type checking on expr. Also runs type unification algorithm.
// Returns the unified type of expr
TyTy::BaseType *
TypeCheckExpr::Resolve (HIR::Expr *expr)
{
TypeCheckExpr resolver;
expr->accept_vis (resolver);
if (resolver.infered == nullptr)
{
// FIXME
// this is an internal error message for debugging and should be removed
// at some point
rust_error_at (expr->get_locus (), "failed to type resolve expression");
return new TyTy::ErrorType (expr->get_mappings ().get_hirid ());
}
auto ref = expr->get_mappings ().get_hirid ();
resolver.infered->set_ref (ref);
resolver.context->insert_type (expr->get_mappings (), resolver.infered);
return resolver.infered;
}
void
TypeCheckExpr::visit (HIR::TupleIndexExpr &expr)
{
auto resolved = TypeCheckExpr::Resolve (expr.get_tuple_expr ().get ());
if (resolved->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (expr.get_tuple_expr ()->get_locus (),
"failed to resolve TupleIndexExpr receiver");
return;
}
// FIXME does this require autoderef here?
if (resolved->get_kind () == TyTy::TypeKind::REF)
{
TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (resolved);
resolved = r->get_base ();
}
bool is_valid_type = resolved->get_kind () == TyTy::TypeKind::ADT
|| resolved->get_kind () == TyTy::TypeKind::TUPLE;
if (!is_valid_type)
{
rust_error_at (expr.get_tuple_expr ()->get_locus (),
"Expected Tuple or ADT got: %s",
resolved->as_string ().c_str ());
return;
}
if (resolved->get_kind () == TyTy::TypeKind::TUPLE)
{
TyTy::TupleType *tuple = static_cast<TyTy::TupleType *> (resolved);
TupleIndex index = expr.get_tuple_index ();
if ((size_t) index >= tuple->num_fields ())
{
rust_error_at (expr.get_locus (), "unknown field at index %i", index);
return;
}
auto field_tyty = tuple->get_field ((size_t) index);
if (field_tyty == nullptr)
{
rust_error_at (expr.get_locus (),
"failed to lookup field type at index %i", index);
return;
}
infered = field_tyty;
return;
}
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (resolved);
rust_assert (!adt->is_enum ());
rust_assert (adt->number_of_variants () == 1);
TyTy::VariantDef *variant = adt->get_variants ().at (0);
TupleIndex index = expr.get_tuple_index ();
if ((size_t) index >= variant->num_fields ())
{
rust_error_at (expr.get_locus (), "unknown field at index %i", index);
return;
}
auto field_tyty = variant->get_field_at_index ((size_t) index);
if (field_tyty == nullptr)
{
rust_error_at (expr.get_locus (),
"failed to lookup field type at index %i", index);
return;
}
infered = field_tyty->get_field_type ();
}
void
TypeCheckExpr::visit (HIR::TupleExpr &expr)
{
if (expr.is_unit ())
{
auto unit_node_id = resolver->get_unit_type_node_id ();
if (!context->lookup_builtin (unit_node_id, &infered))
{
rust_error_at (expr.get_locus (),
"failed to lookup builtin unit type");
}
return;
}
std::vector<TyTy::TyVar> fields;
for (auto &elem : expr.get_tuple_elems ())
{
auto field_ty = TypeCheckExpr::Resolve (elem.get ());
fields.push_back (TyTy::TyVar (field_ty->get_ref ()));
}
infered = new TyTy::TupleType (expr.get_mappings ().get_hirid (),
expr.get_locus (), fields);
}
void
TypeCheckExpr::visit (HIR::ReturnExpr &expr)
{
auto fn_return_tyty = context->peek_return_type ();
rust_assert (fn_return_tyty != nullptr);
TyTy::BaseType *expr_ty
= expr.has_return_expr ()
? TypeCheckExpr::Resolve (expr.get_expr ())
: TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
infered = fn_return_tyty->unify (expr_ty);
fn_return_tyty->append_reference (expr_ty->get_ref ());
for (auto &ref : infered->get_combined_refs ())
fn_return_tyty->append_reference (ref);
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::CallExpr &expr)
{
TyTy::BaseType *function_tyty = TypeCheckExpr::Resolve (expr.get_fnexpr ());
bool valid_tyty = function_tyty->get_kind () == TyTy::TypeKind::ADT
|| function_tyty->get_kind () == TyTy::TypeKind::FNDEF
|| function_tyty->get_kind () == TyTy::TypeKind::FNPTR;
if (!valid_tyty)
{
rust_error_at (expr.get_locus (),
"Failed to resolve expression of function call");
return;
}
TyTy::VariantDef &variant = TyTy::VariantDef::get_error_node ();
if (function_tyty->get_kind () == TyTy::TypeKind::ADT)
{
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (function_tyty);
if (adt->is_enum ())
{
// lookup variant id
HirId variant_id;
bool ok = context->lookup_variant_definition (
expr.get_fnexpr ()->get_mappings ().get_hirid (), &variant_id);
rust_assert (ok);
TyTy::VariantDef *lookup_variant = nullptr;
ok = adt->lookup_variant_by_id (variant_id, &lookup_variant);
rust_assert (ok);
variant = *lookup_variant;
}
else
{
rust_assert (adt->number_of_variants () == 1);
variant = *adt->get_variants ().at (0);
}
}
infered = TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context);
}
void
TypeCheckExpr::visit (HIR::AssignmentExpr &expr)
{
infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
coercion_site (expr.get_mappings ().get_hirid (), lhs, rhs,
expr.get_locus ());
}
void
TypeCheckExpr::visit (HIR::CompoundAssignmentExpr &expr)
{
infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
auto lhs = TypeCheckExpr::Resolve (expr.get_left_expr ().get ());
auto rhs = TypeCheckExpr::Resolve (expr.get_right_expr ().get ());
// we dont care about the result of the unify from a compound assignment
// since this is a unit-type expr
auto result = lhs->unify (rhs);
if (result->get_kind () == TyTy::TypeKind::ERROR)
return;
auto lang_item_type
= Analysis::RustLangItem::CompoundAssignmentOperatorToLangItem (
expr.get_expr_type ());
bool operator_overloaded
= resolve_operator_overload (lang_item_type, expr, lhs, rhs);
if (operator_overloaded)
return;
bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
bool valid = valid_lhs && valid_rhs;
if (!valid)
{
rust_error_at (expr.get_locus (),
"cannot apply this operator to types %s and %s",
lhs->as_string ().c_str (), rhs->as_string ().c_str ());
return;
}
}
void
TypeCheckExpr::visit (HIR::IdentifierExpr &expr)
{
NodeId ast_node_id = expr.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);
}
if (ref_node_id == UNKNOWN_NODEID)
{
// FIXME this needs to go away and just return error node
rust_error_at (expr.get_locus (), "unresolved node: %s",
expr.as_string ().c_str ());
return;
}
// node back to HIR
HirId ref;
if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
{
// FIXME
// this is an internal error
rust_error_at (expr.get_locus (), "123 reverse lookup failure");
return;
}
// the base reference for this name _must_ have a type set
TyTy::BaseType *lookup;
if (!context->lookup_type (ref, &lookup))
{
// FIXME
// this is an internal error
rust_error_at (mappings->lookup_location (ref),
"Failed to resolve IdentifierExpr type: %s",
expr.as_string ().c_str ());
return;
}
infered = lookup->clone ();
// Generic unit structs look like an identifier but they actually need be
// handled as a path-in-expression so this gives us a chance to infer the
// generic parameters.
// see https://github.com/Rust-GCC/gccrs/issues/1447
bool is_unit_struct
= infered->get_kind () == TyTy::TypeKind::ADT && infered->is_unit ();
if (is_unit_struct && infered->needs_generic_substitutions ())
{
infered = SubstMapper::InferSubst (infered, expr.get_locus ());
}
}
void
TypeCheckExpr::visit (HIR::LiteralExpr &expr)
{
infered = resolve_literal (expr.get_mappings (), expr.get_literal (),
expr.get_locus ());
}
void
TypeCheckExpr::visit (HIR::ArithmeticOrLogicalExpr &expr)
{
auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
auto lang_item_type
= Analysis::RustLangItem::OperatorToLangItem (expr.get_expr_type ());
bool operator_overloaded
= resolve_operator_overload (lang_item_type, expr, lhs, rhs);
if (operator_overloaded)
return;
bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
bool valid = valid_lhs && valid_rhs;
if (!valid)
{
rust_error_at (expr.get_locus (),
"cannot apply this operator to types %s and %s",
lhs->as_string ().c_str (), rhs->as_string ().c_str ());
return;
}
switch (expr.get_expr_type ())
{
case ArithmeticOrLogicalOperator::LEFT_SHIFT:
case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
infered = rhs->cast (lhs);
break;
default:
infered = lhs->unify (rhs);
break;
}
}
void
TypeCheckExpr::visit (HIR::ComparisonExpr &expr)
{
auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
auto result = lhs->unify (rhs);
if (result == nullptr || result->get_kind () == TyTy::TypeKind::ERROR)
return;
bool ok = context->lookup_builtin ("bool", &infered);
rust_assert (ok);
}
void
TypeCheckExpr::visit (HIR::LazyBooleanExpr &expr)
{
auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
// we expect the lhs and rhs must be bools at this point
TyTy::BoolType elhs (expr.get_mappings ().get_hirid ());
lhs = elhs.unify (lhs);
if (lhs->get_kind () == TyTy::TypeKind::ERROR)
return;
TyTy::BoolType rlhs (expr.get_mappings ().get_hirid ());
rhs = elhs.unify (rhs);
if (lhs->get_kind () == TyTy::TypeKind::ERROR)
return;
infered = lhs->unify (rhs);
}
void
TypeCheckExpr::visit (HIR::NegationExpr &expr)
{
auto negated_expr_ty = TypeCheckExpr::Resolve (expr.get_expr ().get ());
// check for operator overload
auto lang_item_type = Analysis::RustLangItem::NegationOperatorToLangItem (
expr.get_expr_type ());
bool operator_overloaded
= resolve_operator_overload (lang_item_type, expr, negated_expr_ty,
nullptr);
if (operator_overloaded)
return;
// https://doc.rust-lang.org/reference/expressions/operator-expr.html#negation-operators
switch (expr.get_expr_type ())
{
case NegationOperator::NEGATE: {
bool valid
= (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::FLOAT)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::ISIZE)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::USIZE)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
== TyTy::InferType::INTEGRAL))
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
== TyTy::InferType::FLOAT));
if (!valid)
{
rust_error_at (expr.get_locus (), "cannot apply unary - to %s",
negated_expr_ty->as_string ().c_str ());
return;
}
}
break;
case NegationOperator::NOT: {
bool valid
= (negated_expr_ty->get_kind () == TyTy::TypeKind::BOOL)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
|| (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
== TyTy::InferType::INTEGRAL));
if (!valid)
{
rust_error_at (expr.get_locus (), "cannot apply unary %<!%> to %s",
negated_expr_ty->as_string ().c_str ());
return;
}
}
break;
}
infered = negated_expr_ty->clone ();
infered->append_reference (negated_expr_ty->get_ref ());
}
void
TypeCheckExpr::visit (HIR::IfExpr &expr)
{
TypeCheckExpr::Resolve (expr.get_if_condition ());
TypeCheckExpr::Resolve (expr.get_if_block ());
infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::IfExprConseqElse &expr)
{
TypeCheckExpr::Resolve (expr.get_if_condition ());
auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_else_block ());
if (if_blk_resolved->get_kind () == TyTy::NEVER)
infered = else_blk_resolved;
else if (else_blk_resolved->get_kind () == TyTy::NEVER)
infered = if_blk_resolved;
else
infered = if_blk_resolved->unify (else_blk_resolved);
}
void
TypeCheckExpr::visit (HIR::IfExprConseqIf &expr)
{
TypeCheckExpr::Resolve (expr.get_if_condition ());
auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_conseq_if_expr ());
if (if_blk_resolved->get_kind () == TyTy::NEVER)
infered = else_blk_resolved;
else if (else_blk_resolved->get_kind () == TyTy::NEVER)
infered = if_blk_resolved;
else
infered = if_blk_resolved->unify (else_blk_resolved);
}
void
TypeCheckExpr::visit (HIR::IfLetExpr &expr)
{
// this needs to perform a least upper bound coercion on the blocks and then
// unify the scruintee and arms
TyTy::BaseType *scrutinee_tyty
= TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
for (auto &pattern : expr.get_patterns ())
{
TyTy::BaseType *kase_arm_ty
= TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
return;
}
TypeCheckExpr::Resolve (expr.get_if_block ());
infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::UnsafeBlockExpr &expr)
{
infered = TypeCheckExpr::Resolve (expr.get_block_expr ().get ());
}
void
TypeCheckExpr::visit (HIR::BlockExpr &expr)
{
for (auto &s : expr.get_statements ())
{
if (!s->is_item ())
continue;
TypeCheckStmt::Resolve (s.get ());
}
for (auto &s : expr.get_statements ())
{
if (s->is_item ())
continue;
auto resolved = TypeCheckStmt::Resolve (s.get ());
if (resolved == nullptr)
{
rust_error_at (s->get_locus (), "failure to resolve type");
return;
}
if (s->is_unit_check_needed () && !resolved->is_unit ())
{
auto unit
= TyTy::TupleType::get_unit_type (s->get_mappings ().get_hirid ());
resolved = unit->unify (resolved);
}
}
if (expr.has_expr ())
infered = TypeCheckExpr::Resolve (expr.get_final_expr ().get ())->clone ();
else if (expr.is_tail_reachable ())
infered
= TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
else
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::RangeFromToExpr &expr)
{
auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE;
DefId respective_lang_item_id = UNKNOWN_DEFID;
bool lang_item_defined
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
// we need to have it maybe
if (!lang_item_defined)
{
rust_internal_error_at (
expr.get_locus (), "unable to find relevant lang item: %s",
Analysis::RustLangItem::ToString (lang_item_type).c_str ());
return;
}
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
rust_assert (item != nullptr);
TyTy::BaseType *item_type = nullptr;
bool ok
= context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
rust_assert (ok);
rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
// this is a single generic item lets assert that
rust_assert (adt->get_num_substitutions () == 1);
// resolve the range expressions and these types must unify then we use that
// type to substitute into the ADT
TyTy::BaseType *from_ty
= TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
TyTy::BaseType *to_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
TyTy::BaseType *unified = from_ty->unify (to_ty);
// substitute it in
std::vector<TyTy::SubstitutionArg> subst_mappings;
const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, unified));
TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
infered = SubstMapperInternal::Resolve (adt, subst);
}
void
TypeCheckExpr::visit (HIR::RangeFromExpr &expr)
{
auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_FROM;
DefId respective_lang_item_id = UNKNOWN_DEFID;
bool lang_item_defined
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
// we need to have it maybe
if (!lang_item_defined)
{
rust_internal_error_at (
expr.get_locus (), "unable to find relevant lang item: %s",
Analysis::RustLangItem::ToString (lang_item_type).c_str ());
return;
}
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
rust_assert (item != nullptr);
TyTy::BaseType *item_type = nullptr;
bool ok
= context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
rust_assert (ok);
rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
// this is a single generic item lets assert that
rust_assert (adt->get_num_substitutions () == 1);
// resolve the range expressions and these types must unify then we use that
// type to substitute into the ADT
TyTy::BaseType *from_ty
= TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
// substitute it in
std::vector<TyTy::SubstitutionArg> subst_mappings;
const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, from_ty));
TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
infered = SubstMapperInternal::Resolve (adt, subst);
}
void
TypeCheckExpr::visit (HIR::RangeToExpr &expr)
{
auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_TO;
DefId respective_lang_item_id = UNKNOWN_DEFID;
bool lang_item_defined
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
// we need to have it maybe
if (!lang_item_defined)
{
rust_internal_error_at (
expr.get_locus (), "unable to find relevant lang item: %s",
Analysis::RustLangItem::ToString (lang_item_type).c_str ());
return;
}
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
rust_assert (item != nullptr);
TyTy::BaseType *item_type = nullptr;
bool ok
= context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
rust_assert (ok);
rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
// this is a single generic item lets assert that
rust_assert (adt->get_num_substitutions () == 1);
// resolve the range expressions and these types must unify then we use that
// type to substitute into the ADT
TyTy::BaseType *from_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
// substitute it in
std::vector<TyTy::SubstitutionArg> subst_mappings;
const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, from_ty));
TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
infered = SubstMapperInternal::Resolve (adt, subst);
}
void
TypeCheckExpr::visit (HIR::RangeFullExpr &expr)
{
auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_FULL;
DefId respective_lang_item_id = UNKNOWN_DEFID;
bool lang_item_defined
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
// we need to have it maybe
if (!lang_item_defined)
{
rust_internal_error_at (
expr.get_locus (), "unable to find relevant lang item: %s",
Analysis::RustLangItem::ToString (lang_item_type).c_str ());
return;
}
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
rust_assert (item != nullptr);
TyTy::BaseType *item_type = nullptr;
bool ok
= context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
rust_assert (ok);
rust_assert (item_type->is_unit ());
infered = item_type;
}
void
TypeCheckExpr::visit (HIR::RangeFromToInclExpr &expr)
{
auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_INCLUSIVE;
DefId respective_lang_item_id = UNKNOWN_DEFID;
bool lang_item_defined
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
// we need to have it maybe
if (!lang_item_defined)
{
rust_internal_error_at (
expr.get_locus (), "unable to find relevant lang item: %s",
Analysis::RustLangItem::ToString (lang_item_type).c_str ());
return;
}
// look it up and it _must_ be a struct definition
HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
rust_assert (item != nullptr);
TyTy::BaseType *item_type = nullptr;
bool ok
= context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
rust_assert (ok);
rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
// this is a single generic item lets assert that
rust_assert (adt->get_num_substitutions () == 1);
// resolve the range expressions and these types must unify then we use that
// type to substitute into the ADT
TyTy::BaseType *from_ty
= TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
TyTy::BaseType *to_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
TyTy::BaseType *unified = from_ty->unify (to_ty);
// substitute it in
std::vector<TyTy::SubstitutionArg> subst_mappings;
const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, unified));
TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
infered = SubstMapperInternal::Resolve (adt, subst);
}
void
TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr)
{
auto array_expr_ty = TypeCheckExpr::Resolve (expr.get_array_expr ());
if (array_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
return;
auto index_expr_ty = TypeCheckExpr::Resolve (expr.get_index_expr ());
if (index_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
return;
// first attempt to use direct array index logic
auto direct_array_expr_ty = array_expr_ty;
if (direct_array_expr_ty->get_kind () == TyTy::TypeKind::REF)
{
// lets try and deref it since rust allows this
auto ref = static_cast<TyTy::ReferenceType *> (direct_array_expr_ty);
auto base = ref->get_base ();
if (base->get_kind () == TyTy::TypeKind::ARRAY)
direct_array_expr_ty = base;
}
TyTy::BaseType *size_ty;
bool ok = context->lookup_builtin ("usize", &size_ty);
rust_assert (ok);
bool maybe_simple_array_access = index_expr_ty->can_eq (size_ty, false);
if (maybe_simple_array_access
&& direct_array_expr_ty->get_kind () == TyTy::TypeKind::ARRAY)
{
auto resolved_index_expr = size_ty->unify (index_expr_ty);
if (resolved_index_expr->get_kind () == TyTy::TypeKind::ERROR)
return;
TyTy::ArrayType *array_type
= static_cast<TyTy::ArrayType *> (direct_array_expr_ty);
infered = array_type->get_element_type ()->clone ();
return;
}
// is this a case of core::ops::index?
auto lang_item_type = Analysis::RustLangItem::ItemType::INDEX;
bool operator_overloaded
= resolve_operator_overload (lang_item_type, expr, array_expr_ty,
index_expr_ty);
if (operator_overloaded)
{
// index and index mut always return a reference to the element
TyTy::BaseType *resolved = infered;
rust_assert (resolved->get_kind () == TyTy::TypeKind::REF);
TyTy::ReferenceType *ref = static_cast<TyTy::ReferenceType *> (resolved);
infered = ref->get_base ()->clone ();
return;
}
// error[E0277]: the type `[{integer}]` cannot be indexed by `u32`
RichLocation r (expr.get_locus ());
r.add_range (expr.get_array_expr ()->get_locus ());
r.add_range (expr.get_index_expr ()->get_locus ());
rust_error_at (r, "the type %<%s%> cannot be indexed by %<%s%>",
array_expr_ty->get_name ().c_str (),
index_expr_ty->get_name ().c_str ());
}
void
TypeCheckExpr::visit (HIR::ArrayExpr &expr)
{
HIR::ArrayElems &elements = *expr.get_internal_elements ();
HIR::Expr *capacity_expr = nullptr;
TyTy::BaseType *element_type = nullptr;
switch (elements.get_array_expr_type ())
{
case HIR::ArrayElems::ArrayExprType::COPIED: {
HIR::ArrayElemsCopied &elems
= static_cast<HIR::ArrayElemsCopied &> (elements);
element_type = TypeCheckExpr::Resolve (elems.get_elem_to_copy ());
auto capacity_type
= TypeCheckExpr::Resolve (elems.get_num_copies_expr ());
TyTy::BaseType *expected_ty = nullptr;
bool ok = context->lookup_builtin ("usize", &expected_ty);
rust_assert (ok);
context->insert_type (elems.get_num_copies_expr ()->get_mappings (),
expected_ty);
auto unified = expected_ty->unify (capacity_type);
if (unified->get_kind () == TyTy::TypeKind::ERROR)
return;
capacity_expr = elems.get_num_copies_expr ();
}
break;
case HIR::ArrayElems::ArrayExprType::VALUES: {
HIR::ArrayElemsValues &elems
= static_cast<HIR::ArrayElemsValues &> (elements);
std::vector<TyTy::BaseType *> types;
for (auto &elem : elems.get_values ())
{
types.push_back (TypeCheckExpr::Resolve (elem.get ()));
}
element_type
= TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()).get_tyty ();
for (auto &type : types)
{
element_type = element_type->unify (type);
}
auto crate_num = mappings->get_current_crate ();
Analysis::NodeMapping mapping (crate_num, UNKNOWN_NODEID,
mappings->get_next_hir_id (crate_num),
UNKNOWN_LOCAL_DEFID);
std::string capacity_str = std::to_string (elems.get_num_elements ());
capacity_expr = new HIR::LiteralExpr (mapping, capacity_str,
HIR::Literal::LitType::INT,
PrimitiveCoreType::CORETYPE_USIZE,
Location (), {});
// mark the type for this implicit node
TyTy::BaseType *expected_ty = nullptr;
bool ok = context->lookup_builtin ("usize", &expected_ty);
rust_assert (ok);
context->insert_type (mapping, expected_ty);
}
break;
}
infered = new TyTy::ArrayType (expr.get_mappings ().get_hirid (),
expr.get_locus (), *capacity_expr,
TyTy::TyVar (element_type->get_ref ()));
}
// empty struct
void
TypeCheckExpr::visit (HIR::StructExprStruct &struct_expr)
{
TyTy::BaseType *struct_path_ty
= TypeCheckExpr::Resolve (&struct_expr.get_struct_name ());
if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT)
{
rust_error_at (struct_expr.get_struct_name ().get_locus (),
"expected an ADT type for constructor");
return;
}
infered = struct_path_ty;
}
void
TypeCheckExpr::visit (HIR::StructExprStructFields &struct_expr)
{
infered = TypeCheckStructExpr::Resolve (&struct_expr);
}
void
TypeCheckExpr::visit (HIR::GroupedExpr &expr)
{
infered = TypeCheckExpr::Resolve (expr.get_expr_in_parens ().get ());
}
void
TypeCheckExpr::visit (HIR::FieldAccessExpr &expr)
{
auto struct_base = TypeCheckExpr::Resolve (expr.get_receiver_expr ().get ());
// FIXME does this require autoderef here?
if (struct_base->get_kind () == TyTy::TypeKind::REF)
{
TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (struct_base);
struct_base = r->get_base ();
}
bool is_valid_type = struct_base->get_kind () == TyTy::TypeKind::ADT;
if (!is_valid_type)
{
rust_error_at (expr.get_locus (),
"expected algebraic data type got: [%s]",
struct_base->as_string ().c_str ());
return;
}
TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (struct_base);
rust_assert (!adt->is_enum ());
rust_assert (adt->number_of_variants () == 1);
TyTy::VariantDef *vaiant = adt->get_variants ().at (0);
TyTy::StructFieldType *lookup = nullptr;
bool found = vaiant->lookup_field (expr.get_field_name (), &lookup, nullptr);
if (!found)
{
rust_error_at (expr.get_locus (), "unknown field [%s] for type [%s]",
expr.get_field_name ().c_str (),
adt->as_string ().c_str ());
return;
}
infered = lookup->get_field_type ();
}
void
TypeCheckExpr::visit (HIR::MethodCallExpr &expr)
{
auto receiver_tyty = TypeCheckExpr::Resolve (expr.get_receiver ().get ());
if (receiver_tyty->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (expr.get_receiver ()->get_locus (),
"failed to resolve receiver in MethodCallExpr");
return;
}
context->insert_receiver (expr.get_mappings ().get_hirid (), receiver_tyty);
auto candidate
= MethodResolver::Probe (receiver_tyty,
expr.get_method_name ().get_segment ());
if (candidate.is_error ())
{
rust_error_at (
expr.get_method_name ().get_locus (),
"failed to resolve method for %<%s%>",
expr.get_method_name ().get_segment ().as_string ().c_str ());
return;
}
// Get the adjusted self
Adjuster adj (receiver_tyty);
TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
// store the adjustments for code-generation to know what to do which must be
// stored onto the receiver to so as we don't trigger duplicate deref mappings
// ICE when an argument is a method call
HirId autoderef_mappings_id
= expr.get_receiver ()->get_mappings ().get_hirid ();
context->insert_autoderef_mappings (autoderef_mappings_id,
std::move (candidate.adjustments));
PathProbeCandidate &resolved_candidate = candidate.candidate;
TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
NodeId resolved_node_id
= resolved_candidate.is_impl_candidate ()
? resolved_candidate.item.impl.impl_item->get_impl_mappings ()
.get_nodeid ()
: resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
if (lookup_tyty->get_kind () != TyTy::TypeKind::FNDEF)
{
RichLocation r (expr.get_method_name ().get_locus ());
r.add_range (resolved_candidate.locus);
rust_error_at (r, "associated impl item is not a method");
return;
}
TyTy::BaseType *lookup = lookup_tyty;
TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
if (!fn->is_method ())
{
RichLocation r (expr.get_method_name ().get_locus ());
r.add_range (resolved_candidate.locus);
rust_error_at (r, "associated function is not a method");
return;
}
auto root = receiver_tyty->get_root ();
if (root->get_kind () == TyTy::TypeKind::ADT)
{
const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (root);
if (adt->has_substitutions () && fn->needs_substitution ())
{
// consider the case where we have:
//
// struct Foo<X,Y>(X,Y);
//
// impl<T> Foo<T, i32> {
// fn test<X>(self, a:X) -> (T,X) { (self.0, a) }
// }
//
// In this case we end up with an fn type of:
//
// fn <T,X> test(self:Foo<T,i32>, a:X) -> (T,X)
//
// This means the instance or self we are calling this method for
// will be substituted such that we can get the inherited type
// arguments but then need to use the turbo fish if available or
// infer the remaining arguments. Luckily rust does not allow for
// default types GenericParams on impl blocks since these must
// always be at the end of the list
auto s = fn->get_self_type ()->get_root ();
rust_assert (s->can_eq (adt, false));
rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
const TyTy::ADTType *self_adt
= static_cast<const TyTy::ADTType *> (s);
// we need to grab the Self substitutions as the inherit type
// parameters for this
if (self_adt->needs_substitution ())
{
rust_assert (adt->was_substituted ());
TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
= GetUsedSubstArgs::From (adt);
TyTy::SubstitutionArgumentMappings inherit_type_args
= self_adt->solve_mappings_from_receiver_for_self (
used_args_in_prev_segment);
// there may or may not be inherited type arguments
if (!inherit_type_args.is_error ())
{
// need to apply the inherited type arguments to the
// function
lookup = fn->handle_substitions (inherit_type_args);
}
}
}
}
// apply any remaining generic arguments
if (expr.get_method_name ().has_generic_args ())
{
HIR::GenericArgs &args = expr.get_method_name ().get_generic_args ();
lookup
= SubstMapper::Resolve (lookup, expr.get_method_name ().get_locus (),
&args);
if (lookup->get_kind () == TyTy::TypeKind::ERROR)
return;
}
else if (lookup->needs_generic_substitutions ())
{
lookup = SubstMapper::InferSubst (lookup,
expr.get_method_name ().get_locus ());
}
TyTy::BaseType *function_ret_tyty
= TyTy::TypeCheckMethodCallExpr::go (lookup, expr, adjusted_self, context);
if (function_ret_tyty == nullptr
|| function_ret_tyty->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (expr.get_locus (),
"failed to lookup type to MethodCallExpr");
return;
}
// store the expected fntype
context->insert_type (expr.get_method_name ().get_mappings (), lookup);
// set up the resolved name on the path
resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
resolved_node_id);
// return the result of the function back
infered = function_ret_tyty;
}
void
TypeCheckExpr::visit (HIR::LoopExpr &expr)
{
context->push_new_loop_context (expr.get_mappings ().get_hirid (),
expr.get_locus ());
TyTy::BaseType *block_expr
= TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
if (!block_expr->is_unit ())
{
rust_error_at (expr.get_loop_block ()->get_locus (),
"expected %<()%> got %s",
block_expr->as_string ().c_str ());
return;
}
TyTy::BaseType *loop_context_type = context->pop_loop_context ();
bool loop_context_type_infered
= (loop_context_type->get_kind () != TyTy::TypeKind::INFER)
|| ((loop_context_type->get_kind () == TyTy::TypeKind::INFER)
&& (((TyTy::InferType *) loop_context_type)->get_infer_kind ()
!= TyTy::InferType::GENERAL));
infered
= loop_context_type_infered
? loop_context_type
: TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
{
context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
TypeCheckExpr::Resolve (expr.get_predicate_expr ().get ());
TyTy::BaseType *block_expr
= TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
if (!block_expr->is_unit ())
{
rust_error_at (expr.get_loop_block ()->get_locus (),
"expected %<()%> got %s",
block_expr->as_string ().c_str ());
return;
}
context->pop_loop_context ();
infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::BreakExpr &expr)
{
if (!context->have_loop_context ())
{
rust_error_at (expr.get_locus (), "cannot %<break%> outside of a loop");
return;
}
if (expr.has_break_expr ())
{
TyTy::BaseType *break_expr_tyty
= TypeCheckExpr::Resolve (expr.get_expr ().get ());
TyTy::BaseType *loop_context = context->peek_loop_context ();
if (loop_context->get_kind () == TyTy::TypeKind::ERROR)
{
rust_error_at (expr.get_locus (),
"can only break with a value inside %<loop%>");
return;
}
TyTy::BaseType *unified_ty = loop_context->unify (break_expr_tyty);
context->swap_head_loop_context (unified_ty);
}
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::ContinueExpr &expr)
{
if (!context->have_loop_context ())
{
rust_error_at (expr.get_locus (),
"cannot %<continue%> outside of a loop");
return;
}
infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
}
void
TypeCheckExpr::visit (HIR::BorrowExpr &expr)
{
TyTy::BaseType *resolved_base
= TypeCheckExpr::Resolve (expr.get_expr ().get ());
// In Rust this is valid because of DST's
//
// fn test() {
// let a:&str = "TEST 1";
// let b:&str = &"TEST 2";
// }
if (resolved_base->get_kind () == TyTy::TypeKind::REF)
{
const TyTy::ReferenceType *ref
= static_cast<const TyTy::ReferenceType *> (resolved_base);
// this might end up being a more generic is_dyn object check but lets
// double check dyn traits type-layout first
if (ref->is_dyn_str_type ())
{
infered = resolved_base;
return;
}
}
if (expr.get_is_double_borrow ())
{
// FIXME double_reference
gcc_unreachable ();
}
infered = new TyTy::ReferenceType (expr.get_mappings ().get_hirid (),
TyTy::TyVar (resolved_base->get_ref ()),
expr.get_mut ());
}
void
TypeCheckExpr::visit (HIR::DereferenceExpr &expr)
{
TyTy::BaseType *resolved_base
= TypeCheckExpr::Resolve (expr.get_expr ().get ());
auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF;
bool operator_overloaded
= resolve_operator_overload (lang_item_type, expr, resolved_base, nullptr);
if (operator_overloaded)
{
// operator overloaded deref always refurns a reference type lets assert
// this
rust_assert (infered->get_kind () == TyTy::TypeKind::REF);
resolved_base = infered;
}
bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF
|| resolved_base->get_kind () == TyTy::TypeKind::POINTER;
if (!is_valid_type)
{
rust_error_at (expr.get_locus (), "expected reference type got %s",
resolved_base->as_string ().c_str ());
return;
}
if (resolved_base->get_kind () == TyTy::TypeKind::REF)
{
TyTy::ReferenceType *ref_base
= static_cast<TyTy::ReferenceType *> (resolved_base);
infered = ref_base->get_base ()->clone ();
}
else
{
TyTy::PointerType *ref_base
= static_cast<TyTy::PointerType *> (resolved_base);
infered = ref_base->get_base ()->clone ();
}
}
void
TypeCheckExpr::visit (HIR::TypeCastExpr &expr)
{
TyTy::BaseType *expr_to_convert
= TypeCheckExpr::Resolve (expr.get_casted_expr ().get ());
TyTy::BaseType *tyty_to_convert_to
= TypeCheckType::Resolve (expr.get_type_to_convert_to ().get ());
infered = expr_to_convert->cast (tyty_to_convert_to);
}
void
TypeCheckExpr::visit (HIR::MatchExpr &expr)
{
// this needs to perform a least upper bound coercion on the blocks and then
// unify the scruintee and arms
TyTy::BaseType *scrutinee_tyty
= TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
std::vector<TyTy::BaseType *> kase_block_tys;
for (auto &kase : expr.get_match_cases ())
{
// lets check the arms
HIR::MatchArm &kase_arm = kase.get_arm ();
for (auto &pattern : kase_arm.get_patterns ())
{
TyTy::BaseType *kase_arm_ty
= TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
return;
}
// check the kase type
TyTy::BaseType *kase_block_ty
= TypeCheckExpr::Resolve (kase.get_expr ().get ());
kase_block_tys.push_back (kase_block_ty);
}
if (kase_block_tys.size () == 0)
{
infered
= TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
return;
}
infered = kase_block_tys.at (0);
for (size_t i = 1; i < kase_block_tys.size (); i++)
{
TyTy::BaseType *kase_ty = kase_block_tys.at (i);
infered = infered->unify (kase_ty);
if (infered->get_kind () == TyTy::TypeKind::ERROR)
return;
}
}
bool
TypeCheckExpr::resolve_operator_overload (
Analysis::RustLangItem::ItemType lang_item_type, HIR::OperatorExprMeta expr,
TyTy::BaseType *lhs, TyTy::BaseType *rhs)
{
// look up lang item for arithmetic type
std::string associated_item_name
= Analysis::RustLangItem::ToString (lang_item_type);
DefId respective_lang_item_id = UNKNOWN_DEFID;
bool lang_item_defined
= mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
// probe for the lang-item
if (!lang_item_defined)
return false;
auto segment = HIR::PathIdentSegment (associated_item_name);
auto candidate
= MethodResolver::Probe (lhs, HIR::PathIdentSegment (associated_item_name));
bool have_implementation_for_lang_item = !candidate.is_error ();
if (!have_implementation_for_lang_item)
return false;
// Get the adjusted self
Adjuster adj (lhs);
TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
// is this the case we are recursive
// handle the case where we are within the impl block for this lang_item
// otherwise we end up with a recursive operator overload such as the i32
// operator overload trait
TypeCheckContextItem &fn_context = context->peek_context ();
if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
{
auto &impl_item = fn_context.get_impl_item ();
HIR::ImplBlock *parent = impl_item.first;
HIR::Function *fn = impl_item.second;
if (parent->has_trait_ref ()
&& fn->get_function_name ().compare (associated_item_name) == 0)
{
TraitReference *trait_reference
= TraitResolver::Lookup (*parent->get_trait_ref ().get ());
if (!trait_reference->is_error ())
{
TyTy::BaseType *lookup = nullptr;
bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
&lookup);
rust_assert (ok);
rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
rust_assert (fntype->is_method ());
bool is_lang_item_impl
= trait_reference->get_mappings ().get_defid ()
== respective_lang_item_id;
bool self_is_lang_item_self
= fntype->get_self_type ()->is_equal (*adjusted_self);
bool recursive_operator_overload
= is_lang_item_impl && self_is_lang_item_self;
if (recursive_operator_overload)
return false;
}
}
}
// store the adjustments for code-generation to know what to do
context->insert_autoderef_mappings (expr.get_lvalue_mappings ().get_hirid (),
std::move (candidate.adjustments));
// now its just like a method-call-expr
context->insert_receiver (expr.get_mappings ().get_hirid (), lhs);
PathProbeCandidate &resolved_candidate = candidate.candidate;
TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
NodeId resolved_node_id
= resolved_candidate.is_impl_candidate ()
? resolved_candidate.item.impl.impl_item->get_impl_mappings ()
.get_nodeid ()
: resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
TyTy::BaseType *lookup = lookup_tyty;
TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
rust_assert (fn->is_method ());
auto root = lhs->get_root ();
if (root->get_kind () == TyTy::TypeKind::ADT)
{
const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (root);
if (adt->has_substitutions () && fn->needs_substitution ())
{
// consider the case where we have:
//
// struct Foo<X,Y>(X,Y);
//
// impl<T> Foo<T, i32> {
// fn test<X>(self, a:X) -> (T,X) { (self.0, a) }
// }
//
// In this case we end up with an fn type of:
//
// fn <T,X> test(self:Foo<T,i32>, a:X) -> (T,X)
//
// This means the instance or self we are calling this method for
// will be substituted such that we can get the inherited type
// arguments but then need to use the turbo fish if available or
// infer the remaining arguments. Luckily rust does not allow for
// default types GenericParams on impl blocks since these must
// always be at the end of the list
auto s = fn->get_self_type ()->get_root ();
rust_assert (s->can_eq (adt, false));
rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
const TyTy::ADTType *self_adt
= static_cast<const TyTy::ADTType *> (s);
// we need to grab the Self substitutions as the inherit type
// parameters for this
if (self_adt->needs_substitution ())
{
rust_assert (adt->was_substituted ());
TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
= GetUsedSubstArgs::From (adt);
TyTy::SubstitutionArgumentMappings inherit_type_args
= self_adt->solve_mappings_from_receiver_for_self (
used_args_in_prev_segment);
// there may or may not be inherited type arguments
if (!inherit_type_args.is_error ())
{
// need to apply the inherited type arguments to the
// function
lookup = fn->handle_substitions (inherit_type_args);
}
}
}
}
// handle generics
if (lookup->needs_generic_substitutions ())
lookup = SubstMapper::InferSubst (lookup, expr.get_locus ());
// type check the arguments if required
TyTy::FnType *type = static_cast<TyTy::FnType *> (lookup);
rust_assert (type->num_params () > 0);
auto fnparam = type->param_at (0);
fnparam.second->unify (adjusted_self); // typecheck the self
if (rhs == nullptr)
{
rust_assert (type->num_params () == 1);
}
else
{
rust_assert (type->num_params () == 2);
auto fnparam = type->param_at (1);
fnparam.second->unify (rhs); // typecheck the rhs
}
rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
fn = static_cast<TyTy::FnType *> (lookup);
fn->monomorphize ();
// get the return type
TyTy::BaseType *function_ret_tyty
= type->get_return_type ()->monomorphized_clone ();
// store the expected fntype
context->insert_operator_overload (expr.get_mappings ().get_hirid (), type);
// set up the resolved name on the path
resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
resolved_node_id);
// return the result of the function back
infered = function_ret_tyty;
return true;
}
bool
TypeCheckExpr::validate_arithmetic_type (
const TyTy::BaseType *tyty, HIR::ArithmeticOrLogicalExpr::ExprType expr_type)
{
const TyTy::BaseType *type = tyty->destructure ();
// https://doc.rust-lang.org/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators
// this will change later when traits are added
switch (expr_type)
{
case ArithmeticOrLogicalOperator::ADD:
case ArithmeticOrLogicalOperator::SUBTRACT:
case ArithmeticOrLogicalOperator::MULTIPLY:
case ArithmeticOrLogicalOperator::DIVIDE:
case ArithmeticOrLogicalOperator::MODULUS:
return (type->get_kind () == TyTy::TypeKind::INT)
|| (type->get_kind () == TyTy::TypeKind::UINT)
|| (type->get_kind () == TyTy::TypeKind::FLOAT)
|| (type->get_kind () == TyTy::TypeKind::USIZE)
|| (type->get_kind () == TyTy::TypeKind::ISIZE)
|| (type->get_kind () == TyTy::TypeKind::INFER
&& (((const TyTy::InferType *) type)->get_infer_kind ()
== TyTy::InferType::INTEGRAL))
|| (type->get_kind () == TyTy::TypeKind::INFER
&& (((const TyTy::InferType *) type)->get_infer_kind ()
== TyTy::InferType::FLOAT));
// integers or bools
case ArithmeticOrLogicalOperator::BITWISE_AND:
case ArithmeticOrLogicalOperator::BITWISE_OR:
case ArithmeticOrLogicalOperator::BITWISE_XOR:
return (type->get_kind () == TyTy::TypeKind::INT)
|| (type->get_kind () == TyTy::TypeKind::UINT)
|| (type->get_kind () == TyTy::TypeKind::USIZE)
|| (type->get_kind () == TyTy::TypeKind::ISIZE)
|| (type->get_kind () == TyTy::TypeKind::BOOL)
|| (type->get_kind () == TyTy::TypeKind::INFER
&& (((const TyTy::InferType *) type)->get_infer_kind ()
== TyTy::InferType::INTEGRAL));
// integers only
case ArithmeticOrLogicalOperator::LEFT_SHIFT:
case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
return (type->get_kind () == TyTy::TypeKind::INT)
|| (type->get_kind () == TyTy::TypeKind::UINT)
|| (type->get_kind () == TyTy::TypeKind::USIZE)
|| (type->get_kind () == TyTy::TypeKind::ISIZE)
|| (type->get_kind () == TyTy::TypeKind::INFER
&& (((const TyTy::InferType *) type)->get_infer_kind ()
== TyTy::InferType::INTEGRAL));
}
gcc_unreachable ();
return false;
}
} // namespace Resolver
} // namespace Rust