// 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-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 (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 (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 (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 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 (); Location expr_locus = expr.has_return_expr () ? expr.get_expr ()->get_locus () : expr.get_locus (); TyTy::BaseType *expr_ty = expr.has_return_expr () ? TypeCheckExpr::Resolve (expr.get_expr ()) : TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); infered = unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (fn_return_tyty), TyTy::TyWithLocation (expr_ty, expr_locus), expr.get_locus ()); infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); } void TypeCheckExpr::visit (HIR::CallExpr &expr) { TyTy::BaseType *function_tyty = TypeCheckExpr::Resolve (expr.get_fnexpr ()); rust_debug_loc (expr.get_locus (), "resolved_call_expr to: {%s}", function_tyty->get_name ().c_str ()); TyTy::VariantDef &variant = TyTy::VariantDef::get_error_node (); if (function_tyty->get_kind () == TyTy::TypeKind::ADT) { TyTy::ADTType *adt = static_cast (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); return; } bool resolved_fn_trait_call = resolve_fn_trait_call (expr, function_tyty, &infered); if (resolved_fn_trait_call) return; bool valid_tyty = 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; } 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 (), TyTy::TyWithLocation (lhs, expr.get_lhs ()->get_locus ()), TyTy::TyWithLocation (rhs, expr.get_rhs ()->get_locus ()), 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 coercion_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (lhs, expr.get_left_expr ()->get_locus ()), TyTy::TyWithLocation (rhs, expr.get_right_expr ()->get_locus ()), expr.get_locus ()); 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::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: { TyTy::TyWithLocation from (rhs, expr.get_rhs ()->get_locus ()); TyTy::TyWithLocation to (lhs, expr.get_lhs ()->get_locus ()); infered = cast_site (expr.get_mappings ().get_hirid (), from, to, expr.get_locus ()); } break; default: { infered = unify_site ( expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (lhs, expr.get_lhs ()->get_locus ()), TyTy::TyWithLocation (rhs, expr.get_rhs ()->get_locus ()), expr.get_locus ()); } break; } } void TypeCheckExpr::visit (HIR::ComparisonExpr &expr) { auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ()); auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ()); unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (lhs, expr.get_lhs ()->get_locus ()), TyTy::TyWithLocation (rhs, expr.get_rhs ()->get_locus ()), expr.get_locus ()); 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::BaseType *boolean_node = nullptr; bool ok = context->lookup_builtin ("bool", &boolean_node); rust_assert (ok); // verify the lhs and rhs before unifying together lhs = unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (boolean_node, expr.get_lhs ()->get_locus ()), TyTy::TyWithLocation (lhs, expr.get_lhs ()->get_locus ()), expr.get_locus ()); rhs = unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (boolean_node, expr.get_rhs ()->get_locus ()), TyTy::TyWithLocation (rhs, expr.get_rhs ()->get_locus ()), expr.get_locus ()); infered = unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (lhs, expr.get_lhs ()->get_locus ()), TyTy::TyWithLocation (rhs, expr.get_rhs ()->get_locus ()), expr.get_locus ()); } 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 = unify_site ( expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (if_blk_resolved, expr.get_if_block ()->get_locus ()), TyTy::TyWithLocation (else_blk_resolved, expr.get_else_block ()->get_locus ()), expr.get_locus ()); } } 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 = unify_site ( expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (if_blk_resolved, expr.get_if_block ()->get_locus ()), TyTy::TyWithLocation (else_blk_resolved, expr.get_conseq_if_expr ()->get_locus ()), expr.get_locus ()); } } 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); unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (scrutinee_tyty), TyTy::TyWithLocation (kase_arm_ty, pattern->get_locus ()), expr.get_locus ()); } 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 = unify_site (s->get_mappings ().get_hirid (), TyTy::TyWithLocation (unit), TyTy::TyWithLocation (resolved), s->get_locus ()); } } 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 { // FIXME this seems wrong 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 (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 = unify_site ( expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (from_ty, expr.get_from_expr ()->get_locus ()), TyTy::TyWithLocation (to_ty, expr.get_to_expr ()->get_locus ()), expr.get_locus ()); // substitute it in std::vector 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 (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 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 (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 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 (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 = unify_site ( expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (from_ty, expr.get_from_expr ()->get_locus ()), TyTy::TyWithLocation (to_ty, expr.get_to_expr ()->get_locus ()), expr.get_locus ()); // substitute it in std::vector 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 (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) { unify_site (expr.get_index_expr ()->get_mappings ().get_hirid (), TyTy::TyWithLocation (size_ty), TyTy::TyWithLocation (index_expr_ty, expr.get_index_expr ()->get_locus ()), expr.get_locus ()); TyTy::ArrayType *array_type = static_cast (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 (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 (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); unify_site ( expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (expected_ty), TyTy::TyWithLocation (capacity_type, elems.get_num_copies_expr ()->get_locus ()), expr.get_locus ()); capacity_expr = elems.get_num_copies_expr (); } break; case HIR::ArrayElems::ArrayExprType::VALUES: { HIR::ArrayElemsValues &elems = static_cast (elements); std::vector types; for (auto &elem : elems.get_values ()) { types.push_back (TypeCheckExpr::Resolve (elem.get ())); } // this is a LUB element_type = TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()).get_tyty (); for (auto &type : types) { element_type = unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (element_type), TyTy::TyWithLocation (type, type->get_locus ()), expr.get_locus ()); } 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 (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 (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 candidates = MethodResolver::Probe (receiver_tyty, expr.get_method_name ().get_segment ()); if (candidates.empty ()) { 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; } if (candidates.size () > 1) { RichLocation r (expr.get_method_name ().get_locus ()); for (auto &c : candidates) r.add_range (c.candidate.locus); rust_error_at ( r, "multiple candidates found for method %<%s%>", expr.get_method_name ().get_segment ().as_string ().c_str ()); return; } auto candidate = *candidates.begin (); rust_debug_loc (expr.get_method_name ().get_locus (), "resolved method to: {%u} {%s}", candidate.candidate.ty->get_ref (), candidate.candidate.ty->debug_str ().c_str ()); // 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 (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; } fn->prepare_higher_ranked_bounds (); auto root = receiver_tyty->get_root (); if (root->get_kind () == TyTy::TypeKind::ADT) { const TyTy::ADTType *adt = static_cast (root); if (adt->has_substitutions () && fn->needs_substitution ()) { // consider the case where we have: // // struct Foo(X,Y); // // impl Foo { // fn test(self, a:X) -> (T,X) { (self.0, a) } // } // // In this case we end up with an fn type of: // // fn test(self:Foo, 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 (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 (); rust_debug_loc (args.get_locus (), "applying generic arguments to method_call: {%s}", lookup->debug_str ().c_str ()); 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 ()) { rust_debug ("method needs inference: {%s}", lookup->debug_str ().c_str ()); lookup = SubstMapper::InferSubst (lookup, expr.get_method_name ().get_locus ()); } rust_debug ("type-checking method_call: {%s}", lookup->debug_str ().c_str ()); TyTy::BaseType *function_ret_tyty = TyTy::TypeCheckMethodCallExpr::go (static_cast (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 % 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 %"); return; } TyTy::BaseType *unified_ty = unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (loop_context), TyTy::TyWithLocation (break_expr_tyty, expr.get_expr ()->get_locus ()), expr.get_locus ()); 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 % 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 (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; } } 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 (resolved_base); infered = ref_base->get_base ()->clone (); } else { TyTy::PointerType *ref_base = static_cast (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 ()); TyTy::TyWithLocation from (expr_to_convert, expr.get_casted_expr ()->get_locus ()); TyTy::TyWithLocation to (tyty_to_convert_to, expr.get_type_to_convert_to ()->get_locus ()); infered = cast_site (expr.get_mappings ().get_hirid (), from, to, expr.get_locus ()); } 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 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 = unify_site ( expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (scrutinee_tyty, expr.get_scrutinee_expr ()->get_locus ()), TyTy::TyWithLocation (kase_arm_ty, pattern->get_locus ()), expr.get_locus ()); 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; } // this is a LUB 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 = unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (infered), TyTy::TyWithLocation (kase_ty), expr.get_locus ()); } } void TypeCheckExpr::visit (HIR::ClosureExpr &expr) { TypeCheckContextItem ¤t_context = context->peek_context (); TyTy::FnType *current_context_fndecl = current_context.get_context_type (); HirId ref = expr.get_mappings ().get_hirid (); DefId id = expr.get_mappings ().get_defid (); RustIdent ident{current_context_fndecl->get_ident ().path, expr.get_locus ()}; // get from parent context std::vector subst_refs = current_context_fndecl->clone_substs (); std::vector parameter_types; for (auto &p : expr.get_params ()) { if (p.has_type_given ()) { TyTy::BaseType *param_tyty = TypeCheckType::Resolve (p.get_type ().get ()); TyTy::TyVar param_ty (param_tyty->get_ref ()); parameter_types.push_back (param_ty); TypeCheckPattern::Resolve (p.get_pattern ().get (), param_ty.get_tyty ()); } else { TyTy::TyVar param_ty = TyTy::TyVar::get_implicit_infer_var (p.get_locus ()); parameter_types.push_back (param_ty); TypeCheckPattern::Resolve (p.get_pattern ().get (), param_ty.get_tyty ()); } } // we generate an implicit hirid for the closure args HirId implicit_args_id = mappings->get_next_hir_id (); TyTy::TupleType *closure_args = new TyTy::TupleType (implicit_args_id, expr.get_locus (), parameter_types); context->insert_implicit_type (closure_args); Location result_type_locus = expr.has_return_type () ? expr.get_return_type ()->get_locus () : expr.get_locus (); TyTy::TyVar result_type = expr.has_return_type () ? TyTy::TyVar ( TypeCheckType::Resolve (expr.get_return_type ().get ())->get_ref ()) : TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()); // resolve the block Location closure_expr_locus = expr.get_expr ()->get_locus (); TyTy::BaseType *closure_expr_ty = TypeCheckExpr::Resolve (expr.get_expr ().get ()); coercion_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (result_type.get_tyty (), result_type_locus), TyTy::TyWithLocation (closure_expr_ty, closure_expr_locus), expr.get_locus ()); // generate the closure type NodeId closure_node_id = expr.get_mappings ().get_nodeid (); const std::set &captures = resolver->get_captures (closure_node_id); infered = new TyTy::ClosureType (ref, id, ident, closure_args, result_type, subst_refs, captures); // FIXME // all closures automatically inherit the appropriate fn trait. Lets just // assume FnOnce for now. I think this is based on the return type of the // closure Analysis::RustLangItem::ItemType lang_item_type = Analysis::RustLangItem::ItemType::FN_ONCE; DefId respective_lang_item_id = UNKNOWN_DEFID; bool lang_item_defined = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id); if (!lang_item_defined) { // FIXME // we need to have a unified way or error'ing when we are missing lang // items that is useful rust_fatal_error ( expr.get_locus (), "unable to find lang item: %<%s%>", Analysis::RustLangItem::ToString (lang_item_type).c_str ()); } rust_assert (lang_item_defined); // these lang items are always traits HIR::Item *item = mappings->lookup_defid (respective_lang_item_id); rust_assert (item->get_item_kind () == HIR::Item::ItemKind::Trait); HIR::Trait *trait_item = static_cast (item); TraitReference *trait = TraitResolver::Resolve (*trait_item); rust_assert (!trait->is_error ()); TyTy::TypeBoundPredicate predicate (*trait, expr.get_locus ()); // resolve the trait bound where the <(Args)> are the parameter tuple type HIR::GenericArgs args = HIR::GenericArgs::create_empty (expr.get_locus ()); // lets generate an implicit Type so that it resolves to the implict tuple // type we have created auto crate_num = mappings->get_current_crate (); Analysis::NodeMapping mapping (crate_num, expr.get_mappings ().get_nodeid (), implicit_args_id, UNKNOWN_LOCAL_DEFID); HIR::TupleType *implicit_tuple = new HIR::TupleType (mapping, {} // we dont need to fill this out because it will // auto resolve because the hir id's match , expr.get_locus ()); args.get_type_args ().push_back (std::unique_ptr (implicit_tuple)); // apply the arguments predicate.apply_generic_arguments (&args); // finally inherit the trait bound infered->inherit_bounds ({predicate}); } 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 candidates = MethodResolver::Probe (lhs, HIR::PathIdentSegment (associated_item_name)); bool have_implementation_for_lang_item = candidates.size () > 0; if (!have_implementation_for_lang_item) return false; if (candidates.size () > 1) { // mutliple candidates RichLocation r (expr.get_locus ()); for (auto &c : candidates) r.add_range (c.candidate.locus); rust_error_at ( r, "multiple candidates found for possible operator overload"); return false; } // Get the adjusted self auto candidate = *candidates.begin (); 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 (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 (lookup); rust_assert (fn->is_method ()); fn->prepare_higher_ranked_bounds (); rust_debug_loc (expr.get_locus (), "resolved operator overload to: {%u} {%s}", candidate.candidate.ty->get_ref (), candidate.candidate.ty->debug_str ().c_str ()); auto root = lhs->get_root (); if (root->get_kind () == TyTy::TypeKind::ADT) { const TyTy::ADTType *adt = static_cast (root); if (adt->has_substitutions () && fn->needs_substitution ()) { // consider the case where we have: // // struct Foo(X,Y); // // impl Foo { // fn test(self, a:X) -> (T,X) { (self.0, a) } // } // // In this case we end up with an fn type of: // // fn test(self:Foo, 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 (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 (lookup); rust_assert (type->num_params () > 0); auto fnparam = type->param_at (0); // typecheck the self unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (fnparam.second), TyTy::TyWithLocation (adjusted_self), expr.get_locus ()); if (rhs == nullptr) { rust_assert (type->num_params () == 1); } else { rust_assert (type->num_params () == 2); auto fnparam = type->param_at (1); unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (fnparam.second), TyTy::TyWithLocation (rhs), expr.get_locus ()); } rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); fn = static_cast (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; } HIR::PathIdentSegment TypeCheckExpr::resolve_possible_fn_trait_call_method_name ( const TyTy::BaseType &receiver) { // Question // do we need to probe possible bounds here? I think not, i think when we // support Fn traits they are explicitly specified // FIXME // the logic to map the FnTrait to their respective call trait-item is // duplicated over in the backend/rust-compile-expr.cc for (const auto &bound : receiver.get_specified_bounds ()) { bool found_fn = bound.get_name ().compare ("Fn") == 0; bool found_fn_mut = bound.get_name ().compare ("FnMut") == 0; bool found_fn_once = bound.get_name ().compare ("FnOnce") == 0; if (found_fn) { return HIR::PathIdentSegment ("call"); } else if (found_fn_mut) { return HIR::PathIdentSegment ("call_mut"); } else if (found_fn_once) { return HIR::PathIdentSegment ("call_once"); } } // nothing return HIR::PathIdentSegment (""); } bool TypeCheckExpr::resolve_fn_trait_call (HIR::CallExpr &expr, TyTy::BaseType *receiver_tyty, TyTy::BaseType **result) { // we turn this into a method call expr HIR::PathIdentSegment method_name = resolve_possible_fn_trait_call_method_name (*receiver_tyty); if (method_name.is_error ()) return false; auto candidates = MethodResolver::Probe (receiver_tyty, method_name); if (candidates.empty ()) return false; if (candidates.size () > 1) { RichLocation r (expr.get_locus ()); for (auto &c : candidates) r.add_range (c.candidate.locus); rust_error_at ( r, "multiple candidates found for function trait method call %<%s%>", method_name.as_string ().c_str ()); return false; } if (receiver_tyty->get_kind () == TyTy::TypeKind::CLOSURE) { const TyTy::ClosureType &closure = static_cast (*receiver_tyty); closure.setup_fn_once_output (); } auto candidate = *candidates.begin (); rust_debug_loc (expr.get_locus (), "resolved call-expr to fn trait: {%u} {%s}", candidate.candidate.ty->get_ref (), candidate.candidate.ty->debug_str ().c_str ()); // 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_mappings ().get_hirid (); context->insert_autoderef_mappings (autoderef_mappings_id, std::move (candidate.adjustments)); context->insert_receiver (expr.get_mappings ().get_hirid (), receiver_tyty); 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_locus ()); r.add_range (resolved_candidate.locus); rust_error_at (r, "associated impl item is not a method"); return false; } TyTy::BaseType *lookup = lookup_tyty; TyTy::FnType *fn = static_cast (lookup); if (!fn->is_method ()) { RichLocation r (expr.get_locus ()); r.add_range (resolved_candidate.locus); rust_error_at (r, "associated function is not a method"); return false; } // fn traits only support tuple argument passing so we need to implicitly set // this up to get the same type checking we get in the rest of the pipeline std::vector call_args; for (auto &arg : expr.get_arguments ()) { TyTy::BaseType *a = TypeCheckExpr::Resolve (arg.get ()); call_args.push_back (TyTy::TyVar (a->get_ref ())); } // crate implicit tuple HirId implicit_arg_id = mappings->get_next_hir_id (); Analysis::NodeMapping mapping (mappings->get_current_crate (), UNKNOWN_NODEID, implicit_arg_id, UNKNOWN_LOCAL_DEFID); TyTy::TupleType *tuple = new TyTy::TupleType (implicit_arg_id, expr.get_locus (), call_args); context->insert_implicit_type (implicit_arg_id, tuple); std::vector args; TyTy::Argument a (mapping, tuple, expr.get_locus () /*FIXME is there a better location*/); args.push_back (std::move (a)); TyTy::BaseType *function_ret_tyty = TyTy::TypeCheckMethodCallExpr::go (fn, expr.get_mappings (), args, expr.get_locus (), expr.get_locus (), adjusted_self, context); if (function_ret_tyty == nullptr || function_ret_tyty->get_kind () == TyTy::TypeKind::ERROR) { rust_error_at (expr.get_locus (), "failed check fn trait call-expr MethodCallExpr"); return false; } // store the expected fntype context->insert_operator_overload (expr.get_mappings ().get_hirid (), fn); // 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 *result = 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