| // Copyright (C) 2020-2024 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-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-dot-operator.h" |
| #include "rust-hir-type-check-pattern.h" |
| #include "rust-hir-type-check-expr.h" |
| #include "rust-hir-type-check-stmt.h" |
| #include "rust-hir-type-check-item.h" |
| #include "rust-type-util.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) |
| 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) |
| { |
| if (!context->have_function_context ()) |
| { |
| rust_error_at (expr.get_locus (), ErrorCode::E0572, |
| "return statement outside of function body"); |
| infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); |
| return; |
| } |
| |
| auto fn_return_tyty = context->peek_return_type (); |
| location_t 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 ().get ()) |
| : TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); |
| |
| coercion_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 ().get ()); |
| |
| 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<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); |
| 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 ().get ()); |
| auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ().get ()); |
| |
| 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_lhs ().get ()); |
| auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ().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_lhs ()->get_locus ()), |
| TyTy::TyWithLocation (rhs, expr.get_rhs ()->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 ().get ()); |
| auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ().get ()); |
| |
| 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 ().get ()); |
| auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ().get ()); |
| |
| 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 ().get ()); |
| auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ().get ()); |
| |
| // 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) |
| { |
| TyTy::BaseType *bool_ty = nullptr; |
| bool ok = context->lookup_builtin ("bool", &bool_ty); |
| rust_assert (ok); |
| |
| TyTy::BaseType *cond_type |
| = TypeCheckExpr::Resolve (expr.get_if_condition ().get ()); |
| |
| unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (bool_ty), |
| TyTy::TyWithLocation (cond_type, |
| expr.get_if_condition ()->get_locus ()), |
| expr.get_locus ()); |
| |
| TypeCheckExpr::Resolve (expr.get_if_block ().get ()); |
| |
| infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); |
| } |
| |
| void |
| TypeCheckExpr::visit (HIR::IfExprConseqElse &expr) |
| { |
| TyTy::BaseType *bool_ty = nullptr; |
| bool ok = context->lookup_builtin ("bool", &bool_ty); |
| rust_assert (ok); |
| |
| TyTy::BaseType *cond_type |
| = TypeCheckExpr::Resolve (expr.get_if_condition ().get ()); |
| |
| unify_site (expr.get_mappings ().get_hirid (), TyTy::TyWithLocation (bool_ty), |
| TyTy::TyWithLocation (cond_type, |
| expr.get_if_condition ()->get_locus ()), |
| expr.get_locus ()); |
| |
| auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ().get ()); |
| auto else_blk_resolved |
| = TypeCheckExpr::Resolve (expr.get_else_block ().get ()); |
| |
| 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::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 ().get ()); |
| |
| infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); |
| } |
| |
| void |
| TypeCheckExpr::visit (HIR::IfLetExprConseqElse &expr) |
| { |
| 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 ()); |
| } |
| |
| auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ().get ()); |
| auto else_blk_resolved |
| = TypeCheckExpr::Resolve (expr.get_else_block ().get ()); |
| |
| 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::UnsafeBlockExpr &expr) |
| { |
| infered = TypeCheckExpr::Resolve (expr.get_block_expr ().get ()); |
| } |
| |
| void |
| TypeCheckExpr::visit (HIR::BlockExpr &expr) |
| { |
| if (expr.has_label ()) |
| context->push_new_loop_context (expr.get_mappings ().get_hirid (), |
| expr.get_locus ()); |
| |
| 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 if (expr.has_label ()) |
| { |
| 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 ()); |
| } |
| 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<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 = 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<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, {}, adt->get_substitution_arguments ().get_regions (), |
| 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, {}, adt->get_substitution_arguments ().get_regions (), |
| 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, {}, adt->get_substitution_arguments ().get_regions (), |
| 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 = 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<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, {}, adt->get_substitution_arguments ().get_regions (), |
| expr.get_locus ()); |
| infered = SubstMapperInternal::Resolve (adt, subst); |
| } |
| |
| void |
| TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr) |
| { |
| auto array_expr_ty = TypeCheckExpr::Resolve (expr.get_array_expr ().get ()); |
| if (array_expr_ty->get_kind () == TyTy::TypeKind::ERROR) |
| return; |
| |
| auto index_expr_ty = TypeCheckExpr::Resolve (expr.get_index_expr ().get ()); |
| 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) |
| { |
| 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<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` |
| rich_location r (line_table, 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, ErrorCode::E0277, |
| "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 ().get ()); |
| |
| auto capacity_type |
| = TypeCheckExpr::Resolve (elems.get_num_copies_expr ().get ()); |
| |
| 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 ().get (); |
| } |
| 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 ())); |
| } |
| |
| // 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, |
| UNDEF_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; |
| } |
| |
| TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (struct_path_ty); |
| for (auto variant : adt->get_variants ()) |
| { |
| if (!variant->get_fields ().empty ()) |
| { |
| std::vector<std::string> field_names; |
| for (auto &field : variant->get_fields ()) |
| field_names.push_back (field->get_name ()); |
| Error missing_fields_error |
| = TypeCheckStructExpr::make_missing_field_error ( |
| struct_expr.get_locus (), field_names, |
| struct_path_ty->get_name ()); |
| // We might want to return or handle these in the future emit for now. |
| missing_fields_error.emit (); |
| 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 ().as_string (), |
| &lookup, nullptr); |
| if (!found) |
| { |
| rust_error_at (expr.get_locus (), "unknown field [%s] for type [%s]", |
| expr.get_field_name ().as_string ().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); |
| |
| rust_debug_loc (expr.get_locus (), "attempting to resolve method for %s", |
| receiver_tyty->debug_str ().c_str ()); |
| auto candidates |
| = MethodResolver::Probe (receiver_tyty, |
| expr.get_method_name ().get_segment ()); |
| if (candidates.empty ()) |
| { |
| rich_location richloc (line_table, expr.get_method_name ().get_locus ()); |
| richloc.add_fixit_replace ("method not found"); |
| rust_error_at ( |
| richloc, ErrorCode::E0599, |
| "no method named %qs found in the current scope", |
| expr.get_method_name ().get_segment ().as_string ().c_str ()); |
| return; |
| } |
| |
| if (candidates.size () > 1) |
| { |
| rich_location r (line_table, expr.get_method_name ().get_locus ()); |
| std::string rich_msg |
| = "multiple " + expr.get_method_name ().get_segment ().as_string () |
| + " found"; |
| |
| for (auto &c : candidates) |
| r.add_range (c.candidate.locus); |
| |
| r.add_fixit_replace (rich_msg.c_str ()); |
| |
| rust_error_at ( |
| r, ErrorCode::E0592, "duplicate definitions with name %qs", |
| 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} with [%lu] adjustments", |
| candidate.candidate.ty->get_ref (), |
| candidate.candidate.ty->debug_str ().c_str (), |
| (unsigned long) candidate.adjustments.size ()); |
| |
| // Get the adjusted self |
| Adjuster adj (receiver_tyty); |
| TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments); |
| rust_debug ("receiver: %s adjusted self %s", |
| receiver_tyty->debug_str ().c_str (), |
| adjusted_self->debug_str ().c_str ()); |
| |
| // 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) |
| { |
| rich_location r (line_table, 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 ()) |
| { |
| rich_location r (line_table, 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 (); |
| rust_debug_loc (expr.get_locus (), "resolved method call to: {%u} {%s}", |
| candidate.candidate.ty->get_ref (), |
| candidate.candidate.ty->debug_str ().c_str ()); |
| |
| if (resolved_candidate.is_impl_candidate ()) |
| { |
| auto infer_arguments = TyTy::SubstitutionArgumentMappings::empty (); |
| infer_arguments.get_mut_regions () |
| = fn->get_used_arguments ().get_regions (); |
| HIR::ImplBlock &impl = *resolved_candidate.item.impl.parent; |
| TyTy::BaseType *impl_self_infer |
| = TypeCheckItem::ResolveImplBlockSelfWithInference (impl, |
| expr.get_locus (), |
| &infer_arguments); |
| if (impl_self_infer->get_kind () == TyTy::TypeKind::ERROR) |
| { |
| rich_location r (line_table, expr.get_locus ()); |
| r.add_range (impl.get_type ()->get_locus ()); |
| rust_error_at ( |
| r, "failed to resolve impl type for method call resolution"); |
| return; |
| } |
| |
| if (!infer_arguments.is_empty ()) |
| { |
| lookup = SubstMapperInternal::Resolve (lookup, infer_arguments); |
| lookup->debug (); |
| } |
| } |
| |
| // 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<TyTy::FnType *> (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 |
| if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) |
| { |
| resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (), |
| resolved_node_id); |
| } |
| else |
| { |
| resolver->insert_resolved_misc (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 (), ErrorCode::E0268, |
| "%<break%> outside of a loop or labeled block"); |
| 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 (), ErrorCode::E0571, |
| "can only %<break%> with a value inside a %<loop%> block"); |
| 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 (), ErrorCode::E0268, |
| "%<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; |
| } |
| } |
| |
| 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 ()); |
| |
| rust_debug_loc (expr.get_locus (), "attempting deref operator overload"); |
| 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 ()); |
| |
| 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<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); |
| if (kase_arm_ty->get_kind () == TyTy ::TypeKind::ERROR) |
| return; |
| |
| 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 current_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<TyTy::SubstitutionParamMapping> subst_refs |
| = current_context_fndecl->clone_substs (); |
| |
| std::vector<TyTy::TyVar> parameter_types; |
| for (auto &p : expr.get_params ()) |
| { |
| TyTy::BaseType *param_tyty = nullptr; |
| if (p.has_type_given ()) |
| { |
| param_tyty = TypeCheckType::Resolve (p.get_type ().get ()); |
| } |
| else |
| { |
| param_tyty = ClosureParamInfer::Resolve (p.get_pattern ().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 ()); |
| } |
| |
| // 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_t 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_t 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<NodeId> &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<HIR::Trait *> (item); |
| |
| TraitReference *trait = TraitResolver::Resolve (*trait_item); |
| rust_assert (!trait->is_error ()); |
| |
| TyTy::TypeBoundPredicate predicate (*trait, BoundPolarity::RegularBound, |
| 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<HIR::Type> (implicit_tuple)); |
| |
| // apply the arguments |
| predicate.apply_generic_arguments (&args, false); |
| |
| // 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; |
| |
| // we might be in a static or const context and unknown is fine |
| TypeCheckContextItem current_context = TypeCheckContextItem::get_error (); |
| if (context->have_function_context ()) |
| { |
| current_context = context->peek_context (); |
| } |
| |
| auto segment = HIR::PathIdentSegment (associated_item_name); |
| auto candidates = MethodResolver::Probe (lhs, segment); |
| |
| // remove any recursive candidates |
| std::set<MethodCandidate> resolved_candidates; |
| for (auto &c : candidates) |
| { |
| const TyTy::BaseType *candidate_type = c.candidate.ty; |
| rust_assert (candidate_type->get_kind () == TyTy::TypeKind::FNDEF); |
| |
| const TyTy::FnType &fn |
| = *static_cast<const TyTy::FnType *> (candidate_type); |
| |
| DefId current_fn_defid = current_context.get_defid (); |
| bool recursive_candidated = fn.get_id () == current_fn_defid; |
| if (!recursive_candidated) |
| { |
| resolved_candidates.insert (c); |
| } |
| } |
| |
| std::vector<TyTy::BaseType *> select_args = {}; |
| if (rhs != nullptr) |
| select_args = {rhs}; |
| auto selected_candidates |
| = MethodResolver::Select (resolved_candidates, lhs, select_args); |
| |
| bool have_implementation_for_lang_item = selected_candidates.size () > 0; |
| if (!have_implementation_for_lang_item) |
| return false; |
| |
| if (selected_candidates.size () > 1) |
| { |
| // mutliple candidates |
| rich_location r (line_table, expr.get_locus ()); |
| for (auto &c : resolved_candidates) |
| r.add_range (c.candidate.locus); |
| |
| rust_error_at ( |
| r, "multiple candidates found for possible operator overload"); |
| |
| return false; |
| } |
| |
| // Get the adjusted self |
| MethodCandidate candidate = *selected_candidates.begin (); |
| Adjuster adj (lhs); |
| TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments); |
| |
| // 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 ()); |
| |
| rust_debug ("is_impl_item_candidate: %s", |
| resolved_candidate.is_impl_candidate () ? "true" : "false"); |
| |
| if (resolved_candidate.is_impl_candidate ()) |
| { |
| auto infer_arguments = TyTy::SubstitutionArgumentMappings::error (); |
| HIR::ImplBlock &impl = *resolved_candidate.item.impl.parent; |
| TyTy::BaseType *impl_self_infer |
| = TypeCheckItem::ResolveImplBlockSelfWithInference (impl, |
| expr.get_locus (), |
| &infer_arguments); |
| if (impl_self_infer->get_kind () == TyTy::TypeKind::ERROR) |
| { |
| return false; |
| } |
| if (!infer_arguments.is_empty ()) |
| { |
| lookup = SubstMapperInternal::Resolve (lookup, infer_arguments); |
| } |
| } |
| |
| // in the case where we resolve to a trait bound we have to be careful we are |
| // able to do so there is a case where we are currently resolving the deref |
| // operator overload function which is generic and this might resolve to the |
| // trait item of deref which is not valid as its just another recursive case |
| if (current_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM) |
| { |
| auto &impl_item = current_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 ().as_string ().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; |
| } |
| } |
| } |
| |
| // we found a valid operator overload |
| 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 ()); |
| |
| // 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); |
| |
| // 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<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; |
| } |
| |
| HIR::PathIdentSegment |
| TypeCheckExpr::resolve_possible_fn_trait_call_method_name ( |
| TyTy::BaseType &receiver, TyTy::TypeBoundPredicate *associated_predicate) |
| { |
| // 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 (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) |
| { |
| *associated_predicate = bound; |
| return HIR::PathIdentSegment ("call"); |
| } |
| else if (found_fn_mut) |
| { |
| *associated_predicate = bound; |
| return HIR::PathIdentSegment ("call_mut"); |
| } |
| else if (found_fn_once) |
| { |
| *associated_predicate = bound; |
| return HIR::PathIdentSegment ("call_once"); |
| } |
| } |
| |
| // nothing |
| *associated_predicate = TyTy::TypeBoundPredicate::error (); |
| 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 |
| // TODO: add implicit self argument (?) |
| auto associated_predicate = TyTy::TypeBoundPredicate::error (); |
| HIR::PathIdentSegment method_name |
| = resolve_possible_fn_trait_call_method_name (*receiver_tyty, |
| &associated_predicate); |
| if (method_name.is_error () || associated_predicate.is_error ()) |
| return false; |
| |
| auto candidates = MethodResolver::Probe (receiver_tyty, method_name); |
| if (candidates.empty ()) |
| return false; |
| |
| if (candidates.size () > 1) |
| { |
| rich_location r (line_table, 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<TyTy::ClosureType &> (*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 |
| HIR::Expr *fnexpr = expr.get_fnexpr ().get (); |
| HirId autoderef_mappings_id = fnexpr->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) |
| { |
| rich_location r (line_table, 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<TyTy::FnType *> (lookup); |
| if (!fn->is_method ()) |
| { |
| rich_location r (line_table, 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<TyTy::TyVar> 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<TyTy::Argument> 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)); |
| } |
| |
| rust_unreachable (); |
| return false; |
| } |
| |
| } // namespace Resolver |
| } // namespace Rust |