From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by sourceware.org (Postfix) with ESMTPS id 1C4D63815FDA; Wed, 24 Aug 2022 12:01:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 1C4D63815FDA Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=googlemail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=googlemail.com Received: by mail-wm1-x335.google.com with SMTP id d5so8650105wms.5; Wed, 24 Aug 2022 05:01:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc; bh=uV3oHMQPw5jQsRiNz+02+locx0oY57pEynOTM4Kyqwg=; b=R3oxwh3blbIFdkfa+bJCGnemdd9dh9Ijc99R5huP0Uy2oQVtoodVmhHPrvnmQ0vm4a Ev1mNw3e5U8TeyLwohd4mHUSAjvuUFJQH87q9QgbxY1obYssL+58FY1Qd+0x9KuPgA7n oDAnoBUf/9W+NcgwyRtr3K1pTvveFAUqap/eM0tj1YDq0KPeT1/gtVbguFnMQcgYposG KwKFWALbkF7VnlnSjTiCebgoDfv11uer6S2wUfBXfL+0T/r2ULrfTSAo9dLMDBgvPN/b 3r+8s93+Lc2X/GD6X7TUw2vc8dmZaTWJ3CtYtl2dOHNddhkrx+az1qwcoEklwBEpkps/ fS+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:x-gm-message-state :from:to:cc; bh=uV3oHMQPw5jQsRiNz+02+locx0oY57pEynOTM4Kyqwg=; b=boJP1tCjtAuXmjkrfE8BYUxIHSBYlXNucwpLAKT6EHcllQYxN9TP6A7JAd1yLifG+D zlO5IXJ9LneFwC1uUm6vRsxoGQbvkZK6ha6oRyXm6G294cGWzGAlfOrSkr+H0Uxjc23+ SZmEbWGFklLt/cgS7tYfVXKhz3n4hDcyNx5WF5NQCcBuGaF98dU3skdIz/L2ejdeVuf1 iaRJzwJXKVq7JlsiQBDV/He9d/gxaL08jGaBt4FDMqhae4KNKHY2ZaMtR/ZGft0QbJ33 IRfPzXvawepuKEfXvLhBm7MJYbyD/3SfTkT26aCbFGlFXqyRvBHL4VZIQRxgSQfwVVtX xfJQ== X-Gm-Message-State: ACgBeo0NxGcqG6g0Qn90eAm1zHOwO7CmogihcRKe6mvEgNje9r85+7sb IV+59tnCwtfN26nArm4Zw7LsEcI+oEg= X-Google-Smtp-Source: AA6agR7S4VYCaXWv6rXFKxqCD2Urt/ZtBNxHQWCVheb/LWphY/TXgR4DqNeok1qV2yOQtm9hiMgMMg== X-Received: by 2002:a05:600c:2193:b0:3a5:346f:57d0 with SMTP id e19-20020a05600c219300b003a5346f57d0mr4976682wme.124.1661342461640; Wed, 24 Aug 2022 05:01:01 -0700 (PDT) Received: from localhost.localdomain ([86.14.124.218]) by smtp.gmail.com with ESMTPSA id cc19-20020a5d5c13000000b0022571d43d32sm1697676wrb.21.2022.08.24.05.00.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Aug 2022 05:01:00 -0700 (PDT) From: herron.philip@googlemail.com X-Google-Original-From: philip.herron@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, Philip Herron Subject: [PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait solving pass Date: Wed, 24 Aug 2022 12:59:41 +0100 Message-Id: <20220824115956.737931-23-philip.herron@embecosm.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220824115956.737931-1-philip.herron@embecosm.com> References: <20220824115956.737931-1-philip.herron@embecosm.com> Reply-To: philip.herron@embecosm.com MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,GIT_PATCH_0,KAM_LOTSOFHASH,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: From: Philip Herron TODO --- gcc/rust/typecheck/rust-autoderef.cc | 398 +++ gcc/rust/typecheck/rust-autoderef.h | 178 + gcc/rust/typecheck/rust-casts.cc | 292 ++ gcc/rust/typecheck/rust-casts.h | 53 + gcc/rust/typecheck/rust-coercion.cc | 357 ++ gcc/rust/typecheck/rust-coercion.h | 93 + gcc/rust/typecheck/rust-hir-dot-operator.cc | 263 ++ gcc/rust/typecheck/rust-hir-dot-operator.h | 81 + .../rust-hir-inherent-impl-overlap.h | 186 ++ gcc/rust/typecheck/rust-hir-path-probe.h | 540 +++ gcc/rust/typecheck/rust-hir-trait-ref.h | 472 +++ gcc/rust/typecheck/rust-hir-trait-resolve.cc | 599 ++++ gcc/rust/typecheck/rust-hir-trait-resolve.h | 87 + gcc/rust/typecheck/rust-hir-type-bounds.h | 77 + .../typecheck/rust-hir-type-check-base.cc | 439 +++ gcc/rust/typecheck/rust-hir-type-check-base.h | 80 + .../typecheck/rust-hir-type-check-enumitem.cc | 213 ++ .../typecheck/rust-hir-type-check-enumitem.h | 50 + .../typecheck/rust-hir-type-check-expr.cc | 1567 +++++++++ gcc/rust/typecheck/rust-hir-type-check-expr.h | 131 + .../typecheck/rust-hir-type-check-implitem.cc | 583 ++++ .../typecheck/rust-hir-type-check-implitem.h | 114 + .../typecheck/rust-hir-type-check-item.cc | 237 ++ gcc/rust/typecheck/rust-hir-type-check-item.h | 58 + .../typecheck/rust-hir-type-check-path.cc | 467 +++ .../typecheck/rust-hir-type-check-pattern.cc | 416 +++ .../typecheck/rust-hir-type-check-pattern.h | 62 + .../typecheck/rust-hir-type-check-stmt.cc | 498 +++ gcc/rust/typecheck/rust-hir-type-check-stmt.h | 96 + .../rust-hir-type-check-struct-field.h | 59 + .../typecheck/rust-hir-type-check-struct.cc | 340 ++ .../typecheck/rust-hir-type-check-toplevel.cc | 364 +++ .../typecheck/rust-hir-type-check-toplevel.h | 56 + .../typecheck/rust-hir-type-check-type.cc | 838 +++++ gcc/rust/typecheck/rust-hir-type-check-type.h | 130 + .../typecheck/rust-hir-type-check-util.cc | 41 + gcc/rust/typecheck/rust-hir-type-check-util.h | 50 + gcc/rust/typecheck/rust-hir-type-check.cc | 295 ++ gcc/rust/typecheck/rust-hir-type-check.h | 379 +++ .../typecheck/rust-substitution-mapper.cc | 77 + gcc/rust/typecheck/rust-substitution-mapper.h | 394 +++ gcc/rust/typecheck/rust-tycheck-dump.h | 239 ++ gcc/rust/typecheck/rust-tyctx.cc | 155 + gcc/rust/typecheck/rust-tyty-bounds.cc | 462 +++ gcc/rust/typecheck/rust-tyty-call.cc | 263 ++ gcc/rust/typecheck/rust-tyty-call.h | 147 + gcc/rust/typecheck/rust-tyty-cmp.h | 1554 +++++++++ gcc/rust/typecheck/rust-tyty-rules.h | 1584 +++++++++ gcc/rust/typecheck/rust-tyty-visitor.h | 88 + gcc/rust/typecheck/rust-tyty.cc | 2885 +++++++++++++++++ gcc/rust/typecheck/rust-tyty.h | 2533 +++++++++++++++ 51 files changed, 21620 insertions(+) create mode 100644 gcc/rust/typecheck/rust-autoderef.cc create mode 100644 gcc/rust/typecheck/rust-autoderef.h create mode 100644 gcc/rust/typecheck/rust-casts.cc create mode 100644 gcc/rust/typecheck/rust-casts.h create mode 100644 gcc/rust/typecheck/rust-coercion.cc create mode 100644 gcc/rust/typecheck/rust-coercion.h create mode 100644 gcc/rust/typecheck/rust-hir-dot-operator.cc create mode 100644 gcc/rust/typecheck/rust-hir-dot-operator.h create mode 100644 gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h create mode 100644 gcc/rust/typecheck/rust-hir-path-probe.h create mode 100644 gcc/rust/typecheck/rust-hir-trait-ref.h create mode 100644 gcc/rust/typecheck/rust-hir-trait-resolve.cc create mode 100644 gcc/rust/typecheck/rust-hir-trait-resolve.h create mode 100644 gcc/rust/typecheck/rust-hir-type-bounds.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-base.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-base.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-enumitem.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-enumitem.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-expr.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-expr.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-implitem.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-implitem.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-item.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-item.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-path.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-pattern.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-pattern.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-stmt.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-stmt.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-struct-field.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-struct.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-toplevel.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-toplevel.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-type.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-type.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check-util.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check-util.h create mode 100644 gcc/rust/typecheck/rust-hir-type-check.cc create mode 100644 gcc/rust/typecheck/rust-hir-type-check.h create mode 100644 gcc/rust/typecheck/rust-substitution-mapper.cc create mode 100644 gcc/rust/typecheck/rust-substitution-mapper.h create mode 100644 gcc/rust/typecheck/rust-tycheck-dump.h create mode 100644 gcc/rust/typecheck/rust-tyctx.cc create mode 100644 gcc/rust/typecheck/rust-tyty-bounds.cc create mode 100644 gcc/rust/typecheck/rust-tyty-call.cc create mode 100644 gcc/rust/typecheck/rust-tyty-call.h create mode 100644 gcc/rust/typecheck/rust-tyty-cmp.h create mode 100644 gcc/rust/typecheck/rust-tyty-rules.h create mode 100644 gcc/rust/typecheck/rust-tyty-visitor.h create mode 100644 gcc/rust/typecheck/rust-tyty.cc create mode 100644 gcc/rust/typecheck/rust-tyty.h diff --git a/gcc/rust/typecheck/rust-autoderef.cc b/gcc/rust/typecheck/rust-autoderef.cc new file mode 100644 index 00000000000..423f8e4709b --- /dev/null +++ b/gcc/rust/typecheck/rust-autoderef.cc @@ -0,0 +1,398 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-autoderef.h" +#include "rust-hir-path-probe.h" +#include "rust-hir-dot-operator.h" +#include "rust-hir-trait-resolve.h" + +namespace Rust { +namespace Resolver { + +static bool +resolve_operator_overload_fn ( + Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty, + TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item, + Adjustment::AdjustmentType *requires_ref_adjustment); + +TyTy::BaseType * +Adjuster::adjust_type (const std::vector &adjustments) +{ + if (adjustments.size () == 0) + return base->clone (); + + return adjustments.back ().get_expected ()->clone (); +} + +Adjustment +Adjuster::try_deref_type (const TyTy::BaseType *ty, + Analysis::RustLangItem::ItemType deref_lang_item) +{ + HIR::ImplItem *impl_item = nullptr; + TyTy::FnType *fn = nullptr; + Adjustment::AdjustmentType requires_ref_adjustment + = Adjustment::AdjustmentType::ERROR; + bool operator_overloaded + = resolve_operator_overload_fn (deref_lang_item, ty, &fn, &impl_item, + &requires_ref_adjustment); + if (!operator_overloaded) + { + return Adjustment::get_error (); + } + + auto resolved_base = fn->get_return_type ()->clone (); + bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF; + if (!is_valid_type) + return Adjustment::get_error (); + + TyTy::ReferenceType *ref_base + = static_cast (resolved_base); + + Adjustment::AdjustmentType adjustment_type + = Adjustment::AdjustmentType::ERROR; + switch (deref_lang_item) + { + case Analysis::RustLangItem::ItemType::DEREF: + adjustment_type = Adjustment::AdjustmentType::DEREF; + break; + + case Analysis::RustLangItem::ItemType::DEREF_MUT: + adjustment_type = Adjustment::AdjustmentType::DEREF_MUT; + break; + + default: + break; + } + + return Adjustment::get_op_overload_deref_adjustment (adjustment_type, ty, + ref_base, fn, impl_item, + requires_ref_adjustment); +} + +Adjustment +Adjuster::try_raw_deref_type (const TyTy::BaseType *ty) +{ + bool is_valid_type = ty->get_kind () == TyTy::TypeKind::REF; + if (!is_valid_type) + return Adjustment::get_error (); + + const TyTy::ReferenceType *ref_base + = static_cast (ty); + auto infered = ref_base->get_base ()->clone (); + + return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered); +} + +Adjustment +Adjuster::try_unsize_type (const TyTy::BaseType *ty) +{ + bool is_valid_type = ty->get_kind () == TyTy::TypeKind::ARRAY; + if (!is_valid_type) + return Adjustment::get_error (); + + auto mappings = Analysis::Mappings::get (); + auto context = TypeCheckContext::get (); + + const auto ref_base = static_cast (ty); + auto slice_elem = ref_base->get_element_type (); + + auto slice + = new TyTy::SliceType (mappings->get_next_hir_id (), ty->get_ident ().locus, + TyTy::TyVar (slice_elem->get_ref ())); + context->insert_implicit_type (slice); + + return Adjustment (Adjustment::AdjustmentType::UNSIZE, ty, slice); +} + +static bool +resolve_operator_overload_fn ( + Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty, + TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item, + Adjustment::AdjustmentType *requires_ref_adjustment) +{ + auto context = TypeCheckContext::get (); + auto mappings = Analysis::Mappings::get (); + + // 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); + + if (!lang_item_defined) + return false; + + auto segment = HIR::PathIdentSegment (associated_item_name); + auto candidate + = MethodResolver::Probe (ty, HIR::PathIdentSegment (associated_item_name), + true); + + bool have_implementation_for_lang_item = !candidate.is_error (); + if (!have_implementation_for_lang_item) + return false; + + // Get the adjusted self + Adjuster adj (ty); + 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; + } + } + } + + TyTy::BaseType *lookup_tyty = candidate.candidate.ty; + + // rust only support impl item deref operator overloading ie you must have an + // impl block for it + rust_assert (candidate.candidate.type + == PathProbeCandidate::CandidateType::IMPL_FUNC); + *impl_item = candidate.candidate.item.impl.impl_item; + + 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 ()); + + if (fn->needs_substitution ()) + { + if (ty->get_kind () == TyTy::TypeKind::ADT) + { + const TyTy::ADTType *adt = static_cast (ty); + + 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); + } + } + } + else + { + rust_assert (candidate.adjustments.size () < 2); + + // lets infer the params for this we could probably fix this up by + // actually just performing a substitution of a single param but this + // seems more generic i think. + // + // this is the case where we had say Foo<&Bar>> and we have derefed to + // the &Bar and we are trying to match a method self of Bar which + // requires another deref which is matched to the deref trait impl of + // &&T so this requires another reference and deref call + + lookup = fn->infer_substitions (Location ()); + rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); + fn = static_cast (lookup); + fn->get_self_type ()->unify (adjusted_self); + lookup = fn; + } + } + + if (candidate.adjustments.size () > 0) + *requires_ref_adjustment = candidate.adjustments.at (0).get_type (); + + *resolved_fn = static_cast (lookup); + + return true; +} + +AutoderefCycle::AutoderefCycle (bool autoderef_flag) + : autoderef_flag (autoderef_flag) +{} + +AutoderefCycle::~AutoderefCycle () {} + +void +AutoderefCycle::try_hook (const TyTy::BaseType &) +{} + +bool +AutoderefCycle::cycle (const TyTy::BaseType *receiver) +{ + const TyTy::BaseType *r = receiver; + while (true) + { + if (try_autoderefed (r)) + return true; + + // 4. deref to to 1, if cannot deref then quit + if (autoderef_flag) + return false; + + // try unsize + Adjustment unsize = Adjuster::try_unsize_type (r); + if (!unsize.is_error ()) + { + adjustments.push_back (unsize); + auto unsize_r = unsize.get_expected (); + + if (try_autoderefed (unsize_r)) + return true; + + adjustments.pop_back (); + } + + Adjustment deref + = Adjuster::try_deref_type (r, Analysis::RustLangItem::ItemType::DEREF); + if (!deref.is_error ()) + { + auto deref_r = deref.get_expected (); + adjustments.push_back (deref); + + if (try_autoderefed (deref_r)) + return true; + + adjustments.pop_back (); + } + + Adjustment deref_mut = Adjuster::try_deref_type ( + r, Analysis::RustLangItem::ItemType::DEREF_MUT); + if (!deref_mut.is_error ()) + { + auto deref_r = deref_mut.get_expected (); + adjustments.push_back (deref_mut); + + if (try_autoderefed (deref_r)) + return true; + + adjustments.pop_back (); + } + + if (!deref_mut.is_error ()) + { + auto deref_r = deref_mut.get_expected (); + adjustments.push_back (deref_mut); + Adjustment raw_deref = Adjuster::try_raw_deref_type (deref_r); + adjustments.push_back (raw_deref); + deref_r = raw_deref.get_expected (); + + if (try_autoderefed (deref_r)) + return true; + + adjustments.pop_back (); + adjustments.pop_back (); + } + + if (!deref.is_error ()) + { + r = deref.get_expected (); + adjustments.push_back (deref); + } + Adjustment raw_deref = Adjuster::try_raw_deref_type (r); + if (raw_deref.is_error ()) + return false; + + r = raw_deref.get_expected (); + adjustments.push_back (raw_deref); + } + return false; +} + +bool +AutoderefCycle::try_autoderefed (const TyTy::BaseType *r) +{ + try_hook (*r); + + // 1. try raw + if (select (*r)) + return true; + + // 2. try ref + TyTy::ReferenceType *r1 + = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()), + Mutability::Imm); + adjustments.push_back ( + Adjustment (Adjustment::AdjustmentType::IMM_REF, r, r1)); + if (select (*r1)) + return true; + + adjustments.pop_back (); + + // 3. try mut ref + TyTy::ReferenceType *r2 + = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()), + Mutability::Mut); + adjustments.push_back ( + Adjustment (Adjustment::AdjustmentType::MUT_REF, r, r2)); + if (select (*r2)) + return true; + + adjustments.pop_back (); + + return false; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-autoderef.h b/gcc/rust/typecheck/rust-autoderef.h new file mode 100644 index 00000000000..2f8d64b97e6 --- /dev/null +++ b/gcc/rust/typecheck/rust-autoderef.h @@ -0,0 +1,178 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_AUTODEREF +#define RUST_AUTODEREF + +#include "rust-tyty.h" + +namespace Rust { +namespace Resolver { + +class Adjustment +{ +public: + enum AdjustmentType + { + ERROR, + + IMM_REF, + MUT_REF, + DEREF, + DEREF_MUT, + INDIRECTION, + UNSIZE, + }; + + // ctor for all adjustments except derefs + Adjustment (AdjustmentType type, const TyTy::BaseType *actual, + const TyTy::BaseType *expected) + : Adjustment (type, actual, expected, nullptr, nullptr, + AdjustmentType::ERROR) + {} + + static Adjustment get_op_overload_deref_adjustment ( + AdjustmentType type, const TyTy::BaseType *actual, + const TyTy::BaseType *expected, TyTy::FnType *fn, HIR::ImplItem *deref_item, + Adjustment::AdjustmentType requires_ref_adjustment) + { + rust_assert (type == DEREF || type == DEREF_MUT); + return Adjustment (type, actual, expected, fn, deref_item, + requires_ref_adjustment); + } + + AdjustmentType get_type () const { return type; } + + const TyTy::BaseType *get_actual () const { return actual; } + const TyTy::BaseType *get_expected () const { return expected; } + + std::string as_string () const + { + return Adjustment::type_string (get_type ()) + "->" + + get_expected ()->debug_str (); + } + + static std::string type_string (AdjustmentType type) + { + switch (type) + { + case AdjustmentType::ERROR: + return "ERROR"; + case AdjustmentType::IMM_REF: + return "IMM_REF"; + case AdjustmentType::MUT_REF: + return "MUT_REF"; + case AdjustmentType::DEREF: + return "DEREF"; + case AdjustmentType::DEREF_MUT: + return "DEREF_MUT"; + case AdjustmentType::INDIRECTION: + return "INDIRECTION"; + case AdjustmentType::UNSIZE: + return "UNSIZE"; + } + gcc_unreachable (); + return ""; + } + + static Adjustment get_error () { return Adjustment{ERROR, nullptr, nullptr}; } + + bool is_error () const { return type == ERROR; } + + bool is_deref_adjustment () const { return type == DEREF; } + + bool is_deref_mut_adjustment () const { return type == DEREF_MUT; } + + bool has_operator_overload () const { return deref_operator_fn != nullptr; } + + TyTy::FnType *get_deref_operator_fn () const { return deref_operator_fn; } + + AdjustmentType get_deref_adjustment_type () const + { + return requires_ref_adjustment; + } + + HIR::ImplItem *get_deref_hir_item () const { return deref_item; } + +private: + Adjustment (AdjustmentType type, const TyTy::BaseType *actual, + const TyTy::BaseType *expected, TyTy::FnType *deref_operator_fn, + HIR::ImplItem *deref_item, + Adjustment::AdjustmentType requires_ref_adjustment) + : type (type), actual (actual), expected (expected), + deref_operator_fn (deref_operator_fn), deref_item (deref_item), + requires_ref_adjustment (requires_ref_adjustment) + {} + + AdjustmentType type; + const TyTy::BaseType *actual; + const TyTy::BaseType *expected; + + // - only used for deref operator_overloads + // + // the fn that we are calling + TyTy::FnType *deref_operator_fn; + HIR::ImplItem *deref_item; + // operator overloads can requre a reference + Adjustment::AdjustmentType requires_ref_adjustment; +}; + +class Adjuster +{ +public: + Adjuster (const TyTy::BaseType *ty) : base (ty) {} + + TyTy::BaseType *adjust_type (const std::vector &adjustments); + + static Adjustment + try_deref_type (const TyTy::BaseType *ty, + Analysis::RustLangItem::ItemType deref_lang_item); + + static Adjustment try_raw_deref_type (const TyTy::BaseType *ty); + + static Adjustment try_unsize_type (const TyTy::BaseType *ty); + +private: + const TyTy::BaseType *base; +}; + +class AutoderefCycle +{ +protected: + AutoderefCycle (bool autoderef_flag); + + virtual ~AutoderefCycle (); + + virtual bool select (const TyTy::BaseType &autoderefed) = 0; + + // optional: this is a chance to hook in to grab predicate items on the raw + // type + virtual void try_hook (const TyTy::BaseType &); + + virtual bool cycle (const TyTy::BaseType *receiver); + + bool try_autoderefed (const TyTy::BaseType *r); + + bool autoderef_flag; + std::vector adjustments; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_AUTODEREF diff --git a/gcc/rust/typecheck/rust-casts.cc b/gcc/rust/typecheck/rust-casts.cc new file mode 100644 index 00000000000..61004dfabc3 --- /dev/null +++ b/gcc/rust/typecheck/rust-casts.cc @@ -0,0 +1,292 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-casts.h" + +namespace Rust { +namespace Resolver { + +TypeCastRules::TypeCastRules (Location locus, TyTy::TyWithLocation from, + TyTy::TyWithLocation to) + : locus (locus), from (from), to (to) +{} + +TypeCoercionRules::CoercionResult +TypeCastRules::resolve (Location locus, TyTy::TyWithLocation from, + TyTy::TyWithLocation to) +{ + TypeCastRules cast_rules (locus, from, to); + return cast_rules.check (); +} + +TypeCoercionRules::CoercionResult +TypeCastRules::check () +{ + // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L565-L582 + auto possible_coercion + = TypeCoercionRules::TryCoerce (from.get_ty (), to.get_ty (), locus); + if (!possible_coercion.is_error ()) + return possible_coercion; + + // try the simple cast rules + auto simple_cast = cast_rules (); + if (!simple_cast.is_error ()) + return simple_cast; + + // failed to cast + emit_cast_error (); + return TypeCoercionRules::CoercionResult::get_error (); +} + +TypeCoercionRules::CoercionResult +TypeCastRules::cast_rules () +{ + // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L596 + // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L654 + + rust_debug ("cast_rules from={%s} to={%s}", + from.get_ty ()->debug_str ().c_str (), + to.get_ty ()->debug_str ().c_str ()); + + switch (from.get_ty ()->get_kind ()) + { + case TyTy::TypeKind::INFER: { + TyTy::InferType *from_infer + = static_cast (from.get_ty ()); + switch (from_infer->get_infer_kind ()) + { + case TyTy::InferType::InferTypeKind::GENERAL: + return TypeCoercionRules::CoercionResult{{}, + to.get_ty ()->clone ()}; + + case TyTy::InferType::InferTypeKind::INTEGRAL: + switch (to.get_ty ()->get_kind ()) + { + case TyTy::TypeKind::CHAR: + case TyTy::TypeKind::BOOL: + case TyTy::TypeKind::USIZE: + case TyTy::TypeKind::ISIZE: + case TyTy::TypeKind::UINT: + case TyTy::TypeKind::INT: + case TyTy::TypeKind::POINTER: + return TypeCoercionRules::CoercionResult{ + {}, to.get_ty ()->clone ()}; + + case TyTy::TypeKind::INFER: { + TyTy::InferType *to_infer + = static_cast (to.get_ty ()); + + switch (to_infer->get_infer_kind ()) + { + case TyTy::InferType::InferTypeKind::GENERAL: + case TyTy::InferType::InferTypeKind::INTEGRAL: + return TypeCoercionRules::CoercionResult{ + {}, to.get_ty ()->clone ()}; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + } + break; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + break; + + case TyTy::InferType::InferTypeKind::FLOAT: + switch (to.get_ty ()->get_kind ()) + { + case TyTy::TypeKind::USIZE: + case TyTy::TypeKind::ISIZE: + case TyTy::TypeKind::UINT: + case TyTy::TypeKind::INT: + return TypeCoercionRules::CoercionResult{ + {}, to.get_ty ()->clone ()}; + + case TyTy::TypeKind::INFER: { + TyTy::InferType *to_infer + = static_cast (to.get_ty ()); + + switch (to_infer->get_infer_kind ()) + { + case TyTy::InferType::InferTypeKind::GENERAL: + case TyTy::InferType::InferTypeKind::FLOAT: + return TypeCoercionRules::CoercionResult{ + {}, to.get_ty ()->clone ()}; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + } + break; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + break; + } + } + break; + + case TyTy::TypeKind::BOOL: + switch (to.get_ty ()->get_kind ()) + { + case TyTy::TypeKind::INFER: + case TyTy::TypeKind::USIZE: + case TyTy::TypeKind::ISIZE: + case TyTy::TypeKind::UINT: + case TyTy::TypeKind::INT: + return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + break; + + case TyTy::TypeKind::CHAR: + case TyTy::TypeKind::USIZE: + case TyTy::TypeKind::ISIZE: + case TyTy::TypeKind::UINT: + case TyTy::TypeKind::INT: + switch (to.get_ty ()->get_kind ()) + { + case TyTy::TypeKind::CHAR: { + // only u8 and char + bool was_uint = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT; + bool was_u8 = was_uint + && (static_cast (from.get_ty ()) + ->get_uint_kind () + == TyTy::UintType::UintKind::U8); + if (was_u8) + return TypeCoercionRules::CoercionResult{{}, + to.get_ty ()->clone ()}; + } + break; + + case TyTy::TypeKind::INFER: + case TyTy::TypeKind::USIZE: + case TyTy::TypeKind::ISIZE: + case TyTy::TypeKind::UINT: + case TyTy::TypeKind::INT: + return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + break; + + case TyTy::TypeKind::FLOAT: + switch (to.get_ty ()->get_kind ()) + { + case TyTy::TypeKind::FLOAT: + return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; + + case TyTy::TypeKind::INFER: { + TyTy::InferType *to_infer + = static_cast (to.get_ty ()); + + switch (to_infer->get_infer_kind ()) + { + case TyTy::InferType::InferTypeKind::GENERAL: + case TyTy::InferType::InferTypeKind::FLOAT: + return TypeCoercionRules::CoercionResult{ + {}, to.get_ty ()->clone ()}; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + } + break; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + break; + + case TyTy::TypeKind::REF: + case TyTy::TypeKind::POINTER: + switch (to.get_ty ()->get_kind ()) + { + case TyTy::TypeKind::REF: + case TyTy::TypeKind::POINTER: + return check_ptr_ptr_cast (); + + // FIXME can you cast a pointer to a integral type? + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + break; + + default: + return TypeCoercionRules::CoercionResult::get_error (); + } + + return TypeCoercionRules::CoercionResult::get_error (); +} + +TypeCoercionRules::CoercionResult +TypeCastRules::check_ptr_ptr_cast () +{ + rust_debug ("check_ptr_ptr_cast from={%s} to={%s}", + from.get_ty ()->debug_str ().c_str (), + to.get_ty ()->debug_str ().c_str ()); + + bool from_is_ref = from.get_ty ()->get_kind () == TyTy::TypeKind::REF; + bool to_is_ref = to.get_ty ()->get_kind () == TyTy::TypeKind::REF; + bool from_is_ptr = from.get_ty ()->get_kind () == TyTy::TypeKind::POINTER; + bool to_is_ptr = to.get_ty ()->get_kind () == TyTy::TypeKind::POINTER; + + if (from_is_ptr && to_is_ptr) + { + // mutability is ignored here as all pointer usage requires unsafe + return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; + } + else if (from_is_ref && to_is_ref) + { + // mutability must be coercedable + TyTy::ReferenceType &f + = static_cast (*from.get_ty ()); + TyTy::ReferenceType &t + = static_cast (*to.get_ty ()); + + if (TypeCoercionRules::coerceable_mutability (f.mutability (), + t.mutability ())) + { + return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; + } + } + + return TypeCoercionRules::CoercionResult::get_error (); +} + +void +TypeCastRules::emit_cast_error () const +{ + // error[E0604] + RichLocation r (locus); + r.add_range (from.get_locus ()); + r.add_range (to.get_locus ()); + rust_error_at (r, "invalid cast %<%s%> to %<%s%>", + from.get_ty ()->get_name ().c_str (), + to.get_ty ()->get_name ().c_str ()); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-casts.h b/gcc/rust/typecheck/rust-casts.h new file mode 100644 index 00000000000..e908f49b656 --- /dev/null +++ b/gcc/rust/typecheck/rust-casts.h @@ -0,0 +1,53 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_CASTS +#define RUST_CASTS + +#include "rust-tyty.h" +#include "rust-coercion.h" + +namespace Rust { +namespace Resolver { + +class TypeCastRules +{ +public: + static TypeCoercionRules::CoercionResult + resolve (Location locus, TyTy::TyWithLocation from, TyTy::TyWithLocation to); + +protected: + TypeCoercionRules::CoercionResult check (); + TypeCoercionRules::CoercionResult cast_rules (); + TypeCoercionRules::CoercionResult check_ptr_ptr_cast (); + + void emit_cast_error () const; + +protected: + TypeCastRules (Location locus, TyTy::TyWithLocation from, + TyTy::TyWithLocation to); + + Location locus; + TyTy::TyWithLocation from; + TyTy::TyWithLocation to; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_CASTS diff --git a/gcc/rust/typecheck/rust-coercion.cc b/gcc/rust/typecheck/rust-coercion.cc new file mode 100644 index 00000000000..2ad2b8007ff --- /dev/null +++ b/gcc/rust/typecheck/rust-coercion.cc @@ -0,0 +1,357 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-coercion.h" + +namespace Rust { +namespace Resolver { + +TypeCoercionRules::CoercionResult +TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected, + Location locus) +{ + TypeCoercionRules resolver (expected, locus, true); + bool ok = resolver.do_coercion (receiver); + return ok ? resolver.try_result : CoercionResult::get_error (); +} + +TypeCoercionRules::CoercionResult +TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver, + TyTy::BaseType *expected, Location locus) +{ + TypeCoercionRules resolver (expected, locus, false); + bool ok = resolver.do_coercion (receiver); + return ok ? resolver.try_result : CoercionResult::get_error (); +} + +TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected, Location locus, + bool emit_errors) + : AutoderefCycle (false), mappings (Analysis::Mappings::get ()), + context (TypeCheckContext::get ()), expected (expected), locus (locus), + try_result (CoercionResult::get_error ()), emit_errors (emit_errors) +{} + +bool +TypeCoercionRules::do_coercion (TyTy::BaseType *receiver) +{ + // FIXME this is not finished and might be super simplified + // see: + // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs + + // unsize + bool unsafe_error = false; + CoercionResult unsize_coercion + = coerce_unsized (receiver, expected, unsafe_error); + bool valid_unsize_coercion = !unsize_coercion.is_error (); + if (valid_unsize_coercion) + { + try_result = unsize_coercion; + return true; + } + else if (unsafe_error) + { + // Location lhs = mappings->lookup_location (receiver->get_ref ()); + // Location rhs = mappings->lookup_location (expected->get_ref ()); + // object_unsafe_error (locus, lhs, rhs); + return false; + } + + // pointers + switch (expected->get_kind ()) + { + case TyTy::TypeKind::POINTER: { + TyTy::PointerType *ptr = static_cast (expected); + try_result = coerce_unsafe_ptr (receiver, ptr, ptr->mutability ()); + return !try_result.is_error (); + } + + case TyTy::TypeKind::REF: { + TyTy::ReferenceType *ptr + = static_cast (expected); + try_result + = coerce_borrowed_pointer (receiver, ptr, ptr->mutability ()); + return !try_result.is_error (); + } + break; + + default: + break; + } + + return !try_result.is_error (); +} + +TypeCoercionRules::CoercionResult +TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType *receiver, + TyTy::PointerType *expected, + Mutability to_mutbl) +{ + rust_debug ("coerce_unsafe_ptr(a={%s}, b={%s})", + receiver->debug_str ().c_str (), expected->debug_str ().c_str ()); + + Mutability from_mutbl = Mutability::Imm; + TyTy::BaseType *element = nullptr; + switch (receiver->get_kind ()) + { + case TyTy::TypeKind::REF: { + TyTy::ReferenceType *ref + = static_cast (receiver); + from_mutbl = ref->mutability (); + element = ref->get_base (); + } + break; + + case TyTy::TypeKind::POINTER: { + TyTy::PointerType *ref = static_cast (receiver); + from_mutbl = ref->mutability (); + element = ref->get_base (); + } + break; + + default: { + if (receiver->can_eq (expected, false)) + return CoercionResult{{}, expected->clone ()}; + + return CoercionResult::get_error (); + } + } + + if (!coerceable_mutability (from_mutbl, to_mutbl)) + { + Location lhs = mappings->lookup_location (receiver->get_ref ()); + Location rhs = mappings->lookup_location (expected->get_ref ()); + mismatched_mutability_error (locus, lhs, rhs); + return TypeCoercionRules::CoercionResult::get_error (); + } + + TyTy::PointerType *result + = new TyTy::PointerType (receiver->get_ref (), + TyTy::TyVar (element->get_ref ()), to_mutbl); + if (!result->can_eq (expected, false)) + return CoercionResult::get_error (); + + return CoercionResult{{}, result}; +} + +/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. +/// To match `A` with `B`, autoderef will be performed, +/// calling `deref`/`deref_mut` where necessary. +TypeCoercionRules::CoercionResult +TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver, + TyTy::ReferenceType *expected, + Mutability to_mutbl) +{ + rust_debug ("coerce_borrowed_pointer(a={%s}, b={%s})", + receiver->debug_str ().c_str (), expected->debug_str ().c_str ()); + + Mutability from_mutbl = Mutability::Imm; + switch (receiver->get_kind ()) + { + case TyTy::TypeKind::REF: { + TyTy::ReferenceType *ref + = static_cast (receiver); + from_mutbl = ref->mutability (); + } + break; + + default: { + TyTy::BaseType *result = receiver->unify (expected); + return CoercionResult{{}, result}; + } + } + + if (!coerceable_mutability (from_mutbl, to_mutbl)) + { + Location lhs = mappings->lookup_location (receiver->get_ref ()); + Location rhs = mappings->lookup_location (expected->get_ref ()); + mismatched_mutability_error (locus, lhs, rhs); + return TypeCoercionRules::CoercionResult::get_error (); + } + + AutoderefCycle::cycle (receiver); + return try_result; +} + +// &[T; n] or &mut [T; n] -> &[T] +// or &mut [T; n] -> &mut [T] +// or &Concrete -> &Trait, etc. +TypeCoercionRules::CoercionResult +TypeCoercionRules::coerce_unsized (TyTy::BaseType *source, + TyTy::BaseType *target, bool &unsafe_error) +{ + rust_debug ("coerce_unsized(source={%s}, target={%s})", + source->debug_str ().c_str (), target->debug_str ().c_str ()); + + bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF; + bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF; + bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER; + + bool needs_reborrow = false; + TyTy::BaseType *ty_a = source; + TyTy::BaseType *ty_b = target; + Mutability expected_mutability = Mutability::Imm; + if (source_is_ref && target_is_ref) + { + TyTy::ReferenceType *source_ref + = static_cast (source); + TyTy::ReferenceType *target_ref + = static_cast (target); + + Mutability from_mutbl = source_ref->mutability (); + Mutability to_mutbl = target_ref->mutability (); + if (!coerceable_mutability (from_mutbl, to_mutbl)) + { + unsafe_error = true; + Location lhs = mappings->lookup_location (source->get_ref ()); + Location rhs = mappings->lookup_location (target->get_ref ()); + mismatched_mutability_error (locus, lhs, rhs); + return TypeCoercionRules::CoercionResult::get_error (); + } + + ty_a = source_ref->get_base (); + ty_b = target_ref->get_base (); + needs_reborrow = true; + expected_mutability = to_mutbl; + + adjustments.push_back ( + Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a)); + } + else if (source_is_ref && target_is_ptr) + { + TyTy::ReferenceType *source_ref + = static_cast (source); + TyTy::PointerType *target_ref = static_cast (target); + + Mutability from_mutbl = source_ref->mutability (); + Mutability to_mutbl = target_ref->mutability (); + if (!coerceable_mutability (from_mutbl, to_mutbl)) + { + unsafe_error = true; + Location lhs = mappings->lookup_location (source->get_ref ()); + Location rhs = mappings->lookup_location (target->get_ref ()); + mismatched_mutability_error (locus, lhs, rhs); + return TypeCoercionRules::CoercionResult::get_error (); + } + + ty_a = source_ref->get_base (); + ty_b = target_ref->get_base (); + needs_reborrow = true; + expected_mutability = to_mutbl; + + adjustments.push_back ( + Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a)); + } + + // FIXME + // there is a bunch of code to ensure something is coerce able to a dyn trait + // we need to support but we need to support a few more lang items for that + // see: + // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582 + + const auto a = ty_a; + const auto b = ty_b; + + bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC; + bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC; + + if (expect_dyn && need_unsize) + { + bool bounds_compatible = b->bounds_compatible (*a, locus, true); + if (!bounds_compatible) + { + unsafe_error = true; + return TypeCoercionRules::CoercionResult::get_error (); + } + + // return the unsize coercion + TyTy::BaseType *result = b->clone (); + // result->set_ref (a->get_ref ()); + + // append a dyn coercion adjustment + adjustments.push_back (Adjustment (Adjustment::UNSIZE, a, result)); + + // reborrow if needed + if (needs_reborrow) + { + TyTy::ReferenceType *reborrow + = new TyTy::ReferenceType (source->get_ref (), + TyTy::TyVar (result->get_ref ()), + expected_mutability); + + Adjustment::AdjustmentType borrow_type + = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF + : Adjustment::MUT_REF; + adjustments.push_back (Adjustment (borrow_type, result, reborrow)); + result = reborrow; + } + + return CoercionResult{adjustments, result}; + } + + adjustments.clear (); + return TypeCoercionRules::CoercionResult::get_error (); +} + +bool +TypeCoercionRules::select (const TyTy::BaseType &autoderefed) +{ + if (autoderefed.can_eq (expected, false)) + { + try_result = CoercionResult{adjustments, autoderefed.clone ()}; + return true; + } + return false; +} + +/// Coercing a mutable reference to an immutable works, while +/// coercing `&T` to `&mut T` should be forbidden. +bool +TypeCoercionRules::coerceable_mutability (Mutability from_mutbl, + Mutability to_mutbl) +{ + return to_mutbl == Mutability::Imm || (from_mutbl == to_mutbl); +} + +void +TypeCoercionRules::mismatched_mutability_error (Location expr_locus, + Location lhs, Location rhs) +{ + if (!emit_errors) + return; + + RichLocation r (expr_locus); + r.add_range (lhs); + r.add_range (rhs); + rust_error_at (r, "mismatched mutability"); +} + +void +TypeCoercionRules::object_unsafe_error (Location expr_locus, Location lhs, + Location rhs) +{ + if (!emit_errors) + return; + + RichLocation r (expr_locus); + r.add_range (lhs); + r.add_range (rhs); + rust_error_at (r, "unsafe unsize coercion"); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-coercion.h b/gcc/rust/typecheck/rust-coercion.h new file mode 100644 index 00000000000..da28c7c5e1b --- /dev/null +++ b/gcc/rust/typecheck/rust-coercion.h @@ -0,0 +1,93 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_COERCION +#define RUST_COERCION + +#include "rust-autoderef.h" +#include "rust-hir-type-check.h" + +namespace Rust { +namespace Resolver { + +class TypeCoercionRules : protected AutoderefCycle +{ +public: + struct CoercionResult + { + std::vector adjustments; + TyTy::BaseType *tyty; + + bool is_error () + { + return tyty == nullptr || tyty->get_kind () == TyTy::TypeKind::ERROR; + } + + static CoercionResult get_error () { return CoercionResult{{}, nullptr}; } + }; + + static CoercionResult Coerce (TyTy::BaseType *receiver, + TyTy::BaseType *expected, Location locus); + + static CoercionResult TryCoerce (TyTy::BaseType *receiver, + TyTy::BaseType *expected, Location locus); + + CoercionResult coerce_unsafe_ptr (TyTy::BaseType *receiver, + TyTy::PointerType *expected, + Mutability mutability); + + CoercionResult coerce_borrowed_pointer (TyTy::BaseType *receiver, + TyTy::ReferenceType *expected, + Mutability mutability); + + CoercionResult coerce_unsized (TyTy::BaseType *receiver, + TyTy::BaseType *expected, bool &unsafe_error); + + static bool coerceable_mutability (Mutability from_mutbl, + Mutability to_mutbl); + + void mismatched_mutability_error (Location expr_locus, Location lhs, + Location rhs); + void object_unsafe_error (Location expr_locus, Location lhs, Location rhs); + +protected: + TypeCoercionRules (TyTy::BaseType *expected, Location locus, + bool emit_errors); + + bool select (const TyTy::BaseType &autoderefed) override; + + bool do_coercion (TyTy::BaseType *receiver); + +private: + // context info + Analysis::Mappings *mappings; + TypeCheckContext *context; + + // search + TyTy::BaseType *expected; + Location locus; + + // mutable fields + CoercionResult try_result; + bool emit_errors; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_COERCION diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.cc b/gcc/rust/typecheck/rust-hir-dot-operator.cc new file mode 100644 index 00000000000..d45f0903478 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-dot-operator.cc @@ -0,0 +1,263 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-dot-operator.h" +#include "rust-hir-path-probe.h" +#include "rust-hir-trait-resolve.h" + +namespace Rust { +namespace Resolver { + +MethodResolver::MethodResolver (bool autoderef_flag, + const HIR::PathIdentSegment &segment_name) + : AutoderefCycle (autoderef_flag), mappings (Analysis::Mappings::get ()), + context (TypeCheckContext::get ()), segment_name (segment_name), + try_result (MethodCandidate::get_error ()) +{} + +MethodCandidate +MethodResolver::Probe (const TyTy::BaseType *receiver, + const HIR::PathIdentSegment &segment_name, + bool autoderef_flag) +{ + MethodResolver resolver (autoderef_flag, segment_name); + bool ok = resolver.cycle (receiver); + return ok ? resolver.try_result : MethodCandidate::get_error (); +} + +void +MethodResolver::try_hook (const TyTy::BaseType &r) +{ + const auto &specified_bounds = r.get_specified_bounds (); + predicate_items = get_predicate_items (segment_name, r, specified_bounds); +} + +bool +MethodResolver::select (const TyTy::BaseType &receiver) +{ + struct impl_item_candidate + { + HIR::Function *item; + HIR::ImplBlock *impl_block; + TyTy::FnType *ty; + }; + + // assemble inherent impl items + std::vector inherent_impl_fns; + mappings->iterate_impl_items ( + [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool { + bool is_trait_impl = impl->has_trait_ref (); + if (is_trait_impl) + return true; + + bool is_fn + = item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION; + if (!is_fn) + return true; + + HIR::Function *func = static_cast (item); + if (!func->is_method ()) + return true; + + bool name_matches + = func->get_function_name ().compare (segment_name.as_string ()) == 0; + if (!name_matches) + return true; + + TyTy::BaseType *ty = nullptr; + if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty)) + return true; + if (ty->get_kind () == TyTy::TypeKind::ERROR) + return true; + + rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::FnType *fnty = static_cast (ty); + + inherent_impl_fns.push_back ({func, impl, fnty}); + + return true; + }); + + struct trait_item_candidate + { + const HIR::TraitItemFunc *item; + const HIR::Trait *trait; + TyTy::FnType *ty; + const TraitReference *reference; + const TraitItemReference *item_ref; + }; + + std::vector trait_fns; + mappings->iterate_impl_blocks ([&] (HirId id, + HIR::ImplBlock *impl) mutable -> bool { + bool is_trait_impl = impl->has_trait_ref (); + if (!is_trait_impl) + return true; + + // look for impl implementation else lookup the associated trait item + for (auto &impl_item : impl->get_impl_items ()) + { + bool is_fn = impl_item->get_impl_item_type () + == HIR::ImplItem::ImplItemType::FUNCTION; + if (!is_fn) + continue; + + HIR::Function *func = static_cast (impl_item.get ()); + if (!func->is_method ()) + continue; + + bool name_matches + = func->get_function_name ().compare (segment_name.as_string ()) == 0; + if (!name_matches) + continue; + + TyTy::BaseType *ty = nullptr; + if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty)) + continue; + if (ty->get_kind () == TyTy::TypeKind::ERROR) + continue; + + rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::FnType *fnty = static_cast (ty); + + inherent_impl_fns.push_back ({func, impl, fnty}); + return true; + } + + TraitReference *trait_ref + = TraitResolver::Resolve (*impl->get_trait_ref ().get ()); + rust_assert (!trait_ref->is_error ()); + + auto item_ref + = trait_ref->lookup_trait_item (segment_name.as_string (), + TraitItemReference::TraitItemType::FN); + if (item_ref->is_error ()) + return true; + + const HIR::Trait *trait = trait_ref->get_hir_trait_ref (); + HIR::TraitItem *item = item_ref->get_hir_trait_item (); + rust_assert (item->get_item_kind () == HIR::TraitItem::TraitItemKind::FUNC); + HIR::TraitItemFunc *func = static_cast (item); + + TyTy::BaseType *ty = item_ref->get_tyty (); + rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF); + TyTy::FnType *fnty = static_cast (ty); + + trait_item_candidate candidate{func, trait, fnty, trait_ref, item_ref}; + trait_fns.push_back (candidate); + + return true; + }); + + // lookup specified bounds for an associated item + struct precdicate_candidate + { + TyTy::TypeBoundPredicateItem lookup; + TyTy::FnType *fntype; + }; + + for (auto impl_item : inherent_impl_fns) + { + TyTy::FnType *fn = impl_item.ty; + rust_assert (fn->is_method ()); + + TyTy::BaseType *fn_self = fn->get_self_type (); + if (fn_self->can_eq (&receiver, false)) + { + PathProbeCandidate::ImplItemCandidate c{impl_item.item, + impl_item.impl_block}; + try_result = MethodCandidate{ + PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC, + fn, impl_item.item->get_locus (), c), + adjustments}; + return true; + } + } + + for (auto trait_item : trait_fns) + { + TyTy::FnType *fn = trait_item.ty; + rust_assert (fn->is_method ()); + + TyTy::BaseType *fn_self = fn->get_self_type (); + if (fn_self->can_eq (&receiver, false)) + { + PathProbeCandidate::TraitItemCandidate c{trait_item.reference, + trait_item.item_ref, + nullptr}; + try_result = MethodCandidate{ + PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC, + fn, trait_item.item->get_locus (), c), + adjustments}; + return true; + } + } + + for (const auto &predicate : predicate_items) + { + const TyTy::FnType *fn = predicate.fntype; + rust_assert (fn->is_method ()); + + TyTy::BaseType *fn_self = fn->get_self_type (); + if (fn_self->can_eq (&receiver, false)) + { + const TraitReference *trait_ref + = predicate.lookup.get_parent ()->get (); + const TraitItemReference *trait_item + = predicate.lookup.get_raw_item (); + + PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item, + nullptr}; + try_result = MethodCandidate{ + PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC, + fn->clone (), trait_item->get_locus (), c), + adjustments}; + return true; + } + } + + return false; +} + +std::vector +MethodResolver::get_predicate_items ( + const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver, + const std::vector &specified_bounds) +{ + std::vector predicate_items; + for (auto &bound : specified_bounds) + { + TyTy::TypeBoundPredicateItem lookup + = bound.lookup_associated_item (segment_name.as_string ()); + if (lookup.is_error ()) + continue; + + TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver); + if (ty->get_kind () == TyTy::TypeKind::FNDEF) + { + TyTy::FnType *fnty = static_cast (ty); + predicate_candidate candidate{lookup, fnty}; + predicate_items.push_back (candidate); + } + } + + return predicate_items; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.h b/gcc/rust/typecheck/rust-hir-dot-operator.h new file mode 100644 index 00000000000..750601a2d9e --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-dot-operator.h @@ -0,0 +1,81 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_DOT_OPERATOR +#define RUST_HIR_DOT_OPERATOR + +#include "rust-hir-path-probe.h" + +namespace Rust { +namespace Resolver { + +struct MethodCandidate +{ + PathProbeCandidate candidate; + std::vector adjustments; + + static MethodCandidate get_error () + { + return {PathProbeCandidate::get_error (), {}}; + } + + bool is_error () const { return candidate.is_error (); } +}; + +class MethodResolver : protected AutoderefCycle +{ +public: + struct predicate_candidate + { + TyTy::TypeBoundPredicateItem lookup; + TyTy::FnType *fntype; + }; + + static MethodCandidate Probe (const TyTy::BaseType *receiver, + const HIR::PathIdentSegment &segment_name, + bool autoderef_flag = false); + + static std::vector get_predicate_items ( + const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver, + const std::vector &specified_bounds); + +protected: + MethodResolver (bool autoderef_flag, + const HIR::PathIdentSegment &segment_name); + + void try_hook (const TyTy::BaseType &r) override; + + bool select (const TyTy::BaseType &receiver) override; + +private: + // context info + Analysis::Mappings *mappings; + TypeCheckContext *context; + + // search + const HIR::PathIdentSegment &segment_name; + std::vector predicate_items; + + // mutable fields + MethodCandidate try_result; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_DOT_OPERATOR diff --git a/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h new file mode 100644 index 00000000000..2890b54a00d --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h @@ -0,0 +1,186 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H +#define RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" + +namespace Rust { +namespace Resolver { + +class ImplItemToName : private TypeCheckBase, private HIR::HIRImplVisitor +{ +public: + static bool resolve (HIR::ImplItem *item, std::string &name_result) + { + ImplItemToName resolver (name_result); + item->accept_vis (resolver); + return resolver.ok; + } + + void visit (HIR::TypeAlias &alias) override + { + ok = true; + result.assign (alias.get_new_type_name ()); + } + + void visit (HIR::Function &function) override + { + ok = true; + result.assign (function.get_function_name ()); + } + + void visit (HIR::ConstantItem &constant) override + { + ok = true; + result.assign (constant.get_identifier ()); + } + +private: + ImplItemToName (std::string &result) + : TypeCheckBase (), ok (false), result (result) + {} + + bool ok; + std::string &result; +}; + +class OverlappingImplItemPass : public TypeCheckBase +{ +public: + static void go () + { + OverlappingImplItemPass pass; + + // generate mappings + pass.mappings->iterate_impl_items ( + [&] (HirId id, HIR::ImplItem *impl_item, HIR::ImplBlock *impl) -> bool { + // ignoring trait-impls might need thought later on + if (impl->has_trait_ref ()) + return true; + + pass.process_impl_item (id, impl_item, impl); + return true; + }); + + pass.scan (); + } + + void process_impl_item (HirId id, HIR::ImplItem *impl_item, + HIR::ImplBlock *impl) + { + // lets make a mapping of impl-item Self type to (impl-item,name): + // { + // impl-type -> [ (item, name), ... ] + // } + + HirId impl_type_id = impl->get_type ()->get_mappings ().get_hirid (); + TyTy::BaseType *impl_type = nullptr; + bool ok = context->lookup_type (impl_type_id, &impl_type); + rust_assert (ok); + + std::string impl_item_name; + ok = ImplItemToName::resolve (impl_item, impl_item_name); + rust_assert (ok); + + std::pair elem (impl_item, impl_item_name); + impl_mappings[impl_type].insert (std::move (elem)); + } + + void scan () + { + // we can now brute force the map looking for can_eq on each of the + // impl_items_types to look for possible colliding impl blocks; + for (auto it = impl_mappings.begin (); it != impl_mappings.end (); it++) + { + TyTy::BaseType *query = it->first; + + for (auto iy = impl_mappings.begin (); iy != impl_mappings.end (); iy++) + { + TyTy::BaseType *candidate = iy->first; + if (query == candidate) + continue; + + if (query->can_eq (candidate, false)) + { + // we might be in the case that we have: + // + // *const T vs *const [T] + // + // so lets use an equality check when the + // candidates are both generic to be sure we dont emit a false + // positive + + bool a = query->is_concrete (); + bool b = candidate->is_concrete (); + bool both_generic = !a && !b; + if (both_generic) + { + if (!query->is_equal (*candidate)) + continue; + } + + possible_collision (it->second, iy->second); + } + } + } + } + + void possible_collision ( + std::set > query, + std::set > candidate) + { + for (auto &q : query) + { + HIR::ImplItem *query_impl_item = q.first; + std::string query_impl_item_name = q.second; + + for (auto &c : candidate) + { + HIR::ImplItem *candidate_impl_item = c.first; + std::string candidate_impl_item_name = c.second; + + if (query_impl_item_name.compare (candidate_impl_item_name) == 0) + collision_detected (query_impl_item, candidate_impl_item, + candidate_impl_item_name); + } + } + } + + void collision_detected (HIR::ImplItem *query, HIR::ImplItem *dup, + const std::string &name) + { + RichLocation r (dup->get_locus ()); + r.add_range (query->get_locus ()); + rust_error_at (r, "duplicate definitions with name %s", name.c_str ()); + } + +private: + OverlappingImplItemPass () : TypeCheckBase () {} + + std::map > > + impl_mappings; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H diff --git a/gcc/rust/typecheck/rust-hir-path-probe.h b/gcc/rust/typecheck/rust-hir-path-probe.h new file mode 100644 index 00000000000..bd4f91e49bf --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-path-probe.h @@ -0,0 +1,540 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_PATH_PROBE_H +#define RUST_HIR_PATH_PROBE_H + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" +#include "rust-tyty.h" +#include "rust-substitution-mapper.h" +#include "rust-hir-type-bounds.h" + +namespace Rust { +namespace Resolver { + +struct PathProbeCandidate +{ + enum CandidateType + { + ERROR, + + ENUM_VARIANT, + + IMPL_CONST, + IMPL_TYPE_ALIAS, + IMPL_FUNC, + + TRAIT_ITEM_CONST, + TRAIT_TYPE_ALIAS, + TRAIT_FUNC, + }; + + struct EnumItemCandidate + { + const TyTy::ADTType *parent; + const TyTy::VariantDef *variant; + }; + + struct ImplItemCandidate + { + HIR::ImplItem *impl_item; + HIR::ImplBlock *parent; + }; + + struct TraitItemCandidate + { + const TraitReference *trait_ref; + const TraitItemReference *item_ref; + HIR::ImplBlock *impl; + }; + + CandidateType type; + TyTy::BaseType *ty; + Location locus; + union Candidate + { + EnumItemCandidate enum_field; + ImplItemCandidate impl; + TraitItemCandidate trait; + + Candidate (EnumItemCandidate enum_field) : enum_field (enum_field) {} + Candidate (ImplItemCandidate impl) : impl (impl) {} + Candidate (TraitItemCandidate trait) : trait (trait) {} + } item; + + PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus, + EnumItemCandidate enum_field) + : type (type), ty (ty), item (enum_field) + {} + + PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus, + ImplItemCandidate impl) + : type (type), ty (ty), item (impl) + {} + + PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus, + TraitItemCandidate trait) + : type (type), ty (ty), item (trait) + {} + + std::string as_string () const + { + return "PathProbe candidate TODO - as_string"; + } + + bool is_enum_candidate () const { return type == ENUM_VARIANT; } + + bool is_impl_candidate () const + { + return type == IMPL_CONST || type == IMPL_TYPE_ALIAS || type == IMPL_FUNC; + } + + bool is_trait_candidate () const + { + return type == TRAIT_ITEM_CONST || type == TRAIT_TYPE_ALIAS + || type == TRAIT_FUNC; + } + + bool is_full_trait_item_candidate () const + { + return is_trait_candidate () && item.trait.impl == nullptr; + } + + static PathProbeCandidate get_error () + { + return PathProbeCandidate (ERROR, nullptr, Location (), + ImplItemCandidate{nullptr, nullptr}); + } + + bool is_error () const { return type == ERROR; } +}; + +class PathProbeType : public TypeCheckBase, public HIR::HIRImplVisitor +{ +public: + static std::vector + Probe (const TyTy::BaseType *receiver, + const HIR::PathIdentSegment &segment_name, bool probe_impls, + bool probe_bounds, bool ignore_mandatory_trait_items, + DefId specific_trait_id = UNKNOWN_DEFID) + { + PathProbeType probe (receiver, segment_name, specific_trait_id); + if (probe_impls) + { + if (receiver->get_kind () == TyTy::TypeKind::ADT) + { + const TyTy::ADTType *adt + = static_cast (receiver); + if (adt->is_enum ()) + probe.process_enum_item_for_candiates (adt); + } + + probe.process_impl_items_for_candidates (); + } + + if (!probe_bounds) + return probe.candidates; + + if (!probe.is_reciever_generic ()) + { + std::vector> probed_bounds + = TypeBoundsProbe::Probe (receiver); + for (auto &candidate : probed_bounds) + { + const TraitReference *trait_ref = candidate.first; + if (specific_trait_id != UNKNOWN_DEFID) + { + if (trait_ref->get_mappings ().get_defid () + != specific_trait_id) + continue; + } + + HIR::ImplBlock *impl = candidate.second; + probe.process_associated_trait_for_candidates ( + trait_ref, impl, ignore_mandatory_trait_items); + } + } + + for (const TyTy::TypeBoundPredicate &predicate : + receiver->get_specified_bounds ()) + { + const TraitReference *trait_ref = predicate.get (); + if (specific_trait_id != UNKNOWN_DEFID) + { + if (trait_ref->get_mappings ().get_defid () != specific_trait_id) + continue; + } + + probe.process_predicate_for_candidates (predicate, + ignore_mandatory_trait_items); + } + + return probe.candidates; + } + + void visit (HIR::TypeAlias &alias) override + { + Identifier name = alias.get_new_type_name (); + if (search.as_string ().compare (name) == 0) + { + HirId tyid = alias.get_mappings ().get_hirid (); + TyTy::BaseType *ty = nullptr; + bool ok = context->lookup_type (tyid, &ty); + rust_assert (ok); + + PathProbeCandidate::ImplItemCandidate impl_item_candidate{&alias, + current_impl}; + PathProbeCandidate candidate{ + PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS, ty, + alias.get_locus (), impl_item_candidate}; + candidates.push_back (std::move (candidate)); + } + } + + void visit (HIR::ConstantItem &constant) override + { + Identifier name = constant.get_identifier (); + if (search.as_string ().compare (name) == 0) + { + HirId tyid = constant.get_mappings ().get_hirid (); + TyTy::BaseType *ty = nullptr; + bool ok = context->lookup_type (tyid, &ty); + rust_assert (ok); + + PathProbeCandidate::ImplItemCandidate impl_item_candidate{&constant, + current_impl}; + PathProbeCandidate candidate{ + PathProbeCandidate::CandidateType::IMPL_CONST, ty, + constant.get_locus (), impl_item_candidate}; + candidates.push_back (std::move (candidate)); + } + } + + void visit (HIR::Function &function) override + { + Identifier name = function.get_function_name (); + if (search.as_string ().compare (name) == 0) + { + HirId tyid = function.get_mappings ().get_hirid (); + TyTy::BaseType *ty = nullptr; + bool ok = context->lookup_type (tyid, &ty); + rust_assert (ok); + + PathProbeCandidate::ImplItemCandidate impl_item_candidate{&function, + current_impl}; + PathProbeCandidate candidate{ + PathProbeCandidate::CandidateType::IMPL_FUNC, ty, + function.get_locus (), impl_item_candidate}; + candidates.push_back (std::move (candidate)); + } + } + +protected: + void process_enum_item_for_candiates (const TyTy::ADTType *adt) + { + if (specific_trait_id != UNKNOWN_DEFID) + return; + + TyTy::VariantDef *v; + if (!adt->lookup_variant (search.as_string (), &v)) + return; + + PathProbeCandidate::EnumItemCandidate enum_item_candidate{adt, v}; + PathProbeCandidate candidate{ + PathProbeCandidate::CandidateType::ENUM_VARIANT, receiver->clone (), + mappings->lookup_location (adt->get_ty_ref ()), enum_item_candidate}; + candidates.push_back (std::move (candidate)); + } + + void process_impl_items_for_candidates () + { + mappings->iterate_impl_items ([&] (HirId id, HIR::ImplItem *item, + HIR::ImplBlock *impl) mutable -> bool { + process_impl_item_candidate (id, item, impl); + return true; + }); + } + + void process_impl_item_candidate (HirId id, HIR::ImplItem *item, + HIR::ImplBlock *impl) + { + current_impl = impl; + HirId impl_ty_id = impl->get_type ()->get_mappings ().get_hirid (); + TyTy::BaseType *impl_block_ty = nullptr; + if (!context->lookup_type (impl_ty_id, &impl_block_ty)) + return; + + if (!receiver->can_eq (impl_block_ty, false)) + { + if (!impl_block_ty->can_eq (receiver, false)) + return; + } + + // lets visit the impl_item + item->accept_vis (*this); + } + + void + process_associated_trait_for_candidates (const TraitReference *trait_ref, + HIR::ImplBlock *impl, + bool ignore_mandatory_trait_items) + { + const TraitItemReference *trait_item_ref = nullptr; + if (!trait_ref->lookup_trait_item (search.as_string (), &trait_item_ref)) + return; + + bool trait_item_needs_implementation = !trait_item_ref->is_optional (); + if (ignore_mandatory_trait_items && trait_item_needs_implementation) + return; + + PathProbeCandidate::CandidateType candidate_type; + switch (trait_item_ref->get_trait_item_type ()) + { + case TraitItemReference::TraitItemType::FN: + candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC; + break; + case TraitItemReference::TraitItemType::CONST: + candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST; + break; + case TraitItemReference::TraitItemType::TYPE: + candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS; + break; + + case TraitItemReference::TraitItemType::ERROR: + default: + gcc_unreachable (); + break; + } + + TyTy::BaseType *trait_item_tyty = trait_item_ref->get_tyty (); + + // we can substitute the Self with the receiver here + if (trait_item_tyty->get_kind () == TyTy::TypeKind::FNDEF) + { + TyTy::FnType *fn = static_cast (trait_item_tyty); + TyTy::SubstitutionParamMapping *param = nullptr; + for (auto ¶m_mapping : fn->get_substs ()) + { + const HIR::TypeParam &type_param + = param_mapping.get_generic_param (); + if (type_param.get_type_representation ().compare ("Self") == 0) + { + param = ¶m_mapping; + break; + } + } + rust_assert (param != nullptr); + + std::vector mappings; + mappings.push_back (TyTy::SubstitutionArg (param, receiver->clone ())); + + Location locus; // FIXME + TyTy::SubstitutionArgumentMappings args (std::move (mappings), locus); + trait_item_tyty = SubstMapperInternal::Resolve (trait_item_tyty, args); + } + + PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref, + trait_item_ref, + impl}; + + PathProbeCandidate candidate{candidate_type, trait_item_tyty, + trait_ref->get_locus (), trait_item_candidate}; + candidates.push_back (std::move (candidate)); + } + + void + process_predicate_for_candidates (const TyTy::TypeBoundPredicate &predicate, + bool ignore_mandatory_trait_items) + { + const TraitReference *trait_ref = predicate.get (); + + TyTy::TypeBoundPredicateItem item + = predicate.lookup_associated_item (search.as_string ()); + if (item.is_error ()) + return; + + if (ignore_mandatory_trait_items && item.needs_implementation ()) + return; + + const TraitItemReference *trait_item_ref = item.get_raw_item (); + PathProbeCandidate::CandidateType candidate_type; + switch (trait_item_ref->get_trait_item_type ()) + { + case TraitItemReference::TraitItemType::FN: + candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC; + break; + case TraitItemReference::TraitItemType::CONST: + candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST; + break; + case TraitItemReference::TraitItemType::TYPE: + candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS; + break; + + case TraitItemReference::TraitItemType::ERROR: + default: + gcc_unreachable (); + break; + } + + TyTy::BaseType *trait_item_tyty = item.get_tyty_for_receiver (receiver); + PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref, + trait_item_ref, + nullptr}; + PathProbeCandidate candidate{candidate_type, trait_item_tyty, + trait_item_ref->get_locus (), + trait_item_candidate}; + candidates.push_back (std::move (candidate)); + } + +protected: + PathProbeType (const TyTy::BaseType *receiver, + const HIR::PathIdentSegment &query, DefId specific_trait_id) + : TypeCheckBase (), receiver (receiver), search (query), + current_impl (nullptr), specific_trait_id (specific_trait_id) + {} + + std::vector> + union_bounds ( + const std::vector> + a, + const std::vector> b) + const + { + std::map> mapper; + for (auto &ref : a) + { + mapper.insert ({ref.first->get_mappings ().get_defid (), ref}); + } + for (auto &ref : b) + { + mapper.insert ({ref.first->get_mappings ().get_defid (), ref}); + } + + std::vector> union_set; + for (auto it = mapper.begin (); it != mapper.end (); it++) + { + union_set.push_back ({it->second.first, it->second.second}); + } + return union_set; + } + + bool is_reciever_generic () const + { + const TyTy::BaseType *root = receiver->get_root (); + bool receiver_is_type_param = root->get_kind () == TyTy::TypeKind::PARAM; + bool receiver_is_dyn = root->get_kind () == TyTy::TypeKind::DYNAMIC; + return receiver_is_type_param || receiver_is_dyn; + } + + const TyTy::BaseType *receiver; + const HIR::PathIdentSegment &search; + std::vector candidates; + HIR::ImplBlock *current_impl; + DefId specific_trait_id; +}; + +class ReportMultipleCandidateError : private TypeCheckBase, + private HIR::HIRImplVisitor +{ +public: + static void Report (std::vector &candidates, + const HIR::PathIdentSegment &query, Location query_locus) + { + RichLocation r (query_locus); + ReportMultipleCandidateError visitor (r); + for (auto &c : candidates) + { + switch (c.type) + { + case PathProbeCandidate::CandidateType::ERROR: + case PathProbeCandidate::CandidateType::ENUM_VARIANT: + gcc_unreachable (); + break; + + case PathProbeCandidate::CandidateType::IMPL_CONST: + case PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS: + case PathProbeCandidate::CandidateType::IMPL_FUNC: + c.item.impl.impl_item->accept_vis (visitor); + break; + + case PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST: + case PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS: + case PathProbeCandidate::CandidateType::TRAIT_FUNC: + r.add_range (c.item.trait.item_ref->get_locus ()); + break; + } + } + + rust_error_at (r, "multiple applicable items in scope for: %s", + query.as_string ().c_str ()); + } + + void visit (HIR::TypeAlias &alias) override + { + r.add_range (alias.get_locus ()); + } + + void visit (HIR::ConstantItem &constant) override + { + r.add_range (constant.get_locus ()); + } + + void visit (HIR::Function &function) override + { + r.add_range (function.get_locus ()); + } + +private: + ReportMultipleCandidateError (RichLocation &r) : TypeCheckBase (), r (r) {} + + RichLocation &r; +}; + +class PathProbeImplTrait : public PathProbeType +{ +public: + static std::vector + Probe (const TyTy::BaseType *receiver, + const HIR::PathIdentSegment &segment_name, + const TraitReference *trait_reference) + { + PathProbeImplTrait probe (receiver, segment_name, trait_reference); + // iterate all impls for this trait and receiver + // then search for possible candidates using base class behaviours + probe.process_trait_impl_items_for_candidates (); + return probe.candidates; + } + +private: + void process_trait_impl_items_for_candidates (); + + PathProbeImplTrait (const TyTy::BaseType *receiver, + const HIR::PathIdentSegment &query, + const TraitReference *trait_reference) + : PathProbeType (receiver, query, UNKNOWN_DEFID), + trait_reference (trait_reference) + {} + + const TraitReference *trait_reference; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_PATH_PROBE_H diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h new file mode 100644 index 00000000000..6eec461e8a5 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-trait-ref.h @@ -0,0 +1,472 @@ +// Copyright (C) 2021-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TRAIT_REF_H +#define RUST_HIR_TRAIT_REF_H + +#include "rust-hir-full.h" +#include "rust-tyty-visitor.h" +#include "rust-hir-type-check-util.h" + +namespace Rust { +namespace Resolver { + +// Data Objects for the associated trait items in a structure we can work with +// https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/associated-constants.html +class TypeCheckContext; +class TraitItemReference +{ +public: + enum TraitItemType + { + FN, + CONST, + TYPE, + ERROR + }; + + TraitItemReference (std::string identifier, bool optional, TraitItemType type, + HIR::TraitItem *hir_trait_item, TyTy::BaseType *self, + std::vector substitutions, + Location locus); + + TraitItemReference (TraitItemReference const &other); + + TraitItemReference &operator= (TraitItemReference const &other); + + static TraitItemReference error () + { + return TraitItemReference ("", false, ERROR, nullptr, nullptr, {}, + Location ()); + } + + static TraitItemReference &error_node () + { + static TraitItemReference error = TraitItemReference::error (); + return error; + } + + bool is_error () const { return type == ERROR; } + + std::string as_string () const + { + return "(" + trait_item_type_as_string (type) + " " + identifier + " " + + ")"; + } + + static std::string trait_item_type_as_string (TraitItemType ty) + { + switch (ty) + { + case FN: + return "FN"; + case CONST: + return "CONST"; + case TYPE: + return "TYPE"; + case ERROR: + return "ERROR"; + } + return "ERROR"; + } + + bool is_optional () const { return optional_flag; } + + std::string get_identifier () const { return identifier; } + + TraitItemType get_trait_item_type () const { return type; } + + HIR::TraitItem *get_hir_trait_item () const { return hir_trait_item; } + + Location get_locus () const { return locus; } + + const Analysis::NodeMapping get_mappings () const + { + return hir_trait_item->get_mappings (); + } + + TyTy::BaseType *get_tyty () const + { + rust_assert (hir_trait_item != nullptr); + + switch (type) + { + case CONST: + return get_type_from_constant ( + static_cast (*hir_trait_item)); + break; + + case TYPE: + return get_type_from_typealias ( + static_cast (*hir_trait_item)); + + case FN: + return get_type_from_fn ( + static_cast (*hir_trait_item)); + break; + + default: + return get_error (); + } + + gcc_unreachable (); + return get_error (); + } + + Analysis::NodeMapping get_parent_trait_mappings () const; + + // this is called when the trait is completed resolution and gives the items a + // chance to run their specific type resolution passes. If we call their + // resolution on construction it can lead to a case where the trait being + // resolved recursively trying to resolve the trait itself infinitely since + // the trait will not be stored in its own map yet + void on_resolved (); + + void associated_type_set (TyTy::BaseType *ty) const; + + void associated_type_reset () const; + + bool is_object_safe () const; + +private: + TyTy::ErrorType *get_error () const + { + return new TyTy::ErrorType (get_mappings ().get_hirid ()); + } + + TyTy::BaseType *get_type_from_typealias (/*const*/ + HIR::TraitItemType &type) const; + + TyTy::BaseType * + get_type_from_constant (/*const*/ HIR::TraitItemConst &constant) const; + + TyTy::BaseType *get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const; + + bool is_item_resolved () const; + void resolve_item (HIR::TraitItemType &type); + void resolve_item (HIR::TraitItemConst &constant); + void resolve_item (HIR::TraitItemFunc &func); + + std::string identifier; + bool optional_flag; + TraitItemType type; + HIR::TraitItem *hir_trait_item; + std::vector inherited_substitutions; + Location locus; + + TyTy::BaseType + *self; // this is the implict Self TypeParam required for methods + Resolver::TypeCheckContext *context; +}; + +// this wraps up the HIR::Trait so we can do analysis on it + +class TraitReference +{ +public: + TraitReference (const HIR::Trait *hir_trait_ref, + std::vector item_refs, + std::vector super_traits, + std::vector substs) + : hir_trait_ref (hir_trait_ref), item_refs (item_refs), + super_traits (super_traits) + { + trait_substs.clear (); + trait_substs.reserve (substs.size ()); + for (const auto &p : substs) + trait_substs.push_back (p.clone ()); + } + + TraitReference (TraitReference const &other) + : hir_trait_ref (other.hir_trait_ref), item_refs (other.item_refs), + super_traits (other.super_traits) + { + trait_substs.clear (); + trait_substs.reserve (other.trait_substs.size ()); + for (const auto &p : other.trait_substs) + trait_substs.push_back (p.clone ()); + } + + TraitReference &operator= (TraitReference const &other) + { + hir_trait_ref = other.hir_trait_ref; + item_refs = other.item_refs; + super_traits = other.super_traits; + + trait_substs.clear (); + trait_substs.reserve (other.trait_substs.size ()); + for (const auto &p : other.trait_substs) + trait_substs.push_back (p.clone ()); + + return *this; + } + + TraitReference (TraitReference &&other) = default; + TraitReference &operator= (TraitReference &&other) = default; + + static TraitReference error () + { + return TraitReference (nullptr, {}, {}, {}); + } + + bool is_error () const { return hir_trait_ref == nullptr; } + + static TraitReference &error_node () + { + static TraitReference trait_error_node = TraitReference::error (); + return trait_error_node; + } + + Location get_locus () const { return hir_trait_ref->get_locus (); } + + std::string get_name () const + { + rust_assert (!is_error ()); + return hir_trait_ref->get_name (); + } + + std::string as_string () const + { + if (is_error ()) + return ""; + + std::string item_buf; + for (auto &item : item_refs) + { + item_buf += item.as_string () + ", "; + } + return "HIR Trait: " + get_name () + "->" + + hir_trait_ref->get_mappings ().as_string () + " [" + item_buf + + "]"; + } + + const HIR::Trait *get_hir_trait_ref () const { return hir_trait_ref; } + + const Analysis::NodeMapping &get_mappings () const + { + return hir_trait_ref->get_mappings (); + } + + DefId get_defid () const { return get_mappings ().get_defid (); } + + bool lookup_hir_trait_item (const HIR::TraitItem &item, + TraitItemReference **ref) + { + return lookup_trait_item (item.trait_identifier (), ref); + } + + bool lookup_trait_item (const std::string &ident, TraitItemReference **ref) + { + for (auto &item : item_refs) + { + if (ident.compare (item.get_identifier ()) == 0) + { + *ref = &item; + return true; + } + } + return false; + } + + bool lookup_trait_item_by_type (const std::string &ident, + TraitItemReference::TraitItemType type, + TraitItemReference **ref) + { + for (auto &item : item_refs) + { + if (item.get_trait_item_type () != type) + continue; + + if (ident.compare (item.get_identifier ()) == 0) + { + *ref = &item; + return true; + } + } + return false; + } + + bool lookup_trait_item_by_type (const std::string &ident, + TraitItemReference::TraitItemType type, + const TraitItemReference **ref) const + { + for (auto &item : item_refs) + { + if (item.get_trait_item_type () != type) + continue; + + if (ident.compare (item.get_identifier ()) == 0) + { + *ref = &item; + return true; + } + } + return false; + } + + bool lookup_hir_trait_item (const HIR::TraitItem &item, + const TraitItemReference **ref) const + { + return lookup_trait_item (item.trait_identifier (), ref); + } + + bool lookup_trait_item (const std::string &ident, + const TraitItemReference **ref) const + { + for (auto &item : item_refs) + { + if (ident.compare (item.get_identifier ()) == 0) + { + *ref = &item; + return true; + } + } + return false; + } + + const TraitItemReference * + lookup_trait_item (const std::string &ident, + TraitItemReference::TraitItemType type) const + { + for (auto &item : item_refs) + { + if (item.get_trait_item_type () != type) + continue; + + if (ident.compare (item.get_identifier ()) == 0) + return &item; + } + return &TraitItemReference::error_node (); + } + + size_t size () const { return item_refs.size (); } + + const std::vector &get_trait_items () const + { + return item_refs; + } + + void on_resolved () + { + for (auto &item : item_refs) + { + item.on_resolved (); + } + } + + void clear_associated_types () + { + for (auto &item : item_refs) + { + bool is_assoc_type = item.get_trait_item_type () + == TraitItemReference::TraitItemType::TYPE; + if (is_assoc_type) + item.associated_type_reset (); + } + } + + bool is_equal (const TraitReference &other) const + { + DefId this_id = get_mappings ().get_defid (); + DefId other_id = other.get_mappings ().get_defid (); + return this_id == other_id; + } + + const std::vector get_super_traits () const + { + return super_traits; + } + + bool is_object_safe (bool emit_error, Location locus) const + { + // https: // doc.rust-lang.org/reference/items/traits.html#object-safety + std::vector non_object_super_traits; + for (auto &item : super_traits) + { + if (!item->is_object_safe (false, Location ())) + non_object_super_traits.push_back (item); + } + + std::vector non_object_safe_items; + for (auto &item : get_trait_items ()) + { + if (!item.is_object_safe ()) + non_object_safe_items.push_back (&item); + } + + bool is_safe + = non_object_super_traits.empty () && non_object_safe_items.empty (); + if (emit_error && !is_safe) + { + RichLocation r (locus); + for (auto &item : non_object_super_traits) + r.add_range (item->get_locus ()); + for (auto &item : non_object_safe_items) + r.add_range (item->get_locus ()); + + rust_error_at (r, "trait bound is not object safe"); + } + + return is_safe; + } + + bool trait_has_generics () const { return !trait_substs.empty (); } + + std::vector get_trait_substs () const + { + return trait_substs; + } + +private: + const HIR::Trait *hir_trait_ref; + std::vector item_refs; + std::vector super_traits; + std::vector trait_substs; +}; + +class AssociatedImplTrait +{ +public: + AssociatedImplTrait (TraitReference *trait, HIR::ImplBlock *impl, + TyTy::BaseType *self, + Resolver::TypeCheckContext *context) + : trait (trait), impl (impl), self (self), context (context) + {} + + TraitReference *get_trait () { return trait; } + + HIR::ImplBlock *get_impl_block () { return impl; } + + TyTy::BaseType *get_self () { return self; } + + void setup_associated_types (const TyTy::BaseType *self, + const TyTy::TypeBoundPredicate &bound); + + void reset_associated_types (); + +private: + TraitReference *trait; + HIR::ImplBlock *impl; + TyTy::BaseType *self; + Resolver::TypeCheckContext *context; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TRAIT_REF_H diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.cc b/gcc/rust/typecheck/rust-hir-trait-resolve.cc new file mode 100644 index 00000000000..5ad9540868c --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.cc @@ -0,0 +1,599 @@ +// Copyright (C) 2021-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-trait-resolve.h" +#include "rust-hir-type-check-expr.h" + +namespace Rust { +namespace Resolver { + +void +ResolveTraitItemToRef::visit (HIR::TraitItemType &type) +{ + // create trait-item-ref + Location locus = type.get_locus (); + bool is_optional = false; + std::string identifier = type.get_name (); + + resolved = TraitItemReference (identifier, is_optional, + TraitItemReference::TraitItemType::TYPE, &type, + self, substitutions, locus); +} + +void +ResolveTraitItemToRef::visit (HIR::TraitItemConst &cst) +{ + // create trait-item-ref + Location locus = cst.get_locus (); + bool is_optional = cst.has_expr (); + std::string identifier = cst.get_name (); + + resolved = TraitItemReference (identifier, is_optional, + TraitItemReference::TraitItemType::CONST, &cst, + self, substitutions, locus); +} + +void +ResolveTraitItemToRef::visit (HIR::TraitItemFunc &fn) +{ + // create trait-item-ref + Location locus = fn.get_locus (); + bool is_optional = fn.has_block_defined (); + std::string identifier = fn.get_decl ().get_function_name (); + + resolved = TraitItemReference (identifier, is_optional, + TraitItemReference::TraitItemType::FN, &fn, + self, std::move (substitutions), locus); +} + +ResolveTraitItemToRef::ResolveTraitItemToRef ( + TyTy::BaseType *self, + std::vector &&substitutions) + : TypeCheckBase (), resolved (TraitItemReference::error ()), self (self), + substitutions (std::move (substitutions)) +{} + +// TraitItemReference items + +TraitReference * +TraitResolver::Resolve (HIR::TypePath &path) +{ + TraitResolver resolver; + return resolver.resolve_path (path); +} + +TraitReference * +TraitResolver::Resolve (HIR::Trait &trait) +{ + TraitResolver resolver; + return resolver.resolve_trait (&trait); +} + +TraitReference * +TraitResolver::Lookup (HIR::TypePath &path) +{ + TraitResolver resolver; + return resolver.lookup_path (path); +} + +TraitResolver::TraitResolver () + : TypeCheckBase (), resolved_trait_reference (nullptr) +{} + +TraitReference * +TraitResolver::resolve_path (HIR::TypePath &path) +{ + NodeId ref; + if (!resolver->lookup_resolved_type (path.get_mappings ().get_nodeid (), + &ref)) + { + rust_error_at (path.get_locus (), "Failed to resolve path to node-id"); + return &TraitReference::error_node (); + } + + HirId hir_node = UNKNOWN_HIRID; + if (!mappings->lookup_node_to_hir (ref, &hir_node)) + { + rust_error_at (path.get_locus (), "Failed to resolve path to hir-id"); + return &TraitReference::error_node (); + } + + HIR::Item *resolved_item = mappings->lookup_hir_item (hir_node); + + rust_assert (resolved_item != nullptr); + resolved_item->accept_vis (*this); + rust_assert (resolved_trait_reference != nullptr); + + return resolve_trait (resolved_trait_reference); +} + +TraitReference * +TraitResolver::resolve_trait (HIR::Trait *trait_reference) +{ + TraitReference *tref = &TraitReference::error_node (); + if (context->lookup_trait_reference ( + trait_reference->get_mappings ().get_defid (), &tref)) + { + return tref; + } + + TyTy::BaseType *self = nullptr; + std::vector substitutions; + for (auto &generic_param : trait_reference->get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), param_type); + + auto &typaram = static_cast (*generic_param); + substitutions.push_back ( + TyTy::SubstitutionParamMapping (typaram, param_type)); + + if (typaram.get_type_representation ().compare ("Self") == 0) + { + self = param_type; + } + } + break; + } + } + rust_assert (self != nullptr); + + // Check if there is a super-trait, and apply this bound to the Self + // TypeParam + std::vector specified_bounds; + + // copy the substitition mappings + std::vector self_subst_copy; + for (auto &sub : substitutions) + self_subst_copy.push_back (sub.clone ()); + + // They also inherit themselves as a bound this enables a trait item to + // reference other Self::trait_items + auto self_hrtb + = TyTy::TypeBoundPredicate (trait_reference->get_mappings ().get_defid (), + std::move (self_subst_copy), + trait_reference->get_locus ()); + specified_bounds.push_back (self_hrtb); + + // look for any + std::vector super_traits; + if (trait_reference->has_type_param_bounds ()) + { + for (auto &bound : trait_reference->get_type_param_bounds ()) + { + if (bound->get_bound_type () + == HIR::TypeParamBound::BoundType::TRAITBOUND) + { + HIR::TraitBound *b + = static_cast (bound.get ()); + + // FIXME this might be recursive we need a check for that + auto predicate = get_predicate_from_bound (b->get_path ()); + specified_bounds.push_back (predicate); + super_traits.push_back (predicate.get ()); + } + } + } + self->inherit_bounds (specified_bounds); + + std::vector item_refs; + for (auto &item : trait_reference->get_trait_items ()) + { + // make a copy of the substs + std::vector item_subst; + for (auto &sub : substitutions) + item_subst.push_back (sub.clone ()); + + TraitItemReference trait_item_ref + = ResolveTraitItemToRef::Resolve (*item.get (), self, + std::move (item_subst)); + item_refs.push_back (std::move (trait_item_ref)); + } + + TraitReference trait_object (trait_reference, item_refs, + std::move (super_traits), + std::move (substitutions)); + context->insert_trait_reference ( + trait_reference->get_mappings ().get_defid (), std::move (trait_object)); + + tref = &TraitReference::error_node (); + bool ok = context->lookup_trait_reference ( + trait_reference->get_mappings ().get_defid (), &tref); + rust_assert (ok); + + // hook to allow the trait to resolve its optional item blocks, we cant + // resolve the blocks of functions etc because it can end up in a recursive + // loop of trying to resolve traits as required by the types + tref->on_resolved (); + + return tref; +} + +TraitReference * +TraitResolver::lookup_path (HIR::TypePath &path) +{ + NodeId ref; + if (!resolver->lookup_resolved_type (path.get_mappings ().get_nodeid (), + &ref)) + { + rust_error_at (path.get_locus (), "Failed to resolve path to node-id"); + return &TraitReference::error_node (); + } + + HirId hir_node = UNKNOWN_HIRID; + if (!mappings->lookup_node_to_hir (ref, &hir_node)) + { + rust_error_at (path.get_locus (), "Failed to resolve path to hir-id"); + return &TraitReference::error_node (); + } + + HIR::Item *resolved_item = mappings->lookup_hir_item (hir_node); + + rust_assert (resolved_item != nullptr); + resolved_item->accept_vis (*this); + rust_assert (resolved_trait_reference != nullptr); + + TraitReference *tref = &TraitReference::error_node (); + if (context->lookup_trait_reference ( + resolved_trait_reference->get_mappings ().get_defid (), &tref)) + { + return tref; + } + return &TraitReference::error_node (); +} + +void +TraitItemReference::on_resolved () +{ + switch (type) + { + case CONST: + resolve_item (static_cast (*hir_trait_item)); + break; + + case TYPE: + resolve_item (static_cast (*hir_trait_item)); + break; + + case FN: + resolve_item (static_cast (*hir_trait_item)); + break; + + default: + break; + } +} + +void +TraitItemReference::resolve_item (HIR::TraitItemType &type) +{ + TyTy::BaseType *ty + = new TyTy::PlaceholderType (type.get_name (), + type.get_mappings ().get_hirid ()); + context->insert_type (type.get_mappings (), ty); +} + +void +TraitItemReference::resolve_item (HIR::TraitItemConst &constant) +{ + // TODO +} + +void +TraitItemReference::resolve_item (HIR::TraitItemFunc &func) +{ + if (!is_optional ()) + return; + + TyTy::BaseType *item_tyty = get_tyty (); + if (item_tyty->get_kind () == TyTy::TypeKind::ERROR) + return; + + // check the block and return types + rust_assert (item_tyty->get_kind () == TyTy::TypeKind::FNDEF); + + // need to get the return type from this + TyTy::FnType *resolved_fn_type = static_cast (item_tyty); + auto expected_ret_tyty = resolved_fn_type->get_return_type (); + context->push_return_type (TypeCheckContextItem (&func), expected_ret_tyty); + + auto block_expr_ty = TypeCheckExpr::Resolve (func.get_block_expr ().get ()); + + context->pop_return_type (); + + if (block_expr_ty->get_kind () != TyTy::NEVER) + expected_ret_tyty->unify (block_expr_ty); +} + +void +TraitItemReference::associated_type_set (TyTy::BaseType *ty) const +{ + rust_assert (get_trait_item_type () == TraitItemType::TYPE); + + TyTy::BaseType *item_ty = get_tyty (); + rust_assert (item_ty->get_kind () == TyTy::TypeKind::PLACEHOLDER); + TyTy::PlaceholderType *placeholder + = static_cast (item_ty); + + placeholder->set_associated_type (ty->get_ty_ref ()); +} + +void +TraitItemReference::associated_type_reset () const +{ + rust_assert (get_trait_item_type () == TraitItemType::TYPE); + + TyTy::BaseType *item_ty = get_tyty (); + rust_assert (item_ty->get_kind () == TyTy::TypeKind::PLACEHOLDER); + TyTy::PlaceholderType *placeholder + = static_cast (item_ty); + + placeholder->clear_associated_type (); +} + +void +AssociatedImplTrait::setup_associated_types ( + const TyTy::BaseType *self, const TyTy::TypeBoundPredicate &bound) +{ + // compute the constrained impl block generic arguments based on self and the + // higher ranked trait bound + TyTy::BaseType *receiver = self->clone (); + + // impl SliceIndex<[Y]> for Range + // vs + // I: SliceIndex<[]> and Range<> + // + // we need to figure out what Y is + + TyTy::BaseType *associated_self = get_self (); + rust_assert (associated_self->can_eq (self, false)); + + // grab the parameters + HIR::ImplBlock &impl_block = *get_impl_block (); + std::vector substitutions; + for (auto &generic_param : impl_block.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + TyTy::BaseType *l = nullptr; + bool ok = context->lookup_type ( + generic_param->get_mappings ().get_hirid (), &l); + if (ok && l->get_kind () == TyTy::TypeKind::PARAM) + { + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), + static_cast (l))); + } + } + break; + } + } + + // generate inference variables for these bound arguments so we can compute + // their values + Location locus; + std::vector args; + for (auto &p : substitutions) + { + if (p.needs_substitution ()) + { + TyTy::TyVar infer_var = TyTy::TyVar::get_implicit_infer_var (locus); + args.push_back (TyTy::SubstitutionArg (&p, infer_var.get_tyty ())); + } + else + { + args.push_back ( + TyTy::SubstitutionArg (&p, p.get_param_ty ()->resolve ())); + } + } + + // this callback gives us the parameters that get substituted so we can + // compute the constrained type parameters for this impl block + std::map param_mappings; + TyTy::ParamSubstCb param_subst_cb + = [&] (const TyTy::ParamType &p, const TyTy::SubstitutionArg &a) { + param_mappings[p.get_symbol ()] = a.get_tyty ()->get_ref (); + }; + + TyTy::SubstitutionArgumentMappings infer_arguments (std::move (args), locus, + param_subst_cb); + TyTy::BaseType *impl_self_infer + = (associated_self->needs_generic_substitutions ()) + ? SubstMapperInternal::Resolve (associated_self, infer_arguments) + : associated_self; + + // FIXME this needs to do a lookup for the trait-reference DefId instead of + // assuming its the first one in the list + rust_assert (associated_self->num_specified_bounds () > 0); + TyTy::TypeBoundPredicate &impl_predicate + = associated_self->get_specified_bounds ().at (0); + + // infer the arguments on the predicate + std::vector impl_trait_predicate_args; + for (const auto &arg : impl_predicate.get_substs ()) + { + const TyTy::ParamType *p = arg.get_param_ty (); + if (p->get_symbol ().compare ("Self") == 0) + continue; + + TyTy::BaseType *r = p->resolve (); + r = SubstMapperInternal::Resolve (r, infer_arguments); + impl_trait_predicate_args.push_back (r); + } + + // we need to unify the receiver with the impl-block Self so that we compute + // the type correctly as our receiver may be generic and we are inferring its + // generic arguments and this Self might be the concrete version or vice + // versa. + auto result = receiver->unify (impl_self_infer); + rust_assert (result->get_kind () != TyTy::TypeKind::ERROR); + + // unify the bounds arguments + std::vector hrtb_bound_arguments; + for (const auto &arg : bound.get_substs ()) + { + const TyTy::ParamType *p = arg.get_param_ty (); + if (p->get_symbol ().compare ("Self") == 0) + continue; + + TyTy::BaseType *r = p->resolve (); + hrtb_bound_arguments.push_back (r); + } + + rust_assert (impl_trait_predicate_args.size () + == hrtb_bound_arguments.size ()); + for (size_t i = 0; i < impl_trait_predicate_args.size (); i++) + { + TyTy::BaseType *a = impl_trait_predicate_args.at (i); + TyTy::BaseType *b = hrtb_bound_arguments.at (i); + + result = a->unify (b); + rust_assert (result->get_kind () != TyTy::TypeKind::ERROR); + } + + // create the argument list + std::vector associated_arguments; + for (auto &p : substitutions) + { + std::string symbol = p.get_param_ty ()->get_symbol (); + auto it = param_mappings.find (symbol); + rust_assert (it != param_mappings.end ()); + + HirId id = it->second; + TyTy::BaseType *argument = nullptr; + bool ok = context->lookup_type (id, &argument); + rust_assert (ok); + + TyTy::SubstitutionArg arg (&p, argument); + associated_arguments.push_back (arg); + } + + TyTy::SubstitutionArgumentMappings associated_type_args ( + std::move (associated_arguments), locus); + + ImplTypeIterator iter (*impl, [&] (HIR::TypeAlias &type) { + TraitItemReference *resolved_trait_item = nullptr; + bool ok = trait->lookup_trait_item (type.get_new_type_name (), + &resolved_trait_item); + if (!ok) + return; + if (resolved_trait_item->get_trait_item_type () + != TraitItemReference::TraitItemType::TYPE) + return; + + TyTy::BaseType *lookup; + if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup)) + return; + + // this might be generic + TyTy::BaseType *substituted + = SubstMapperInternal::Resolve (lookup, associated_type_args); + resolved_trait_item->associated_type_set (substituted); + }); + iter.go (); +} + +void +AssociatedImplTrait::reset_associated_types () +{ + trait->clear_associated_types (); +} + +Analysis::NodeMapping +TraitItemReference::get_parent_trait_mappings () const +{ + auto mappings = Analysis::Mappings::get (); + + HIR::Trait *trait + = mappings->lookup_trait_item_mapping (get_mappings ().get_hirid ()); + rust_assert (trait != nullptr); + + return trait->get_mappings (); +} + +bool +TraitItemReference::is_object_safe () const +{ + // https://doc.rust-lang.org/reference/items/traits.html#object-safety + switch (get_trait_item_type ()) + { + case TraitItemReference::TraitItemType::FN: { + // lets be boring and just check that this is indeed a method will do + // for now + const HIR::TraitItem *item = get_hir_trait_item (); + const HIR::TraitItemFunc *fn + = static_cast (item); + return fn->get_decl ().is_method (); + } + + // constants are not available via dyn dispatch and so is not object safe + case TraitItemReference::TraitItemType::CONST: + return false; + + // types are object safe since they are not available via dyn dispatch + case TraitItemReference::TraitItemType::TYPE: + return true; + + // this is just an error so lets just fail it + case TraitItemReference::TraitItemType::ERROR: + return false; + } + return false; +} + +// rust-hir-path-probe.h + +void +PathProbeImplTrait::process_trait_impl_items_for_candidates () +{ + mappings->iterate_impl_items ( + [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool { + // just need to check if this is an impl block for this trait the next + // function checks the receiver + if (!impl->has_trait_ref ()) + return true; + + TraitReference *resolved + = TraitResolver::Lookup (*(impl->get_trait_ref ().get ())); + if (!trait_reference->is_equal (*resolved)) + return true; + + process_impl_item_candidate (id, item, impl); + return true; + }); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h new file mode 100644 index 00000000000..c4aaf42b141 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h @@ -0,0 +1,87 @@ +// Copyright (C) 2021-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TRAIT_RESOLVE_H +#define RUST_HIR_TRAIT_RESOLVE_H + +#include "rust-hir-type-check-base.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-trait-ref.h" + +namespace Rust { +namespace Resolver { + +class ResolveTraitItemToRef : public TypeCheckBase, + private HIR::HIRTraitItemVisitor +{ +public: + static TraitItemReference + Resolve (HIR::TraitItem &item, TyTy::BaseType *self, + std::vector substitutions) + { + ResolveTraitItemToRef resolver (self, std::move (substitutions)); + item.accept_vis (resolver); + return std::move (resolver.resolved); + } + + void visit (HIR::TraitItemType &type) override; + + void visit (HIR::TraitItemConst &cst) override; + + void visit (HIR::TraitItemFunc &fn) override; + +private: + ResolveTraitItemToRef ( + TyTy::BaseType *self, + std::vector &&substitutions); + + TraitItemReference resolved; + TyTy::BaseType *self; + std::vector substitutions; +}; + +class TraitResolver : public TypeCheckBase, private HIR::HIRFullVisitorBase +{ + using HIR::HIRFullVisitorBase::visit; + +public: + static TraitReference *Resolve (HIR::TypePath &path); + + static TraitReference *Resolve (HIR::Trait &trait); + + static TraitReference *Lookup (HIR::TypePath &path); + +private: + TraitResolver (); + + TraitReference *resolve_path (HIR::TypePath &path); + + TraitReference *resolve_trait (HIR::Trait *trait_reference); + + TraitReference *lookup_path (HIR::TypePath &path); + + HIR::Trait *resolved_trait_reference; + +public: + void visit (HIR::Trait &trait) override { resolved_trait_reference = &trait; } +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TRAIT_RESOLVE_H diff --git a/gcc/rust/typecheck/rust-hir-type-bounds.h b/gcc/rust/typecheck/rust-hir-type-bounds.h new file mode 100644 index 00000000000..44400efbbf7 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-bounds.h @@ -0,0 +1,77 @@ +// Copyright (C) 2021-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_BOUNDS_H +#define RUST_HIR_TYPE_BOUNDS_H + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" +#include "rust-tyty.h" + +namespace Rust { +namespace Resolver { + +class TypeBoundsProbe : public TypeCheckBase +{ +public: + static std::vector> + Probe (const TyTy::BaseType *receiver) + { + TypeBoundsProbe probe (receiver); + probe.scan (); + return probe.trait_references; + } + + static bool is_bound_satisfied_for_type (TyTy::BaseType *receiver, + TraitReference *ref) + { + for (auto &bound : receiver->get_specified_bounds ()) + { + const TraitReference *b = bound.get (); + if (b->is_equal (*ref)) + return true; + } + + std::vector> bounds + = Probe (receiver); + for (auto &bound : bounds) + { + const TraitReference *b = bound.first; + if (b->is_equal (*ref)) + return true; + } + + return false; + } + +private: + void scan (); + +private: + TypeBoundsProbe (const TyTy::BaseType *receiver) + : TypeCheckBase (), receiver (receiver) + {} + + const TyTy::BaseType *receiver; + std::vector> trait_references; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_BOUNDS_H diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc b/gcc/rust/typecheck/rust-hir-type-check-base.cc new file mode 100644 index 00000000000..ac5c3b97475 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc @@ -0,0 +1,439 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-base.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-type-check-expr.h" +#include "rust-coercion.h" +#include "rust-casts.h" + +namespace Rust { +namespace Resolver { + +bool +TypeCheckBase::check_for_unconstrained ( + const std::vector ¶ms_to_constrain, + const TyTy::SubstitutionArgumentMappings &constraint_a, + const TyTy::SubstitutionArgumentMappings &constraint_b, + const TyTy::BaseType *reference) +{ + std::set symbols_to_constrain; + std::map symbol_to_location; + for (const auto &p : params_to_constrain) + { + HirId ref = p.get_param_ty ()->get_ref (); + symbols_to_constrain.insert (ref); + symbol_to_location.insert ({ref, p.get_param_locus ()}); + } + + // set up the set of constrained symbols + std::set constrained_symbols; + for (const auto &c : constraint_a.get_mappings ()) + { + const TyTy::BaseType *arg = c.get_tyty (); + if (arg != nullptr) + { + const TyTy::BaseType *p = arg->get_root (); + constrained_symbols.insert (p->get_ty_ref ()); + } + } + for (const auto &c : constraint_b.get_mappings ()) + { + const TyTy::BaseType *arg = c.get_tyty (); + if (arg != nullptr) + { + const TyTy::BaseType *p = arg->get_root (); + constrained_symbols.insert (p->get_ty_ref ()); + } + } + + const auto root = reference->get_root (); + if (root->get_kind () == TyTy::TypeKind::PARAM) + { + const TyTy::ParamType *p = static_cast (root); + constrained_symbols.insert (p->get_ty_ref ()); + } + + // check for unconstrained + bool unconstrained = false; + for (auto &sym : symbols_to_constrain) + { + bool used = constrained_symbols.find (sym) != constrained_symbols.end (); + if (!used) + { + Location locus = symbol_to_location.at (sym); + rust_error_at (locus, "unconstrained type parameter"); + unconstrained = true; + } + } + return unconstrained; +} + +TyTy::BaseType * +TypeCheckBase::resolve_literal (const Analysis::NodeMapping &expr_mappings, + HIR::Literal &literal, Location locus) +{ + TyTy::BaseType *infered = nullptr; + switch (literal.get_lit_type ()) + { + case HIR::Literal::LitType::INT: { + bool ok = false; + + switch (literal.get_type_hint ()) + { + case CORETYPE_I8: + ok = context->lookup_builtin ("i8", &infered); + break; + case CORETYPE_I16: + ok = context->lookup_builtin ("i16", &infered); + break; + case CORETYPE_I32: + ok = context->lookup_builtin ("i32", &infered); + break; + case CORETYPE_I64: + ok = context->lookup_builtin ("i64", &infered); + break; + case CORETYPE_I128: + ok = context->lookup_builtin ("i128", &infered); + break; + + case CORETYPE_U8: + ok = context->lookup_builtin ("u8", &infered); + break; + case CORETYPE_U16: + ok = context->lookup_builtin ("u16", &infered); + break; + case CORETYPE_U32: + ok = context->lookup_builtin ("u32", &infered); + break; + case CORETYPE_U64: + ok = context->lookup_builtin ("u64", &infered); + break; + case CORETYPE_U128: + ok = context->lookup_builtin ("u128", &infered); + break; + + case CORETYPE_F32: + literal.set_lit_type (HIR::Literal::LitType::FLOAT); + ok = context->lookup_builtin ("f32", &infered); + break; + case CORETYPE_F64: + literal.set_lit_type (HIR::Literal::LitType::FLOAT); + ok = context->lookup_builtin ("f64", &infered); + break; + + case CORETYPE_ISIZE: + ok = context->lookup_builtin ("isize", &infered); + break; + + case CORETYPE_USIZE: + ok = context->lookup_builtin ("usize", &infered); + break; + + default: + ok = true; + infered + = new TyTy::InferType (expr_mappings.get_hirid (), + TyTy::InferType::InferTypeKind::INTEGRAL, + locus); + break; + } + rust_assert (ok); + } + break; + + case HIR::Literal::LitType::FLOAT: { + bool ok = false; + + switch (literal.get_type_hint ()) + { + case CORETYPE_F32: + ok = context->lookup_builtin ("f32", &infered); + break; + case CORETYPE_F64: + ok = context->lookup_builtin ("f64", &infered); + break; + + default: + ok = true; + infered + = new TyTy::InferType (expr_mappings.get_hirid (), + TyTy::InferType::InferTypeKind::FLOAT, + locus); + break; + } + rust_assert (ok); + } + break; + + case HIR::Literal::LitType::BOOL: { + auto ok = context->lookup_builtin ("bool", &infered); + rust_assert (ok); + } + break; + + case HIR::Literal::LitType::CHAR: { + auto ok = context->lookup_builtin ("char", &infered); + rust_assert (ok); + } + break; + + case HIR::Literal::LitType::BYTE: { + auto ok = context->lookup_builtin ("u8", &infered); + rust_assert (ok); + } + break; + + case HIR::Literal::LitType::STRING: { + TyTy::BaseType *base = nullptr; + auto ok = context->lookup_builtin ("str", &base); + rust_assert (ok); + + infered = new TyTy::ReferenceType (expr_mappings.get_hirid (), + TyTy::TyVar (base->get_ref ()), + Mutability::Imm); + } + break; + + case HIR::Literal::LitType::BYTE_STRING: { + /* This is an arraytype of u8 reference (&[u8;size]). It isn't in + UTF-8, but really just a byte array. Code to construct the array + reference copied from ArrayElemsValues and ArrayType. */ + TyTy::BaseType *u8; + auto ok = context->lookup_builtin ("u8", &u8); + rust_assert (ok); + + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping capacity_mapping (crate_num, UNKNOWN_NODEID, + mappings->get_next_hir_id ( + crate_num), + UNKNOWN_LOCAL_DEFID); + + /* Capacity is the size of the string (number of chars). + It is a constant, but for fold it to get a tree. */ + std::string capacity_str + = std::to_string (literal.as_string ().size ()); + HIR::LiteralExpr *literal_capacity + = new HIR::LiteralExpr (capacity_mapping, capacity_str, + HIR::Literal::LitType::INT, + PrimitiveCoreType::CORETYPE_USIZE, locus, {}); + + // mark the type for this implicit node + TyTy::BaseType *expected_ty = nullptr; + ok = context->lookup_builtin ("usize", &expected_ty); + rust_assert (ok); + context->insert_type (capacity_mapping, expected_ty); + + Analysis::NodeMapping array_mapping (crate_num, UNKNOWN_NODEID, + mappings->get_next_hir_id ( + crate_num), + UNKNOWN_LOCAL_DEFID); + + TyTy::ArrayType *array + = new TyTy::ArrayType (array_mapping.get_hirid (), locus, + *literal_capacity, + TyTy::TyVar (u8->get_ref ())); + context->insert_type (array_mapping, array); + + infered = new TyTy::ReferenceType (expr_mappings.get_hirid (), + TyTy::TyVar (array->get_ref ()), + Mutability::Imm); + } + break; + + default: + gcc_unreachable (); + break; + } + + return infered; +} + +TyTy::ADTType::ReprOptions +TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, Location locus) +{ + TyTy::ADTType::ReprOptions repr; + repr.pack = 0; + repr.align = 0; + + for (const auto &attr : attrs) + { + bool is_repr = attr.get_path ().as_string ().compare ("repr") == 0; + if (is_repr) + { + const AST::AttrInput &input = attr.get_attr_input (); + bool is_token_tree = input.get_attr_input_type () + == AST::AttrInput::AttrInputType::TOKEN_TREE; + rust_assert (is_token_tree); + const auto &option = static_cast (input); + AST::AttrInputMetaItemContainer *meta_items + = option.parse_to_meta_item (); + + const std::string inline_option + = meta_items->get_items ().at (0)->as_string (); + + // TODO: it would probably be better to make the MetaItems more aware + // of constructs with nesting like #[repr(packed(2))] rather than + // manually parsing the string "packed(2)" here. + + size_t oparen = inline_option.find ('(', 0); + bool is_pack = false, is_align = false; + unsigned char value = 1; + + if (oparen == std::string::npos) + { + is_pack = inline_option.compare ("packed") == 0; + is_align = inline_option.compare ("align") == 0; + } + + else + { + std::string rep = inline_option.substr (0, oparen); + is_pack = rep.compare ("packed") == 0; + is_align = rep.compare ("align") == 0; + + size_t cparen = inline_option.find (')', oparen); + if (cparen == std::string::npos) + { + rust_error_at (locus, "malformed attribute"); + } + + std::string value_str = inline_option.substr (oparen, cparen); + value = strtoul (value_str.c_str () + 1, NULL, 10); + } + + if (is_pack) + repr.pack = value; + else if (is_align) + repr.align = value; + + // Multiple repr options must be specified with e.g. #[repr(C, + // packed(2))]. + break; + } + } + + return repr; +} + +TyTy::BaseType * +TypeCheckBase::coercion_site (HirId id, TyTy::BaseType *expected, + TyTy::BaseType *expr, Location locus) +{ + rust_debug ("coercion_site id={%u} expected={%s} expr={%s}", id, + expected->debug_str ().c_str (), expr->debug_str ().c_str ()); + + auto context = TypeCheckContext::get (); + if (expected->get_kind () == TyTy::TypeKind::ERROR + || expr->get_kind () == TyTy::TypeKind::ERROR) + return expr; + + // can we autoderef it? + auto result = TypeCoercionRules::Coerce (expr, expected, locus); + + // the result needs to be unified + TyTy::BaseType *receiver = expr; + if (!result.is_error ()) + { + receiver = result.tyty; + } + + rust_debug ("coerce_default_unify(a={%s}, b={%s})", + receiver->debug_str ().c_str (), expected->debug_str ().c_str ()); + TyTy::BaseType *coerced = expected->unify (receiver); + context->insert_autoderef_mappings (id, std::move (result.adjustments)); + return coerced; +} + +TyTy::BaseType * +TypeCheckBase::cast_site (HirId id, TyTy::TyWithLocation from, + TyTy::TyWithLocation to, Location cast_locus) +{ + rust_debug ("cast_site id={%u} from={%s} to={%s}", id, + from.get_ty ()->debug_str ().c_str (), + to.get_ty ()->debug_str ().c_str ()); + + auto context = TypeCheckContext::get (); + if (from.get_ty ()->get_kind () == TyTy::TypeKind::ERROR + || to.get_ty ()->get_kind () == TyTy::TypeKind::ERROR) + return to.get_ty (); + + // do the cast + auto result = TypeCastRules::resolve (cast_locus, from, to); + + // we assume error has already been emitted + if (result.is_error ()) + return to.get_ty (); + + // the result needs to be unified + TyTy::BaseType *casted_result = result.tyty; + rust_debug ("cast_default_unify(a={%s}, b={%s})", + casted_result->debug_str ().c_str (), + to.get_ty ()->debug_str ().c_str ()); + TyTy::BaseType *casted = to.get_ty ()->unify (casted_result); + context->insert_cast_autoderef_mappings (id, std::move (result.adjustments)); + return casted; +} + +void +TypeCheckBase::resolve_generic_params ( + const std::vector> &generic_params, + std::vector &substitutions) +{ + for (auto &generic_param : generic_params) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + // FIXME: Skipping Lifetime completely until better + // handling. + break; + case HIR::GenericParam::GenericKind::CONST: { + auto param + = static_cast (generic_param.get ()); + auto specified_type + = TypeCheckType::Resolve (param->get_type ().get ()); + + if (param->has_default_expression ()) + { + auto expr_type = TypeCheckExpr::Resolve ( + param->get_default_expression ().get ()); + + specified_type->unify (expr_type); + } + + context->insert_type (generic_param->get_mappings (), + specified_type); + } + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.h b/gcc/rust/typecheck/rust-hir-type-check-base.h new file mode 100644 index 00000000000..aa42d9d6dfd --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-base.h @@ -0,0 +1,80 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_BASE +#define RUST_HIR_TYPE_CHECK_BASE + +#include "rust-diagnostics.h" +#include "rust-hir-type-check.h" +#include "rust-name-resolver.h" +#include "rust-hir-visitor.h" +#include "rust-hir-map.h" +#include "rust-backend.h" + +namespace Rust { +namespace Resolver { + +class TraitReference; +class TypeCheckBase +{ +public: + virtual ~TypeCheckBase () {} + + static TyTy::BaseType *coercion_site (HirId id, TyTy::BaseType *lhs, + TyTy::BaseType *rhs, + Location coercion_locus); + + static TyTy::BaseType *cast_site (HirId id, TyTy::TyWithLocation from, + TyTy::TyWithLocation to, + Location cast_locus); + +protected: + TypeCheckBase () + : mappings (Analysis::Mappings::get ()), resolver (Resolver::get ()), + context (TypeCheckContext::get ()) + {} + + TraitReference *resolve_trait_path (HIR::TypePath &); + + TyTy::TypeBoundPredicate get_predicate_from_bound (HIR::TypePath &path); + + bool check_for_unconstrained ( + const std::vector ¶ms_to_constrain, + const TyTy::SubstitutionArgumentMappings &constraint_a, + const TyTy::SubstitutionArgumentMappings &constraint_b, + const TyTy::BaseType *reference); + + TyTy::BaseType *resolve_literal (const Analysis::NodeMapping &mappings, + HIR::Literal &literal, Location locus); + + TyTy::ADTType::ReprOptions parse_repr_options (const AST::AttrVec &attrs, + Location locus); + + void resolve_generic_params ( + const std::vector> &generic_params, + std::vector &substitutions); + + Analysis::Mappings *mappings; + Resolver *resolver; + TypeCheckContext *context; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_BASE diff --git a/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc b/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc new file mode 100644 index 00000000000..e65b2011d36 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc @@ -0,0 +1,213 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-full.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-enumitem.h" + +namespace Rust { +namespace Resolver { + +TyTy::VariantDef * +TypeCheckEnumItem::Resolve (HIR::EnumItem *item, int64_t last_discriminant) +{ + TypeCheckEnumItem resolver (last_discriminant); + switch (item->get_enum_item_kind ()) + { + case HIR::EnumItem::EnumItemKind::Named: + resolver.visit (static_cast (*item)); + break; + + case HIR::EnumItem::EnumItemKind::Tuple: + resolver.visit (static_cast (*item)); + break; + + case HIR::EnumItem::EnumItemKind::Struct: + resolver.visit (static_cast (*item)); + break; + + case HIR::EnumItem::EnumItemKind::Discriminant: + resolver.visit (static_cast (*item)); + break; + } + return resolver.variant; +} + +TypeCheckEnumItem::TypeCheckEnumItem (int64_t last_discriminant) + : TypeCheckBase (), variant (nullptr), last_discriminant (last_discriminant) +{} + +void +TypeCheckEnumItem::visit (HIR::EnumItem &item) +{ + if (last_discriminant == INT64_MAX) + rust_error_at (item.get_locus (), "discriminant too big"); + + Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (), + item.get_mappings ().get_nodeid (), + mappings->get_next_hir_id ( + item.get_mappings ().get_crate_num ()), + item.get_mappings ().get_local_defid ()); + HIR::LiteralExpr *discim_expr + = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant), + HIR::Literal::LitType::INT, + PrimitiveCoreType::CORETYPE_I64, item.get_locus (), + {}); + + TyTy::BaseType *isize = nullptr; + bool ok = context->lookup_builtin ("isize", &isize); + rust_assert (ok); + context->insert_type (mapping, isize); + + const CanonicalPath *canonical_path = nullptr; + ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, item.get_locus ()}; + variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (), + item.get_identifier (), ident, discim_expr); +} + +void +TypeCheckEnumItem::visit (HIR::EnumItemDiscriminant &item) +{ + if (last_discriminant == INT64_MAX) + rust_error_at (item.get_locus (), "discriminant too big"); + + auto &discriminant = item.get_discriminant_expression (); + auto capacity_type = TypeCheckExpr::Resolve (discriminant.get ()); + if (capacity_type->get_kind () == TyTy::TypeKind::ERROR) + return; + + TyTy::ISizeType *expected_ty + = new TyTy::ISizeType (discriminant->get_mappings ().get_hirid ()); + context->insert_type (discriminant->get_mappings (), expected_ty); + + auto unified = expected_ty->unify (capacity_type); + if (unified->get_kind () == TyTy::TypeKind::ERROR) + return; + + const CanonicalPath *canonical_path = nullptr; + bool ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, item.get_locus ()}; + variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (), + item.get_identifier (), ident, + item.get_discriminant_expression ().get ()); +} + +void +TypeCheckEnumItem::visit (HIR::EnumItemTuple &item) +{ + if (last_discriminant == INT64_MAX) + rust_error_at (item.get_locus (), "discriminant too big"); + + std::vector fields; + size_t idx = 0; + for (auto &field : item.get_tuple_fields ()) + { + TyTy::BaseType *field_type + = TypeCheckType::Resolve (field.get_field_type ().get ()); + TyTy::StructFieldType *ty_field + = new TyTy::StructFieldType (field.get_mappings ().get_hirid (), + std::to_string (idx), field_type); + fields.push_back (ty_field); + context->insert_type (field.get_mappings (), ty_field->get_field_type ()); + idx++; + } + + Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (), + item.get_mappings ().get_nodeid (), + mappings->get_next_hir_id ( + item.get_mappings ().get_crate_num ()), + item.get_mappings ().get_local_defid ()); + HIR::LiteralExpr *discim_expr + = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant), + HIR::Literal::LitType::INT, + PrimitiveCoreType::CORETYPE_I64, item.get_locus (), + {}); + + TyTy::BaseType *isize = nullptr; + bool ok = context->lookup_builtin ("isize", &isize); + rust_assert (ok); + context->insert_type (mapping, isize); + + const CanonicalPath *canonical_path = nullptr; + ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, item.get_locus ()}; + variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (), + item.get_identifier (), ident, + TyTy::VariantDef::VariantType::TUPLE, + discim_expr, fields); +} + +void +TypeCheckEnumItem::visit (HIR::EnumItemStruct &item) +{ + if (last_discriminant == INT64_MAX) + rust_error_at (item.get_locus (), "discriminant too big"); + + std::vector fields; + for (auto &field : item.get_struct_fields ()) + { + TyTy::BaseType *field_type + = TypeCheckType::Resolve (field.get_field_type ().get ()); + TyTy::StructFieldType *ty_field + = new TyTy::StructFieldType (field.get_mappings ().get_hirid (), + field.get_field_name (), field_type); + fields.push_back (ty_field); + context->insert_type (field.get_mappings (), ty_field->get_field_type ()); + } + + Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (), + item.get_mappings ().get_nodeid (), + mappings->get_next_hir_id ( + item.get_mappings ().get_crate_num ()), + item.get_mappings ().get_local_defid ()); + HIR::LiteralExpr *discrim_expr + = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant), + HIR::Literal::LitType::INT, + PrimitiveCoreType::CORETYPE_I64, item.get_locus (), + {}); + + TyTy::BaseType *isize = nullptr; + bool ok = context->lookup_builtin ("isize", &isize); + rust_assert (ok); + context->insert_type (mapping, isize); + + const CanonicalPath *canonical_path = nullptr; + ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, item.get_locus ()}; + variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (), + item.get_identifier (), ident, + TyTy::VariantDef::VariantType::STRUCT, + discrim_expr, fields); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-enumitem.h b/gcc/rust/typecheck/rust-hir-type-check-enumitem.h new file mode 100644 index 00000000000..c771ea3782d --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-enumitem.h @@ -0,0 +1,50 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_ENUMITEM +#define RUST_HIR_TYPE_CHECK_ENUMITEM + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckEnumItem : public TypeCheckBase +{ +public: + static TyTy::VariantDef *Resolve (HIR::EnumItem *item, + int64_t last_discriminant); + +protected: + void visit (HIR::EnumItem &item); + void visit (HIR::EnumItemDiscriminant &item); + void visit (HIR::EnumItemTuple &item); + void visit (HIR::EnumItemStruct &item); + +private: + TypeCheckEnumItem (int64_t last_discriminant); + + TyTy::VariantDef *variant; + int64_t last_discriminant; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_ENUMITEM diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc new file mode 100644 index 00000000000..4371f5a59a5 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc @@ -0,0 +1,1567 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#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 (); + rust_assert (fn_return_tyty != nullptr); + + TyTy::BaseType *expr_ty + = expr.has_return_expr () + ? TypeCheckExpr::Resolve (expr.get_expr ()) + : TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); + + infered = fn_return_tyty->unify (expr_ty); + fn_return_tyty->append_reference (expr_ty->get_ref ()); + for (auto &ref : infered->get_combined_refs ()) + fn_return_tyty->append_reference (ref); + + infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); +} + +void +TypeCheckExpr::visit (HIR::CallExpr &expr) +{ + TyTy::BaseType *function_tyty = TypeCheckExpr::Resolve (expr.get_fnexpr ()); + + bool valid_tyty = function_tyty->get_kind () == TyTy::TypeKind::ADT + || function_tyty->get_kind () == TyTy::TypeKind::FNDEF + || function_tyty->get_kind () == TyTy::TypeKind::FNPTR; + if (!valid_tyty) + { + rust_error_at (expr.get_locus (), + "Failed to resolve expression of function call"); + return; + } + + TyTy::VariantDef &variant = TyTy::VariantDef::get_error_node (); + if (function_tyty->get_kind () == TyTy::TypeKind::ADT) + { + TyTy::ADTType *adt = static_cast (function_tyty); + if (adt->is_enum ()) + { + // lookup variant id + HirId variant_id; + bool ok = context->lookup_variant_definition ( + expr.get_fnexpr ()->get_mappings ().get_hirid (), &variant_id); + rust_assert (ok); + + TyTy::VariantDef *lookup_variant = nullptr; + ok = adt->lookup_variant_by_id (variant_id, &lookup_variant); + rust_assert (ok); + + variant = *lookup_variant; + } + else + { + rust_assert (adt->number_of_variants () == 1); + variant = *adt->get_variants ().at (0); + } + } + + infered = TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context); +} + +void +TypeCheckExpr::visit (HIR::AssignmentExpr &expr) +{ + infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); + + auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ()); + auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ()); + + coercion_site (expr.get_mappings ().get_hirid (), lhs, rhs, + expr.get_locus ()); +} + +void +TypeCheckExpr::visit (HIR::CompoundAssignmentExpr &expr) +{ + infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); + + auto lhs = TypeCheckExpr::Resolve (expr.get_left_expr ().get ()); + auto rhs = TypeCheckExpr::Resolve (expr.get_right_expr ().get ()); + + // we dont care about the result of the unify from a compound assignment + // since this is a unit-type expr + auto result = lhs->unify (rhs); + if (result->get_kind () == TyTy::TypeKind::ERROR) + return; + + auto lang_item_type + = Analysis::RustLangItem::CompoundAssignmentOperatorToLangItem ( + expr.get_expr_type ()); + bool operator_overloaded + = resolve_operator_overload (lang_item_type, expr, lhs, rhs); + if (operator_overloaded) + return; + + bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ()); + bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ()); + bool valid = valid_lhs && valid_rhs; + if (!valid) + { + rust_error_at (expr.get_locus (), + "cannot apply this operator to types %s and %s", + lhs->as_string ().c_str (), rhs->as_string ().c_str ()); + return; + } +} + +void +TypeCheckExpr::visit (HIR::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 = lhs->unify (rhs); + break; + } +} + +void +TypeCheckExpr::visit (HIR::ComparisonExpr &expr) +{ + auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ()); + auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ()); + + auto result = lhs->unify (rhs); + if (result == nullptr || result->get_kind () == TyTy::TypeKind::ERROR) + return; + + bool ok = context->lookup_builtin ("bool", &infered); + rust_assert (ok); +} + +void +TypeCheckExpr::visit (HIR::LazyBooleanExpr &expr) +{ + auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ()); + auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ()); + + // we expect the lhs and rhs must be bools at this point + TyTy::BoolType elhs (expr.get_mappings ().get_hirid ()); + lhs = elhs.unify (lhs); + if (lhs->get_kind () == TyTy::TypeKind::ERROR) + return; + + TyTy::BoolType rlhs (expr.get_mappings ().get_hirid ()); + rhs = elhs.unify (rhs); + if (lhs->get_kind () == TyTy::TypeKind::ERROR) + return; + + infered = lhs->unify (rhs); +} + +void +TypeCheckExpr::visit (HIR::NegationExpr &expr) +{ + auto negated_expr_ty = TypeCheckExpr::Resolve (expr.get_expr ().get ()); + + // check for operator overload + auto lang_item_type = Analysis::RustLangItem::NegationOperatorToLangItem ( + expr.get_expr_type ()); + bool operator_overloaded + = resolve_operator_overload (lang_item_type, expr, negated_expr_ty, + nullptr); + if (operator_overloaded) + return; + + // https://doc.rust-lang.org/reference/expressions/operator-expr.html#negation-operators + switch (expr.get_expr_type ()) + { + case NegationOperator::NEGATE: { + bool valid + = (negated_expr_ty->get_kind () == TyTy::TypeKind::INT) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::FLOAT) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::ISIZE) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::USIZE) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER + && (((TyTy::InferType *) negated_expr_ty)->get_infer_kind () + == TyTy::InferType::INTEGRAL)) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER + && (((TyTy::InferType *) negated_expr_ty)->get_infer_kind () + == TyTy::InferType::FLOAT)); + if (!valid) + { + rust_error_at (expr.get_locus (), "cannot apply unary - to %s", + negated_expr_ty->as_string ().c_str ()); + return; + } + } + break; + + case NegationOperator::NOT: { + bool valid + = (negated_expr_ty->get_kind () == TyTy::TypeKind::BOOL) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::INT) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT) + || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER + && (((TyTy::InferType *) negated_expr_ty)->get_infer_kind () + == TyTy::InferType::INTEGRAL)); + if (!valid) + { + rust_error_at (expr.get_locus (), "cannot apply unary % to %s", + negated_expr_ty->as_string ().c_str ()); + return; + } + } + break; + } + + infered = negated_expr_ty->clone (); + infered->append_reference (negated_expr_ty->get_ref ()); +} + +void +TypeCheckExpr::visit (HIR::IfExpr &expr) +{ + TypeCheckExpr::Resolve (expr.get_if_condition ()); + TypeCheckExpr::Resolve (expr.get_if_block ()); + + infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); +} + +void +TypeCheckExpr::visit (HIR::IfExprConseqElse &expr) +{ + TypeCheckExpr::Resolve (expr.get_if_condition ()); + auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ()); + auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_else_block ()); + + if (if_blk_resolved->get_kind () == TyTy::NEVER) + infered = else_blk_resolved; + else if (else_blk_resolved->get_kind () == TyTy::NEVER) + infered = if_blk_resolved; + else + infered = if_blk_resolved->unify (else_blk_resolved); +} + +void +TypeCheckExpr::visit (HIR::IfExprConseqIf &expr) +{ + TypeCheckExpr::Resolve (expr.get_if_condition ()); + auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ()); + auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_conseq_if_expr ()); + + if (if_blk_resolved->get_kind () == TyTy::NEVER) + infered = else_blk_resolved; + else if (else_blk_resolved->get_kind () == TyTy::NEVER) + infered = if_blk_resolved; + else + infered = if_blk_resolved->unify (else_blk_resolved); +} + +void +TypeCheckExpr::visit (HIR::IfLetExpr &expr) +{ + // this needs to perform a least upper bound coercion on the blocks and then + // unify the scruintee and arms + TyTy::BaseType *scrutinee_tyty + = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ()); + + for (auto &pattern : expr.get_patterns ()) + { + TyTy::BaseType *kase_arm_ty + = TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty); + + TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty); + if (checked_kase->get_kind () == TyTy::TypeKind::ERROR) + return; + } + + TypeCheckExpr::Resolve (expr.get_if_block ()); + + infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); +} + +void +TypeCheckExpr::visit (HIR::UnsafeBlockExpr &expr) +{ + infered = TypeCheckExpr::Resolve (expr.get_block_expr ().get ()); +} + +void +TypeCheckExpr::visit (HIR::BlockExpr &expr) +{ + for (auto &s : expr.get_statements ()) + { + if (!s->is_item ()) + continue; + + TypeCheckStmt::Resolve (s.get ()); + } + + for (auto &s : expr.get_statements ()) + { + if (s->is_item ()) + continue; + + auto resolved = TypeCheckStmt::Resolve (s.get ()); + if (resolved == nullptr) + { + rust_error_at (s->get_locus (), "failure to resolve type"); + return; + } + + if (s->is_unit_check_needed () && !resolved->is_unit ()) + { + auto unit + = TyTy::TupleType::get_unit_type (s->get_mappings ().get_hirid ()); + resolved = unit->unify (resolved); + } + } + + if (expr.has_expr ()) + infered = TypeCheckExpr::Resolve (expr.get_final_expr ().get ())->clone (); + else if (expr.is_tail_reachable ()) + infered + = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); + else + infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); +} + +void +TypeCheckExpr::visit (HIR::RangeFromToExpr &expr) +{ + auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE; + + DefId respective_lang_item_id = UNKNOWN_DEFID; + bool lang_item_defined + = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id); + + // we need to have it maybe + if (!lang_item_defined) + { + rust_internal_error_at ( + expr.get_locus (), "unable to find relevant lang item: %s", + Analysis::RustLangItem::ToString (lang_item_type).c_str ()); + return; + } + + // look it up and it _must_ be a struct definition + HIR::Item *item = mappings->lookup_defid (respective_lang_item_id); + rust_assert (item != nullptr); + + TyTy::BaseType *item_type = nullptr; + bool ok + = context->lookup_type (item->get_mappings ().get_hirid (), &item_type); + rust_assert (ok); + rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast (item_type); + + // this is a single generic item lets assert that + rust_assert (adt->get_num_substitutions () == 1); + + // resolve the range expressions and these types must unify then we use that + // type to substitute into the ADT + TyTy::BaseType *from_ty + = TypeCheckExpr::Resolve (expr.get_from_expr ().get ()); + TyTy::BaseType *to_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ()); + TyTy::BaseType *unified = from_ty->unify (to_ty); + + // substitute it in + std::vector 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 = from_ty->unify (to_ty); + + // 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) + { + auto resolved_index_expr = size_ty->unify (index_expr_ty); + if (resolved_index_expr->get_kind () == TyTy::TypeKind::ERROR) + return; + + TyTy::ArrayType *array_type + = static_cast (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); + + auto unified = expected_ty->unify (capacity_type); + if (unified->get_kind () == TyTy::TypeKind::ERROR) + return; + + capacity_expr = elems.get_num_copies_expr (); + } + break; + + case HIR::ArrayElems::ArrayExprType::VALUES: { + HIR::ArrayElemsValues &elems + = static_cast (elements); + + std::vector types; + for (auto &elem : elems.get_values ()) + { + types.push_back (TypeCheckExpr::Resolve (elem.get ())); + } + + element_type + = TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()).get_tyty (); + for (auto &type : types) + { + element_type = element_type->unify (type); + } + + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping mapping (crate_num, UNKNOWN_NODEID, + mappings->get_next_hir_id (crate_num), + UNKNOWN_LOCAL_DEFID); + std::string capacity_str = std::to_string (elems.get_num_elements ()); + capacity_expr = new HIR::LiteralExpr (mapping, capacity_str, + HIR::Literal::LitType::INT, + PrimitiveCoreType::CORETYPE_USIZE, + Location (), {}); + + // mark the type for this implicit node + TyTy::BaseType *expected_ty = nullptr; + bool ok = context->lookup_builtin ("usize", &expected_ty); + rust_assert (ok); + context->insert_type (mapping, expected_ty); + } + break; + } + + infered = new TyTy::ArrayType (expr.get_mappings ().get_hirid (), + expr.get_locus (), *capacity_expr, + TyTy::TyVar (element_type->get_ref ())); +} + +// empty struct +void +TypeCheckExpr::visit (HIR::StructExprStruct &struct_expr) +{ + TyTy::BaseType *struct_path_ty + = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ()); + if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT) + { + rust_error_at (struct_expr.get_struct_name ().get_locus (), + "expected an ADT type for constructor"); + return; + } + + infered = struct_path_ty; +} + +void +TypeCheckExpr::visit (HIR::StructExprStructFields &struct_expr) +{ + infered = TypeCheckStructExpr::Resolve (&struct_expr); +} + +void +TypeCheckExpr::visit (HIR::GroupedExpr &expr) +{ + infered = TypeCheckExpr::Resolve (expr.get_expr_in_parens ().get ()); +} + +void +TypeCheckExpr::visit (HIR::FieldAccessExpr &expr) +{ + auto struct_base = TypeCheckExpr::Resolve (expr.get_receiver_expr ().get ()); + + // FIXME does this require autoderef here? + if (struct_base->get_kind () == TyTy::TypeKind::REF) + { + TyTy::ReferenceType *r = static_cast (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 candidate + = MethodResolver::Probe (receiver_tyty, + expr.get_method_name ().get_segment ()); + if (candidate.is_error ()) + { + rust_error_at ( + expr.get_method_name ().get_locus (), + "failed to resolve method for %<%s%>", + expr.get_method_name ().get_segment ().as_string ().c_str ()); + return; + } + + // Get the adjusted self + Adjuster adj (receiver_tyty); + TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments); + + // store the adjustments for code-generation to know what to do which must be + // stored onto the receiver to so as we don't trigger duplicate deref mappings + // ICE when an argument is a method call + HirId autoderef_mappings_id + = expr.get_receiver ()->get_mappings ().get_hirid (); + context->insert_autoderef_mappings (autoderef_mappings_id, + std::move (candidate.adjustments)); + + PathProbeCandidate &resolved_candidate = candidate.candidate; + TyTy::BaseType *lookup_tyty = candidate.candidate.ty; + NodeId resolved_node_id + = resolved_candidate.is_impl_candidate () + ? resolved_candidate.item.impl.impl_item->get_impl_mappings () + .get_nodeid () + : resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid (); + + if (lookup_tyty->get_kind () != TyTy::TypeKind::FNDEF) + { + RichLocation r (expr.get_method_name ().get_locus ()); + r.add_range (resolved_candidate.locus); + rust_error_at (r, "associated impl item is not a method"); + return; + } + + TyTy::BaseType *lookup = lookup_tyty; + TyTy::FnType *fn = static_cast (lookup); + if (!fn->is_method ()) + { + RichLocation r (expr.get_method_name ().get_locus ()); + r.add_range (resolved_candidate.locus); + rust_error_at (r, "associated function is not a method"); + return; + } + + auto root = receiver_tyty->get_root (); + if (root->get_kind () == TyTy::TypeKind::ADT) + { + const TyTy::ADTType *adt = static_cast (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 (); + lookup + = SubstMapper::Resolve (lookup, expr.get_method_name ().get_locus (), + &args); + if (lookup->get_kind () == TyTy::TypeKind::ERROR) + return; + } + else if (lookup->needs_generic_substitutions ()) + { + lookup = SubstMapper::InferSubst (lookup, + expr.get_method_name ().get_locus ()); + } + + TyTy::BaseType *function_ret_tyty + = TyTy::TypeCheckMethodCallExpr::go (lookup, expr, adjusted_self, context); + if (function_ret_tyty == nullptr + || function_ret_tyty->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (expr.get_locus (), + "failed to lookup type to MethodCallExpr"); + return; + } + + // store the expected fntype + context->insert_type (expr.get_method_name ().get_mappings (), lookup); + + // set up the resolved name on the path + resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (), + resolved_node_id); + + // return the result of the function back + infered = function_ret_tyty; +} + +void +TypeCheckExpr::visit (HIR::LoopExpr &expr) +{ + context->push_new_loop_context (expr.get_mappings ().get_hirid (), + expr.get_locus ()); + TyTy::BaseType *block_expr + = TypeCheckExpr::Resolve (expr.get_loop_block ().get ()); + if (!block_expr->is_unit ()) + { + rust_error_at (expr.get_loop_block ()->get_locus (), + "expected %<()%> got %s", + block_expr->as_string ().c_str ()); + return; + } + + TyTy::BaseType *loop_context_type = context->pop_loop_context (); + + bool loop_context_type_infered + = (loop_context_type->get_kind () != TyTy::TypeKind::INFER) + || ((loop_context_type->get_kind () == TyTy::TypeKind::INFER) + && (((TyTy::InferType *) loop_context_type)->get_infer_kind () + != TyTy::InferType::GENERAL)); + + infered + = loop_context_type_infered + ? loop_context_type + : TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); +} + +void +TypeCheckExpr::visit (HIR::WhileLoopExpr &expr) +{ + context->push_new_while_loop_context (expr.get_mappings ().get_hirid ()); + + TypeCheckExpr::Resolve (expr.get_predicate_expr ().get ()); + TyTy::BaseType *block_expr + = TypeCheckExpr::Resolve (expr.get_loop_block ().get ()); + + if (!block_expr->is_unit ()) + { + rust_error_at (expr.get_loop_block ()->get_locus (), + "expected %<()%> got %s", + block_expr->as_string ().c_str ()); + return; + } + + context->pop_loop_context (); + infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); +} + +void +TypeCheckExpr::visit (HIR::BreakExpr &expr) +{ + if (!context->have_loop_context ()) + { + rust_error_at (expr.get_locus (), "cannot % 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 = loop_context->unify (break_expr_tyty); + context->swap_head_loop_context (unified_ty); + } + + infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ()); +} + +void +TypeCheckExpr::visit (HIR::ContinueExpr &expr) +{ + if (!context->have_loop_context ()) + { + rust_error_at (expr.get_locus (), + "cannot % 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; + } + } + + if (expr.get_is_double_borrow ()) + { + // FIXME double_reference + gcc_unreachable (); + } + + infered = new TyTy::ReferenceType (expr.get_mappings ().get_hirid (), + TyTy::TyVar (resolved_base->get_ref ()), + expr.get_mut ()); +} + +void +TypeCheckExpr::visit (HIR::DereferenceExpr &expr) +{ + TyTy::BaseType *resolved_base + = TypeCheckExpr::Resolve (expr.get_expr ().get ()); + + auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF; + bool operator_overloaded + = resolve_operator_overload (lang_item_type, expr, resolved_base, nullptr); + if (operator_overloaded) + { + // operator overloaded deref always refurns a reference type lets assert + // this + rust_assert (infered->get_kind () == TyTy::TypeKind::REF); + resolved_base = infered; + } + + bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF + || resolved_base->get_kind () == TyTy::TypeKind::POINTER; + if (!is_valid_type) + { + rust_error_at (expr.get_locus (), "expected reference type got %s", + resolved_base->as_string ().c_str ()); + return; + } + + if (resolved_base->get_kind () == TyTy::TypeKind::REF) + { + TyTy::ReferenceType *ref_base + = static_cast (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 = scrutinee_tyty->unify (kase_arm_ty); + if (checked_kase->get_kind () == TyTy::TypeKind::ERROR) + return; + } + + // check the kase type + TyTy::BaseType *kase_block_ty + = TypeCheckExpr::Resolve (kase.get_expr ().get ()); + kase_block_tys.push_back (kase_block_ty); + } + + if (kase_block_tys.size () == 0) + { + infered + = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ()); + return; + } + + infered = kase_block_tys.at (0); + for (size_t i = 1; i < kase_block_tys.size (); i++) + { + TyTy::BaseType *kase_ty = kase_block_tys.at (i); + infered = infered->unify (kase_ty); + if (infered->get_kind () == TyTy::TypeKind::ERROR) + return; + } +} + +bool +TypeCheckExpr::resolve_operator_overload ( + Analysis::RustLangItem::ItemType lang_item_type, HIR::OperatorExprMeta expr, + TyTy::BaseType *lhs, TyTy::BaseType *rhs) +{ + // look up lang item for arithmetic type + std::string associated_item_name + = Analysis::RustLangItem::ToString (lang_item_type); + DefId respective_lang_item_id = UNKNOWN_DEFID; + bool lang_item_defined + = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id); + + // probe for the lang-item + if (!lang_item_defined) + return false; + + auto segment = HIR::PathIdentSegment (associated_item_name); + auto candidate + = MethodResolver::Probe (lhs, HIR::PathIdentSegment (associated_item_name)); + + bool have_implementation_for_lang_item = !candidate.is_error (); + if (!have_implementation_for_lang_item) + return false; + + // Get the adjusted self + Adjuster adj (lhs); + TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments); + + // is this the case we are recursive + // handle the case where we are within the impl block for this lang_item + // otherwise we end up with a recursive operator overload such as the i32 + // operator overload trait + TypeCheckContextItem &fn_context = context->peek_context (); + if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM) + { + auto &impl_item = fn_context.get_impl_item (); + HIR::ImplBlock *parent = impl_item.first; + HIR::Function *fn = impl_item.second; + + if (parent->has_trait_ref () + && fn->get_function_name ().compare (associated_item_name) == 0) + { + TraitReference *trait_reference + = TraitResolver::Lookup (*parent->get_trait_ref ().get ()); + if (!trait_reference->is_error ()) + { + TyTy::BaseType *lookup = nullptr; + bool ok = context->lookup_type (fn->get_mappings ().get_hirid (), + &lookup); + rust_assert (ok); + rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); + + TyTy::FnType *fntype = static_cast (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 ()); + + 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); + fnparam.second->unify (adjusted_self); // typecheck the self + if (rhs == nullptr) + { + rust_assert (type->num_params () == 1); + } + else + { + rust_assert (type->num_params () == 2); + auto fnparam = type->param_at (1); + fnparam.second->unify (rhs); // typecheck the rhs + } + + rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF); + fn = static_cast (lookup); + fn->monomorphize (); + + // get the return type + TyTy::BaseType *function_ret_tyty + = type->get_return_type ()->monomorphized_clone (); + + // store the expected fntype + context->insert_operator_overload (expr.get_mappings ().get_hirid (), type); + + // set up the resolved name on the path + resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (), + resolved_node_id); + + // return the result of the function back + infered = function_ret_tyty; + + return true; +} + +bool +TypeCheckExpr::validate_arithmetic_type ( + const TyTy::BaseType *tyty, HIR::ArithmeticOrLogicalExpr::ExprType expr_type) +{ + const TyTy::BaseType *type = tyty->destructure (); + + // https://doc.rust-lang.org/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators + // this will change later when traits are added + switch (expr_type) + { + case ArithmeticOrLogicalOperator::ADD: + case ArithmeticOrLogicalOperator::SUBTRACT: + case ArithmeticOrLogicalOperator::MULTIPLY: + case ArithmeticOrLogicalOperator::DIVIDE: + case ArithmeticOrLogicalOperator::MODULUS: + return (type->get_kind () == TyTy::TypeKind::INT) + || (type->get_kind () == TyTy::TypeKind::UINT) + || (type->get_kind () == TyTy::TypeKind::FLOAT) + || (type->get_kind () == TyTy::TypeKind::USIZE) + || (type->get_kind () == TyTy::TypeKind::ISIZE) + || (type->get_kind () == TyTy::TypeKind::INFER + && (((const TyTy::InferType *) type)->get_infer_kind () + == TyTy::InferType::INTEGRAL)) + || (type->get_kind () == TyTy::TypeKind::INFER + && (((const TyTy::InferType *) type)->get_infer_kind () + == TyTy::InferType::FLOAT)); + + // integers or bools + case ArithmeticOrLogicalOperator::BITWISE_AND: + case ArithmeticOrLogicalOperator::BITWISE_OR: + case ArithmeticOrLogicalOperator::BITWISE_XOR: + return (type->get_kind () == TyTy::TypeKind::INT) + || (type->get_kind () == TyTy::TypeKind::UINT) + || (type->get_kind () == TyTy::TypeKind::USIZE) + || (type->get_kind () == TyTy::TypeKind::ISIZE) + || (type->get_kind () == TyTy::TypeKind::BOOL) + || (type->get_kind () == TyTy::TypeKind::INFER + && (((const TyTy::InferType *) type)->get_infer_kind () + == TyTy::InferType::INTEGRAL)); + + // integers only + case ArithmeticOrLogicalOperator::LEFT_SHIFT: + case ArithmeticOrLogicalOperator::RIGHT_SHIFT: + return (type->get_kind () == TyTy::TypeKind::INT) + || (type->get_kind () == TyTy::TypeKind::UINT) + || (type->get_kind () == TyTy::TypeKind::USIZE) + || (type->get_kind () == TyTy::TypeKind::ISIZE) + || (type->get_kind () == TyTy::TypeKind::INFER + && (((const TyTy::InferType *) type)->get_infer_kind () + == TyTy::InferType::INTEGRAL)); + } + + gcc_unreachable (); + return false; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h new file mode 100644 index 00000000000..19a6c791a9d --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h @@ -0,0 +1,131 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_EXPR +#define RUST_HIR_TYPE_CHECK_EXPR + +#include "rust-hir-type-check-base.h" +#include "rust-tyty.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckExpr : public TypeCheckBase, private HIR::HIRExpressionVisitor +{ +public: + static TyTy::BaseType *Resolve (HIR::Expr *expr); + + void visit (HIR::TupleIndexExpr &expr) override; + void visit (HIR::TupleExpr &expr) override; + void visit (HIR::ReturnExpr &expr) override; + void visit (HIR::CallExpr &expr) override; + void visit (HIR::MethodCallExpr &expr) override; + void visit (HIR::AssignmentExpr &expr) override; + void visit (HIR::CompoundAssignmentExpr &expr) override; + void visit (HIR::LiteralExpr &expr) override; + void visit (HIR::ArithmeticOrLogicalExpr &expr) override; + void visit (HIR::ComparisonExpr &expr) override; + void visit (HIR::LazyBooleanExpr &expr) override; + void visit (HIR::NegationExpr &expr) override; + void visit (HIR::IfExpr &expr) override; + void visit (HIR::IfExprConseqElse &expr) override; + void visit (HIR::IfExprConseqIf &expr) override; + void visit (HIR::IfLetExpr &expr) override; + void visit (HIR::BlockExpr &expr) override; + void visit (HIR::UnsafeBlockExpr &expr) override; + void visit (HIR::ArrayIndexExpr &expr) override; + void visit (HIR::ArrayExpr &expr) override; + void visit (HIR::StructExprStruct &struct_expr) override; + void visit (HIR::StructExprStructFields &struct_expr) override; + void visit (HIR::GroupedExpr &expr) override; + void visit (HIR::FieldAccessExpr &expr) override; + void visit (HIR::QualifiedPathInExpression &expr) override; + void visit (HIR::PathInExpression &expr) override; + void visit (HIR::LoopExpr &expr) override; + void visit (HIR::BreakExpr &expr) override; + void visit (HIR::ContinueExpr &expr) override; + void visit (HIR::BorrowExpr &expr) override; + void visit (HIR::DereferenceExpr &expr) override; + void visit (HIR::TypeCastExpr &expr) override; + void visit (HIR::MatchExpr &expr) override; + void visit (HIR::RangeFromToExpr &expr) override; + void visit (HIR::RangeFromExpr &expr) override; + void visit (HIR::RangeToExpr &expr) override; + void visit (HIR::RangeFullExpr &expr) override; + void visit (HIR::RangeFromToInclExpr &expr) override; + void visit (HIR::WhileLoopExpr &expr) override; + + // TODO + void visit (HIR::ClosureExprInnerTyped &) override {} + void visit (HIR::ClosureExprInner &expr) override {} + void visit (HIR::ErrorPropagationExpr &expr) override {} + void visit (HIR::RangeToInclExpr &expr) override {} + void visit (HIR::WhileLetLoopExpr &expr) override {} + void visit (HIR::ForLoopExpr &expr) override {} + void visit (HIR::IfExprConseqIfLet &expr) override {} + void visit (HIR::IfLetExprConseqElse &expr) override {} + void visit (HIR::IfLetExprConseqIf &expr) override {} + void visit (HIR::IfLetExprConseqIfLet &expr) override {} + void visit (HIR::AwaitExpr &expr) override {} + void visit (HIR::AsyncBlockExpr &expr) override {} + + // don't need to implement these see rust-hir-type-check-struct-field.h + void visit (HIR::StructExprFieldIdentifier &field) override + { + gcc_unreachable (); + } + void visit (HIR::StructExprFieldIdentifierValue &field) override + { + gcc_unreachable (); + } + void visit (HIR::StructExprFieldIndexValue &field) override + { + gcc_unreachable (); + } + +protected: + bool + resolve_operator_overload (Analysis::RustLangItem::ItemType lang_item_type, + HIR::OperatorExprMeta expr, TyTy::BaseType *lhs, + TyTy::BaseType *rhs); + +private: + TypeCheckExpr (); + + TyTy::BaseType *resolve_root_path (HIR::PathInExpression &expr, + size_t *offset, + NodeId *root_resolved_node_id); + + void resolve_segments (NodeId root_resolved_node_id, + std::vector &segments, + size_t offset, TyTy::BaseType *tyseg, + const Analysis::NodeMapping &expr_mappings, + Location expr_locus); + + bool + validate_arithmetic_type (const TyTy::BaseType *tyty, + HIR::ArithmeticOrLogicalExpr::ExprType expr_type); + + /* The return value of TypeCheckExpr::Resolve */ + TyTy::BaseType *infered; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_EXPR diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.cc b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc new file mode 100644 index 00000000000..784e4990409 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc @@ -0,0 +1,583 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-implitem.h" +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-pattern.h" +#include "rust-tyty.h" + +namespace Rust { +namespace Resolver { + +TypeCheckTopLevelExternItem::TypeCheckTopLevelExternItem ( + const HIR::ExternBlock &parent) + : TypeCheckBase (), parent (parent) +{} + +void +TypeCheckTopLevelExternItem::Resolve (HIR::ExternalItem *item, + const HIR::ExternBlock &parent) +{ + TypeCheckTopLevelExternItem resolver (parent); + item->accept_vis (resolver); +} + +void +TypeCheckTopLevelExternItem::visit (HIR::ExternalStaticItem &item) +{ + TyTy::BaseType *actual_type + = TypeCheckType::Resolve (item.get_item_type ().get ()); + + context->insert_type (item.get_mappings (), actual_type); +} + +void +TypeCheckTopLevelExternItem::visit (HIR::ExternalFunctionItem &function) +{ + std::vector substitutions; + if (function.has_generics ()) + { + for (auto &generic_param : function.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + TyTy::BaseType *ret_type = nullptr; + if (!function.has_return_type ()) + ret_type + = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ()); + else + { + auto resolved + = TypeCheckType::Resolve (function.get_return_type ().get ()); + if (resolved == nullptr) + { + rust_error_at (function.get_locus (), + "failed to resolve return type"); + return; + } + + ret_type = resolved->clone (); + ret_type->set_ref ( + function.get_return_type ()->get_mappings ().get_hirid ()); + } + + std::vector > params; + for (auto ¶m : function.get_function_params ()) + { + // get the name as well required for later on + auto param_tyty = TypeCheckType::Resolve (param.get_type ().get ()); + + // these are implicit mappings and not used + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (), + mappings->get_next_hir_id (crate_num), + UNKNOWN_LOCAL_DEFID); + + HIR::IdentifierPattern *param_pattern + = new HIR::IdentifierPattern (mapping, param.get_param_name (), + Location (), false, Mutability::Imm, + std::unique_ptr (nullptr)); + + params.push_back ( + std::pair (param_pattern, + param_tyty)); + + context->insert_type (param.get_mappings (), param_tyty); + + // FIXME do we need error checking for patterns here? + // see https://github.com/Rust-GCC/gccrs/issues/995 + } + + uint8_t flags = TyTy::FnType::FNTYPE_IS_EXTERN_FLAG; + if (function.is_variadic ()) + flags |= TyTy::FnType::FNTYPE_IS_VARADIC_FLAG; + + RustIdent ident{ + CanonicalPath::new_seg (function.get_mappings ().get_nodeid (), + function.get_item_name ()), + function.get_locus ()}; + + auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (), + function.get_mappings ().get_defid (), + function.get_item_name (), ident, flags, + parent.get_abi (), std::move (params), + ret_type, std::move (substitutions)); + + context->insert_type (function.get_mappings (), fnType); +} + +TypeCheckTopLevelImplItem::TypeCheckTopLevelImplItem ( + TyTy::BaseType *self, + std::vector substitutions) + : TypeCheckBase (), self (self), substitutions (substitutions) +{} + +void +TypeCheckTopLevelImplItem::Resolve ( + HIR::ImplItem *item, TyTy::BaseType *self, + std::vector substitutions) +{ + TypeCheckTopLevelImplItem resolver (self, substitutions); + item->accept_vis (resolver); +} + +void +TypeCheckTopLevelImplItem::visit (HIR::TypeAlias &alias) +{ + TyTy::BaseType *actual_type + = TypeCheckType::Resolve (alias.get_type_aliased ().get ()); + + context->insert_type (alias.get_mappings (), actual_type); + + for (auto &where_clause_item : alias.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } +} + +void +TypeCheckTopLevelImplItem::visit (HIR::ConstantItem &constant) +{ + TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ()); + TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ()); + + context->insert_type (constant.get_mappings (), type->unify (expr_type)); +} + +void +TypeCheckTopLevelImplItem::visit (HIR::Function &function) +{ + if (function.has_generics ()) + { + for (auto &generic_param : function.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + for (auto &where_clause_item : function.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } + + TyTy::BaseType *ret_type = nullptr; + if (!function.has_function_return_type ()) + ret_type + = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ()); + else + { + auto resolved + = TypeCheckType::Resolve (function.get_return_type ().get ()); + if (resolved == nullptr) + { + rust_error_at (function.get_locus (), + "failed to resolve return type"); + return; + } + + ret_type = resolved->clone (); + ret_type->set_ref ( + function.get_return_type ()->get_mappings ().get_hirid ()); + } + + std::vector > params; + if (function.is_method ()) + { + // these are implicit mappings and not used + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (), + mappings->get_next_hir_id (crate_num), + UNKNOWN_LOCAL_DEFID); + + // add the synthetic self param at the front, this is a placeholder for + // compilation to know parameter names. The types are ignored but we + // reuse the HIR identifier pattern which requires it + HIR::SelfParam &self_param = function.get_self_param (); + HIR::IdentifierPattern *self_pattern + = new HIR::IdentifierPattern (mapping, "self", self_param.get_locus (), + self_param.is_ref (), + self_param.get_mut (), + std::unique_ptr (nullptr)); + + // might have a specified type + TyTy::BaseType *self_type = nullptr; + if (self_param.has_type ()) + { + std::unique_ptr &specified_type = self_param.get_type (); + self_type = TypeCheckType::Resolve (specified_type.get ()); + } + else + { + switch (self_param.get_self_kind ()) + { + case HIR::SelfParam::IMM: + case HIR::SelfParam::MUT: + self_type = self->clone (); + break; + + case HIR::SelfParam::IMM_REF: + self_type = new TyTy::ReferenceType ( + self_param.get_mappings ().get_hirid (), + TyTy::TyVar (self->get_ref ()), Mutability::Imm); + break; + + case HIR::SelfParam::MUT_REF: + self_type = new TyTy::ReferenceType ( + self_param.get_mappings ().get_hirid (), + TyTy::TyVar (self->get_ref ()), Mutability::Mut); + break; + + default: + gcc_unreachable (); + return; + } + } + + context->insert_type (self_param.get_mappings (), self_type); + params.push_back ( + std::pair (self_pattern, self_type)); + } + + for (auto ¶m : function.get_function_params ()) + { + // get the name as well required for later on + auto param_tyty = TypeCheckType::Resolve (param.get_type ()); + params.push_back ( + std::pair (param.get_param_name (), + param_tyty)); + + context->insert_type (param.get_mappings (), param_tyty); + TypeCheckPattern::Resolve (param.get_param_name (), param_tyty); + } + + const CanonicalPath *canonical_path = nullptr; + bool ok + = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, function.get_locus ()}; + auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (), + function.get_mappings ().get_defid (), + function.get_function_name (), ident, + function.is_method () + ? TyTy::FnType::FNTYPE_IS_METHOD_FLAG + : TyTy::FnType::FNTYPE_DEFAULT_FLAGS, + ABI::RUST, std::move (params), ret_type, + std::move (substitutions)); + + context->insert_type (function.get_mappings (), fnType); +} + +TypeCheckImplItem::TypeCheckImplItem (HIR::ImplBlock *parent, + TyTy::BaseType *self) + : TypeCheckBase (), parent (parent), self (self) +{} + +void +TypeCheckImplItem::Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item, + TyTy::BaseType *self) +{ + TypeCheckImplItem resolver (parent, self); + item->accept_vis (resolver); +} + +void +TypeCheckImplItem::visit (HIR::Function &function) +{ + TyTy::BaseType *lookup; + if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup)) + { + rust_error_at (function.get_locus (), "failed to lookup function type"); + return; + } + + if (lookup->get_kind () != TyTy::TypeKind::FNDEF) + { + rust_error_at (function.get_locus (), + "found invalid type for function [%s]", + lookup->as_string ().c_str ()); + return; + } + + // need to get the return type from this + TyTy::FnType *resolve_fn_type = static_cast (lookup); + auto expected_ret_tyty = resolve_fn_type->get_return_type (); + context->push_return_type (TypeCheckContextItem (parent, &function), + expected_ret_tyty); + + auto block_expr_ty + = TypeCheckExpr::Resolve (function.get_definition ().get ()); + + context->pop_return_type (); + expected_ret_tyty->unify (block_expr_ty); +} + +void +TypeCheckImplItem::visit (HIR::ConstantItem &const_item) +{} + +void +TypeCheckImplItem::visit (HIR::TypeAlias &type_alias) +{} + +TypeCheckImplItemWithTrait::TypeCheckImplItemWithTrait ( + HIR::ImplBlock *parent, TyTy::BaseType *self, + TyTy::TypeBoundPredicate &trait_reference, + std::vector substitutions) + : TypeCheckImplItem (parent, self), trait_reference (trait_reference), + resolved_trait_item (TyTy::TypeBoundPredicateItem::error ()), + substitutions (substitutions) +{ + rust_assert (is_trait_impl_block ()); +} + +TyTy::TypeBoundPredicateItem +TypeCheckImplItemWithTrait::Resolve ( + HIR::ImplBlock *parent, HIR::ImplItem *item, TyTy::BaseType *self, + TyTy::TypeBoundPredicate &trait_reference, + std::vector substitutions) +{ + TypeCheckImplItemWithTrait resolver (parent, self, trait_reference, + substitutions); + item->accept_vis (resolver); + return resolver.resolved_trait_item; +} + +void +TypeCheckImplItemWithTrait::visit (HIR::ConstantItem &constant) +{ + // normal resolution of the item + TypeCheckImplItem::visit (constant); + TyTy::BaseType *lookup; + if (!context->lookup_type (constant.get_mappings ().get_hirid (), &lookup)) + return; + + // map the impl item to the associated trait item + const auto tref = trait_reference.get (); + const TraitItemReference *raw_trait_item = nullptr; + bool found + = tref->lookup_trait_item_by_type (constant.get_identifier (), + TraitItemReference::TraitItemType::CONST, + &raw_trait_item); + + // unknown trait item + if (!found || raw_trait_item->is_error ()) + { + RichLocation r (constant.get_locus ()); + r.add_range (trait_reference.get_locus ()); + rust_error_at (r, "constant %<%s%> is not a member of trait %<%s%>", + constant.get_identifier ().c_str (), + trait_reference.get_name ().c_str ()); + return; + } + + // get the item from the predicate + resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item); + rust_assert (!resolved_trait_item.is_error ()); + + // merge the attributes + const HIR::TraitItem *hir_trait_item + = resolved_trait_item.get_raw_item ()->get_hir_trait_item (); + merge_attributes (constant.get_outer_attrs (), *hir_trait_item); + + // check the types are compatible + auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self); + if (!trait_item_type->can_eq (lookup, true)) + { + RichLocation r (constant.get_locus ()); + r.add_range (resolved_trait_item.get_locus ()); + + rust_error_at ( + r, "constant %<%s%> has an incompatible type for trait %<%s%>", + constant.get_identifier ().c_str (), + trait_reference.get_name ().c_str ()); + } +} + +void +TypeCheckImplItemWithTrait::visit (HIR::TypeAlias &type) +{ + // normal resolution of the item + TypeCheckImplItem::visit (type); + TyTy::BaseType *lookup; + if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup)) + return; + + // map the impl item to the associated trait item + const auto tref = trait_reference.get (); + const TraitItemReference *raw_trait_item = nullptr; + bool found + = tref->lookup_trait_item_by_type (type.get_new_type_name (), + TraitItemReference::TraitItemType::TYPE, + &raw_trait_item); + + // unknown trait item + if (!found || raw_trait_item->is_error ()) + { + RichLocation r (type.get_locus ()); + r.add_range (trait_reference.get_locus ()); + rust_error_at (r, "type alias %<%s%> is not a member of trait %<%s%>", + type.get_new_type_name ().c_str (), + trait_reference.get_name ().c_str ()); + return; + } + + // get the item from the predicate + resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item); + rust_assert (!resolved_trait_item.is_error ()); + + // merge the attributes + const HIR::TraitItem *hir_trait_item + = resolved_trait_item.get_raw_item ()->get_hir_trait_item (); + merge_attributes (type.get_outer_attrs (), *hir_trait_item); + + // check the types are compatible + auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self); + if (!trait_item_type->can_eq (lookup, true)) + { + RichLocation r (type.get_locus ()); + r.add_range (resolved_trait_item.get_locus ()); + + rust_error_at ( + r, "type alias %<%s%> has an incompatible type for trait %<%s%>", + type.get_new_type_name ().c_str (), + trait_reference.get_name ().c_str ()); + } + + // its actually a projection, since we need a way to actually bind the + // generic substitutions to the type itself + TyTy::ProjectionType *projection + = new TyTy::ProjectionType (type.get_mappings ().get_hirid (), lookup, tref, + raw_trait_item->get_mappings ().get_defid (), + substitutions); + + context->insert_type (type.get_mappings (), projection); + raw_trait_item->associated_type_set (projection); +} + +void +TypeCheckImplItemWithTrait::visit (HIR::Function &function) +{ + // we get the error checking from the base method here + TypeCheckImplItem::visit (function); + TyTy::BaseType *lookup; + if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup)) + return; + + // map the impl item to the associated trait item + const auto tref = trait_reference.get (); + const TraitItemReference *raw_trait_item = nullptr; + bool found + = tref->lookup_trait_item_by_type (function.get_function_name (), + TraitItemReference::TraitItemType::FN, + &raw_trait_item); + + // unknown trait item + if (!found || raw_trait_item->is_error ()) + { + RichLocation r (function.get_locus ()); + r.add_range (trait_reference.get_locus ()); + rust_error_at (r, "method %<%s%> is not a member of trait %<%s%>", + function.get_function_name ().c_str (), + trait_reference.get_name ().c_str ()); + return; + } + + // get the item from the predicate + resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item); + rust_assert (!resolved_trait_item.is_error ()); + + // merge the attributes + const HIR::TraitItem *hir_trait_item + = resolved_trait_item.get_raw_item ()->get_hir_trait_item (); + merge_attributes (function.get_outer_attrs (), *hir_trait_item); + + // check the types are compatible + auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self); + if (!trait_item_type->can_eq (lookup, true)) + { + RichLocation r (function.get_locus ()); + r.add_range (resolved_trait_item.get_locus ()); + + rust_error_at (r, + "method %<%s%> has an incompatible type for trait %<%s%>", + function.get_function_name ().c_str (), + trait_reference.get_name ().c_str ()); + } +} + +void +TypeCheckImplItemWithTrait::merge_attributes (AST::AttrVec &impl_item_attrs, + const HIR::TraitItem &trait_item) +{ + for (const auto &attr : trait_item.get_outer_attrs ()) + { + impl_item_attrs.push_back (attr); + } +} + +bool +TypeCheckImplItemWithTrait::is_trait_impl_block () const +{ + return !trait_reference.is_error (); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.h b/gcc/rust/typecheck/rust-hir-type-check-implitem.h new file mode 100644 index 00000000000..f2f3faab9e0 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h @@ -0,0 +1,114 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_IMPLITEM_H +#define RUST_HIR_TYPE_CHECK_IMPLITEM_H + +#include "rust-hir-type-check-base.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckTopLevelExternItem : public TypeCheckBase, + public HIR::HIRExternalItemVisitor +{ +public: + static void Resolve (HIR::ExternalItem *item, const HIR::ExternBlock &parent); + + void visit (HIR::ExternalStaticItem &item) override; + void visit (HIR::ExternalFunctionItem &function) override; + +private: + TypeCheckTopLevelExternItem (const HIR::ExternBlock &parent); + + const HIR::ExternBlock &parent; +}; + +class TypeCheckTopLevelImplItem : public TypeCheckBase, + public HIR::HIRImplVisitor +{ +public: + static void + Resolve (HIR::ImplItem *item, TyTy::BaseType *self, + std::vector substitutions); + + void visit (HIR::TypeAlias &alias) override; + void visit (HIR::ConstantItem &constant) override; + void visit (HIR::Function &function) override; + +private: + TypeCheckTopLevelImplItem ( + TyTy::BaseType *self, + std::vector substitutions); + + TyTy::BaseType *self; + std::vector substitutions; +}; + +class TypeCheckImplItem : public TypeCheckBase, public HIR::HIRImplVisitor +{ +public: + static void Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item, + TyTy::BaseType *self); + + void visit (HIR::Function &function) override; + void visit (HIR::ConstantItem &const_item) override; + void visit (HIR::TypeAlias &type_alias) override; + +protected: + TypeCheckImplItem (HIR::ImplBlock *parent, TyTy::BaseType *self); + + HIR::ImplBlock *parent; + TyTy::BaseType *self; +}; + +class TypeCheckImplItemWithTrait : public TypeCheckImplItem +{ +public: + static TyTy::TypeBoundPredicateItem + Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item, TyTy::BaseType *self, + TyTy::TypeBoundPredicate &trait_reference, + std::vector substitutions); + + void visit (HIR::ConstantItem &constant) override; + void visit (HIR::TypeAlias &type) override; + void visit (HIR::Function &function) override; + +protected: + // this allows us to inherit the must_use specified on a trait definition onto + // its implementation + void merge_attributes (AST::AttrVec &impl_item_attrs, + const HIR::TraitItem &trait_item); + +private: + TypeCheckImplItemWithTrait ( + HIR::ImplBlock *parent, TyTy::BaseType *self, + TyTy::TypeBoundPredicate &trait_reference, + std::vector substitutions); + + bool is_trait_impl_block () const; + + TyTy::TypeBoundPredicate &trait_reference; + TyTy::TypeBoundPredicateItem resolved_trait_item; + std::vector substitutions; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_IMPLITEM_H diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc new file mode 100644 index 00000000000..d31a6df4777 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc @@ -0,0 +1,237 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-item.h" +#include "rust-hir-full.h" +#include "rust-hir-type-check-implitem.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-type-check-stmt.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-trait-resolve.h" + +namespace Rust { +namespace Resolver { + +TypeCheckItem::TypeCheckItem () : TypeCheckBase () {} + +void +TypeCheckItem::Resolve (HIR::Item &item) +{ + rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM); + HIR::VisItem &vis_item = static_cast (item); + + TypeCheckItem resolver; + vis_item.accept_vis (resolver); +} + +void +TypeCheckItem::visit (HIR::ImplBlock &impl_block) +{ + std::vector substitutions; + if (impl_block.has_generics ()) + { + for (auto &generic_param : impl_block.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + TyTy::BaseType *l = nullptr; + bool ok = context->lookup_type ( + generic_param->get_mappings ().get_hirid (), &l); + if (ok && l->get_kind () == TyTy::TypeKind::PARAM) + { + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), + static_cast (l))); + } + } + break; + } + } + } + + auto specified_bound = TyTy::TypeBoundPredicate::error (); + TraitReference *trait_reference = &TraitReference::error_node (); + if (impl_block.has_trait_ref ()) + { + std::unique_ptr &ref = impl_block.get_trait_ref (); + trait_reference = TraitResolver::Resolve (*ref.get ()); + rust_assert (!trait_reference->is_error ()); + + // we don't error out here see: gcc/testsuite/rust/compile/traits2.rs + // for example + specified_bound = get_predicate_from_bound (*ref.get ()); + } + + TyTy::BaseType *self = nullptr; + if (!context->lookup_type ( + impl_block.get_type ()->get_mappings ().get_hirid (), &self)) + { + rust_error_at (impl_block.get_locus (), + "failed to resolve Self for ImplBlock"); + return; + } + + // inherit the bounds + if (!specified_bound.is_error ()) + self->inherit_bounds ({specified_bound}); + + // check for any unconstrained type-params + const TyTy::SubstitutionArgumentMappings trait_constraints + = specified_bound.get_substitution_arguments (); + const TyTy::SubstitutionArgumentMappings impl_constraints + = GetUsedSubstArgs::From (self); + + bool impl_block_has_unconstrained_typarams + = check_for_unconstrained (substitutions, trait_constraints, + impl_constraints, self); + if (impl_block_has_unconstrained_typarams) + return; + + // validate the impl items + bool is_trait_impl_block = !trait_reference->is_error (); + std::vector trait_item_refs; + for (auto &impl_item : impl_block.get_impl_items ()) + { + if (!is_trait_impl_block) + TypeCheckImplItem::Resolve (&impl_block, impl_item.get (), self); + else + { + auto trait_item_ref + = TypeCheckImplItemWithTrait::Resolve (&impl_block, + impl_item.get (), self, + specified_bound, + substitutions); + trait_item_refs.push_back (trait_item_ref.get_raw_item ()); + } + } + + bool impl_block_missing_trait_items + = is_trait_impl_block + && trait_reference->size () != trait_item_refs.size (); + if (impl_block_missing_trait_items) + { + // filter the missing impl_items + std::vector> + missing_trait_items; + for (const auto &trait_item_ref : trait_reference->get_trait_items ()) + { + bool found = false; + for (auto implemented_trait_item : trait_item_refs) + { + std::string trait_item_name = trait_item_ref.get_identifier (); + std::string impl_item_name + = implemented_trait_item->get_identifier (); + found = trait_item_name.compare (impl_item_name) == 0; + if (found) + break; + } + + bool is_required_trait_item = !trait_item_ref.is_optional (); + if (!found && is_required_trait_item) + missing_trait_items.push_back (trait_item_ref); + } + + if (missing_trait_items.size () > 0) + { + std::string missing_items_buf; + RichLocation r (impl_block.get_locus ()); + for (size_t i = 0; i < missing_trait_items.size (); i++) + { + bool has_more = (i + 1) < missing_trait_items.size (); + const TraitItemReference &missing_trait_item + = missing_trait_items.at (i); + missing_items_buf += missing_trait_item.get_identifier () + + (has_more ? ", " : ""); + r.add_range (missing_trait_item.get_locus ()); + } + + rust_error_at (r, "missing %s in implementation of trait %<%s%>", + missing_items_buf.c_str (), + trait_reference->get_name ().c_str ()); + } + } + + if (is_trait_impl_block) + { + trait_reference->clear_associated_types (); + + AssociatedImplTrait associated (trait_reference, &impl_block, self, + context); + context->insert_associated_trait_impl ( + impl_block.get_mappings ().get_hirid (), std::move (associated)); + context->insert_associated_impl_mapping ( + trait_reference->get_mappings ().get_hirid (), self, + impl_block.get_mappings ().get_hirid ()); + } +} + +void +TypeCheckItem::visit (HIR::Function &function) +{ + TyTy::BaseType *lookup; + if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup)) + { + rust_error_at (function.get_locus (), "failed to lookup function type"); + return; + } + + if (lookup->get_kind () != TyTy::TypeKind::FNDEF) + { + rust_error_at (function.get_locus (), + "found invalid type for function [%s]", + lookup->as_string ().c_str ()); + return; + } + + // need to get the return type from this + TyTy::FnType *resolved_fn_type = static_cast (lookup); + auto expected_ret_tyty = resolved_fn_type->get_return_type (); + context->push_return_type (TypeCheckContextItem (&function), + expected_ret_tyty); + + auto block_expr_ty + = TypeCheckExpr::Resolve (function.get_definition ().get ()); + + context->pop_return_type (); + + if (block_expr_ty->get_kind () != TyTy::NEVER) + expected_ret_tyty->unify (block_expr_ty); +} + +void +TypeCheckItem::visit (HIR::Module &module) +{ + for (auto &item : module.get_items ()) + TypeCheckItem::Resolve (*item.get ()); +} + +void +TypeCheckItem::visit (HIR::Trait &trait) +{ + TraitResolver::Resolve (trait); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h new file mode 100644 index 00000000000..ba4de19c9c7 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-item.h @@ -0,0 +1,58 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_ITEM +#define RUST_HIR_TYPE_CHECK_ITEM + +#include "rust-hir-type-check-base.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckItem : private TypeCheckBase, private HIR::HIRVisItemVisitor +{ +public: + static void Resolve (HIR::Item &item); + + void visit (HIR::ImplBlock &impl_block) override; + void visit (HIR::Function &function) override; + void visit (HIR::Module &module) override; + void visit (HIR::Trait &trait) override; + + // FIXME - get rid of toplevel pass + void visit (HIR::TypeAlias &alias) override{}; + void visit (HIR::TupleStruct &struct_decl) override{}; + void visit (HIR::StructStruct &struct_decl) override{}; + void visit (HIR::Enum &enum_decl) override{}; + void visit (HIR::Union &union_decl) override{}; + void visit (HIR::StaticItem &var) override{}; + void visit (HIR::ConstantItem &constant) override{}; + void visit (HIR::ExternBlock &extern_block) override{}; + + // nothing to do + void visit (HIR::ExternCrate &crate) override {} + void visit (HIR::UseDeclaration &use_decl) override {} + +private: + TypeCheckItem (); +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_ITEM diff --git a/gcc/rust/typecheck/rust-hir-type-check-path.cc b/gcc/rust/typecheck/rust-hir-type-check-path.cc new file mode 100644 index 00000000000..84f3b6ea6e6 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-path.cc @@ -0,0 +1,467 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-trait-resolve.h" + +namespace Rust { +namespace Resolver { + +void +TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr) +{ + HIR::QualifiedPathType qual_path_type = expr.get_path_type (); + TyTy::BaseType *root + = TypeCheckType::Resolve (qual_path_type.get_type ().get ()); + if (root->get_kind () == TyTy::TypeKind::ERROR) + return; + + if (!qual_path_type.has_as_clause ()) + { + NodeId root_resolved_node_id = UNKNOWN_NODEID; + resolve_segments (root_resolved_node_id, expr.get_segments (), 0, root, + expr.get_mappings (), expr.get_locus ()); + return; + } + + // Resolve the trait now + std::unique_ptr &trait_path_ref = qual_path_type.get_trait (); + TraitReference *trait_ref = TraitResolver::Resolve (*trait_path_ref.get ()); + if (trait_ref->is_error ()) + return; + + // does this type actually implement this type-bound? + if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref)) + return; + + // then we need to look at the next segment to create perform the correct + // projection type + if (expr.get_segments ().empty ()) + return; + + // get the predicate for the bound + auto specified_bound = get_predicate_from_bound (*trait_path_ref.get ()); + if (specified_bound.is_error ()) + return; + + // inherit the bound + root->inherit_bounds ({specified_bound}); + + // setup the associated types + const TraitReference *specified_bound_ref = specified_bound.get (); + auto candidates = TypeBoundsProbe::Probe (root); + AssociatedImplTrait *associated_impl_trait = nullptr; + for (auto &probed_bound : candidates) + { + const TraitReference *bound_trait_ref = probed_bound.first; + const HIR::ImplBlock *associated_impl = probed_bound.second; + + HirId impl_block_id = associated_impl->get_mappings ().get_hirid (); + AssociatedImplTrait *associated = nullptr; + bool found_impl_trait + = context->lookup_associated_trait_impl (impl_block_id, &associated); + if (found_impl_trait) + { + bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref); + bool found_self = associated->get_self ()->can_eq (root, false); + if (found_trait && found_self) + { + associated_impl_trait = associated; + break; + } + } + } + + if (associated_impl_trait != nullptr) + { + associated_impl_trait->setup_associated_types (root, specified_bound); + } + + // lookup the associated item from the specified bound + HIR::PathExprSegment &item_seg = expr.get_segments ().at (0); + HIR::PathIdentSegment item_seg_identifier = item_seg.get_segment (); + TyTy::TypeBoundPredicateItem item + = specified_bound.lookup_associated_item (item_seg_identifier.as_string ()); + if (item.is_error ()) + { + rust_error_at (item_seg.get_locus (), "unknown associated item"); + return; + } + + // infer the root type + infered = item.get_tyty_for_receiver (root); + + // turbo-fish segment path:: + if (item_seg.has_generic_args ()) + { + if (!infered->can_substitute ()) + { + rust_error_at (item_seg.get_locus (), + "substitutions not supported for %s", + infered->as_string ().c_str ()); + infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); + return; + } + infered = SubstMapper::Resolve (infered, expr.get_locus (), + &item_seg.get_generic_args ()); + } + + // continue on as a path-in-expression + const TraitItemReference *trait_item_ref = item.get_raw_item (); + NodeId root_resolved_node_id = trait_item_ref->get_mappings ().get_nodeid (); + bool fully_resolved = expr.get_segments ().size () <= 1; + + if (fully_resolved) + { + resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (), + root_resolved_node_id); + context->insert_receiver (expr.get_mappings ().get_hirid (), root); + return; + } + + resolve_segments (root_resolved_node_id, expr.get_segments (), 1, infered, + expr.get_mappings (), expr.get_locus ()); +} + +void +TypeCheckExpr::visit (HIR::PathInExpression &expr) +{ + NodeId resolved_node_id = UNKNOWN_NODEID; + size_t offset = -1; + TyTy::BaseType *tyseg = resolve_root_path (expr, &offset, &resolved_node_id); + if (tyseg->get_kind () == TyTy::TypeKind::ERROR) + return; + + if (tyseg->needs_generic_substitutions ()) + { + tyseg = SubstMapper::InferSubst (tyseg, expr.get_locus ()); + } + + bool fully_resolved = offset == expr.get_segments ().size (); + if (fully_resolved) + { + infered = tyseg; + return; + } + + resolve_segments (resolved_node_id, expr.get_segments (), offset, tyseg, + expr.get_mappings (), expr.get_locus ()); +} + +TyTy::BaseType * +TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset, + NodeId *root_resolved_node_id) +{ + TyTy::BaseType *root_tyty = nullptr; + *offset = 0; + for (size_t i = 0; i < expr.get_num_segments (); i++) + { + HIR::PathExprSegment &seg = expr.get_segments ().at (i); + + bool have_more_segments = (expr.get_num_segments () - 1 != i); + bool is_root = *offset == 0; + NodeId ast_node_id = seg.get_mappings ().get_nodeid (); + + // then lookup the reference_node_id + NodeId ref_node_id = UNKNOWN_NODEID; + if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id)) + { + resolver->lookup_resolved_type (ast_node_id, &ref_node_id); + } + + // ref_node_id is the NodeId that the segments refers to. + if (ref_node_id == UNKNOWN_NODEID) + { + if (root_tyty != nullptr && *offset > 0) + { + // then we can let the impl path probe take over now + return root_tyty; + } + + rust_error_at (seg.get_locus (), + "failed to type resolve root segment"); + return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); + } + + // node back to HIR + HirId ref; + if (!mappings->lookup_node_to_hir (ref_node_id, &ref)) + { + rust_error_at (seg.get_locus (), "456 reverse lookup failure"); + rust_debug_loc (seg.get_locus (), + "failure with [%s] mappings [%s] ref_node_id [%u]", + seg.as_string ().c_str (), + seg.get_mappings ().as_string ().c_str (), + ref_node_id); + + return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); + } + + auto seg_is_module = (nullptr != mappings->lookup_module (ref)); + auto seg_is_crate = mappings->is_local_hirid_crate (ref); + if (seg_is_module || seg_is_crate) + { + // A::B::C::this_is_a_module::D::E::F + // ^^^^^^^^^^^^^^^^ + // Currently handling this. + if (have_more_segments) + { + (*offset)++; + continue; + } + + // In the case of : + // A::B::C::this_is_a_module + // ^^^^^^^^^^^^^^^^ + // This is an error, we are not expecting a module. + rust_error_at (seg.get_locus (), "expected value"); + return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); + } + + TyTy::BaseType *lookup = nullptr; + if (!context->lookup_type (ref, &lookup)) + { + if (is_root) + { + rust_error_at (seg.get_locus (), + "failed to resolve root segment"); + return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); + } + return root_tyty; + } + + // if we have a previous segment type + if (root_tyty != nullptr) + { + // if this next segment needs substitution we must apply the + // previous type arguments + // + // such as: GenericStruct::<_>::new(123, 456) + if (lookup->needs_generic_substitutions ()) + { + if (!root_tyty->needs_generic_substitutions ()) + { + auto used_args_in_prev_segment + = GetUsedSubstArgs::From (root_tyty); + lookup + = SubstMapperInternal::Resolve (lookup, + used_args_in_prev_segment); + } + } + } + + // turbo-fish segment path:: + if (seg.has_generic_args ()) + { + if (!lookup->can_substitute ()) + { + rust_error_at (expr.get_locus (), + "substitutions not supported for %s", + root_tyty->as_string ().c_str ()); + return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); + } + + lookup = SubstMapper::Resolve (lookup, expr.get_locus (), + &seg.get_generic_args ()); + if (lookup->get_kind () == TyTy::TypeKind::ERROR) + return new TyTy::ErrorType (expr.get_mappings ().get_hirid ()); + } + + *root_resolved_node_id = ref_node_id; + *offset = *offset + 1; + root_tyty = lookup; + } + + return root_tyty; +} + +void +TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id, + std::vector &segments, + size_t offset, TyTy::BaseType *tyseg, + const Analysis::NodeMapping &expr_mappings, + Location expr_locus) +{ + NodeId resolved_node_id = root_resolved_node_id; + TyTy::BaseType *prev_segment = tyseg; + bool reciever_is_generic = prev_segment->get_kind () == TyTy::TypeKind::PARAM; + + for (size_t i = offset; i < segments.size (); i++) + { + HIR::PathExprSegment &seg = segments.at (i); + + bool probe_bounds = true; + bool probe_impls = !reciever_is_generic; + bool ignore_mandatory_trait_items = !reciever_is_generic; + + // probe the path is done in two parts one where we search impls if no + // candidate is found then we search extensions from traits + auto candidates + = PathProbeType::Probe (prev_segment, seg.get_segment (), probe_impls, + false, ignore_mandatory_trait_items); + if (candidates.size () == 0) + { + candidates + = PathProbeType::Probe (prev_segment, seg.get_segment (), false, + probe_bounds, ignore_mandatory_trait_items); + + if (candidates.size () == 0) + { + rust_error_at ( + seg.get_locus (), + "failed to resolve path segment using an impl Probe"); + return; + } + } + + if (candidates.size () > 1) + { + ReportMultipleCandidateError::Report (candidates, seg.get_segment (), + seg.get_locus ()); + return; + } + + auto &candidate = candidates.at (0); + prev_segment = tyseg; + tyseg = candidate.ty; + + HIR::ImplBlock *associated_impl_block = nullptr; + if (candidate.is_enum_candidate ()) + { + const TyTy::VariantDef *variant = candidate.item.enum_field.variant; + + HirId variant_id = variant->get_id (); + HIR::Item *enum_item = mappings->lookup_hir_item (variant_id); + rust_assert (enum_item != nullptr); + + resolved_node_id = enum_item->get_mappings ().get_nodeid (); + + // insert the id of the variant we are resolved to + context->insert_variant_definition (expr_mappings.get_hirid (), + variant_id); + } + else if (candidate.is_impl_candidate ()) + { + resolved_node_id + = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid (); + + associated_impl_block = candidate.item.impl.parent; + } + else + { + resolved_node_id + = candidate.item.trait.item_ref->get_mappings ().get_nodeid (); + + // lookup the associated-impl-trait + HIR::ImplBlock *impl = candidate.item.trait.impl; + if (impl != nullptr) + { + // get the associated impl block + associated_impl_block = impl; + } + } + + if (associated_impl_block != nullptr) + { + // get the type of the parent Self + HirId impl_ty_id + = associated_impl_block->get_type ()->get_mappings ().get_hirid (); + TyTy::BaseType *impl_block_ty = nullptr; + bool ok = context->lookup_type (impl_ty_id, &impl_block_ty); + rust_assert (ok); + + if (impl_block_ty->needs_generic_substitutions ()) + impl_block_ty + = SubstMapper::InferSubst (impl_block_ty, seg.get_locus ()); + + prev_segment = prev_segment->unify (impl_block_ty); + } + + if (tyseg->needs_generic_substitutions ()) + { + if (!prev_segment->needs_generic_substitutions ()) + { + auto used_args_in_prev_segment + = GetUsedSubstArgs::From (prev_segment); + + if (!used_args_in_prev_segment.is_error ()) + { + if (SubstMapperInternal::mappings_are_bound ( + tyseg, used_args_in_prev_segment)) + { + tyseg = SubstMapperInternal::Resolve ( + tyseg, used_args_in_prev_segment); + } + } + } + } + + if (seg.has_generic_args ()) + { + if (!tyseg->can_substitute ()) + { + rust_error_at (expr_locus, "substitutions not supported for %s", + tyseg->as_string ().c_str ()); + return; + } + + tyseg = SubstMapper::Resolve (tyseg, expr_locus, + &seg.get_generic_args ()); + if (tyseg->get_kind () == TyTy::TypeKind::ERROR) + return; + } + else if (tyseg->needs_generic_substitutions () && !reciever_is_generic) + { + Location locus = seg.get_locus (); + tyseg = SubstMapper::InferSubst (tyseg, locus); + if (tyseg->get_kind () == TyTy::TypeKind::ERROR) + return; + } + } + + rust_assert (resolved_node_id != UNKNOWN_NODEID); + if (tyseg->needs_generic_substitutions () && !reciever_is_generic) + { + Location locus = segments.back ().get_locus (); + tyseg = SubstMapper::InferSubst (tyseg, locus); + if (tyseg->get_kind () == TyTy::TypeKind::ERROR) + return; + } + + context->insert_receiver (expr_mappings.get_hirid (), prev_segment); + + // name scope first + if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) + { + resolver->insert_resolved_name (expr_mappings.get_nodeid (), + resolved_node_id); + } + // check the type scope + else if (resolver->get_type_scope ().decl_was_declared_here ( + resolved_node_id)) + { + resolver->insert_resolved_type (expr_mappings.get_nodeid (), + resolved_node_id); + } + + infered = tyseg; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc new file mode 100644 index 00000000000..429511d0292 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc @@ -0,0 +1,416 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-pattern.h" +#include "rust-hir-type-check-expr.h" + +namespace Rust { +namespace Resolver { + +TypeCheckPattern::TypeCheckPattern (TyTy::BaseType *parent) + : TypeCheckBase (), parent (parent), infered (nullptr) +{} + +TyTy::BaseType * +TypeCheckPattern::Resolve (HIR::Pattern *pattern, TyTy::BaseType *parent) +{ + TypeCheckPattern resolver (parent); + pattern->accept_vis (resolver); + + if (resolver.infered == nullptr) + return new TyTy::ErrorType (pattern->get_pattern_mappings ().get_hirid ()); + + resolver.context->insert_type (pattern->get_pattern_mappings (), + resolver.infered); + return resolver.infered; +} + +void +TypeCheckPattern::visit (HIR::PathInExpression &pattern) +{ + infered = TypeCheckExpr::Resolve (&pattern); +} + +void +TypeCheckPattern::visit (HIR::TupleStructPattern &pattern) +{ + infered = TypeCheckExpr::Resolve (&pattern.get_path ()); + if (infered->get_kind () == TyTy::TypeKind::ERROR) + return; + + rust_assert (infered->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast (infered); + rust_assert (adt->number_of_variants () > 0); + + TyTy::VariantDef *variant = adt->get_variants ().at (0); + if (adt->is_enum ()) + { + HirId variant_id = UNKNOWN_HIRID; + bool ok = context->lookup_variant_definition ( + pattern.get_path ().get_mappings ().get_hirid (), &variant_id); + rust_assert (ok); + + ok = adt->lookup_variant_by_id (variant_id, &variant); + rust_assert (ok); + } + + // error[E0532]: expected tuple struct or tuple variant, found struct variant + // `Foo::D` + if (variant->get_variant_type () != TyTy::VariantDef::VariantType::TUPLE) + { + std::string variant_type + = TyTy::VariantDef::variant_type_string (variant->get_variant_type ()); + + rust_error_at ( + pattern.get_locus (), + "expected tuple struct or tuple variant, found %s variant %<%s::%s%>", + variant_type.c_str (), adt->get_name ().c_str (), + variant->get_identifier ().c_str ()); + return; + } + + // check the elements + // error[E0023]: this pattern has 2 fields, but the corresponding tuple + // variant has 1 field + // error[E0023]: this pattern has 0 fields, but the corresponding tuple + // variant has 1 field + + std::unique_ptr &items = pattern.get_items (); + switch (items->get_item_type ()) + { + case HIR::TupleStructItems::RANGE: { + // TODO + gcc_unreachable (); + } + break; + + case HIR::TupleStructItems::NO_RANGE: { + HIR::TupleStructItemsNoRange &items_no_range + = static_cast (*items.get ()); + + if (items_no_range.get_patterns ().size () != variant->num_fields ()) + { + rust_error_at ( + pattern.get_locus (), + "this pattern has %lu fields but the corresponding " + "tuple variant has %lu field", + (unsigned long) items_no_range.get_patterns ().size (), + (unsigned long) variant->num_fields ()); + // we continue on to try and setup the types as best we can for + // type checking + } + + // iterate the fields and set them up, I wish we had ZIP + size_t i = 0; + for (auto &pattern : items_no_range.get_patterns ()) + { + if (i >= variant->num_fields ()) + break; + + TyTy::StructFieldType *field = variant->get_field_at_index (i++); + TyTy::BaseType *fty = field->get_field_type (); + + // setup the type on this pattern type + context->insert_type (pattern->get_pattern_mappings (), fty); + } + } + break; + } +} + +void +TypeCheckPattern::visit (HIR::StructPattern &pattern) +{ + infered = TypeCheckExpr::Resolve (&pattern.get_path ()); + if (infered->get_kind () == TyTy::TypeKind::ERROR) + return; + + rust_assert (infered->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast (infered); + rust_assert (adt->number_of_variants () > 0); + + TyTy::VariantDef *variant = adt->get_variants ().at (0); + if (adt->is_enum ()) + { + HirId variant_id = UNKNOWN_HIRID; + bool ok = context->lookup_variant_definition ( + pattern.get_path ().get_mappings ().get_hirid (), &variant_id); + rust_assert (ok); + + ok = adt->lookup_variant_by_id (variant_id, &variant); + rust_assert (ok); + } + + // error[E0532]: expected tuple struct or tuple variant, found struct variant + // `Foo::D` + if (variant->get_variant_type () != TyTy::VariantDef::VariantType::STRUCT) + { + std::string variant_type + = TyTy::VariantDef::variant_type_string (variant->get_variant_type ()); + rust_error_at (pattern.get_locus (), + "expected struct variant, found %s variant %s", + variant_type.c_str (), + variant->get_identifier ().c_str ()); + return; + } + + // check the elements + // error[E0027]: pattern does not mention fields `x`, `y` + // error[E0026]: variant `Foo::D` does not have a field named `b` + + std::vector named_fields; + auto &struct_pattern_elems = pattern.get_struct_pattern_elems (); + for (auto &field : struct_pattern_elems.get_struct_pattern_fields ()) + { + switch (field->get_item_type ()) + { + case HIR::StructPatternField::ItemType::TUPLE_PAT: { + // TODO + gcc_unreachable (); + } + break; + + case HIR::StructPatternField::ItemType::IDENT_PAT: { + // TODO + gcc_unreachable (); + } + break; + + case HIR::StructPatternField::ItemType::IDENT: { + HIR::StructPatternFieldIdent &ident + = static_cast (*field.get ()); + + TyTy::StructFieldType *field = nullptr; + if (!variant->lookup_field (ident.get_identifier (), &field, + nullptr)) + { + rust_error_at (ident.get_locus (), + "variant %s does not have a field named %s", + variant->get_identifier ().c_str (), + ident.get_identifier ().c_str ()); + break; + } + named_fields.push_back (ident.get_identifier ()); + + // setup the type on this pattern + TyTy::BaseType *fty = field->get_field_type (); + context->insert_type (ident.get_mappings (), fty); + } + break; + } + } + + if (named_fields.size () != variant->num_fields ()) + { + std::map missing_names; + + // populate with all fields + for (auto &field : variant->get_fields ()) + missing_names[field->get_name ()] = true; + + // then eliminate with named_fields + for (auto &named : named_fields) + missing_names.erase (named); + + // then get the list of missing names + size_t i = 0; + std::string missing_fields_str; + for (auto it = missing_names.begin (); it != missing_names.end (); it++) + { + bool has_next = (i + 1) < missing_names.size (); + missing_fields_str += it->first + (has_next ? ", " : ""); + i++; + } + + rust_error_at (pattern.get_locus (), "pattern does not mention fields %s", + missing_fields_str.c_str ()); + } +} + +void +TypeCheckPattern::visit (HIR::WildcardPattern &pattern) +{ + // wildcard patterns within the MatchArm's are simply just the same type as + // the parent + infered = parent->clone (); + infered->set_ref (pattern.get_pattern_mappings ().get_hirid ()); +} + +void +TypeCheckPattern::visit (HIR::TuplePattern &pattern) +{ + std::unique_ptr items; + switch (pattern.get_items ()->get_pattern_type ()) + { + case HIR::TuplePatternItems::TuplePatternItemType::MULTIPLE: { + HIR::TuplePatternItemsMultiple &ref + = *static_cast ( + pattern.get_items ().get ()); + + std::vector pattern_elems; + for (size_t i = 0; i < ref.get_patterns ().size (); i++) + { + auto &p = ref.get_patterns ()[i]; + TyTy::BaseType *par_type = parent; + if (parent->get_kind () == TyTy::TUPLE) + { + TyTy::TupleType &par = *static_cast (parent); + par_type = par.get_field (i); + } + + TyTy::BaseType *elem + = TypeCheckPattern::Resolve (p.get (), par_type); + pattern_elems.push_back (TyTy::TyVar (elem->get_ref ())); + } + infered + = new TyTy::TupleType (pattern.get_pattern_mappings ().get_hirid (), + pattern.get_locus (), pattern_elems); + } + break; + + case HIR::TuplePatternItems::TuplePatternItemType::RANGED: { + // HIR::TuplePatternItemsRanged &ref + // = *static_cast ( + // pattern.get_items ().get ()); + // TODO + gcc_unreachable (); + } + break; + } +} + +void +TypeCheckPattern::visit (HIR::LiteralPattern &pattern) +{ + infered = resolve_literal (pattern.get_pattern_mappings (), + pattern.get_literal (), pattern.get_locus ()); +} + +void +TypeCheckPattern::visit (HIR::RangePattern &pattern) +{ + // Resolve the upper and lower bounds, and ensure they are compatible types + TyTy::BaseType *upper = nullptr, *lower = nullptr; + + // TODO: It would be nice to factor this out into a helper since the logic for + // both bounds is exactly the same... + switch (pattern.get_upper_bound ()->get_bound_type ()) + { + case HIR::RangePatternBound::RangePatternBoundType::LITERAL: { + HIR::RangePatternBoundLiteral &ref + = *static_cast ( + pattern.get_upper_bound ().get ()); + + HIR::Literal lit = ref.get_literal (); + + upper = resolve_literal (pattern.get_pattern_mappings (), lit, + pattern.get_locus ()); + } + break; + + case HIR::RangePatternBound::RangePatternBoundType::PATH: { + HIR::RangePatternBoundPath &ref + = *static_cast ( + pattern.get_upper_bound ().get ()); + + upper = TypeCheckExpr::Resolve (&ref.get_path ()); + } + break; + + case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: { + HIR::RangePatternBoundQualPath &ref + = *static_cast ( + pattern.get_upper_bound ().get ()); + + upper = TypeCheckExpr::Resolve (&ref.get_qualified_path ()); + } + break; + } + + switch (pattern.get_lower_bound ()->get_bound_type ()) + { + case HIR::RangePatternBound::RangePatternBoundType::LITERAL: { + HIR::RangePatternBoundLiteral &ref + = *static_cast ( + pattern.get_lower_bound ().get ()); + + HIR::Literal lit = ref.get_literal (); + + lower = resolve_literal (pattern.get_pattern_mappings (), lit, + pattern.get_locus ()); + } + break; + + case HIR::RangePatternBound::RangePatternBoundType::PATH: { + HIR::RangePatternBoundPath &ref + = *static_cast ( + pattern.get_lower_bound ().get ()); + + lower = TypeCheckExpr::Resolve (&ref.get_path ()); + } + break; + + case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: { + HIR::RangePatternBoundQualPath &ref + = *static_cast ( + pattern.get_lower_bound ().get ()); + + lower = TypeCheckExpr::Resolve (&ref.get_qualified_path ()); + } + break; + } + + infered = upper->unify (lower); +} + +void +TypeCheckPattern::visit (HIR::IdentifierPattern &pattern) +{ + infered = parent; +} + +void +TypeCheckPattern::visit (HIR::GroupedPattern &pattern) +{ + // TODO + gcc_unreachable (); +} + +void +TypeCheckPattern::visit (HIR::QualifiedPathInExpression &pattern) +{ + // TODO + gcc_unreachable (); +} + +void +TypeCheckPattern::visit (HIR::ReferencePattern &pattern) +{ + // TODO + gcc_unreachable (); +} + +void +TypeCheckPattern::visit (HIR::SlicePattern &pattern) +{ + // TODO + gcc_unreachable (); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.h b/gcc/rust/typecheck/rust-hir-type-check-pattern.h new file mode 100644 index 00000000000..8af106033b7 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.h @@ -0,0 +1,62 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_PATTERN +#define RUST_HIR_TYPE_CHECK_PATTERN + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckPattern : public TypeCheckBase, public HIR::HIRPatternVisitor +{ +public: + static TyTy::BaseType *Resolve (HIR::Pattern *pattern, + TyTy::BaseType *parent); + + void visit (HIR::PathInExpression &pattern) override; + void visit (HIR::StructPattern &pattern) override; + void visit (HIR::TupleStructPattern &pattern) override; + void visit (HIR::WildcardPattern &pattern) override; + void visit (HIR::TuplePattern &pattern) override; + void visit (HIR::LiteralPattern &pattern) override; + void visit (HIR::RangePattern &pattern) override; + void visit (HIR::IdentifierPattern &pattern) override; + void visit (HIR::GroupedPattern &pattern) override; + void visit (HIR::QualifiedPathInExpression &pattern) override; + void visit (HIR::ReferencePattern &pattern) override; + void visit (HIR::SlicePattern &pattern) override; + +private: + TypeCheckPattern (TyTy::BaseType *parent); + + static TyTy::BaseType * + typecheck_range_pattern_bound (HIR::RangePatternBound *bound, + Analysis::NodeMapping mappings, + Location locus); + + TyTy::BaseType *parent; + TyTy::BaseType *infered; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_PATTERN diff --git a/gcc/rust/typecheck/rust-hir-type-check-stmt.cc b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc new file mode 100644 index 00000000000..9f34ed49165 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc @@ -0,0 +1,498 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-stmt.h" +#include "rust-hir-full.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-enumitem.h" +#include "rust-hir-type-check-implitem.h" +#include "rust-hir-type-check-pattern.h" + +namespace Rust { +namespace Resolver { + +TyTy::BaseType * +TypeCheckStmt::Resolve (HIR::Stmt *stmt) +{ + TypeCheckStmt resolver; + stmt->accept_vis (resolver); + return resolver.infered; +} + +void +TypeCheckStmt::visit (HIR::ExprStmtWithBlock &stmt) +{ + infered = TypeCheckExpr::Resolve (stmt.get_expr ()); +} + +void +TypeCheckStmt::visit (HIR::ExprStmtWithoutBlock &stmt) +{ + infered = TypeCheckExpr::Resolve (stmt.get_expr ()); +} + +void +TypeCheckStmt::visit (HIR::EmptyStmt &stmt) +{ + infered = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ()); +} + +void +TypeCheckStmt::visit (HIR::ExternBlock &extern_block) +{ + for (auto &item : extern_block.get_extern_items ()) + { + TypeCheckTopLevelExternItem::Resolve (item.get (), extern_block); + } +} + +void +TypeCheckStmt::visit (HIR::ConstantItem &constant) +{ + TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ()); + TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ()); + + infered = type->unify (expr_type); + context->insert_type (constant.get_mappings (), infered); +} + +void +TypeCheckStmt::visit (HIR::LetStmt &stmt) +{ + infered = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ()); + + const HIR::Pattern &stmt_pattern = *stmt.get_pattern (); + TyTy::BaseType *init_expr_ty = nullptr; + if (stmt.has_init_expr ()) + { + init_expr_ty = TypeCheckExpr::Resolve (stmt.get_init_expr ()); + if (init_expr_ty->get_kind () == TyTy::TypeKind::ERROR) + return; + + init_expr_ty->append_reference ( + stmt_pattern.get_pattern_mappings ().get_hirid ()); + } + + TyTy::BaseType *specified_ty = nullptr; + if (stmt.has_type ()) + specified_ty = TypeCheckType::Resolve (stmt.get_type ()); + + // let x:i32 = 123; + if (specified_ty != nullptr && init_expr_ty != nullptr) + { + // FIXME use this result and look at the regressions + coercion_site (stmt.get_mappings ().get_hirid (), specified_ty, + init_expr_ty, stmt.get_locus ()); + context->insert_type (stmt_pattern.get_pattern_mappings (), specified_ty); + } + else + { + // let x:i32; + if (specified_ty != nullptr) + { + context->insert_type (stmt_pattern.get_pattern_mappings (), + specified_ty); + } + // let x = 123; + else if (init_expr_ty != nullptr) + { + context->insert_type (stmt_pattern.get_pattern_mappings (), + init_expr_ty); + } + // let x; + else + { + context->insert_type ( + stmt_pattern.get_pattern_mappings (), + new TyTy::InferType ( + stmt_pattern.get_pattern_mappings ().get_hirid (), + TyTy::InferType::InferTypeKind::GENERAL, stmt.get_locus ())); + } + } +} + +void +TypeCheckStmt::visit (HIR::TupleStruct &struct_decl) +{ + std::vector substitutions; + if (struct_decl.has_generics ()) + { + for (auto &generic_param : struct_decl.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + std::vector fields; + size_t idx = 0; + for (auto &field : struct_decl.get_fields ()) + { + TyTy::BaseType *field_type + = TypeCheckType::Resolve (field.get_field_type ().get ()); + TyTy::StructFieldType *ty_field + = new TyTy::StructFieldType (field.get_mappings ().get_hirid (), + std::to_string (idx), field_type); + fields.push_back (ty_field); + context->insert_type (field.get_mappings (), ty_field->get_field_type ()); + idx++; + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok = mappings->lookup_canonical_path ( + struct_decl.get_mappings ().get_nodeid (), &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, struct_decl.get_locus ()}; + + // there is only a single variant + std::vector variants; + variants.push_back (new TyTy::VariantDef ( + struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (), + ident, TyTy::VariantDef::VariantType::TUPLE, nullptr, std::move (fields))); + + // Process #[repr(...)] attribute, if any + const AST::AttrVec &attrs = struct_decl.get_outer_attrs (); + TyTy::ADTType::ReprOptions repr + = parse_repr_options (attrs, struct_decl.get_locus ()); + + TyTy::BaseType *type + = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + struct_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::TUPLE_STRUCT, + std::move (variants), std::move (substitutions), repr); + + context->insert_type (struct_decl.get_mappings (), type); + infered = type; +} + +void +TypeCheckStmt::visit (HIR::Enum &enum_decl) +{ + std::vector substitutions; + if (enum_decl.has_generics ()) + { + for (auto &generic_param : enum_decl.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + std::vector variants; + int64_t discriminant_value = 0; + for (auto &variant : enum_decl.get_variants ()) + { + TyTy::VariantDef *field_type + = TypeCheckEnumItem::Resolve (variant.get (), discriminant_value); + + discriminant_value++; + variants.push_back (field_type); + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok + = mappings->lookup_canonical_path (enum_decl.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, enum_decl.get_locus ()}; + + TyTy::BaseType *type + = new TyTy::ADTType (enum_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + enum_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::ENUM, std::move (variants), + std::move (substitutions)); + + context->insert_type (enum_decl.get_mappings (), type); + infered = type; +} + +void +TypeCheckStmt::visit (HIR::StructStruct &struct_decl) +{ + std::vector substitutions; + if (struct_decl.has_generics ()) + { + for (auto &generic_param : struct_decl.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + std::vector fields; + for (auto &field : struct_decl.get_fields ()) + { + TyTy::BaseType *field_type + = TypeCheckType::Resolve (field.get_field_type ().get ()); + TyTy::StructFieldType *ty_field + = new TyTy::StructFieldType (field.get_mappings ().get_hirid (), + field.get_field_name (), field_type); + fields.push_back (ty_field); + context->insert_type (field.get_mappings (), ty_field->get_field_type ()); + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok = mappings->lookup_canonical_path ( + struct_decl.get_mappings ().get_nodeid (), &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, struct_decl.get_locus ()}; + + // there is only a single variant + std::vector variants; + variants.push_back (new TyTy::VariantDef ( + struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (), + ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields))); + + // Process #[repr(...)] attribute, if any + const AST::AttrVec &attrs = struct_decl.get_outer_attrs (); + TyTy::ADTType::ReprOptions repr + = parse_repr_options (attrs, struct_decl.get_locus ()); + + TyTy::BaseType *type + = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + struct_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::STRUCT_STRUCT, + std::move (variants), std::move (substitutions), repr); + + context->insert_type (struct_decl.get_mappings (), type); + infered = type; +} + +void +TypeCheckStmt::visit (HIR::Union &union_decl) +{ + std::vector substitutions; + if (union_decl.has_generics ()) + { + for (auto &generic_param : union_decl.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + std::vector fields; + for (auto &variant : union_decl.get_variants ()) + { + TyTy::BaseType *variant_type + = TypeCheckType::Resolve (variant.get_field_type ().get ()); + TyTy::StructFieldType *ty_variant + = new TyTy::StructFieldType (variant.get_mappings ().get_hirid (), + variant.get_field_name (), variant_type); + fields.push_back (ty_variant); + context->insert_type (variant.get_mappings (), + ty_variant->get_field_type ()); + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok + = mappings->lookup_canonical_path (union_decl.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, union_decl.get_locus ()}; + + // there is only a single variant + std::vector variants; + variants.push_back (new TyTy::VariantDef ( + union_decl.get_mappings ().get_hirid (), union_decl.get_identifier (), + ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields))); + + TyTy::BaseType *type + = new TyTy::ADTType (union_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + union_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::UNION, std::move (variants), + std::move (substitutions)); + + context->insert_type (union_decl.get_mappings (), type); + infered = type; +} + +void +TypeCheckStmt::visit (HIR::Function &function) +{ + std::vector substitutions; + if (function.has_generics ()) + { + for (auto &generic_param : function.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + TyTy::BaseType *ret_type = nullptr; + if (!function.has_function_return_type ()) + ret_type + = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ()); + else + { + auto resolved + = TypeCheckType::Resolve (function.get_return_type ().get ()); + if (resolved == nullptr) + { + rust_error_at (function.get_locus (), + "failed to resolve return type"); + return; + } + + ret_type = resolved->clone (); + ret_type->set_ref ( + function.get_return_type ()->get_mappings ().get_hirid ()); + } + + std::vector > params; + for (auto ¶m : function.get_function_params ()) + { + // get the name as well required for later on + auto param_tyty = TypeCheckType::Resolve (param.get_type ()); + params.push_back ( + std::pair (param.get_param_name (), + param_tyty)); + + context->insert_type (param.get_mappings (), param_tyty); + TypeCheckPattern::Resolve (param.get_param_name (), param_tyty); + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok + = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, function.get_locus ()}; + auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (), + function.get_mappings ().get_defid (), + function.get_function_name (), ident, + TyTy::FnType::FNTYPE_DEFAULT_FLAGS, ABI::RUST, + std::move (params), ret_type, + std::move (substitutions)); + context->insert_type (function.get_mappings (), fnType); + + TyTy::FnType *resolved_fn_type = fnType; + auto expected_ret_tyty = resolved_fn_type->get_return_type (); + context->push_return_type (TypeCheckContextItem (&function), + expected_ret_tyty); + + auto block_expr_ty + = TypeCheckExpr::Resolve (function.get_definition ().get ()); + + context->pop_return_type (); + + if (block_expr_ty->get_kind () != TyTy::NEVER) + expected_ret_tyty->unify (block_expr_ty); + + infered = fnType; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-stmt.h b/gcc/rust/typecheck/rust-hir-type-check-stmt.h new file mode 100644 index 00000000000..a79f17a59ce --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.h @@ -0,0 +1,96 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_STMT +#define RUST_HIR_TYPE_CHECK_STMT + +#include "rust-hir-type-check-base.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckStmt : private TypeCheckBase, private HIR::HIRStmtVisitor +{ +public: + static TyTy::BaseType *Resolve (HIR::Stmt *stmt); + + void visit (HIR::ExprStmtWithBlock &stmt) override; + void visit (HIR::ExprStmtWithoutBlock &stmt) override; + void visit (HIR::EmptyStmt &stmt) override; + void visit (HIR::ExternBlock &extern_block) override; + void visit (HIR::ConstantItem &constant) override; + void visit (HIR::LetStmt &stmt) override; + void visit (HIR::TupleStruct &struct_decl) override; + void visit (HIR::Enum &enum_decl) override; + void visit (HIR::StructStruct &struct_decl) override; + void visit (HIR::Union &union_decl) override; + void visit (HIR::Function &function) override; + + void visit (HIR::EnumItemTuple &) override + { /* TODO? */ + } + void visit (HIR::EnumItemStruct &) override + { /* TODO? */ + } + void visit (HIR::EnumItem &item) override + { /* TODO? */ + } + void visit (HIR::EnumItemDiscriminant &) override + { /* TODO? */ + } + void visit (HIR::TypePathSegmentFunction &segment) override + { /* TODO? */ + } + void visit (HIR::TypePath &path) override + { /* TODO? */ + } + void visit (HIR::QualifiedPathInType &path) override + { /* TODO? */ + } + void visit (HIR::Module &module) override + { /* TODO? */ + } + void visit (HIR::ExternCrate &crate) override + { /* TODO? */ + } + void visit (HIR::UseDeclaration &use_decl) override + { /* TODO? */ + } + void visit (HIR::TypeAlias &type_alias) override + { /* TODO? */ + } + void visit (HIR::StaticItem &static_item) override + { /* TODO? */ + } + void visit (HIR::Trait &trait) override + { /* TODO? */ + } + void visit (HIR::ImplBlock &impl) override + { /* TODO? */ + } + +private: + TypeCheckStmt () : TypeCheckBase (), infered (nullptr) {} + + TyTy::BaseType *infered; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_STMT diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct-field.h b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h new file mode 100644 index 00000000000..22af1aad4c3 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h @@ -0,0 +1,59 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_STRUCT_FIELD +#define RUST_HIR_TYPE_CHECK_STRUCT_FIELD + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" +#include "rust-hir-type-check-type.h" +#include "rust-tyty.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckStructExpr : public TypeCheckBase +{ +public: + static TyTy::BaseType *Resolve (HIR::StructExprStructFields *expr); + +protected: + void resolve (HIR::StructExprStructFields &struct_expr); + + void visit (HIR::StructExprFieldIdentifierValue &field); + void visit (HIR::StructExprFieldIndexValue &field); + void visit (HIR::StructExprFieldIdentifier &field); + +private: + TypeCheckStructExpr (HIR::Expr *e); + + // result + TyTy::BaseType *resolved; + + // internal state: + TyTy::ADTType *struct_path_resolved; + TyTy::VariantDef *variant; + TyTy::BaseType *resolved_field_value_expr; + std::set fields_assigned; + std::map adtFieldIndexToField; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_STRUCT_FIELD diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct.cc b/gcc/rust/typecheck/rust-hir-type-check-struct.cc new file mode 100644 index 00000000000..b2261e8cdb3 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-struct.cc @@ -0,0 +1,340 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check.h" +#include "rust-hir-full.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-struct-field.h" + +namespace Rust { +namespace Resolver { + +TypeCheckStructExpr::TypeCheckStructExpr (HIR::Expr *e) + : TypeCheckBase (), + resolved (new TyTy::ErrorType (e->get_mappings ().get_hirid ())), + struct_path_resolved (nullptr), + variant (&TyTy::VariantDef::get_error_node ()) +{} + +TyTy::BaseType * +TypeCheckStructExpr::Resolve (HIR::StructExprStructFields *expr) +{ + TypeCheckStructExpr resolver (expr); + resolver.resolve (*expr); + return resolver.resolved; +} + +void +TypeCheckStructExpr::resolve (HIR::StructExprStructFields &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; + } + + struct_path_resolved = static_cast (struct_path_ty); + TyTy::ADTType *struct_def = struct_path_resolved; + if (struct_expr.has_struct_base ()) + { + TyTy::BaseType *base_resolved + = TypeCheckExpr::Resolve (struct_expr.struct_base->base_struct.get ()); + struct_def = static_cast ( + struct_path_resolved->unify (base_resolved)); + if (struct_def == nullptr) + { + rust_fatal_error (struct_expr.struct_base->base_struct->get_locus (), + "incompatible types for base struct reference"); + return; + } + } + + // figure out the variant + if (struct_path_resolved->is_enum ()) + { + // lookup variant id + HirId variant_id; + bool ok = context->lookup_variant_definition ( + struct_expr.get_struct_name ().get_mappings ().get_hirid (), + &variant_id); + rust_assert (ok); + + ok = struct_path_resolved->lookup_variant_by_id (variant_id, &variant); + rust_assert (ok); + } + else + { + rust_assert (struct_path_resolved->number_of_variants () == 1); + variant = struct_path_resolved->get_variants ().at (0); + } + + std::vector infered_fields; + bool ok = true; + + for (auto &field : struct_expr.get_fields ()) + { + resolved_field_value_expr = nullptr; + + switch (field->get_kind ()) + { + case HIR::StructExprField::StructExprFieldKind::IDENTIFIER: + visit (static_cast (*field.get ())); + break; + + case HIR::StructExprField::StructExprFieldKind::IDENTIFIER_VALUE: + visit ( + static_cast (*field.get ())); + break; + + case HIR::StructExprField::StructExprFieldKind::INDEX_VALUE: + visit (static_cast (*field.get ())); + break; + } + + if (resolved_field_value_expr == nullptr) + { + rust_fatal_error (field->get_locus (), + "failed to resolve type for field"); + ok = false; + break; + } + + context->insert_type (field->get_mappings (), resolved_field_value_expr); + } + + // something failed setting up the fields + if (!ok) + { + rust_error_at (struct_expr.get_locus (), + "constructor type resolution failure"); + return; + } + + // check the arguments are all assigned and fix up the ordering + if (fields_assigned.size () != variant->num_fields ()) + { + if (struct_def->is_union ()) + { + if (fields_assigned.size () != 1 || struct_expr.has_struct_base ()) + { + rust_error_at ( + struct_expr.get_locus (), + "union must have exactly one field variant assigned"); + return; + } + } + else if (!struct_expr.has_struct_base ()) + { + rust_error_at (struct_expr.get_locus (), + "constructor is missing fields"); + return; + } + else + { + // we have a struct base to assign the missing fields from. + // the missing fields can be implicit FieldAccessExprs for the value + std::set missing_fields; + for (auto &field : variant->get_fields ()) + { + auto it = fields_assigned.find (field->get_name ()); + if (it == fields_assigned.end ()) + missing_fields.insert (field->get_name ()); + } + + // we can generate FieldAccessExpr or TupleAccessExpr for the + // values of the missing fields. + for (auto &missing : missing_fields) + { + HIR::Expr *receiver + = struct_expr.struct_base->base_struct->clone_expr_impl (); + + HIR::StructExprField *implicit_field = nullptr; + + AST::AttrVec outer_attribs; + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping mapping ( + crate_num, + struct_expr.struct_base->base_struct->get_mappings () + .get_nodeid (), + mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID); + + HIR::Expr *field_value = new HIR::FieldAccessExpr ( + mapping, std::unique_ptr (receiver), missing, + std::move (outer_attribs), + struct_expr.struct_base->base_struct->get_locus ()); + + implicit_field = new HIR::StructExprFieldIdentifierValue ( + mapping, missing, std::unique_ptr (field_value), + struct_expr.struct_base->base_struct->get_locus ()); + + size_t field_index; + bool ok = variant->lookup_field (missing, nullptr, &field_index); + rust_assert (ok); + + adtFieldIndexToField[field_index] = implicit_field; + struct_expr.get_fields ().push_back ( + std::unique_ptr (implicit_field)); + } + } + } + + if (struct_def->is_union ()) + { + // There is exactly one field in this constructor, we need to + // figure out the field index to make sure we initialize the + // right union field. + for (size_t i = 0; i < adtFieldIndexToField.size (); i++) + { + if (adtFieldIndexToField[i]) + { + struct_expr.union_index = i; + break; + } + } + rust_assert (struct_expr.union_index != -1); + } + else + { + // everything is ok, now we need to ensure all field values are ordered + // correctly. The GIMPLE backend uses a simple algorithm that assumes each + // assigned field in the constructor is in the same order as the field in + // the type + for (auto &field : struct_expr.get_fields ()) + field.release (); + + std::vector > ordered_fields; + for (size_t i = 0; i < adtFieldIndexToField.size (); i++) + { + ordered_fields.push_back ( + std::unique_ptr (adtFieldIndexToField[i])); + } + struct_expr.set_fields_as_owner (std::move (ordered_fields)); + } + + resolved = struct_def; +} + +void +TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifierValue &field) +{ + auto it = fields_assigned.find (field.field_name); + if (it != fields_assigned.end ()) + { + rust_fatal_error (field.get_locus (), "used more than once"); + return; + } + + size_t field_index; + TyTy::StructFieldType *field_type; + bool ok = variant->lookup_field (field.field_name, &field_type, &field_index); + if (!ok) + { + rust_error_at (field.get_locus (), "unknown field"); + return; + } + + TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ()); + resolved_field_value_expr + = coercion_site (field.get_mappings ().get_hirid (), + field_type->get_field_type (), value, field.get_locus ()); + if (resolved_field_value_expr != nullptr) + { + fields_assigned.insert (field.field_name); + adtFieldIndexToField[field_index] = &field; + } +} + +void +TypeCheckStructExpr::visit (HIR::StructExprFieldIndexValue &field) +{ + std::string field_name (std::to_string (field.get_tuple_index ())); + auto it = fields_assigned.find (field_name); + if (it != fields_assigned.end ()) + { + rust_fatal_error (field.get_locus (), "used more than once"); + return; + } + + size_t field_index; + TyTy::StructFieldType *field_type; + bool ok = variant->lookup_field (field_name, &field_type, &field_index); + if (!ok) + { + rust_error_at (field.get_locus (), "unknown field"); + return; + } + + TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ()); + resolved_field_value_expr + = coercion_site (field.get_mappings ().get_hirid (), + field_type->get_field_type (), value, field.get_locus ()); + if (resolved_field_value_expr != nullptr) + { + fields_assigned.insert (field_name); + adtFieldIndexToField[field_index] = &field; + } +} + +void +TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field) +{ + auto it = fields_assigned.find (field.get_field_name ()); + if (it != fields_assigned.end ()) + { + rust_fatal_error (field.get_locus (), "used more than once"); + return; + } + + size_t field_index; + TyTy::StructFieldType *field_type; + bool ok = variant->lookup_field (field.get_field_name (), &field_type, + &field_index); + if (!ok) + { + rust_error_at (field.get_locus (), "unknown field"); + return; + } + + // we can make the field look like a path expr to take advantage of existing + // code + Analysis::NodeMapping mappings_copy1 = field.get_mappings (); + Analysis::NodeMapping mappings_copy2 = field.get_mappings (); + + HIR::PathIdentSegment ident_seg (field.get_field_name ()); + HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (), + HIR::GenericArgs::create_empty ()); + HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false, + {}); + TyTy::BaseType *value = TypeCheckExpr::Resolve (&expr); + + resolved_field_value_expr + = coercion_site (field.get_mappings ().get_hirid (), + field_type->get_field_type (), value, field.get_locus ()); + if (resolved_field_value_expr != nullptr) + + { + fields_assigned.insert (field.get_field_name ()); + adtFieldIndexToField[field_index] = &field; + } +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc new file mode 100644 index 00000000000..27f36b642fc --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc @@ -0,0 +1,364 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-toplevel.h" +#include "rust-hir-type-check-enumitem.h" +#include "rust-hir-type-check-type.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-pattern.h" +#include "rust-hir-type-check-implitem.h" + +namespace Rust { +namespace Resolver { + +TypeCheckTopLevel::TypeCheckTopLevel () : TypeCheckBase () {} + +void +TypeCheckTopLevel::Resolve (HIR::Item &item) +{ + rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM); + HIR::VisItem &vis_item = static_cast (item); + + TypeCheckTopLevel resolver; + vis_item.accept_vis (resolver); +} + +void +TypeCheckTopLevel::visit (HIR::TypeAlias &alias) +{ + TyTy::BaseType *actual_type + = TypeCheckType::Resolve (alias.get_type_aliased ().get ()); + + context->insert_type (alias.get_mappings (), actual_type); + + for (auto &where_clause_item : alias.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } +} + +void +TypeCheckTopLevel::visit (HIR::TupleStruct &struct_decl) +{ + std::vector substitutions; + if (struct_decl.has_generics ()) + resolve_generic_params (struct_decl.get_generic_params (), substitutions); + + for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } + + std::vector fields; + size_t idx = 0; + for (auto &field : struct_decl.get_fields ()) + { + TyTy::BaseType *field_type + = TypeCheckType::Resolve (field.get_field_type ().get ()); + TyTy::StructFieldType *ty_field + = new TyTy::StructFieldType (field.get_mappings ().get_hirid (), + std::to_string (idx), field_type); + fields.push_back (ty_field); + context->insert_type (field.get_mappings (), ty_field->get_field_type ()); + idx++; + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok = mappings->lookup_canonical_path ( + struct_decl.get_mappings ().get_nodeid (), &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, struct_decl.get_locus ()}; + + // its a single variant ADT + std::vector variants; + variants.push_back (new TyTy::VariantDef ( + struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (), + ident, TyTy::VariantDef::VariantType::TUPLE, nullptr, std::move (fields))); + + // Process #[repr(X)] attribute, if any + const AST::AttrVec &attrs = struct_decl.get_outer_attrs (); + TyTy::ADTType::ReprOptions repr + = parse_repr_options (attrs, struct_decl.get_locus ()); + + TyTy::BaseType *type + = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + struct_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::TUPLE_STRUCT, + std::move (variants), std::move (substitutions), repr); + + context->insert_type (struct_decl.get_mappings (), type); +} + +void +TypeCheckTopLevel::visit (HIR::Module &module) +{ + for (auto &item : module.get_items ()) + TypeCheckTopLevel::Resolve (*item.get ()); +} + +void +TypeCheckTopLevel::visit (HIR::StructStruct &struct_decl) +{ + std::vector substitutions; + if (struct_decl.has_generics ()) + resolve_generic_params (struct_decl.get_generic_params (), substitutions); + + for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } + + std::vector fields; + for (auto &field : struct_decl.get_fields ()) + { + TyTy::BaseType *field_type + = TypeCheckType::Resolve (field.get_field_type ().get ()); + TyTy::StructFieldType *ty_field + = new TyTy::StructFieldType (field.get_mappings ().get_hirid (), + field.get_field_name (), field_type); + fields.push_back (ty_field); + context->insert_type (field.get_mappings (), ty_field->get_field_type ()); + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok = mappings->lookup_canonical_path ( + struct_decl.get_mappings ().get_nodeid (), &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, struct_decl.get_locus ()}; + + // its a single variant ADT + std::vector variants; + variants.push_back (new TyTy::VariantDef ( + struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (), + ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields))); + + // Process #[repr(X)] attribute, if any + const AST::AttrVec &attrs = struct_decl.get_outer_attrs (); + TyTy::ADTType::ReprOptions repr + = parse_repr_options (attrs, struct_decl.get_locus ()); + + TyTy::BaseType *type + = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + struct_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::STRUCT_STRUCT, + std::move (variants), std::move (substitutions), repr); + + context->insert_type (struct_decl.get_mappings (), type); +} + +void +TypeCheckTopLevel::visit (HIR::Enum &enum_decl) +{ + std::vector substitutions; + if (enum_decl.has_generics ()) + resolve_generic_params (enum_decl.get_generic_params (), substitutions); + + std::vector variants; + int64_t discriminant_value = 0; + for (auto &variant : enum_decl.get_variants ()) + { + TyTy::VariantDef *field_type + = TypeCheckEnumItem::Resolve (variant.get (), discriminant_value); + + discriminant_value++; + variants.push_back (field_type); + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok + = mappings->lookup_canonical_path (enum_decl.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, enum_decl.get_locus ()}; + + // multi variant ADT + TyTy::BaseType *type + = new TyTy::ADTType (enum_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + enum_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::ENUM, std::move (variants), + std::move (substitutions)); + + context->insert_type (enum_decl.get_mappings (), type); +} + +void +TypeCheckTopLevel::visit (HIR::Union &union_decl) +{ + std::vector substitutions; + if (union_decl.has_generics ()) + resolve_generic_params (union_decl.get_generic_params (), substitutions); + + for (auto &where_clause_item : union_decl.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } + + std::vector fields; + for (auto &variant : union_decl.get_variants ()) + { + TyTy::BaseType *variant_type + = TypeCheckType::Resolve (variant.get_field_type ().get ()); + TyTy::StructFieldType *ty_variant + = new TyTy::StructFieldType (variant.get_mappings ().get_hirid (), + variant.get_field_name (), variant_type); + fields.push_back (ty_variant); + context->insert_type (variant.get_mappings (), + ty_variant->get_field_type ()); + } + + // get the path + const CanonicalPath *canonical_path = nullptr; + bool ok + = mappings->lookup_canonical_path (union_decl.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + RustIdent ident{*canonical_path, union_decl.get_locus ()}; + + // there is only a single variant + std::vector variants; + variants.push_back (new TyTy::VariantDef ( + union_decl.get_mappings ().get_hirid (), union_decl.get_identifier (), + ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields))); + + TyTy::BaseType *type + = new TyTy::ADTType (union_decl.get_mappings ().get_hirid (), + mappings->get_next_hir_id (), + union_decl.get_identifier (), ident, + TyTy::ADTType::ADTKind::UNION, std::move (variants), + std::move (substitutions)); + + context->insert_type (union_decl.get_mappings (), type); +} + +void +TypeCheckTopLevel::visit (HIR::StaticItem &var) +{ + TyTy::BaseType *type = TypeCheckType::Resolve (var.get_type ()); + TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (var.get_expr ()); + + context->insert_type (var.get_mappings (), type->unify (expr_type)); +} + +void +TypeCheckTopLevel::visit (HIR::ConstantItem &constant) +{ + TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ()); + TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ()); + + context->insert_type (constant.get_mappings (), type->unify (expr_type)); +} + +void +TypeCheckTopLevel::visit (HIR::Function &function) +{ + std::vector substitutions; + if (function.has_generics ()) + resolve_generic_params (function.get_generic_params (), substitutions); + + for (auto &where_clause_item : function.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } + + TyTy::BaseType *ret_type = nullptr; + if (!function.has_function_return_type ()) + ret_type + = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ()); + else + { + auto resolved + = TypeCheckType::Resolve (function.get_return_type ().get ()); + if (resolved->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (function.get_locus (), + "failed to resolve return type"); + return; + } + + ret_type = resolved->clone (); + ret_type->set_ref ( + function.get_return_type ()->get_mappings ().get_hirid ()); + } + + std::vector> params; + for (auto ¶m : function.get_function_params ()) + { + // get the name as well required for later on + auto param_tyty = TypeCheckType::Resolve (param.get_type ()); + params.push_back ( + std::pair (param.get_param_name (), + param_tyty)); + + context->insert_type (param.get_mappings (), param_tyty); + TypeCheckPattern::Resolve (param.get_param_name (), param_tyty); + } + + const CanonicalPath *canonical_path = nullptr; + bool ok + = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, function.get_locus ()}; + auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (), + function.get_mappings ().get_defid (), + function.get_function_name (), ident, + TyTy::FnType::FNTYPE_DEFAULT_FLAGS, ABI::RUST, + std::move (params), ret_type, + std::move (substitutions)); + + context->insert_type (function.get_mappings (), fnType); +} + +void +TypeCheckTopLevel::visit (HIR::ImplBlock &impl_block) +{ + std::vector substitutions; + if (impl_block.has_generics ()) + resolve_generic_params (impl_block.get_generic_params (), substitutions); + + for (auto &where_clause_item : impl_block.get_where_clause ().get_items ()) + { + ResolveWhereClauseItem::Resolve (*where_clause_item.get ()); + } + + auto self = TypeCheckType::Resolve (impl_block.get_type ().get ()); + if (self->get_kind () == TyTy::TypeKind::ERROR) + return; + + for (auto &impl_item : impl_block.get_impl_items ()) + TypeCheckTopLevelImplItem::Resolve (impl_item.get (), self, substitutions); +} + +void +TypeCheckTopLevel::visit (HIR::ExternBlock &extern_block) +{ + for (auto &item : extern_block.get_extern_items ()) + { + TypeCheckTopLevelExternItem::Resolve (item.get (), extern_block); + } +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.h b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h new file mode 100644 index 00000000000..d0db07d7281 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h @@ -0,0 +1,56 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_TOPLEVEL +#define RUST_HIR_TYPE_CHECK_TOPLEVEL + +#include "rust-hir-type-check-base.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckTopLevel : private TypeCheckBase, public HIR::HIRVisItemVisitor +{ +public: + static void Resolve (HIR::Item &item); + + void visit (HIR::Module &module) override; + void visit (HIR::Function &function) override; + void visit (HIR::TypeAlias &alias) override; + void visit (HIR::TupleStruct &struct_decl) override; + void visit (HIR::StructStruct &struct_decl) override; + void visit (HIR::Enum &enum_decl) override; + void visit (HIR::Union &union_decl) override; + void visit (HIR::StaticItem &var) override; + void visit (HIR::ConstantItem &constant) override; + void visit (HIR::ImplBlock &impl_block) override; + void visit (HIR::ExternBlock &extern_block) override; + + // nothing to do + void visit (HIR::Trait &trait_block) override {} + void visit (HIR::ExternCrate &crate) override {} + void visit (HIR::UseDeclaration &use_decl) override {} + +private: + TypeCheckTopLevel (); +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_TOPLEVEL diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc new file mode 100644 index 00000000000..3538d77b220 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc @@ -0,0 +1,838 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-type.h" +#include "rust-hir-trait-resolve.h" +#include "rust-hir-type-check-expr.h" + +namespace Rust { +namespace Resolver { + +HIR::GenericArgs +TypeCheckResolveGenericArguments::resolve (HIR::TypePathSegment *segment) +{ + TypeCheckResolveGenericArguments resolver (segment->get_locus ()); + switch (segment->get_type ()) + { + case HIR::TypePathSegment::SegmentType::GENERIC: + resolver.visit (static_cast (*segment)); + break; + + default: + break; + } + return resolver.args; +} + +void +TypeCheckResolveGenericArguments::visit (HIR::TypePathSegmentGeneric &generic) +{ + args = generic.get_generic_args (); +} + +TyTy::BaseType * +TypeCheckType::Resolve (HIR::Type *type) +{ + TypeCheckType resolver (type->get_mappings ().get_hirid ()); + type->accept_vis (resolver); + rust_assert (resolver.translated != nullptr); + resolver.context->insert_type (type->get_mappings (), resolver.translated); + return resolver.translated; +} + +void +TypeCheckType::visit (HIR::BareFunctionType &fntype) +{ + TyTy::BaseType *return_type + = fntype.has_return_type () + ? TypeCheckType::Resolve (fntype.get_return_type ().get ()) + : TyTy::TupleType::get_unit_type (fntype.get_mappings ().get_hirid ()); + + std::vector params; + for (auto ¶m : fntype.get_function_params ()) + { + TyTy::BaseType *ptype = TypeCheckType::Resolve (param.get_type ().get ()); + params.push_back (TyTy::TyVar (ptype->get_ref ())); + } + + translated = new TyTy::FnPtr (fntype.get_mappings ().get_hirid (), + fntype.get_locus (), std::move (params), + TyTy::TyVar (return_type->get_ref ())); +} + +void +TypeCheckType::visit (HIR::TupleType &tuple) +{ + if (tuple.is_unit_type ()) + { + auto unit_node_id = resolver->get_unit_type_node_id (); + if (!context->lookup_builtin (unit_node_id, &translated)) + { + rust_error_at (tuple.get_locus (), + "failed to lookup builtin unit type"); + } + return; + } + + std::vector fields; + for (auto &elem : tuple.get_elems ()) + { + auto field_ty = TypeCheckType::Resolve (elem.get ()); + fields.push_back (TyTy::TyVar (field_ty->get_ref ())); + } + + translated = new TyTy::TupleType (tuple.get_mappings ().get_hirid (), + tuple.get_locus (), fields); +} + +void +TypeCheckType::visit (HIR::TypePath &path) +{ + // lookup the Node this resolves to + NodeId ref; + auto nid = path.get_mappings ().get_nodeid (); + bool is_fully_resolved = resolver->lookup_resolved_type (nid, &ref); + + TyTy::BaseType *lookup = nullptr; + if (!is_fully_resolved) + { + // this can happen so we need to look up the root then resolve the + // remaining segments if possible + size_t offset = 0; + NodeId resolved_node_id = UNKNOWN_NODEID; + TyTy::BaseType *root + = resolve_root_path (path, &offset, &resolved_node_id); + + rust_assert (root != nullptr); + if (root->get_kind () == TyTy::TypeKind::ERROR) + return; + + translated + = resolve_segments (resolved_node_id, path.get_mappings ().get_hirid (), + path.get_segments (), offset, root, + path.get_mappings (), path.get_locus ()); + return; + } + + HirId hir_lookup; + if (!context->lookup_type_by_node_id (ref, &hir_lookup)) + { + rust_error_at (path.get_locus (), "failed to lookup HIR %d for node '%s'", + ref, path.as_string ().c_str ()); + return; + } + + if (!context->lookup_type (hir_lookup, &lookup)) + { + rust_error_at (path.get_locus (), "failed to lookup HIR TyTy"); + return; + } + + TyTy::BaseType *path_type = lookup->clone (); + path_type->set_ref (path.get_mappings ().get_hirid ()); + + HIR::TypePathSegment *final_seg = path.get_final_segment ().get (); + HIR::GenericArgs args = TypeCheckResolveGenericArguments::resolve (final_seg); + + bool is_big_self = final_seg->is_ident_only () + && (final_seg->as_string ().compare ("Self") == 0); + + if (path_type->needs_generic_substitutions ()) + { + if (is_big_self) + { + translated = path_type; + return; + } + + translated = SubstMapper::Resolve (path_type, path.get_locus (), &args); + } + else if (!args.is_empty ()) + { + rust_error_at (path.get_locus (), + "TypePath %s declares generic arguments but " + "the type %s does not have any", + path.as_string ().c_str (), + path_type->as_string ().c_str ()); + } + else + { + translated = path_type; + } +} + +void +TypeCheckType::visit (HIR::QualifiedPathInType &path) +{ + HIR::QualifiedPathType qual_path_type = path.get_path_type (); + TyTy::BaseType *root + = TypeCheckType::Resolve (qual_path_type.get_type ().get ()); + if (root->get_kind () == TyTy::TypeKind::ERROR) + { + rust_debug_loc (path.get_locus (), "failed to resolve the root"); + return; + } + + if (!qual_path_type.has_as_clause ()) + { + // then this is just a normal path-in-expression + NodeId root_resolved_node_id = UNKNOWN_NODEID; + bool ok = resolver->lookup_resolved_type ( + qual_path_type.get_type ()->get_mappings ().get_nodeid (), + &root_resolved_node_id); + rust_assert (ok); + + translated = resolve_segments (root_resolved_node_id, + path.get_mappings ().get_hirid (), + path.get_segments (), 0, translated, + path.get_mappings (), path.get_locus ()); + + return; + } + + // Resolve the trait now + TraitReference *trait_ref + = TraitResolver::Resolve (*qual_path_type.get_trait ().get ()); + if (trait_ref->is_error ()) + return; + + // does this type actually implement this type-bound? + if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref)) + { + rust_error_at (qual_path_type.get_locus (), + "root does not satisfy specified trait-bound"); + return; + } + + // get the predicate for the bound + auto specified_bound + = get_predicate_from_bound (*qual_path_type.get_trait ().get ()); + if (specified_bound.is_error ()) + return; + + // inherit the bound + root->inherit_bounds ({specified_bound}); + + // setup the associated types + const TraitReference *specified_bound_ref = specified_bound.get (); + auto candidates = TypeBoundsProbe::Probe (root); + AssociatedImplTrait *associated_impl_trait = nullptr; + for (auto &probed_bound : candidates) + { + const TraitReference *bound_trait_ref = probed_bound.first; + const HIR::ImplBlock *associated_impl = probed_bound.second; + + HirId impl_block_id = associated_impl->get_mappings ().get_hirid (); + AssociatedImplTrait *associated = nullptr; + bool found_impl_trait + = context->lookup_associated_trait_impl (impl_block_id, &associated); + if (found_impl_trait) + { + bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref); + bool found_self = associated->get_self ()->can_eq (root, false); + if (found_trait && found_self) + { + associated_impl_trait = associated; + break; + } + } + } + + if (associated_impl_trait != nullptr) + { + associated_impl_trait->setup_associated_types (root, specified_bound); + } + + // lookup the associated item from the specified bound + std::unique_ptr &item_seg + = path.get_associated_segment (); + HIR::PathIdentSegment item_seg_identifier = item_seg->get_ident_segment (); + TyTy::TypeBoundPredicateItem item + = specified_bound.lookup_associated_item (item_seg_identifier.as_string ()); + if (item.is_error ()) + { + rust_error_at (item_seg->get_locus (), "unknown associated item"); + return; + } + + // infer the root type + translated = item.get_tyty_for_receiver (root); + + // turbo-fish segment path:: + if (item_seg->get_type () == HIR::TypePathSegment::SegmentType::GENERIC) + { + HIR::TypePathSegmentGeneric &generic_seg + = static_cast (*item_seg.get ()); + + // turbo-fish segment path:: + if (generic_seg.has_generic_args ()) + { + if (!translated->can_substitute ()) + { + rust_error_at (item_seg->get_locus (), + "substitutions not supported for %s", + translated->as_string ().c_str ()); + translated + = new TyTy::ErrorType (path.get_mappings ().get_hirid ()); + return; + } + translated = SubstMapper::Resolve (translated, path.get_locus (), + &generic_seg.get_generic_args ()); + } + } + + // continue on as a path-in-expression + const TraitItemReference *trait_item_ref = item.get_raw_item (); + NodeId root_resolved_node_id = trait_item_ref->get_mappings ().get_nodeid (); + bool fully_resolved = path.get_segments ().empty (); + if (fully_resolved) + { + resolver->insert_resolved_type (path.get_mappings ().get_nodeid (), + root_resolved_node_id); + context->insert_receiver (path.get_mappings ().get_hirid (), root); + return; + } + + translated + = resolve_segments (root_resolved_node_id, + path.get_mappings ().get_hirid (), path.get_segments (), + 0, translated, path.get_mappings (), path.get_locus ()); +} + +TyTy::BaseType * +TypeCheckType::resolve_root_path (HIR::TypePath &path, size_t *offset, + NodeId *root_resolved_node_id) +{ + TyTy::BaseType *root_tyty = nullptr; + *offset = 0; + for (size_t i = 0; i < path.get_num_segments (); i++) + { + std::unique_ptr &seg = path.get_segments ().at (i); + + bool have_more_segments = (path.get_num_segments () - 1 != i); + bool is_root = *offset == 0; + NodeId ast_node_id = seg->get_mappings ().get_nodeid (); + + // then lookup the reference_node_id + NodeId ref_node_id = UNKNOWN_NODEID; + if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id)) + { + resolver->lookup_resolved_type (ast_node_id, &ref_node_id); + } + + // ref_node_id is the NodeId that the segments refers to. + if (ref_node_id == UNKNOWN_NODEID) + { + if (is_root) + { + rust_error_at (seg->get_locus (), + "unknown reference for resolved name: %<%s%>", + seg->get_ident_segment ().as_string ().c_str ()); + return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); + } + return root_tyty; + } + + // node back to HIR + HirId ref = UNKNOWN_HIRID; + if (!mappings->lookup_node_to_hir (ref_node_id, &ref)) + { + if (is_root) + { + rust_error_at (seg->get_locus (), "789 reverse lookup failure"); + rust_debug_loc ( + seg->get_locus (), + "failure with [%s] mappings [%s] ref_node_id [%u]", + seg->as_string ().c_str (), + seg->get_mappings ().as_string ().c_str (), ref_node_id); + + return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); + } + + return root_tyty; + } + + auto seg_is_module = (nullptr != mappings->lookup_module (ref)); + auto seg_is_crate = mappings->is_local_hirid_crate (ref); + if (seg_is_module || seg_is_crate) + { + // A::B::C::this_is_a_module::D::E::F + // ^^^^^^^^^^^^^^^^ + // Currently handling this. + if (have_more_segments) + { + (*offset)++; + continue; + } + + // In the case of : + // A::B::C::this_is_a_module + // ^^^^^^^^^^^^^^^^ + // This is an error, we are not expecting a module. + rust_error_at (seg->get_locus (), "expected value"); + return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); + } + + TyTy::BaseType *lookup = nullptr; + if (!context->lookup_type (ref, &lookup)) + { + if (is_root) + { + rust_error_at (seg->get_locus (), + "failed to resolve root segment"); + return new TyTy::ErrorType (path.get_mappings ().get_hirid ()); + } + return root_tyty; + } + + // if we have a previous segment type + if (root_tyty != nullptr) + { + // if this next segment needs substitution we must apply the + // previous type arguments + // + // such as: GenericStruct::<_>::new(123, 456) + if (lookup->needs_generic_substitutions ()) + { + if (!root_tyty->needs_generic_substitutions ()) + { + auto used_args_in_prev_segment + = GetUsedSubstArgs::From (root_tyty); + lookup + = SubstMapperInternal::Resolve (lookup, + used_args_in_prev_segment); + } + } + } + + // turbo-fish segment path:: + if (seg->is_generic_segment ()) + { + HIR::TypePathSegmentGeneric *generic_segment + = static_cast (seg.get ()); + + if (!lookup->can_substitute ()) + { + rust_error_at (seg->get_locus (), + "substitutions not supported for %s", + lookup->as_string ().c_str ()); + return new TyTy::ErrorType (lookup->get_ref ()); + } + lookup = SubstMapper::Resolve (lookup, path.get_locus (), + &generic_segment->get_generic_args ()); + } + + *root_resolved_node_id = ref_node_id; + *offset = *offset + 1; + root_tyty = lookup; + } + + return root_tyty; +} + +TyTy::BaseType * +TypeCheckType::resolve_segments ( + NodeId root_resolved_node_id, HirId expr_id, + std::vector> &segments, size_t offset, + TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings, + Location expr_locus) +{ + NodeId resolved_node_id = root_resolved_node_id; + TyTy::BaseType *prev_segment = tyseg; + for (size_t i = offset; i < segments.size (); i++) + { + std::unique_ptr &seg = segments.at (i); + + bool reciever_is_generic + = prev_segment->get_kind () == TyTy::TypeKind::PARAM; + bool probe_bounds = true; + bool probe_impls = !reciever_is_generic; + bool ignore_mandatory_trait_items = !reciever_is_generic; + + // probe the path is done in two parts one where we search impls if no + // candidate is found then we search extensions from traits + auto candidates + = PathProbeType::Probe (prev_segment, seg->get_ident_segment (), + probe_impls, false, + ignore_mandatory_trait_items); + if (candidates.size () == 0) + { + candidates + = PathProbeType::Probe (prev_segment, seg->get_ident_segment (), + false, probe_bounds, + ignore_mandatory_trait_items); + + if (candidates.size () == 0) + { + rust_error_at ( + seg->get_locus (), + "failed to resolve path segment using an impl Probe"); + return new TyTy::ErrorType (expr_id); + } + } + + if (candidates.size () > 1) + { + ReportMultipleCandidateError::Report (candidates, + seg->get_ident_segment (), + seg->get_locus ()); + return new TyTy::ErrorType (expr_id); + } + + auto &candidate = candidates.at (0); + prev_segment = tyseg; + tyseg = candidate.ty; + + if (candidate.is_impl_candidate ()) + { + resolved_node_id + = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid (); + } + else + { + resolved_node_id + = candidate.item.trait.item_ref->get_mappings ().get_nodeid (); + } + + if (seg->is_generic_segment ()) + { + HIR::TypePathSegmentGeneric *generic_segment + = static_cast (seg.get ()); + + if (!tyseg->can_substitute ()) + { + rust_error_at (expr_locus, "substitutions not supported for %s", + tyseg->as_string ().c_str ()); + return new TyTy::ErrorType (expr_id); + } + + tyseg = SubstMapper::Resolve (tyseg, expr_locus, + &generic_segment->get_generic_args ()); + if (tyseg->get_kind () == TyTy::TypeKind::ERROR) + return new TyTy::ErrorType (expr_id); + } + } + + context->insert_receiver (expr_mappings.get_hirid (), prev_segment); + if (tyseg->needs_generic_substitutions ()) + { + Location locus = segments.back ()->get_locus (); + if (!prev_segment->needs_generic_substitutions ()) + { + auto used_args_in_prev_segment + = GetUsedSubstArgs::From (prev_segment); + if (!used_args_in_prev_segment.is_error ()) + tyseg + = SubstMapperInternal::Resolve (tyseg, used_args_in_prev_segment); + } + else + { + tyseg = SubstMapper::InferSubst (tyseg, locus); + } + + if (tyseg->get_kind () == TyTy::TypeKind::ERROR) + return new TyTy::ErrorType (expr_id); + } + + rust_assert (resolved_node_id != UNKNOWN_NODEID); + + // lookup if the name resolver was able to canonically resolve this or not + NodeId path_resolved_id = UNKNOWN_NODEID; + if (resolver->lookup_resolved_name (expr_mappings.get_nodeid (), + &path_resolved_id)) + { + rust_assert (path_resolved_id == resolved_node_id); + } + // check the type scope + else if (resolver->lookup_resolved_type (expr_mappings.get_nodeid (), + &path_resolved_id)) + { + rust_assert (path_resolved_id == resolved_node_id); + } + else + { + // name scope first + if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id)) + { + resolver->insert_resolved_name (expr_mappings.get_nodeid (), + resolved_node_id); + } + // check the type scope + else if (resolver->get_type_scope ().decl_was_declared_here ( + resolved_node_id)) + { + resolver->insert_resolved_type (expr_mappings.get_nodeid (), + resolved_node_id); + } + } + + return tyseg; +} + +void +TypeCheckType::visit (HIR::TraitObjectType &type) +{ + std::vector specified_bounds; + for (auto &bound : type.get_type_param_bounds ()) + { + if (bound->get_bound_type () + != HIR::TypeParamBound::BoundType::TRAITBOUND) + continue; + + HIR::TypeParamBound &b = *bound.get (); + HIR::TraitBound &trait_bound = static_cast (b); + + TyTy::TypeBoundPredicate predicate + = get_predicate_from_bound (trait_bound.get_path ()); + + if (!predicate.is_error () + && predicate.is_object_safe (true, type.get_locus ())) + specified_bounds.push_back (std::move (predicate)); + } + + RustIdent ident{CanonicalPath::create_empty (), type.get_locus ()}; + translated + = new TyTy::DynamicObjectType (type.get_mappings ().get_hirid (), ident, + std::move (specified_bounds)); +} + +void +TypeCheckType::visit (HIR::ArrayType &type) +{ + auto capacity_type = TypeCheckExpr::Resolve (type.get_size_expr ()); + if (capacity_type->get_kind () == TyTy::TypeKind::ERROR) + return; + + TyTy::BaseType *expected_ty = nullptr; + bool ok = context->lookup_builtin ("usize", &expected_ty); + rust_assert (ok); + context->insert_type (type.get_size_expr ()->get_mappings (), expected_ty); + + auto unified = expected_ty->unify (capacity_type); + if (unified->get_kind () == TyTy::TypeKind::ERROR) + return; + + TyTy::BaseType *base = TypeCheckType::Resolve (type.get_element_type ()); + translated = new TyTy::ArrayType (type.get_mappings ().get_hirid (), + type.get_locus (), *type.get_size_expr (), + TyTy::TyVar (base->get_ref ())); +} + +void +TypeCheckType::visit (HIR::SliceType &type) +{ + TyTy::BaseType *base + = TypeCheckType::Resolve (type.get_element_type ().get ()); + translated + = new TyTy::SliceType (type.get_mappings ().get_hirid (), type.get_locus (), + TyTy::TyVar (base->get_ref ())); +} +void +TypeCheckType::visit (HIR::ReferenceType &type) +{ + TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ()); + translated + = new TyTy::ReferenceType (type.get_mappings ().get_hirid (), + TyTy::TyVar (base->get_ref ()), type.get_mut ()); +} + +void +TypeCheckType::visit (HIR::RawPointerType &type) +{ + TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ()); + translated + = new TyTy::PointerType (type.get_mappings ().get_hirid (), + TyTy::TyVar (base->get_ref ()), type.get_mut ()); +} + +void +TypeCheckType::visit (HIR::InferredType &type) +{ + translated = new TyTy::InferType (type.get_mappings ().get_hirid (), + TyTy::InferType::InferTypeKind::GENERAL, + type.get_locus ()); +} + +void +TypeCheckType::visit (HIR::NeverType &type) +{ + TyTy::BaseType *lookup = nullptr; + bool ok = context->lookup_builtin ("!", &lookup); + rust_assert (ok); + + translated = lookup->clone (); +} + +TyTy::ParamType * +TypeResolveGenericParam::Resolve (HIR::GenericParam *param) +{ + TypeResolveGenericParam resolver; + switch (param->get_kind ()) + { + case HIR::GenericParam::GenericKind::TYPE: + resolver.visit (static_cast (*param)); + break; + + case HIR::GenericParam::GenericKind::CONST: + resolver.visit (static_cast (*param)); + break; + + case HIR::GenericParam::GenericKind::LIFETIME: + resolver.visit (static_cast (*param)); + break; + } + return resolver.resolved; +} + +void +TypeResolveGenericParam::visit (HIR::LifetimeParam ¶m) +{ + // nothing to do +} + +void +TypeResolveGenericParam::visit (HIR::ConstGenericParam ¶m) +{ + // TODO +} + +void +TypeResolveGenericParam::visit (HIR::TypeParam ¶m) +{ + if (param.has_type ()) + TypeCheckType::Resolve (param.get_type ().get ()); + + std::vector specified_bounds; + if (param.has_type_param_bounds ()) + { + for (auto &bound : param.get_type_param_bounds ()) + { + switch (bound->get_bound_type ()) + { + case HIR::TypeParamBound::BoundType::TRAITBOUND: { + HIR::TraitBound *b + = static_cast (bound.get ()); + + TyTy::TypeBoundPredicate predicate + = get_predicate_from_bound (b->get_path ()); + if (!predicate.is_error ()) + specified_bounds.push_back (std::move (predicate)); + } + break; + + default: + break; + } + } + } + + resolved + = new TyTy::ParamType (param.get_type_representation (), param.get_locus (), + param.get_mappings ().get_hirid (), param, + specified_bounds); +} + +void +ResolveWhereClauseItem::Resolve (HIR::WhereClauseItem &item) +{ + ResolveWhereClauseItem resolver; + switch (item.get_item_type ()) + { + case HIR::WhereClauseItem::LIFETIME: + resolver.visit (static_cast (item)); + break; + + case HIR::WhereClauseItem::TYPE_BOUND: + resolver.visit (static_cast (item)); + break; + } +} + +void +ResolveWhereClauseItem::visit (HIR::LifetimeWhereClauseItem &item) +{} + +void +ResolveWhereClauseItem::visit (HIR::TypeBoundWhereClauseItem &item) +{ + auto &binding_type_path = item.get_bound_type (); + TyTy::BaseType *binding = TypeCheckType::Resolve (binding_type_path.get ()); + + std::vector specified_bounds; + for (auto &bound : item.get_type_param_bounds ()) + { + switch (bound->get_bound_type ()) + { + case HIR::TypeParamBound::BoundType::TRAITBOUND: { + HIR::TraitBound *b = static_cast (bound.get ()); + + TyTy::TypeBoundPredicate predicate + = get_predicate_from_bound (b->get_path ()); + if (!predicate.is_error ()) + specified_bounds.push_back (std::move (predicate)); + } + break; + + default: + break; + } + } + binding->inherit_bounds (specified_bounds); + + // When we apply these bounds we must lookup which type this binding + // resolves to, as this is the type which will be used during resolution + // of the block. + NodeId ast_node_id = binding_type_path->get_mappings ().get_nodeid (); + + // then lookup the reference_node_id + NodeId ref_node_id = UNKNOWN_NODEID; + if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id)) + { + // FIXME + rust_error_at (Location (), + "Failed to lookup type reference for node: %s", + binding_type_path->as_string ().c_str ()); + return; + } + + // node back to HIR + HirId ref; + if (!mappings->lookup_node_to_hir (ref_node_id, &ref)) + { + // FIXME + rust_error_at (Location (), "where-clause reverse lookup failure"); + return; + } + + // the base reference for this name _must_ have a type set + TyTy::BaseType *lookup; + if (!context->lookup_type (ref, &lookup)) + { + rust_error_at (mappings->lookup_location (ref), + "Failed to resolve where-clause binding type: %s", + binding_type_path->as_string ().c_str ()); + return; + } + + // FIXME + // rust_assert (binding->is_equal (*lookup)); + lookup->inherit_bounds (specified_bounds); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h new file mode 100644 index 00000000000..90d5ddbb411 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-type.h @@ -0,0 +1,130 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_TYPE +#define RUST_HIR_TYPE_CHECK_TYPE + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" +#include "rust-substitution-mapper.h" +#include "rust-hir-path-probe.h" + +namespace Rust { +namespace Resolver { + +// FIXME +// This simply fetches the HIR:::GenericArgs from the base class. Check to see +// if we can get rid of this class +class TypeCheckResolveGenericArguments : public TypeCheckBase +{ +public: + static HIR::GenericArgs resolve (HIR::TypePathSegment *segment); + + void visit (HIR::TypePathSegmentGeneric &generic); + +private: + TypeCheckResolveGenericArguments (Location locus) + : TypeCheckBase (), args (HIR::GenericArgs::create_empty (locus)) + {} + + HIR::GenericArgs args; +}; + +class TypeCheckType : public TypeCheckBase, public HIR::HIRTypeVisitor +{ +public: + static TyTy::BaseType *Resolve (HIR::Type *type); + + void visit (HIR::BareFunctionType &fntype) override; + void visit (HIR::TupleType &tuple) override; + void visit (HIR::TypePath &path) override; + void visit (HIR::QualifiedPathInType &path) override; + void visit (HIR::ArrayType &type) override; + void visit (HIR::SliceType &type) override; + void visit (HIR::ReferenceType &type) override; + void visit (HIR::RawPointerType &type) override; + void visit (HIR::InferredType &type) override; + void visit (HIR::NeverType &type) override; + void visit (HIR::TraitObjectType &type) override; + + void visit (HIR::TypePathSegmentFunction &segment) override + { /* TODO */ + } + void visit (HIR::TraitBound &bound) override + { /* TODO */ + } + void visit (HIR::ImplTraitType &type) override + { /* TODO */ + } + void visit (HIR::ParenthesisedType &type) override + { /* TODO */ + } + void visit (HIR::ImplTraitTypeOneBound &type) override + { /* TODO */ + } + +private: + TypeCheckType (HirId id) + : TypeCheckBase (), translated (new TyTy::ErrorType (id)) + {} + + TyTy::BaseType *resolve_root_path (HIR::TypePath &path, size_t *offset, + NodeId *root_resolved_node_id); + + TyTy::BaseType *resolve_segments ( + NodeId root_resolved_node_id, HirId expr_id, + std::vector> &segments, size_t offset, + TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings, + Location expr_locus); + + TyTy::BaseType *translated; +}; + +class TypeResolveGenericParam : public TypeCheckBase +{ +public: + static TyTy::ParamType *Resolve (HIR::GenericParam *param); + +protected: + void visit (HIR::TypeParam ¶m); + void visit (HIR::LifetimeParam ¶m); + void visit (HIR::ConstGenericParam ¶m); + +private: + TypeResolveGenericParam () : TypeCheckBase (), resolved (nullptr) {} + + TyTy::ParamType *resolved; +}; + +class ResolveWhereClauseItem : public TypeCheckBase +{ +public: + static void Resolve (HIR::WhereClauseItem &item); + +protected: + void visit (HIR::LifetimeWhereClauseItem &item); + void visit (HIR::TypeBoundWhereClauseItem &item); + +private: + ResolveWhereClauseItem () : TypeCheckBase () {} +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_TYPE diff --git a/gcc/rust/typecheck/rust-hir-type-check-util.cc b/gcc/rust/typecheck/rust-hir-type-check-util.cc new file mode 100644 index 00000000000..e25f431a507 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-util.cc @@ -0,0 +1,41 @@ +// Copyright (C) 2021-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check-util.h" +#include "rust-hir-full.h" + +namespace Rust { +namespace Resolver { + +void +ImplTypeIterator::go () +{ + for (auto &item : impl.get_impl_items ()) + { + item->accept_vis (*this); + } +} + +void +ImplTypeIterator::visit (HIR::TypeAlias &alias) +{ + cb (alias); +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check-util.h b/gcc/rust/typecheck/rust-hir-type-check-util.h new file mode 100644 index 00000000000..1a4b17a3303 --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check-util.h @@ -0,0 +1,50 @@ +// Copyright (C) 2021-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK_UTIL_H +#define RUST_HIR_TYPE_CHECK_UTIL_H + +#include "rust-system.h" +#include "rust-hir-visitor.h" + +namespace Rust { +namespace Resolver { + +class ImplTypeIterator : public HIR::HIRFullVisitorBase +{ + using HIR::HIRFullVisitorBase::visit; + +public: + ImplTypeIterator (HIR::ImplBlock &impl, + std::function cb) + : impl (impl), cb (cb) + {} + + void go (); + + void visit (HIR::TypeAlias &alias) override; + +private: + HIR::ImplBlock &impl; + std::function cb; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK_UTIL_H diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc new file mode 100644 index 00000000000..c314585cd3d --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check.cc @@ -0,0 +1,295 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check.h" +#include "rust-hir-full.h" +#include "rust-hir-type-check-toplevel.h" +#include "rust-hir-type-check-item.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-pattern.h" +#include "rust-hir-type-check-struct-field.h" +#include "rust-hir-inherent-impl-overlap.h" + +extern bool +saw_errors (void); + +namespace Rust { +namespace Resolver { + +void +TypeResolution::Resolve (HIR::Crate &crate) +{ + for (auto it = crate.items.begin (); it != crate.items.end (); it++) + TypeCheckTopLevel::Resolve (*it->get ()); + + if (saw_errors ()) + return; + + OverlappingImplItemPass::go (); + if (saw_errors ()) + return; + + for (auto it = crate.items.begin (); it != crate.items.end (); it++) + TypeCheckItem::Resolve (*it->get ()); + + if (saw_errors ()) + return; + + auto mappings = Analysis::Mappings::get (); + auto context = TypeCheckContext::get (); + + // default inference variables if possible + context->iterate ([&] (HirId id, TyTy::BaseType *ty) mutable -> bool { + // nothing to do + if (ty->get_kind () != TyTy::TypeKind::INFER) + return true; + + TyTy::InferType *infer_var = static_cast (ty); + TyTy::BaseType *default_type; + bool ok = infer_var->default_type (&default_type); + if (!ok) + { + rust_error_at (mappings->lookup_location (id), + "type annotations needed"); + return true; + } + else + { + auto result = ty->unify (default_type); + result->set_ref (id); + context->insert_type ( + Analysis::NodeMapping (mappings->get_current_crate (), 0, id, + UNKNOWN_LOCAL_DEFID), + result); + } + + return true; + }); +} + +// rust-hir-trait-ref.h + +TraitItemReference::TraitItemReference ( + std::string identifier, bool optional, TraitItemType type, + HIR::TraitItem *hir_trait_item, TyTy::BaseType *self, + std::vector substitutions, Location locus) + : identifier (identifier), optional_flag (optional), type (type), + hir_trait_item (hir_trait_item), + inherited_substitutions (std::move (substitutions)), locus (locus), + self (self), context (TypeCheckContext::get ()) +{} + +TraitItemReference::TraitItemReference (TraitItemReference const &other) + : identifier (other.identifier), optional_flag (other.optional_flag), + type (other.type), hir_trait_item (other.hir_trait_item), + locus (other.locus), self (other.self), context (TypeCheckContext::get ()) +{ + inherited_substitutions.clear (); + inherited_substitutions.reserve (other.inherited_substitutions.size ()); + for (size_t i = 0; i < other.inherited_substitutions.size (); i++) + inherited_substitutions.push_back ( + other.inherited_substitutions.at (i).clone ()); +} + +TraitItemReference & +TraitItemReference::operator= (TraitItemReference const &other) +{ + identifier = other.identifier; + optional_flag = other.optional_flag; + type = other.type; + hir_trait_item = other.hir_trait_item; + self = other.self; + locus = other.locus; + context = other.context; + + inherited_substitutions.clear (); + inherited_substitutions.reserve (other.inherited_substitutions.size ()); + for (size_t i = 0; i < other.inherited_substitutions.size (); i++) + inherited_substitutions.push_back ( + other.inherited_substitutions.at (i).clone ()); + + return *this; +} + +TyTy::BaseType * +TraitItemReference::get_type_from_typealias (/*const*/ + HIR::TraitItemType &type) const +{ + TyTy::TyVar var (get_mappings ().get_hirid ()); + return var.get_tyty (); +} + +TyTy::BaseType * +TraitItemReference::get_type_from_constant ( + /*const*/ HIR::TraitItemConst &constant) const +{ + TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ().get ()); + if (constant.has_expr ()) + { + TyTy::BaseType *expr + = TypeCheckExpr::Resolve (constant.get_expr ().get ()); + + return type->unify (expr); + } + return type; +} + +TyTy::BaseType * +TraitItemReference::get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const +{ + std::vector substitutions + = inherited_substitutions; + + HIR::TraitFunctionDecl &function = fn.get_decl (); + if (function.has_generics ()) + { + for (auto &generic_param : function.get_generic_params ()) + { + switch (generic_param.get ()->get_kind ()) + { + case HIR::GenericParam::GenericKind::LIFETIME: + case HIR::GenericParam::GenericKind::CONST: + // FIXME: Skipping Lifetime and Const completely until better + // handling. + break; + + case HIR::GenericParam::GenericKind::TYPE: { + auto param_type + = TypeResolveGenericParam::Resolve (generic_param.get ()); + context->insert_type (generic_param->get_mappings (), + param_type); + + substitutions.push_back (TyTy::SubstitutionParamMapping ( + static_cast (*generic_param), param_type)); + } + break; + } + } + } + + TyTy::BaseType *ret_type = nullptr; + if (!function.has_return_type ()) + ret_type = TyTy::TupleType::get_unit_type (fn.get_mappings ().get_hirid ()); + else + { + auto resolved + = TypeCheckType::Resolve (function.get_return_type ().get ()); + if (resolved->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (fn.get_locus (), "failed to resolve return type"); + return get_error (); + } + + ret_type = resolved->clone (); + ret_type->set_ref ( + function.get_return_type ()->get_mappings ().get_hirid ()); + } + + std::vector > params; + if (function.is_method ()) + { + // these are implicit mappings and not used + auto mappings = Analysis::Mappings::get (); + auto crate_num = mappings->get_current_crate (); + Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (), + mappings->get_next_hir_id (crate_num), + UNKNOWN_LOCAL_DEFID); + + // add the synthetic self param at the front, this is a placeholder + // for compilation to know parameter names. The types are ignored + // but we reuse the HIR identifier pattern which requires it + HIR::SelfParam &self_param = function.get_self (); + HIR::IdentifierPattern *self_pattern + = new HIR::IdentifierPattern (mapping, "self", self_param.get_locus (), + self_param.is_ref (), + self_param.is_mut () ? Mutability::Mut + : Mutability::Imm, + std::unique_ptr (nullptr)); + // might have a specified type + TyTy::BaseType *self_type = nullptr; + if (self_param.has_type ()) + { + std::unique_ptr &specified_type = self_param.get_type (); + self_type = TypeCheckType::Resolve (specified_type.get ()); + } + else + { + switch (self_param.get_self_kind ()) + { + case HIR::SelfParam::IMM: + case HIR::SelfParam::MUT: + self_type = self->clone (); + break; + + case HIR::SelfParam::IMM_REF: + self_type = new TyTy::ReferenceType ( + self_param.get_mappings ().get_hirid (), + TyTy::TyVar (self->get_ref ()), Mutability::Imm); + break; + + case HIR::SelfParam::MUT_REF: + self_type = new TyTy::ReferenceType ( + self_param.get_mappings ().get_hirid (), + TyTy::TyVar (self->get_ref ()), Mutability::Mut); + break; + + default: + gcc_unreachable (); + return nullptr; + } + } + + context->insert_type (self_param.get_mappings (), self_type); + params.push_back ( + std::pair (self_pattern, self_type)); + } + + for (auto ¶m : function.get_function_params ()) + { + // get the name as well required for later on + auto param_tyty = TypeCheckType::Resolve (param.get_type ()); + params.push_back ( + std::pair (param.get_param_name (), + param_tyty)); + + context->insert_type (param.get_mappings (), param_tyty); + TypeCheckPattern::Resolve (param.get_param_name (), param_tyty); + } + + auto mappings = Analysis::Mappings::get (); + const CanonicalPath *canonical_path = nullptr; + bool ok = mappings->lookup_canonical_path (fn.get_mappings ().get_nodeid (), + &canonical_path); + rust_assert (ok); + + RustIdent ident{*canonical_path, fn.get_locus ()}; + auto resolved + = new TyTy::FnType (fn.get_mappings ().get_hirid (), + fn.get_mappings ().get_defid (), + function.get_function_name (), ident, + function.is_method () + ? TyTy::FnType::FNTYPE_IS_METHOD_FLAG + : TyTy::FnType::FNTYPE_DEFAULT_FLAGS, + ABI::RUST, std::move (params), ret_type, substitutions); + + context->insert_type (fn.get_mappings (), resolved); + return resolved; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-hir-type-check.h b/gcc/rust/typecheck/rust-hir-type-check.h new file mode 100644 index 00000000000..21694dd302b --- /dev/null +++ b/gcc/rust/typecheck/rust-hir-type-check.h @@ -0,0 +1,379 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_HIR_TYPE_CHECK +#define RUST_HIR_TYPE_CHECK + +#include "rust-hir-full-decls.h" +#include "rust-hir-map.h" +#include "rust-tyty.h" +#include "rust-hir-trait-ref.h" +#include "rust-autoderef.h" + +namespace Rust { +namespace Resolver { + +class TypeCheckContextItem +{ +public: + enum ItemType + { + ITEM, + IMPL_ITEM, + TRAIT_ITEM, + }; + + TypeCheckContextItem (HIR::Function *item) + : type (ItemType::ITEM), item (item) + {} + + TypeCheckContextItem (HIR::ImplBlock *impl_block, HIR::Function *item) + : type (ItemType::IMPL_ITEM), item (impl_block, item) + {} + + TypeCheckContextItem (HIR::TraitItemFunc *trait_item) + : type (ItemType::TRAIT_ITEM), item (trait_item) + {} + + ItemType get_type () const { return type; } + + HIR::Function *get_item () + { + rust_assert (get_type () == ItemType::ITEM); + return item.item; + } + + std::pair &get_impl_item () + { + rust_assert (get_type () == ItemType::IMPL_ITEM); + return item.impl_item; + }; + + HIR::TraitItemFunc *get_trait_item () + { + rust_assert (get_type () == ItemType::TRAIT_ITEM); + return item.trait_item; + } + +private: + union Item + { + HIR::Function *item; + std::pair impl_item; + HIR::TraitItemFunc *trait_item; + + Item (HIR::Function *item) : item (item) {} + + Item (HIR::ImplBlock *impl_block, HIR::Function *item) + : impl_item ({impl_block, item}) + {} + + Item (HIR::TraitItemFunc *trait_item) : trait_item (trait_item) {} + }; + + ItemType type; + Item item; +}; + +class TypeCheckContext +{ +public: + static TypeCheckContext *get (); + + ~TypeCheckContext (); + + bool lookup_builtin (NodeId id, TyTy::BaseType **type); + bool lookup_builtin (std::string name, TyTy::BaseType **type); + void insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type); + + void insert_type (const Analysis::NodeMapping &mappings, + TyTy::BaseType *type); + void insert_implicit_type (TyTy::BaseType *type); + bool lookup_type (HirId id, TyTy::BaseType **type) const; + + void insert_implicit_type (HirId id, TyTy::BaseType *type); + + void insert_type_by_node_id (NodeId ref, HirId id); + bool lookup_type_by_node_id (NodeId ref, HirId *id); + + TyTy::BaseType *peek_return_type (); + TypeCheckContextItem &peek_context (); + void push_return_type (TypeCheckContextItem item, + TyTy::BaseType *return_type); + void pop_return_type (); + + void iterate (std::function cb) + { + for (auto it = resolved.begin (); it != resolved.end (); it++) + { + if (!cb (it->first, it->second)) + return; + } + } + + bool have_loop_context () const { return !loop_type_stack.empty (); } + + void push_new_loop_context (HirId id, Location locus) + { + TyTy::BaseType *infer_var + = new TyTy::InferType (id, TyTy::InferType::InferTypeKind::GENERAL, + locus); + loop_type_stack.push_back (infer_var); + } + + void push_new_while_loop_context (HirId id) + { + TyTy::BaseType *infer_var = new TyTy::ErrorType (id); + loop_type_stack.push_back (infer_var); + } + + TyTy::BaseType *peek_loop_context () { return loop_type_stack.back (); } + + TyTy::BaseType *pop_loop_context () + { + auto back = peek_loop_context (); + loop_type_stack.pop_back (); + return back; + } + + void swap_head_loop_context (TyTy::BaseType *val) + { + loop_type_stack.pop_back (); + loop_type_stack.push_back (val); + } + + void insert_trait_reference (DefId id, TraitReference &&ref) + { + rust_assert (trait_context.find (id) == trait_context.end ()); + trait_context.emplace (id, std::move (ref)); + } + + bool lookup_trait_reference (DefId id, TraitReference **ref) + { + auto it = trait_context.find (id); + if (it == trait_context.end ()) + return false; + + *ref = &it->second; + return true; + } + + void insert_receiver (HirId id, TyTy::BaseType *t) + { + receiver_context[id] = t; + } + + bool lookup_receiver (HirId id, TyTy::BaseType **ref) + { + auto it = receiver_context.find (id); + if (it == receiver_context.end ()) + return false; + + *ref = it->second; + return true; + } + + void insert_associated_trait_impl (HirId id, AssociatedImplTrait &&associated) + { + rust_assert (associated_impl_traits.find (id) + == associated_impl_traits.end ()); + associated_impl_traits.emplace (id, std::move (associated)); + } + + bool lookup_associated_trait_impl (HirId id, AssociatedImplTrait **associated) + { + auto it = associated_impl_traits.find (id); + if (it == associated_impl_traits.end ()) + return false; + + *associated = &it->second; + return true; + } + + void insert_associated_type_mapping (HirId id, HirId mapping) + { + associated_type_mappings[id] = mapping; + } + + void clear_associated_type_mapping (HirId id) + { + auto it = associated_type_mappings.find (id); + if (it != associated_type_mappings.end ()) + associated_type_mappings.erase (it); + } + + // lookup any associated type mappings, the out parameter of mapping is + // allowed to be nullptr which allows this interface to do a simple does exist + // check + bool lookup_associated_type_mapping (HirId id, HirId *mapping) + { + auto it = associated_type_mappings.find (id); + if (it == associated_type_mappings.end ()) + return false; + + if (mapping != nullptr) + *mapping = it->second; + + return true; + } + + void insert_associated_impl_mapping (HirId trait_id, + const TyTy::BaseType *impl_type, + HirId impl_id) + { + auto it = associated_traits_to_impls.find (trait_id); + if (it == associated_traits_to_impls.end ()) + { + associated_traits_to_impls[trait_id] = {}; + } + + associated_traits_to_impls[trait_id].push_back ({impl_type, impl_id}); + } + + bool lookup_associated_impl_mapping_for_self (HirId trait_id, + const TyTy::BaseType *self, + HirId *mapping) + { + auto it = associated_traits_to_impls.find (trait_id); + if (it == associated_traits_to_impls.end ()) + return false; + + for (auto &item : it->second) + { + if (item.first->can_eq (self, false)) + { + *mapping = item.second; + return true; + } + } + return false; + } + + void insert_autoderef_mappings (HirId id, + std::vector &&adjustments) + { + rust_assert (autoderef_mappings.find (id) == autoderef_mappings.end ()); + autoderef_mappings.emplace (id, std::move (adjustments)); + } + + bool lookup_autoderef_mappings (HirId id, + std::vector **adjustments) + { + auto it = autoderef_mappings.find (id); + if (it == autoderef_mappings.end ()) + return false; + + *adjustments = &it->second; + return true; + } + + void insert_cast_autoderef_mappings (HirId id, + std::vector &&adjustments) + { + rust_assert (cast_autoderef_mappings.find (id) + == cast_autoderef_mappings.end ()); + cast_autoderef_mappings.emplace (id, std::move (adjustments)); + } + + bool lookup_cast_autoderef_mappings (HirId id, + std::vector **adjustments) + { + auto it = cast_autoderef_mappings.find (id); + if (it == cast_autoderef_mappings.end ()) + return false; + + *adjustments = &it->second; + return true; + } + + void insert_variant_definition (HirId id, HirId variant) + { + auto it = variants.find (id); + rust_assert (it == variants.end ()); + + variants[id] = variant; + } + + bool lookup_variant_definition (HirId id, HirId *variant) + { + auto it = variants.find (id); + if (it == variants.end ()) + return false; + + *variant = it->second; + return true; + } + + void insert_operator_overload (HirId id, TyTy::FnType *call_site) + { + auto it = operator_overloads.find (id); + rust_assert (it == operator_overloads.end ()); + + operator_overloads[id] = call_site; + } + + bool lookup_operator_overload (HirId id, TyTy::FnType **call) + { + auto it = operator_overloads.find (id); + if (it == operator_overloads.end ()) + return false; + + *call = it->second; + return true; + } + +private: + TypeCheckContext (); + + std::map node_id_refs; + std::map resolved; + std::vector> builtins; + std::vector> + return_type_stack; + std::vector loop_type_stack; + std::map trait_context; + std::map receiver_context; + std::map associated_impl_traits; + + // trait-id -> list of < self-tyty:impl-id> + std::map>> + associated_traits_to_impls; + + std::map associated_type_mappings; + + // adjustment mappings + std::map> autoderef_mappings; + std::map> cast_autoderef_mappings; + + // operator overloads + std::map operator_overloads; + + // variants + std::map variants; +}; + +class TypeResolution +{ +public: + static void Resolve (HIR::Crate &crate); +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_HIR_TYPE_CHECK diff --git a/gcc/rust/typecheck/rust-substitution-mapper.cc b/gcc/rust/typecheck/rust-substitution-mapper.cc new file mode 100644 index 00000000000..f80368a0339 --- /dev/null +++ b/gcc/rust/typecheck/rust-substitution-mapper.cc @@ -0,0 +1,77 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-substitution-mapper.h" +#include "rust-hir-type-check.h" + +namespace Rust { +namespace Resolver { + +TyTy::BaseType * +SubstMapperInternal::Resolve (TyTy::BaseType *base, + TyTy::SubstitutionArgumentMappings &mappings) +{ + auto context = TypeCheckContext::get (); + + SubstMapperInternal mapper (base->get_ref (), mappings); + base->accept_vis (mapper); + rust_assert (mapper.resolved != nullptr); + + // insert these new implict types into the context + TyTy::BaseType *unused = nullptr; + bool is_ty_available + = context->lookup_type (mapper.resolved->get_ty_ref (), &unused); + if (!is_ty_available) + { + context->insert_type ( + Analysis::NodeMapping (0, 0, mapper.resolved->get_ty_ref (), 0), + mapper.resolved); + } + bool is_ref_available + = context->lookup_type (mapper.resolved->get_ref (), &unused); + if (!is_ref_available) + { + context->insert_type (Analysis::NodeMapping (0, 0, + mapper.resolved->get_ref (), + 0), + mapper.resolved); + } + + return mapper.resolved; +} + +bool +SubstMapperInternal::mappings_are_bound ( + TyTy::BaseType *tyseg, TyTy::SubstitutionArgumentMappings &mappings) +{ + if (tyseg->get_kind () == TyTy::TypeKind::ADT) + { + TyTy::ADTType *adt = static_cast (tyseg); + return adt->are_mappings_bound (mappings); + } + else if (tyseg->get_kind () == TyTy::TypeKind::FNDEF) + { + TyTy::FnType *fn = static_cast (tyseg); + return fn->are_mappings_bound (mappings); + } + + return false; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-substitution-mapper.h b/gcc/rust/typecheck/rust-substitution-mapper.h new file mode 100644 index 00000000000..028e10c0efe --- /dev/null +++ b/gcc/rust/typecheck/rust-substitution-mapper.h @@ -0,0 +1,394 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_SUBSTITUTION_MAPPER_H +#define RUST_SUBSTITUTION_MAPPER_H + +#include "rust-tyty.h" +#include "rust-tyty-visitor.h" + +namespace Rust { +namespace Resolver { + +class SubstMapper : public TyTy::TyVisitor +{ +public: + static TyTy::BaseType *Resolve (TyTy::BaseType *base, Location locus, + HIR::GenericArgs *generics = nullptr) + { + SubstMapper mapper (base->get_ref (), generics, locus); + base->accept_vis (mapper); + rust_assert (mapper.resolved != nullptr); + return mapper.resolved; + } + + static TyTy::BaseType *InferSubst (TyTy::BaseType *base, Location locus) + { + return SubstMapper::Resolve (base, locus, nullptr); + } + + bool have_generic_args () const { return generics != nullptr; } + + void visit (TyTy::FnType &type) override + { + TyTy::FnType *concrete = nullptr; + if (!have_generic_args ()) + { + TyTy::BaseType *substs = type.infer_substitions (locus); + rust_assert (substs->get_kind () == TyTy::TypeKind::FNDEF); + concrete = static_cast (substs); + } + else + { + TyTy::SubstitutionArgumentMappings mappings + = type.get_mappings_from_generic_args (*generics); + if (mappings.is_error ()) + return; + + concrete = type.handle_substitions (mappings); + } + + if (concrete != nullptr) + resolved = concrete; + } + + void visit (TyTy::ADTType &type) override + { + TyTy::ADTType *concrete = nullptr; + if (!have_generic_args ()) + { + TyTy::BaseType *substs = type.infer_substitions (locus); + rust_assert (substs->get_kind () == TyTy::TypeKind::ADT); + concrete = static_cast (substs); + } + else + { + TyTy::SubstitutionArgumentMappings mappings + = type.get_mappings_from_generic_args (*generics); + if (mappings.is_error ()) + return; + + concrete = type.handle_substitions (mappings); + } + + if (concrete != nullptr) + resolved = concrete; + } + + void visit (TyTy::PlaceholderType &type) override + { + rust_assert (type.can_resolve ()); + resolved = SubstMapper::Resolve (type.resolve (), locus, generics); + } + + void visit (TyTy::ProjectionType &type) override + { + TyTy::ProjectionType *concrete = nullptr; + if (!have_generic_args ()) + { + TyTy::BaseType *substs = type.infer_substitions (locus); + rust_assert (substs->get_kind () == TyTy::TypeKind::ADT); + concrete = static_cast (substs); + } + else + { + TyTy::SubstitutionArgumentMappings mappings + = type.get_mappings_from_generic_args (*generics); + if (mappings.is_error ()) + return; + + concrete = type.handle_substitions (mappings); + } + + if (concrete != nullptr) + resolved = concrete; + } + + // nothing to do for these + void visit (TyTy::InferType &) override { gcc_unreachable (); } + void visit (TyTy::TupleType &) override { gcc_unreachable (); } + void visit (TyTy::FnPtr &) override { gcc_unreachable (); } + void visit (TyTy::ArrayType &) override { gcc_unreachable (); } + void visit (TyTy::SliceType &) override { gcc_unreachable (); } + void visit (TyTy::BoolType &) override { gcc_unreachable (); } + void visit (TyTy::IntType &) override { gcc_unreachable (); } + void visit (TyTy::UintType &) override { gcc_unreachable (); } + void visit (TyTy::FloatType &) override { gcc_unreachable (); } + void visit (TyTy::USizeType &) override { gcc_unreachable (); } + void visit (TyTy::ISizeType &) override { gcc_unreachable (); } + void visit (TyTy::ErrorType &) override { gcc_unreachable (); } + void visit (TyTy::CharType &) override { gcc_unreachable (); } + void visit (TyTy::ReferenceType &) override { gcc_unreachable (); } + void visit (TyTy::PointerType &) override { gcc_unreachable (); } + void visit (TyTy::ParamType &) override { gcc_unreachable (); } + void visit (TyTy::StrType &) override { gcc_unreachable (); } + void visit (TyTy::NeverType &) override { gcc_unreachable (); } + void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); } + void visit (TyTy::ClosureType &) override { gcc_unreachable (); } + +private: + SubstMapper (HirId ref, HIR::GenericArgs *generics, Location locus) + : resolved (new TyTy::ErrorType (ref)), generics (generics), locus (locus) + {} + + TyTy::BaseType *resolved; + HIR::GenericArgs *generics; + Location locus; +}; + +class SubstMapperInternal : public TyTy::TyVisitor +{ +public: + static TyTy::BaseType *Resolve (TyTy::BaseType *base, + TyTy::SubstitutionArgumentMappings &mappings); + + static bool mappings_are_bound (TyTy::BaseType *ty, + TyTy::SubstitutionArgumentMappings &mappings); + + void visit (TyTy::FnType &type) override + { + TyTy::SubstitutionArgumentMappings adjusted + = type.adjust_mappings_for_this (mappings); + if (adjusted.is_error ()) + return; + + TyTy::BaseType *concrete = type.handle_substitions (adjusted); + if (concrete != nullptr) + resolved = concrete; + } + + void visit (TyTy::ADTType &type) override + { + TyTy::SubstitutionArgumentMappings adjusted + = type.adjust_mappings_for_this (mappings); + if (adjusted.is_error ()) + return; + + TyTy::BaseType *concrete = type.handle_substitions (adjusted); + if (concrete != nullptr) + resolved = concrete; + } + + // these don't support generic arguments but might contain a type param + void visit (TyTy::TupleType &type) override + { + resolved = type.handle_substitions (mappings); + } + + void visit (TyTy::ReferenceType &type) override + { + resolved = type.handle_substitions (mappings); + } + + void visit (TyTy::PointerType &type) override + { + resolved = type.handle_substitions (mappings); + } + + void visit (TyTy::ParamType &type) override + { + resolved = type.handle_substitions (mappings); + } + + void visit (TyTy::PlaceholderType &type) override + { + rust_assert (type.can_resolve ()); + if (mappings.trait_item_mode ()) + { + resolved = type.resolve (); + } + else + { + resolved = SubstMapperInternal::Resolve (type.resolve (), mappings); + } + } + + void visit (TyTy::ProjectionType &type) override + { + resolved = type.handle_substitions (mappings); + } + + void visit (TyTy::ClosureType &type) override + { + resolved = type.handle_substitions (mappings); + } + + void visit (TyTy::ArrayType &type) override + { + resolved = type.handle_substitions (mappings); + } + + void visit (TyTy::SliceType &type) override + { + resolved = type.handle_substitions (mappings); + } + + // nothing to do for these + void visit (TyTy::InferType &type) override { resolved = type.clone (); } + void visit (TyTy::FnPtr &type) override { resolved = type.clone (); } + void visit (TyTy::BoolType &type) override { resolved = type.clone (); } + void visit (TyTy::IntType &type) override { resolved = type.clone (); } + void visit (TyTy::UintType &type) override { resolved = type.clone (); } + void visit (TyTy::FloatType &type) override { resolved = type.clone (); } + void visit (TyTy::USizeType &type) override { resolved = type.clone (); } + void visit (TyTy::ISizeType &type) override { resolved = type.clone (); } + void visit (TyTy::ErrorType &type) override { resolved = type.clone (); } + void visit (TyTy::CharType &type) override { resolved = type.clone (); } + void visit (TyTy::StrType &type) override { resolved = type.clone (); } + void visit (TyTy::NeverType &type) override { resolved = type.clone (); } + void visit (TyTy::DynamicObjectType &type) override + { + resolved = type.clone (); + } + +private: + SubstMapperInternal (HirId ref, TyTy::SubstitutionArgumentMappings &mappings) + : resolved (new TyTy::ErrorType (ref)), mappings (mappings) + {} + + TyTy::BaseType *resolved; + TyTy::SubstitutionArgumentMappings &mappings; +}; + +class SubstMapperFromExisting : public TyTy::TyVisitor +{ +public: + static TyTy::BaseType *Resolve (TyTy::BaseType *concrete, + TyTy::BaseType *receiver) + { + rust_assert (concrete->get_kind () == receiver->get_kind ()); + + SubstMapperFromExisting mapper (concrete, receiver); + concrete->accept_vis (mapper); + return mapper.resolved; + } + + void visit (TyTy::FnType &type) override + { + rust_assert (type.was_substituted ()); + + TyTy::FnType *to_sub = static_cast (receiver); + resolved = to_sub->handle_substitions (type.get_substitution_arguments ()); + } + + void visit (TyTy::ADTType &type) override + { + rust_assert (type.was_substituted ()); + + TyTy::ADTType *to_sub = static_cast (receiver); + resolved = to_sub->handle_substitions (type.get_substitution_arguments ()); + } + + void visit (TyTy::ClosureType &type) override + { + rust_assert (type.was_substituted ()); + + TyTy::ClosureType *to_sub = static_cast (receiver); + resolved = to_sub->handle_substitions (type.get_substitution_arguments ()); + } + + void visit (TyTy::InferType &) override { gcc_unreachable (); } + void visit (TyTy::TupleType &) override { gcc_unreachable (); } + void visit (TyTy::FnPtr &) override { gcc_unreachable (); } + void visit (TyTy::ArrayType &) override { gcc_unreachable (); } + void visit (TyTy::SliceType &) override { gcc_unreachable (); } + void visit (TyTy::BoolType &) override { gcc_unreachable (); } + void visit (TyTy::IntType &) override { gcc_unreachable (); } + void visit (TyTy::UintType &) override { gcc_unreachable (); } + void visit (TyTy::FloatType &) override { gcc_unreachable (); } + void visit (TyTy::USizeType &) override { gcc_unreachable (); } + void visit (TyTy::ISizeType &) override { gcc_unreachable (); } + void visit (TyTy::ErrorType &) override { gcc_unreachable (); } + void visit (TyTy::CharType &) override { gcc_unreachable (); } + void visit (TyTy::ReferenceType &) override { gcc_unreachable (); } + void visit (TyTy::PointerType &) override { gcc_unreachable (); } + void visit (TyTy::ParamType &) override { gcc_unreachable (); } + void visit (TyTy::StrType &) override { gcc_unreachable (); } + void visit (TyTy::NeverType &) override { gcc_unreachable (); } + void visit (TyTy::PlaceholderType &) override { gcc_unreachable (); } + void visit (TyTy::ProjectionType &) override { gcc_unreachable (); } + void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); } + +private: + SubstMapperFromExisting (TyTy::BaseType *concrete, TyTy::BaseType *receiver) + : concrete (concrete), receiver (receiver), resolved (nullptr) + {} + + TyTy::BaseType *concrete; + TyTy::BaseType *receiver; + + TyTy::BaseType *resolved; +}; + +class GetUsedSubstArgs : public TyTy::TyConstVisitor +{ +public: + static TyTy::SubstitutionArgumentMappings From (const TyTy::BaseType *from) + { + GetUsedSubstArgs mapper; + from->accept_vis (mapper); + return mapper.args; + } + + void visit (const TyTy::FnType &type) override + { + args = type.get_substitution_arguments (); + } + + void visit (const TyTy::ADTType &type) override + { + args = type.get_substitution_arguments (); + } + + void visit (const TyTy::ClosureType &type) override + { + args = type.get_substitution_arguments (); + } + + void visit (const TyTy::InferType &) override {} + void visit (const TyTy::TupleType &) override {} + void visit (const TyTy::FnPtr &) override {} + void visit (const TyTy::ArrayType &) override {} + void visit (const TyTy::SliceType &) override {} + void visit (const TyTy::BoolType &) override {} + void visit (const TyTy::IntType &) override {} + void visit (const TyTy::UintType &) override {} + void visit (const TyTy::FloatType &) override {} + void visit (const TyTy::USizeType &) override {} + void visit (const TyTy::ISizeType &) override {} + void visit (const TyTy::ErrorType &) override {} + void visit (const TyTy::CharType &) override {} + void visit (const TyTy::ReferenceType &) override {} + void visit (const TyTy::PointerType &) override {} + void visit (const TyTy::ParamType &) override {} + void visit (const TyTy::StrType &) override {} + void visit (const TyTy::NeverType &) override {} + void visit (const TyTy::PlaceholderType &) override {} + void visit (const TyTy::ProjectionType &) override {} + void visit (const TyTy::DynamicObjectType &) override {} + +private: + GetUsedSubstArgs () : args (TyTy::SubstitutionArgumentMappings::error ()) {} + + TyTy::SubstitutionArgumentMappings args; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_SUBSTITUTION_MAPPER_H diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h new file mode 100644 index 00000000000..ccf0f625e4b --- /dev/null +++ b/gcc/rust/typecheck/rust-tycheck-dump.h @@ -0,0 +1,239 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_TYCHECK_DUMP +#define RUST_TYCHECK_DUMP + +#include "rust-hir-type-check-base.h" +#include "rust-hir-full.h" + +namespace Rust { +namespace Resolver { + +class TypeResolverDump : private TypeCheckBase, private HIR::HIRFullVisitorBase +{ + using HIR::HIRFullVisitorBase::visit; + +public: + static void go (HIR::Crate &crate, std::ofstream &out) + { + TypeResolverDump dumper; + for (auto &item : crate.items) + { + item->accept_vis (dumper); + dumper.dump += "\n"; + } + + out << dumper.dump; + } + + void visit (HIR::StructStruct &struct_decl) override + { + dump += indent () + "struct " + type_string (struct_decl.get_mappings ()) + + "\n"; + } + + void visit (HIR::Union &union_decl) override + { + dump + += indent () + "union " + type_string (union_decl.get_mappings ()) + "\n"; + } + + void visit (HIR::TupleStruct &struct_decl) override + { + dump += indent () + "struct" + type_string (struct_decl.get_mappings ()) + + "\n"; + } + + void visit (HIR::ImplBlock &impl_block) override + { + dump += indent () + "impl " + + type_string (impl_block.get_type ()->get_mappings ()) + " {\n"; + indentation_level++; + + for (auto &impl_item : impl_block.get_impl_items ()) + { + impl_item->accept_vis (*this); + dump += "\n"; + } + + indentation_level--; + dump += indent () + "}\n"; + } + + void visit (HIR::ConstantItem &constant) override + { + dump += indent () + "constant " + constant.get_identifier () + ":" + + type_string (constant.get_mappings ()) + " = "; + constant.get_expr ()->accept_vis (*this); + dump += ";\n"; + } + + void visit (HIR::Function &function) override + { + dump += indent () + "fn " + function.get_function_name () + " " + + type_string (function.get_mappings ()) + "\n"; + dump += indent () + "{\n"; + + HIR::BlockExpr *function_body = function.get_definition ().get (); + function_body->accept_vis (*this); + + dump += indent () + "}\n"; + } + + void visit (HIR::BlockExpr &expr) override + { + dump += "{\n"; + indentation_level++; + + for (auto &s : expr.get_statements ()) + { + dump += indent (); + s->accept_vis (*this); + dump += ";\n"; + } + + if (expr.has_expr ()) + { + dump += indent (); + expr.expr->accept_vis (*this); + dump += ";\n"; + } + + indentation_level--; + dump += "}\n"; + } + + void visit (HIR::UnsafeBlockExpr &expr) override + { + dump += "unsafe "; + expr.get_block_expr ()->accept_vis (*this); + } + + void visit (HIR::LetStmt &stmt) override + { + dump += "let " + stmt.get_pattern ()->as_string () + ":" + + type_string (stmt.get_pattern ()->get_pattern_mappings ()); + if (stmt.has_init_expr ()) + { + dump += " = "; + stmt.get_init_expr ()->accept_vis (*this); + } + } + + void visit (HIR::ExprStmtWithBlock &stmt) override + { + stmt.get_expr ()->accept_vis (*this); + } + + void visit (HIR::ExprStmtWithoutBlock &stmt) override + { + stmt.get_expr ()->accept_vis (*this); + } + + void visit (HIR::AssignmentExpr &expr) override + { + expr.get_lhs ()->accept_vis (*this); + dump += " = "; + expr.get_rhs ()->accept_vis (*this); + } + + void visit (HIR::LiteralExpr &expr) override + { + dump += expr.get_literal ().as_string () + ":" + + type_string (expr.get_mappings ()); + } + + void visit (HIR::ArrayExpr &expr) override + { + dump += type_string (expr.get_mappings ()) + ":["; + + HIR::ArrayElems *elements = expr.get_internal_elements (); + elements->accept_vis (*this); + + dump += "]"; + } + + void visit (HIR::ArrayElemsValues &elems) override + { + for (auto &elem : elems.get_values ()) + { + elem->accept_vis (*this); + dump += ","; + } + } + + void visit (HIR::GroupedExpr &expr) override + { + HIR::Expr *paren_expr = expr.get_expr_in_parens ().get (); + dump += "("; + paren_expr->accept_vis (*this); + dump += ")"; + } + + void visit (HIR::PathInExpression &expr) override + { + dump += type_string (expr.get_mappings ()); + } + + void visit (HIR::StructExprStructFields &expr) override + { + dump += "ctor: " + type_string (expr.get_mappings ()); + } + +protected: + std::string type_string (const Analysis::NodeMapping &mappings) + { + TyTy::BaseType *lookup = nullptr; + if (!context->lookup_type (mappings.get_hirid (), &lookup)) + return ""; + + std::string buf = "["; + for (auto &ref : lookup->get_combined_refs ()) + { + buf += std::to_string (ref); + buf += ", "; + } + buf += "]"; + + std::string repr = lookup->as_string (); + return "<" + repr + " HIRID: " + std::to_string (mappings.get_hirid ()) + + " RF:" + std::to_string (lookup->get_ref ()) + " TF:" + + std::to_string (lookup->get_ty_ref ()) + +" - " + buf + ">"; + } + + std::string indent () + { + std::string buf; + for (size_t i = 0; i < indentation_level; ++i) + buf += " "; + + return buf; + } + +private: + TypeResolverDump () : TypeCheckBase (), indentation_level (0) {} + + std::string dump; + size_t indentation_level; +}; + +} // namespace Resolver +} // namespace Rust + +#endif // RUST_TYCHECK_DUMP diff --git a/gcc/rust/typecheck/rust-tyctx.cc b/gcc/rust/typecheck/rust-tyctx.cc new file mode 100644 index 00000000000..d8a49e8b9ea --- /dev/null +++ b/gcc/rust/typecheck/rust-tyctx.cc @@ -0,0 +1,155 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-check.h" + +namespace Rust { +namespace Resolver { + +TypeCheckContext * +TypeCheckContext::get () +{ + static TypeCheckContext *instance; + if (instance == nullptr) + instance = new TypeCheckContext (); + + return instance; +} + +TypeCheckContext::TypeCheckContext () {} + +TypeCheckContext::~TypeCheckContext () {} + +bool +TypeCheckContext::lookup_builtin (NodeId id, TyTy::BaseType **type) +{ + auto ref_it = node_id_refs.find (id); + if (ref_it == node_id_refs.end ()) + return false; + + auto it = resolved.find (ref_it->second); + if (it == resolved.end ()) + return false; + + *type = it->second; + return true; +} + +bool +TypeCheckContext::lookup_builtin (std::string name, TyTy::BaseType **type) +{ + for (auto &builtin : builtins) + { + if (name.compare (builtin->as_string ()) == 0) + { + *type = builtin.get (); + return true; + } + } + return false; +} + +void +TypeCheckContext::insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type) +{ + node_id_refs[ref] = id; + resolved[id] = type; + builtins.push_back (std::unique_ptr (type)); +} + +void +TypeCheckContext::insert_type (const Analysis::NodeMapping &mappings, + TyTy::BaseType *type) +{ + rust_assert (type != nullptr); + NodeId ref = mappings.get_nodeid (); + HirId id = mappings.get_hirid (); + node_id_refs[ref] = id; + resolved[id] = type; +} + +void +TypeCheckContext::insert_implicit_type (TyTy::BaseType *type) +{ + rust_assert (type != nullptr); + resolved[type->get_ref ()] = type; +} + +void +TypeCheckContext::insert_implicit_type (HirId id, TyTy::BaseType *type) +{ + rust_assert (type != nullptr); + resolved[id] = type; +} + +bool +TypeCheckContext::lookup_type (HirId id, TyTy::BaseType **type) const +{ + auto it = resolved.find (id); + if (it == resolved.end ()) + return false; + + *type = it->second; + return true; +} + +void +TypeCheckContext::insert_type_by_node_id (NodeId ref, HirId id) +{ + rust_assert (node_id_refs.find (ref) == node_id_refs.end ()); + node_id_refs[ref] = id; +} + +bool +TypeCheckContext::lookup_type_by_node_id (NodeId ref, HirId *id) +{ + auto it = node_id_refs.find (ref); + if (it == node_id_refs.end ()) + return false; + + *id = it->second; + return true; +} + +TyTy::BaseType * +TypeCheckContext::peek_return_type () +{ + return return_type_stack.back ().second; +} + +void +TypeCheckContext::push_return_type (TypeCheckContextItem item, + TyTy::BaseType *return_type) +{ + return_type_stack.push_back ({std::move (item), return_type}); +} + +void +TypeCheckContext::pop_return_type () +{ + return_type_stack.pop_back (); +} + +TypeCheckContextItem & +TypeCheckContext::peek_context () +{ + return return_type_stack.back ().first; +} + +} // namespace Resolver +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc new file mode 100644 index 00000000000..7a1562ab544 --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty-bounds.cc @@ -0,0 +1,462 @@ +// Copyright (C) 2021-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-hir-type-bounds.h" +#include "rust-hir-trait-resolve.h" + +namespace Rust { +namespace Resolver { + +void +TypeBoundsProbe::scan () +{ + std::vector> + possible_trait_paths; + mappings->iterate_impl_blocks ( + [&] (HirId id, HIR::ImplBlock *impl) mutable -> bool { + // we are filtering for trait-impl-blocks + if (!impl->has_trait_ref ()) + return true; + + TyTy::BaseType *impl_type = nullptr; + bool ok + = context->lookup_type (impl->get_type ()->get_mappings ().get_hirid (), + &impl_type); + if (!ok) + return true; + + if (!receiver->can_eq (impl_type, false)) + { + if (!impl_type->can_eq (receiver, false)) + return true; + } + + possible_trait_paths.push_back ({impl->get_trait_ref ().get (), impl}); + return true; + }); + + for (auto &path : possible_trait_paths) + { + HIR::TypePath *trait_path = path.first; + TraitReference *trait_ref = TraitResolver::Resolve (*trait_path); + + if (!trait_ref->is_error ()) + trait_references.push_back ({trait_ref, path.second}); + } +} + +TraitReference * +TypeCheckBase::resolve_trait_path (HIR::TypePath &path) +{ + return TraitResolver::Resolve (path); +} + +TyTy::TypeBoundPredicate +TypeCheckBase::get_predicate_from_bound (HIR::TypePath &type_path) +{ + TraitReference *trait = resolve_trait_path (type_path); + if (trait->is_error ()) + return TyTy::TypeBoundPredicate::error (); + + TyTy::TypeBoundPredicate predicate (*trait, type_path.get_locus ()); + HIR::GenericArgs args + = HIR::GenericArgs::create_empty (type_path.get_locus ()); + + auto &final_seg = type_path.get_final_segment (); + if (final_seg->is_generic_segment ()) + { + auto final_generic_seg + = static_cast (final_seg.get ()); + if (final_generic_seg->has_generic_args ()) + { + args = final_generic_seg->get_generic_args (); + } + } + + if (predicate.requires_generic_args ()) + { + // this is applying generic arguments to a trait reference + predicate.apply_generic_arguments (&args); + } + + return predicate; +} + +} // namespace Resolver + +namespace TyTy { + +TypeBoundPredicate::TypeBoundPredicate ( + const Resolver::TraitReference &trait_reference, Location locus) + : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()), + reference (trait_reference.get_mappings ().get_defid ()), locus (locus), + error_flag (false) +{ + substitutions.clear (); + for (const auto &p : trait_reference.get_trait_substs ()) + substitutions.push_back (p.clone ()); + + // we setup a dummy implict self argument + SubstitutionArg placeholder_self (&get_substs ().front (), nullptr); + used_arguments.get_mappings ().push_back (placeholder_self); +} + +TypeBoundPredicate::TypeBoundPredicate ( + DefId reference, std::vector subst, Location locus) + : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()), + reference (reference), locus (locus), error_flag (false) +{ + substitutions.clear (); + for (const auto &p : subst) + substitutions.push_back (p.clone ()); + + // we setup a dummy implict self argument + SubstitutionArg placeholder_self (&get_substs ().front (), nullptr); + used_arguments.get_mappings ().push_back (placeholder_self); +} + +TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other) + : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()), + reference (other.reference), locus (other.locus), + error_flag (other.error_flag) +{ + substitutions.clear (); + for (const auto &p : other.get_substs ()) + substitutions.push_back (p.clone ()); + + std::vector mappings; + for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++) + { + const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i); + SubstitutionArg arg (oa); + mappings.push_back (std::move (arg)); + } + + // we need to remap the argument mappings based on this copied constructor + std::vector copied_arg_mappings; + size_t i = 0; + for (const auto &m : other.used_arguments.get_mappings ()) + { + TyTy::BaseType *argument + = m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone (); + SubstitutionArg c (&substitutions.at (i++), argument); + copied_arg_mappings.push_back (std::move (c)); + } + + used_arguments + = SubstitutionArgumentMappings (copied_arg_mappings, + other.used_arguments.get_locus ()); +} + +TypeBoundPredicate & +TypeBoundPredicate::operator= (const TypeBoundPredicate &other) +{ + reference = other.reference; + locus = other.locus; + error_flag = other.error_flag; + used_arguments = SubstitutionArgumentMappings::error (); + + substitutions.clear (); + for (const auto &p : other.get_substs ()) + substitutions.push_back (p.clone ()); + + std::vector mappings; + for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++) + { + const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i); + SubstitutionArg arg (oa); + mappings.push_back (std::move (arg)); + } + + // we need to remap the argument mappings based on this copied constructor + std::vector copied_arg_mappings; + size_t i = 0; + for (const auto &m : other.used_arguments.get_mappings ()) + { + TyTy::BaseType *argument + = m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone (); + SubstitutionArg c (&substitutions.at (i++), argument); + copied_arg_mappings.push_back (std::move (c)); + } + + used_arguments + = SubstitutionArgumentMappings (copied_arg_mappings, + other.used_arguments.get_locus ()); + + return *this; +} + +TypeBoundPredicate +TypeBoundPredicate::error () +{ + auto p = TypeBoundPredicate (UNKNOWN_DEFID, {}, Location ()); + p.error_flag = true; + return p; +} + +std::string +TypeBoundPredicate::as_string () const +{ + return get ()->as_string () + subst_as_string (); +} + +std::string +TypeBoundPredicate::as_name () const +{ + return get ()->get_name () + subst_as_string (); +} + +const Resolver::TraitReference * +TypeBoundPredicate::get () const +{ + auto context = Resolver::TypeCheckContext::get (); + + Resolver::TraitReference *ref = nullptr; + bool ok = context->lookup_trait_reference (reference, &ref); + rust_assert (ok); + + return ref; +} + +std::string +TypeBoundPredicate::get_name () const +{ + return get ()->get_name (); +} + +bool +TypeBoundPredicate::is_object_safe (bool emit_error, Location locus) const +{ + const Resolver::TraitReference *trait = get (); + rust_assert (trait != nullptr); + return trait->is_object_safe (emit_error, locus); +} + +void +TypeBoundPredicate::apply_generic_arguments (HIR::GenericArgs *generic_args) +{ + // we need to get the substitutions argument mappings but also remember that + // we have an implicit Self argument which we must be careful to respect + rust_assert (!used_arguments.is_empty ()); + rust_assert (!substitutions.empty ()); + + // now actually perform a substitution + used_arguments = get_mappings_from_generic_args (*generic_args); + + error_flag |= used_arguments.is_error (); + auto &subst_mappings = used_arguments; + for (auto &sub : get_substs ()) + { + SubstitutionArg arg = SubstitutionArg::error (); + bool ok + = subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg); + if (ok && arg.get_tyty () != nullptr) + sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ()); + } +} + +bool +TypeBoundPredicate::contains_item (const std::string &search) const +{ + auto trait_ref = get (); + const Resolver::TraitItemReference *trait_item_ref = nullptr; + return trait_ref->lookup_trait_item (search, &trait_item_ref); +} + +TypeBoundPredicateItem +TypeBoundPredicate::lookup_associated_item (const std::string &search) const +{ + auto trait_ref = get (); + const Resolver::TraitItemReference *trait_item_ref = nullptr; + if (!trait_ref->lookup_trait_item (search, &trait_item_ref)) + return TypeBoundPredicateItem::error (); + + return TypeBoundPredicateItem (this, trait_item_ref); +} + +TypeBoundPredicateItem +TypeBoundPredicate::lookup_associated_item ( + const Resolver::TraitItemReference *ref) const +{ + return lookup_associated_item (ref->get_identifier ()); +} + +BaseType * +TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType *receiver) +{ + TyTy::BaseType *trait_item_tyty = get_raw_item ()->get_tyty (); + if (parent->get_substitution_arguments ().is_empty ()) + return trait_item_tyty; + + const Resolver::TraitItemReference *tref = get_raw_item (); + bool is_associated_type = tref->get_trait_item_type (); + if (is_associated_type) + return trait_item_tyty; + + // set up the self mapping + SubstitutionArgumentMappings gargs = parent->get_substitution_arguments (); + rust_assert (!gargs.is_empty ()); + + // setup the adjusted mappings + std::vector adjusted_mappings; + for (size_t i = 0; i < gargs.get_mappings ().size (); i++) + { + auto &mapping = gargs.get_mappings ().at (i); + + bool is_implicit_self = i == 0; + TyTy::BaseType *argument + = is_implicit_self ? receiver->clone () : mapping.get_tyty (); + + SubstitutionArg arg (mapping.get_param_mapping (), argument); + adjusted_mappings.push_back (std::move (arg)); + } + + SubstitutionArgumentMappings adjusted (adjusted_mappings, gargs.get_locus (), + gargs.get_subst_cb (), + true /* trait-mode-flag */); + return Resolver::SubstMapperInternal::Resolve (trait_item_tyty, adjusted); +} +bool +TypeBoundPredicate::is_error () const +{ + auto context = Resolver::TypeCheckContext::get (); + + Resolver::TraitReference *ref = nullptr; + bool ok = context->lookup_trait_reference (reference, &ref); + + return !ok || error_flag; +} + +BaseType * +TypeBoundPredicate::handle_substitions ( + SubstitutionArgumentMappings subst_mappings) +{ + for (auto &sub : get_substs ()) + { + if (sub.get_param_ty () == nullptr) + continue; + + ParamType *p = sub.get_param_ty (); + BaseType *r = p->resolve (); + BaseType *s = Resolver::SubstMapperInternal::Resolve (r, subst_mappings); + + p->set_ty_ref (s->get_ty_ref ()); + } + + // FIXME more error handling at some point + // used_arguments = subst_mappings; + // error_flag |= used_arguments.is_error (); + + return nullptr; +} + +bool +TypeBoundPredicate::requires_generic_args () const +{ + if (is_error ()) + return false; + + return substitutions.size () > 1; +} + +// trait item reference + +const Resolver::TraitItemReference * +TypeBoundPredicateItem::get_raw_item () const +{ + return trait_item_ref; +} + +bool +TypeBoundPredicateItem::needs_implementation () const +{ + return !get_raw_item ()->is_optional (); +} + +Location +TypeBoundPredicateItem::get_locus () const +{ + return get_raw_item ()->get_locus (); +} + +// TypeBoundsMappings + +TypeBoundsMappings::TypeBoundsMappings ( + std::vector specified_bounds) + : specified_bounds (specified_bounds) +{} + +std::vector & +TypeBoundsMappings::get_specified_bounds () +{ + return specified_bounds; +} + +const std::vector & +TypeBoundsMappings::get_specified_bounds () const +{ + return specified_bounds; +} + +size_t +TypeBoundsMappings::num_specified_bounds () const +{ + return specified_bounds.size (); +} + +std::string +TypeBoundsMappings::raw_bounds_as_string () const +{ + std::string buf; + for (size_t i = 0; i < specified_bounds.size (); i++) + { + const TypeBoundPredicate &b = specified_bounds.at (i); + bool has_next = (i + 1) < specified_bounds.size (); + buf += b.as_string () + (has_next ? " + " : ""); + } + return buf; +} + +std::string +TypeBoundsMappings::bounds_as_string () const +{ + return "bounds:[" + raw_bounds_as_string () + "]"; +} + +std::string +TypeBoundsMappings::raw_bounds_as_name () const +{ + std::string buf; + for (size_t i = 0; i < specified_bounds.size (); i++) + { + const TypeBoundPredicate &b = specified_bounds.at (i); + bool has_next = (i + 1) < specified_bounds.size (); + buf += b.as_name () + (has_next ? " + " : ""); + } + + return buf; +} + +void +TypeBoundsMappings::add_bound (TypeBoundPredicate predicate) +{ + specified_bounds.push_back (predicate); +} + +} // namespace TyTy +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty-call.cc b/gcc/rust/typecheck/rust-tyty-call.cc new file mode 100644 index 00000000000..1ce82c943f5 --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty-call.cc @@ -0,0 +1,263 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-tyty-call.h" +#include "rust-hir-type-check-expr.h" + +namespace Rust { +namespace TyTy { + +void +TypeCheckCallExpr::visit (ADTType &type) +{ + rust_assert (!variant.is_error ()); + if (variant.get_variant_type () != TyTy::VariantDef::VariantType::TUPLE) + { + rust_error_at ( + call.get_locus (), + "expected function, tuple struct or tuple variant, found struct %<%s%>", + type.get_name ().c_str ()); + return; + } + + if (call.num_params () != variant.num_fields ()) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) call.num_params (), + (unsigned long) variant.num_fields ()); + return; + } + + size_t i = 0; + for (auto &argument : call.get_arguments ()) + { + StructFieldType *field = variant.get_field_at_index (i); + BaseType *field_tyty = field->get_field_type (); + + BaseType *arg = Resolver::TypeCheckExpr::Resolve (argument.get ()); + if (arg->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (argument->get_locus (), + "failed to resolve argument type"); + return; + } + + auto res = Resolver::TypeCheckBase::coercion_site ( + argument->get_mappings ().get_hirid (), field_tyty, arg, + argument->get_locus ()); + if (res->get_kind () == TyTy::TypeKind::ERROR) + { + return; + } + + delete res; + i++; + } + + if (i != call.num_params ()) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) i, (unsigned long) call.num_params ()); + return; + } + + resolved = type.clone (); +} + +void +TypeCheckCallExpr::visit (FnType &type) +{ + type.monomorphize (); + if (call.num_params () != type.num_params ()) + { + if (type.is_varadic ()) + { + if (call.num_params () < type.num_params ()) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) call.num_params (), + (unsigned long) type.num_params ()); + return; + } + } + else + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) call.num_params (), + (unsigned long) type.num_params ()); + return; + } + } + + size_t i = 0; + for (auto &argument : call.get_arguments ()) + { + auto argument_expr_tyty + = Resolver::TypeCheckExpr::Resolve (argument.get ()); + if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at ( + argument->get_locus (), + "failed to resolve type for argument expr in CallExpr"); + return; + } + + // it might be a varadic function + if (i < type.num_params ()) + { + auto fnparam = type.param_at (i); + auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site ( + argument->get_mappings ().get_hirid (), fnparam.second, + argument_expr_tyty, argument->get_locus ()); + if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (argument->get_locus (), + "Type Resolution failure on parameter"); + return; + } + } + + i++; + } + + if (i < call.num_params ()) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) i, (unsigned long) call.num_params ()); + return; + } + + type.monomorphize (); + resolved = type.get_return_type ()->clone (); +} + +void +TypeCheckCallExpr::visit (FnPtr &type) +{ + if (call.num_params () != type.num_params ()) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) call.num_params (), + (unsigned long) type.num_params ()); + return; + } + + size_t i = 0; + for (auto &argument : call.get_arguments ()) + { + auto fnparam = type.param_at (i); + auto argument_expr_tyty + = Resolver::TypeCheckExpr::Resolve (argument.get ()); + if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at ( + argument->get_locus (), + "failed to resolve type for argument expr in CallExpr"); + return; + } + + auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site ( + argument->get_mappings ().get_hirid (), fnparam, argument_expr_tyty, + argument->get_locus ()); + if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (argument->get_locus (), + "Type Resolution failure on parameter"); + return; + } + + i++; + } + + if (i != call.num_params ()) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) i, (unsigned long) call.num_params ()); + return; + } + + resolved = type.get_return_type ()->monomorphized_clone (); +} + +// method call checker + +void +TypeCheckMethodCallExpr::visit (FnType &type) +{ + type.get_self_type ()->unify (adjusted_self); + + // +1 for the receiver self + size_t num_args_to_call = call.num_params () + 1; + if (num_args_to_call != type.num_params ()) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) call.num_params (), + (unsigned long) type.num_params ()); + return; + } + + size_t i = 1; + for (auto &argument : call.get_arguments ()) + { + auto fnparam = type.param_at (i); + auto argument_expr_tyty + = Resolver::TypeCheckExpr::Resolve (argument.get ()); + if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at ( + argument->get_locus (), + "failed to resolve type for argument expr in CallExpr"); + return; + } + + auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site ( + argument->get_mappings ().get_hirid (), fnparam.second, + argument_expr_tyty, argument->get_locus ()); + if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (argument->get_locus (), + "Type Resolution failure on parameter"); + return; + } + + i++; + } + + if (i != num_args_to_call) + { + rust_error_at (call.get_locus (), + "unexpected number of arguments %lu expected %lu", + (unsigned long) i, (unsigned long) call.num_params ()); + return; + } + + type.monomorphize (); + + resolved = type.get_return_type ()->monomorphized_clone (); +} + +} // namespace TyTy +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty-call.h b/gcc/rust/typecheck/rust-tyty-call.h new file mode 100644 index 00000000000..51817e6446d --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty-call.h @@ -0,0 +1,147 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_TYTY_CALL +#define RUST_TYTY_CALL + +#include "rust-diagnostics.h" +#include "rust-hir-full.h" +#include "rust-tyty-visitor.h" +#include "rust-tyty.h" +#include "rust-hir-type-check.h" + +namespace Rust { +namespace TyTy { + +class TypeCheckCallExpr : private TyVisitor +{ +public: + static BaseType *go (BaseType *ref, HIR::CallExpr &call, + TyTy::VariantDef &variant, + Resolver::TypeCheckContext *context) + { + TypeCheckCallExpr checker (call, variant, context); + ref->accept_vis (checker); + return checker.resolved; + } + + void visit (InferType &) override { gcc_unreachable (); } + void visit (TupleType &) override { gcc_unreachable (); } + void visit (ArrayType &) override { gcc_unreachable (); } + void visit (SliceType &) override { gcc_unreachable (); } + void visit (BoolType &) override { gcc_unreachable (); } + void visit (IntType &) override { gcc_unreachable (); } + void visit (UintType &) override { gcc_unreachable (); } + void visit (FloatType &) override { gcc_unreachable (); } + void visit (USizeType &) override { gcc_unreachable (); } + void visit (ISizeType &) override { gcc_unreachable (); } + void visit (ErrorType &) override { gcc_unreachable (); } + void visit (CharType &) override { gcc_unreachable (); } + void visit (ReferenceType &) override { gcc_unreachable (); } + void visit (PointerType &) override { gcc_unreachable (); } + void visit (ParamType &) override { gcc_unreachable (); } + void visit (StrType &) override { gcc_unreachable (); } + void visit (NeverType &) override { gcc_unreachable (); } + void visit (PlaceholderType &) override { gcc_unreachable (); } + void visit (ProjectionType &) override { gcc_unreachable (); } + void visit (DynamicObjectType &) override { gcc_unreachable (); } + void visit (ClosureType &type) override { gcc_unreachable (); } + + // tuple-structs + void visit (ADTType &type) override; + + // call fns + void visit (FnType &type) override; + void visit (FnPtr &type) override; + +private: + TypeCheckCallExpr (HIR::CallExpr &c, TyTy::VariantDef &variant, + Resolver::TypeCheckContext *context) + : resolved (new TyTy::ErrorType (c.get_mappings ().get_hirid ())), call (c), + variant (variant), context (context), + mappings (Analysis::Mappings::get ()) + {} + + BaseType *resolved; + HIR::CallExpr &call; + TyTy::VariantDef &variant; + Resolver::TypeCheckContext *context; + Analysis::Mappings *mappings; +}; + +class TypeCheckMethodCallExpr : private TyVisitor +{ +public: + // Resolve the Method parameters and return back the return type + static BaseType *go (BaseType *ref, HIR::MethodCallExpr &call, + TyTy::BaseType *adjusted_self, + Resolver::TypeCheckContext *context) + { + TypeCheckMethodCallExpr checker (call, adjusted_self, context); + ref->accept_vis (checker); + return checker.resolved; + } + + void visit (InferType &) override { gcc_unreachable (); } + void visit (TupleType &) override { gcc_unreachable (); } + void visit (ArrayType &) override { gcc_unreachable (); } + void visit (SliceType &) override { gcc_unreachable (); } + void visit (BoolType &) override { gcc_unreachable (); } + void visit (IntType &) override { gcc_unreachable (); } + void visit (UintType &) override { gcc_unreachable (); } + void visit (FloatType &) override { gcc_unreachable (); } + void visit (USizeType &) override { gcc_unreachable (); } + void visit (ISizeType &) override { gcc_unreachable (); } + void visit (ErrorType &) override { gcc_unreachable (); } + void visit (ADTType &) override { gcc_unreachable (); }; + void visit (CharType &) override { gcc_unreachable (); } + void visit (ReferenceType &) override { gcc_unreachable (); } + void visit (PointerType &) override { gcc_unreachable (); } + void visit (ParamType &) override { gcc_unreachable (); } + void visit (StrType &) override { gcc_unreachable (); } + void visit (NeverType &) override { gcc_unreachable (); } + void visit (PlaceholderType &) override { gcc_unreachable (); } + void visit (ProjectionType &) override { gcc_unreachable (); } + void visit (DynamicObjectType &) override { gcc_unreachable (); } + + // FIXME + void visit (FnPtr &type) override { gcc_unreachable (); } + + // call fns + void visit (FnType &type) override; + void visit (ClosureType &type) override { gcc_unreachable (); } + +private: + TypeCheckMethodCallExpr (HIR::MethodCallExpr &c, + TyTy::BaseType *adjusted_self, + Resolver::TypeCheckContext *context) + : resolved (nullptr), call (c), adjusted_self (adjusted_self), + context (context), mappings (Analysis::Mappings::get ()) + {} + + BaseType *resolved; + HIR::MethodCallExpr &call; + TyTy::BaseType *adjusted_self; + Resolver::TypeCheckContext *context; + Analysis::Mappings *mappings; +}; + +} // namespace TyTy +} // namespace Rust + +#endif // RUST_TYTY_CALL diff --git a/gcc/rust/typecheck/rust-tyty-cmp.h b/gcc/rust/typecheck/rust-tyty-cmp.h new file mode 100644 index 00000000000..07d1dea7464 --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty-cmp.h @@ -0,0 +1,1554 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_TYTY_CMP_H +#define RUST_TYTY_CMP_H + +#include "rust-diagnostics.h" +#include "rust-tyty.h" +#include "rust-tyty-visitor.h" +#include "rust-hir-map.h" +#include "rust-hir-type-check.h" + +namespace Rust { +namespace TyTy { + +class BaseCmp : public TyConstVisitor +{ +public: + virtual bool can_eq (const BaseType *other) + { + if (other->get_kind () == TypeKind::PARAM) + { + const ParamType *p = static_cast (other); + other = p->resolve (); + } + if (other->get_kind () == TypeKind::PLACEHOLDER) + { + const PlaceholderType *p = static_cast (other); + if (p->can_resolve ()) + { + other = p->resolve (); + } + } + if (other->get_kind () == TypeKind::PROJECTION) + { + const ProjectionType *p = static_cast (other); + other = p->get (); + } + + other->accept_vis (*this); + return ok; + } + + virtual void visit (const TupleType &type) override + { + ok = false; + + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const ADTType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const InferType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const FnType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const FnPtr &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const ArrayType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const SliceType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const BoolType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const IntType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const UintType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const USizeType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const ISizeType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const FloatType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const ErrorType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const CharType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const ReferenceType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const PointerType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const StrType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const NeverType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const ProjectionType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const PlaceholderType &type) override + { + // it is ok for types to can eq to a placeholder + ok = true; + } + + virtual void visit (const ParamType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const DynamicObjectType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + + virtual void visit (const ClosureType &type) override + { + ok = false; + if (emit_error_flag) + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus + = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + } + +protected: + BaseCmp (const BaseType *base, bool emit_errors) + : mappings (Analysis::Mappings::get ()), + context (Resolver::TypeCheckContext::get ()), ok (false), + emit_error_flag (emit_errors) + {} + + Analysis::Mappings *mappings; + Resolver::TypeCheckContext *context; + + bool ok; + bool emit_error_flag; + +private: + /* Returns a pointer to the ty that created this rule. */ + virtual const BaseType *get_base () const = 0; +}; + +class InferCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + InferCmp (const InferType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const BoolType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const IntType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const UintType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const USizeType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const ISizeType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const FloatType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const ArrayType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const SliceType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const ADTType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const TupleType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const InferType &type) override + { + switch (base->get_infer_kind ()) + { + case InferType::InferTypeKind::GENERAL: + ok = true; + return; + + case InferType::InferTypeKind::INTEGRAL: { + if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL) + { + ok = true; + return; + } + else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL) + { + ok = true; + return; + } + } + break; + + case InferType::InferTypeKind::FLOAT: { + if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT) + { + ok = true; + return; + } + else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL) + { + ok = true; + return; + } + } + break; + } + + BaseCmp::visit (type); + } + + void visit (const CharType &type) override + { + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + } + + void visit (const ReferenceType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const PointerType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const ParamType &) override { ok = true; } + + void visit (const DynamicObjectType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + + void visit (const ClosureType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + ok = true; + return; + } + + BaseCmp::visit (type); + } + +private: + const BaseType *get_base () const override { return base; } + const InferType *base; +}; + +class FnCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + FnCmp (const FnType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL; + } + + void visit (const FnType &type) override + { + if (base->num_params () != type.num_params ()) + { + BaseCmp::visit (type); + return; + } + + for (size_t i = 0; i < base->num_params (); i++) + { + auto a = base->param_at (i).second; + auto b = type.param_at (i).second; + + if (!a->can_eq (b, emit_error_flag)) + { + emit_error_flag = false; + BaseCmp::visit (type); + return; + } + } + + if (!base->get_return_type ()->can_eq (type.get_return_type (), + emit_error_flag)) + { + emit_error_flag = false; + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const FnType *base; +}; + +class FnptrCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + FnptrCmp (const FnPtr *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + + void visit (const FnPtr &type) override + { + if (base->num_params () != type.num_params ()) + { + BaseCmp::visit (type); + return; + } + + auto this_ret_type = base->get_return_type (); + auto other_ret_type = type.get_return_type (); + if (!this_ret_type->can_eq (other_ret_type, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + + for (size_t i = 0; i < base->num_params (); i++) + { + auto this_param = base->param_at (i); + auto other_param = type.param_at (i); + if (!this_param->can_eq (other_param, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + } + + ok = true; + } + + void visit (const FnType &type) override + { + if (base->num_params () != type.num_params ()) + { + BaseCmp::visit (type); + return; + } + + auto this_ret_type = base->get_return_type (); + auto other_ret_type = type.get_return_type (); + if (!this_ret_type->can_eq (other_ret_type, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + + for (size_t i = 0; i < base->num_params (); i++) + { + auto this_param = base->param_at (i); + auto other_param = type.param_at (i).second; + if (!this_param->can_eq (other_param, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const FnPtr *base; +}; + +class ClosureCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + ClosureCmp (const ClosureType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const ClosureType *base; +}; + +class ArrayCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + ArrayCmp (const ArrayType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const ArrayType &type) override + { + // check base type + const BaseType *base_element = base->get_element_type (); + const BaseType *other_element = type.get_element_type (); + if (!base_element->can_eq (other_element, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const ArrayType *base; +}; + +class SliceCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + SliceCmp (const SliceType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const SliceType &type) override + { + // check base type + const BaseType *base_element = base->get_element_type (); + const BaseType *other_element = type.get_element_type (); + if (!base_element->can_eq (other_element, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const SliceType *base; +}; + +class BoolCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + BoolCmp (const BoolType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const BoolType &type) override { ok = true; } + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL; + } + +private: + const BaseType *get_base () const override { return base; } + const BoolType *base; +}; + +class IntCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + IntCmp (const IntType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT; + } + + void visit (const IntType &type) override + { + ok = type.get_int_kind () == base->get_int_kind (); + } + +private: + const BaseType *get_base () const override { return base; } + const IntType *base; +}; + +class UintCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + UintCmp (const UintType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT; + } + + void visit (const UintType &type) override + { + ok = type.get_uint_kind () == base->get_uint_kind (); + } + +private: + const BaseType *get_base () const override { return base; } + const UintType *base; +}; + +class FloatCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + FloatCmp (const FloatType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () != InferType::InferTypeKind::INTEGRAL; + } + + void visit (const FloatType &type) override + { + ok = type.get_float_kind () == base->get_float_kind (); + } + +private: + const BaseType *get_base () const override { return base; } + const FloatType *base; +}; + +class ADTCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + ADTCmp (const ADTType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const ADTType &type) override + { + if (base->get_adt_kind () != type.get_adt_kind ()) + { + BaseCmp::visit (type); + return; + } + + if (base->get_identifier ().compare (type.get_identifier ()) != 0) + { + BaseCmp::visit (type); + return; + } + + if (base->number_of_variants () != type.number_of_variants ()) + { + BaseCmp::visit (type); + return; + } + + for (size_t i = 0; i < type.number_of_variants (); ++i) + { + TyTy::VariantDef *a = base->get_variants ().at (i); + TyTy::VariantDef *b = type.get_variants ().at (i); + + if (a->num_fields () != b->num_fields ()) + { + BaseCmp::visit (type); + return; + } + + for (size_t j = 0; j < a->num_fields (); j++) + { + TyTy::StructFieldType *base_field = a->get_field_at_index (j); + TyTy::StructFieldType *other_field = b->get_field_at_index (j); + + TyTy::BaseType *this_field_ty = base_field->get_field_type (); + TyTy::BaseType *other_field_ty = other_field->get_field_type (); + + if (!this_field_ty->can_eq (other_field_ty, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + } + } + + ok = true; + } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const ADTType *base; +}; + +class TupleCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + TupleCmp (const TupleType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const TupleType &type) override + { + if (base->num_fields () != type.num_fields ()) + { + BaseCmp::visit (type); + return; + } + + for (size_t i = 0; i < base->num_fields (); i++) + { + BaseType *bo = base->get_field (i); + BaseType *fo = type.get_field (i); + + if (!bo->can_eq (fo, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + } + + ok = true; + } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const TupleType *base; +}; + +class USizeCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + USizeCmp (const USizeType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT; + } + + void visit (const USizeType &type) override { ok = true; } + +private: + const BaseType *get_base () const override { return base; } + const USizeType *base; +}; + +class ISizeCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + ISizeCmp (const ISizeType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT; + } + + void visit (const ISizeType &type) override { ok = true; } + +private: + const BaseType *get_base () const override { return base; } + const ISizeType *base; +}; + +class CharCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + CharCmp (const CharType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const InferType &type) override + { + ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL; + } + + void visit (const CharType &type) override { ok = true; } + +private: + const BaseType *get_base () const override { return base; } + const CharType *base; +}; + +class ReferenceCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + ReferenceCmp (const ReferenceType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const ReferenceType &type) override + { + auto base_type = base->get_base (); + auto other_base_type = type.get_base (); + + bool mutability_match = base->is_mutable () == type.is_mutable (); + if (!mutability_match) + { + BaseCmp::visit (type); + return; + } + + if (!base_type->can_eq (other_base_type, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const ReferenceType *base; +}; + +class PointerCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + PointerCmp (const PointerType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const PointerType &type) override + { + auto base_type = base->get_base (); + auto other_base_type = type.get_base (); + + // rust is permissive about mutablity here you can always go from mutable to + // immutable but not the otherway round + bool mutability_ok = base->is_mutable () ? type.is_mutable () : true; + if (!mutability_ok) + { + BaseCmp::visit (type); + return; + } + + if (!base_type->can_eq (other_base_type, emit_error_flag)) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const PointerType *base; +}; + +class ParamCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + ParamCmp (const ParamType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + // param types are a placeholder we shouldn't have cases where we unify + // against it. eg: struct foo { a: T }; When we invoke it we can do either: + // + // foo{ a: 123 }. + // Then this enforces the i32 type to be referenced on the + // field via an hirid. + // + // rust also allows for a = foo{a:123}; Where we can use an Inference Variable + // to handle the typing of the struct + bool can_eq (const BaseType *other) override + { + if (!base->can_resolve ()) + return BaseCmp::can_eq (other); + + auto lookup = base->resolve (); + return lookup->can_eq (other, emit_error_flag); + } + + // imagine the case where we have: + // struct Foo(T); + // Then we declare a generic impl block + // impl Foo { ... } + // both of these types are compatible so we mostly care about the number of + // generic arguments + void visit (const ParamType &) override { ok = true; } + + void visit (const InferType &) override { ok = true; } + + void visit (const FnType &) override { ok = true; } + + void visit (const FnPtr &) override { ok = true; } + + void visit (const ADTType &) override { ok = true; } + + void visit (const ArrayType &) override { ok = true; } + + void visit (const SliceType &) override { ok = true; } + + void visit (const BoolType &) override { ok = true; } + + void visit (const IntType &) override { ok = true; } + + void visit (const UintType &) override { ok = true; } + + void visit (const USizeType &) override { ok = true; } + + void visit (const ISizeType &) override { ok = true; } + + void visit (const FloatType &) override { ok = true; } + + void visit (const CharType &) override { ok = true; } + + void visit (const ReferenceType &) override { ok = true; } + + void visit (const PointerType &) override { ok = true; } + + void visit (const StrType &) override { ok = true; } + + void visit (const NeverType &) override { ok = true; } + + void visit (const DynamicObjectType &) override { ok = true; } + + void visit (const PlaceholderType &type) override + { + ok = base->get_symbol ().compare (type.get_symbol ()) == 0; + } + +private: + const BaseType *get_base () const override { return base; } + const ParamType *base; +}; + +class StrCmp : public BaseCmp +{ + // FIXME we will need a enum for the StrType like ByteBuf etc.. + using Rust::TyTy::BaseCmp::visit; + +public: + StrCmp (const StrType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const StrType &type) override { ok = true; } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const StrType *base; +}; + +class NeverCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + NeverCmp (const NeverType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const NeverType &type) override { ok = true; } + + void visit (const InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseCmp::visit (type); + return; + } + + ok = true; + } + +private: + const BaseType *get_base () const override { return base; } + const NeverType *base; +}; + +class PlaceholderCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + PlaceholderCmp (const PlaceholderType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + bool can_eq (const BaseType *other) override + { + if (!base->can_resolve ()) + return BaseCmp::can_eq (other); + + BaseType *lookup = base->resolve (); + return lookup->can_eq (other, emit_error_flag); + } + + void visit (const TupleType &) override { ok = true; } + + void visit (const ADTType &) override { ok = true; } + + void visit (const InferType &) override { ok = true; } + + void visit (const FnType &) override { ok = true; } + + void visit (const FnPtr &) override { ok = true; } + + void visit (const ArrayType &) override { ok = true; } + + void visit (const BoolType &) override { ok = true; } + + void visit (const IntType &) override { ok = true; } + + void visit (const UintType &) override { ok = true; } + + void visit (const USizeType &) override { ok = true; } + + void visit (const ISizeType &) override { ok = true; } + + void visit (const FloatType &) override { ok = true; } + + void visit (const ErrorType &) override { ok = true; } + + void visit (const CharType &) override { ok = true; } + + void visit (const ReferenceType &) override { ok = true; } + + void visit (const ParamType &) override { ok = true; } + + void visit (const StrType &) override { ok = true; } + + void visit (const NeverType &) override { ok = true; } + + void visit (const SliceType &) override { ok = true; } + +private: + const BaseType *get_base () const override { return base; } + + const PlaceholderType *base; +}; + +class DynamicCmp : public BaseCmp +{ + using Rust::TyTy::BaseCmp::visit; + +public: + DynamicCmp (const DynamicObjectType *base, bool emit_errors) + : BaseCmp (base, emit_errors), base (base) + {} + + void visit (const DynamicObjectType &type) override + { + if (base->num_specified_bounds () != type.num_specified_bounds ()) + { + BaseCmp::visit (type); + return; + } + + Location ref_locus = mappings->lookup_location (type.get_ref ()); + ok = base->bounds_compatible (type, ref_locus, false); + } + +private: + const BaseType *get_base () const override { return base; } + + const DynamicObjectType *base; +}; + +} // namespace TyTy +} // namespace Rust + +#endif // RUST_TYTY_CMP_H diff --git a/gcc/rust/typecheck/rust-tyty-rules.h b/gcc/rust/typecheck/rust-tyty-rules.h new file mode 100644 index 00000000000..77d912a5921 --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty-rules.h @@ -0,0 +1,1584 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_TYTY_RULES +#define RUST_TYTY_RULES + +#include "rust-diagnostics.h" +#include "rust-tyty.h" +#include "rust-tyty-visitor.h" +#include "rust-hir-map.h" +#include "rust-hir-type-check.h" + +namespace Rust { +namespace TyTy { + +/* Rules specify how to unify two Ty. For example, the result of unifying the + two tuples (u64, A) and (B, i64) would be (u64, i64). + + Performing a unification requires a double dispatch. To illustrate, suppose + we want to unify `ty1` and `ty2`. Here's what it looks like: + 1. The caller calls `ty1.unify(ty2)`. This is the first dispatch. + 2. `ty1` creates a rule specific to its type(e.g. TupleRules). + 3. The rule calls `ty2.accept_vis(rule)`. This is the second dispatch. + 4. `ty2` calls `rule.visit(*this)`, which will method-overload to the + correct implementation at compile time. + + The nice thing about Rules is that they seperate unification logic from the + representation of Ty. To support unifying a new Ty, implement its + `accept_vis` and `unify` method to pass the unification request to Rules. + Then, create a new `XXXRules` class and implement one `visit` method for + every Ty it can unify with. */ +class BaseRules : public TyVisitor +{ +public: + virtual ~BaseRules () {} + + /* Unify two ty. Returns a pointer to the newly-created unified ty, or nullptr + if the two types cannot be unified. The caller is responsible for releasing + the memory of the returned ty. + + This method is meant to be used internally by Ty. If you're trying to unify + two ty, you can simply call `unify` on ty themselves. */ + virtual BaseType *unify (BaseType *other) + { + if (other->get_kind () == TypeKind::PARAM) + { + ParamType *p = static_cast (other); + other = p->resolve (); + } + else if (other->get_kind () == TypeKind::PLACEHOLDER) + { + PlaceholderType *p = static_cast (other); + if (p->can_resolve ()) + { + other = p->resolve (); + return get_base ()->unify (other); + } + } + else if (other->get_kind () == TypeKind::PROJECTION) + { + ProjectionType *p = static_cast (other); + other = p->get (); + return get_base ()->unify (other); + } + + other->accept_vis (*this); + if (resolved->get_kind () == TyTy::TypeKind::ERROR) + return resolved; + + resolved->append_reference (get_base ()->get_ref ()); + resolved->append_reference (other->get_ref ()); + for (auto ref : get_base ()->get_combined_refs ()) + resolved->append_reference (ref); + for (auto ref : other->get_combined_refs ()) + resolved->append_reference (ref); + + other->append_reference (resolved->get_ref ()); + other->append_reference (get_base ()->get_ref ()); + get_base ()->append_reference (resolved->get_ref ()); + get_base ()->append_reference (other->get_ref ()); + + bool result_resolved = resolved->get_kind () != TyTy::TypeKind::INFER; + bool result_is_infer_var = resolved->get_kind () == TyTy::TypeKind::INFER; + bool results_is_non_general_infer_var + = (result_is_infer_var + && (static_cast (resolved))->get_infer_kind () + != TyTy::InferType::GENERAL); + if (result_resolved || results_is_non_general_infer_var) + { + for (auto &ref : resolved->get_combined_refs ()) + { + TyTy::BaseType *ref_tyty = nullptr; + bool ok = context->lookup_type (ref, &ref_tyty); + if (!ok) + continue; + + // if any of the types are inference variables lets fix them + if (ref_tyty->get_kind () == TyTy::TypeKind::INFER) + { + context->insert_type ( + Analysis::NodeMapping (mappings->get_current_crate (), + UNKNOWN_NODEID, ref, + UNKNOWN_LOCAL_DEFID), + resolved->clone ()); + } + } + } + return resolved; + } + + virtual void visit (TupleType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ADTType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (InferType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (FnType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (FnPtr &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ArrayType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (SliceType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (BoolType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (IntType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (UintType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (USizeType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ISizeType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (FloatType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ErrorType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (CharType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ReferenceType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (PointerType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ParamType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (StrType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (NeverType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (PlaceholderType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ProjectionType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (DynamicObjectType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + + virtual void visit (ClosureType &type) override + { + Location ref_locus = mappings->lookup_location (type.get_ref ()); + Location base_locus = mappings->lookup_location (get_base ()->get_ref ()); + RichLocation r (ref_locus); + r.add_range (base_locus); + rust_error_at (r, "expected [%s] got [%s]", + get_base ()->as_string ().c_str (), + type.as_string ().c_str ()); + } + +protected: + BaseRules (BaseType *base) + : mappings (Analysis::Mappings::get ()), + context (Resolver::TypeCheckContext::get ()), + resolved (new ErrorType (base->get_ref (), base->get_ref ())) + {} + + Analysis::Mappings *mappings; + Resolver::TypeCheckContext *context; + + /* Temporary storage for the result of a unification. + We could return the result directly instead of storing it in the rule + object, but that involves modifying the visitor pattern to accommodate + the return value, which is too complex. */ + BaseType *resolved; + +private: + /* Returns a pointer to the ty that created this rule. */ + virtual BaseType *get_base () = 0; +}; + +class InferRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + InferRules (InferType *base) : BaseRules (base), base (base) {} + + void visit (BoolType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (IntType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (UintType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (USizeType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (ISizeType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () + == TyTy::InferType::InferTypeKind::INTEGRAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (FloatType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL) + || (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (ArrayType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (SliceType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (ADTType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (TupleType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (InferType &type) override + { + switch (base->get_infer_kind ()) + { + case InferType::InferTypeKind::GENERAL: + resolved = type.clone (); + return; + + case InferType::InferTypeKind::INTEGRAL: { + if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL) + { + resolved = type.clone (); + return; + } + else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL) + { + resolved = base->clone (); + return; + } + } + break; + + case InferType::InferTypeKind::FLOAT: { + if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT) + { + resolved = type.clone (); + return; + } + else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL) + { + resolved = base->clone (); + return; + } + } + break; + } + + BaseRules::visit (type); + } + + void visit (CharType &type) override + { + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + } + + void visit (ReferenceType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (PointerType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (ParamType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (DynamicObjectType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + + void visit (ClosureType &type) override + { + bool is_valid + = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL); + if (is_valid) + { + resolved = type.clone (); + return; + } + + BaseRules::visit (type); + } + +private: + BaseType *get_base () override { return base; } + + InferType *base; +}; + +class FnRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + FnRules (FnType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (FnType &type) override + { + if (base->num_params () != type.num_params ()) + { + BaseRules::visit (type); + return; + } + + for (size_t i = 0; i < base->num_params (); i++) + { + auto a = base->param_at (i).second; + auto b = type.param_at (i).second; + + auto unified_param = a->unify (b); + if (unified_param == nullptr) + { + BaseRules::visit (type); + return; + } + } + + auto unified_return + = base->get_return_type ()->unify (type.get_return_type ()); + if (unified_return == nullptr) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + FnType *base; +}; + +class FnptrRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + FnptrRules (FnPtr *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (FnPtr &type) override + { + auto this_ret_type = base->get_return_type (); + auto other_ret_type = type.get_return_type (); + auto unified_result = this_ret_type->unify (other_ret_type); + if (unified_result == nullptr + || unified_result->get_kind () == TypeKind::ERROR) + { + BaseRules::visit (type); + return; + } + + if (base->num_params () != type.num_params ()) + { + BaseRules::visit (type); + return; + } + + for (size_t i = 0; i < base->num_params (); i++) + { + auto this_param = base->param_at (i); + auto other_param = type.param_at (i); + auto unified_param = this_param->unify (other_param); + if (unified_param == nullptr + || unified_param->get_kind () == TypeKind::ERROR) + { + BaseRules::visit (type); + return; + } + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (FnType &type) override + { + auto this_ret_type = base->get_return_type (); + auto other_ret_type = type.get_return_type (); + auto unified_result = this_ret_type->unify (other_ret_type); + if (unified_result == nullptr + || unified_result->get_kind () == TypeKind::ERROR) + { + BaseRules::visit (type); + return; + } + + if (base->num_params () != type.num_params ()) + { + BaseRules::visit (type); + return; + } + + for (size_t i = 0; i < base->num_params (); i++) + { + auto this_param = base->param_at (i); + auto other_param = type.param_at (i).second; + auto unified_param = this_param->unify (other_param); + if (unified_param == nullptr + || unified_param->get_kind () == TypeKind::ERROR) + { + BaseRules::visit (type); + return; + } + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + FnPtr *base; +}; + +class ClosureRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + ClosureRules (ClosureType *base) : BaseRules (base), base (base) {} + + // TODO + +private: + BaseType *get_base () override { return base; } + + ClosureType *base; +}; + +class ArrayRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + ArrayRules (ArrayType *base) : BaseRules (base), base (base) {} + + void visit (ArrayType &type) override + { + // check base type + auto base_resolved + = base->get_element_type ()->unify (type.get_element_type ()); + if (base_resolved == nullptr) + { + BaseRules::visit (type); + return; + } + + resolved + = new ArrayType (type.get_ref (), type.get_ty_ref (), + type.get_ident ().locus, type.get_capacity_expr (), + TyVar (base_resolved->get_ref ())); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + ArrayType *base; +}; + +class SliceRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + SliceRules (SliceType *base) : BaseRules (base), base (base) {} + + void visit (SliceType &type) override + { + // check base type + auto base_resolved + = base->get_element_type ()->unify (type.get_element_type ()); + if (base_resolved == nullptr) + { + BaseRules::visit (type); + return; + } + + resolved = new SliceType (type.get_ref (), type.get_ty_ref (), + type.get_ident ().locus, + TyVar (base_resolved->get_ref ())); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + SliceType *base; +}; + +class BoolRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + BoolRules (BoolType *base) : BaseRules (base), base (base) {} + + void visit (BoolType &type) override + { + resolved = new BoolType (type.get_ref (), type.get_ty_ref ()); + } + + void visit (InferType &type) override + { + switch (type.get_infer_kind ()) + { + case InferType::InferTypeKind::GENERAL: + resolved = base->clone (); + break; + + default: + BaseRules::visit (type); + break; + } + } + +private: + BaseType *get_base () override { return base; } + + BoolType *base; +}; + +class IntRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + IntRules (IntType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + // cant assign a float inference variable + if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (IntType &type) override + { + if (type.get_int_kind () != base->get_int_kind ()) + { + BaseRules::visit (type); + return; + } + + resolved + = new IntType (type.get_ref (), type.get_ty_ref (), type.get_int_kind ()); + } + +private: + BaseType *get_base () override { return base; } + + IntType *base; +}; + +class UintRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + UintRules (UintType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + // cant assign a float inference variable + if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (UintType &type) override + { + if (type.get_uint_kind () != base->get_uint_kind ()) + { + BaseRules::visit (type); + return; + } + + resolved = new UintType (type.get_ref (), type.get_ty_ref (), + type.get_uint_kind ()); + } + +private: + BaseType *get_base () override { return base; } + + UintType *base; +}; + +class FloatRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + FloatRules (FloatType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (FloatType &type) override + { + if (type.get_float_kind () != base->get_float_kind ()) + { + BaseRules::visit (type); + return; + } + + resolved = new FloatType (type.get_ref (), type.get_ty_ref (), + type.get_float_kind ()); + } + +private: + BaseType *get_base () override { return base; } + + FloatType *base; +}; + +class ADTRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + ADTRules (ADTType *base) : BaseRules (base), base (base) {} + + void visit (ADTType &type) override + { + if (base->get_adt_kind () != type.get_adt_kind ()) + { + BaseRules::visit (type); + return; + } + + if (base->get_identifier ().compare (type.get_identifier ()) != 0) + { + BaseRules::visit (type); + return; + } + + if (base->number_of_variants () != type.number_of_variants ()) + { + BaseRules::visit (type); + return; + } + + for (size_t i = 0; i < type.number_of_variants (); ++i) + { + TyTy::VariantDef *a = base->get_variants ().at (i); + TyTy::VariantDef *b = type.get_variants ().at (i); + + if (a->num_fields () != b->num_fields ()) + { + BaseRules::visit (type); + return; + } + + for (size_t j = 0; j < a->num_fields (); j++) + { + TyTy::StructFieldType *base_field = a->get_field_at_index (j); + TyTy::StructFieldType *other_field = b->get_field_at_index (j); + + TyTy::BaseType *this_field_ty = base_field->get_field_type (); + TyTy::BaseType *other_field_ty = other_field->get_field_type (); + + BaseType *unified_ty = this_field_ty->unify (other_field_ty); + if (unified_ty->get_kind () == TyTy::TypeKind::ERROR) + return; + } + } + + // generic args for the unit-struct case + if (type.is_unit () && base->is_unit ()) + { + rust_assert (type.get_num_substitutions () + == base->get_num_substitutions ()); + + for (size_t i = 0; i < type.get_num_substitutions (); i++) + { + auto &a = base->get_substs ().at (i); + auto &b = type.get_substs ().at (i); + + auto pa = a.get_param_ty (); + auto pb = b.get_param_ty (); + + auto res = pa->unify (pb); + if (res->get_kind () == TyTy::TypeKind::ERROR) + { + return; + } + } + } + + resolved = type.clone (); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + ADTType *base; +}; + +class TupleRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + TupleRules (TupleType *base) : BaseRules (base), base (base) {} + + void visit (TupleType &type) override + { + if (base->num_fields () != type.num_fields ()) + { + BaseRules::visit (type); + return; + } + + std::vector fields; + for (size_t i = 0; i < base->num_fields (); i++) + { + BaseType *bo = base->get_field (i); + BaseType *fo = type.get_field (i); + + BaseType *unified_ty = bo->unify (fo); + if (unified_ty->get_kind () == TyTy::TypeKind::ERROR) + return; + + fields.push_back (TyVar (unified_ty->get_ref ())); + } + + resolved = new TyTy::TupleType (type.get_ref (), type.get_ty_ref (), + type.get_ident ().locus, fields); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + TupleType *base; +}; + +class USizeRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + USizeRules (USizeType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + // cant assign a float inference variable + if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (USizeType &type) override { resolved = type.clone (); } + +private: + BaseType *get_base () override { return base; } + + USizeType *base; +}; + +class ISizeRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + ISizeRules (ISizeType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + // cant assign a float inference variable + if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (ISizeType &type) override { resolved = type.clone (); } + +private: + BaseType *get_base () override { return base; } + + ISizeType *base; +}; + +class CharRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + CharRules (CharType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + + void visit (CharType &type) override { resolved = type.clone (); } + +private: + BaseType *get_base () override { return base; } + + CharType *base; +}; + +class ReferenceRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + ReferenceRules (ReferenceType *base) : BaseRules (base), base (base) {} + + void visit (ReferenceType &type) override + { + auto base_type = base->get_base (); + auto other_base_type = type.get_base (); + + TyTy::BaseType *base_resolved = base_type->unify (other_base_type); + if (base_resolved == nullptr + || base_resolved->get_kind () == TypeKind::ERROR) + { + BaseRules::visit (type); + return; + } + + // rust is permissive about mutablity here you can always go from mutable to + // immutable but not the otherway round + bool mutability_ok = base->is_mutable () ? type.is_mutable () : true; + if (!mutability_ok) + { + BaseRules::visit (type); + return; + } + + resolved = new ReferenceType (base->get_ref (), base->get_ty_ref (), + TyVar (base_resolved->get_ref ()), + base->mutability ()); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + ReferenceType *base; +}; + +class PointerRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + PointerRules (PointerType *base) : BaseRules (base), base (base) {} + + void visit (PointerType &type) override + { + auto base_type = base->get_base (); + auto other_base_type = type.get_base (); + + TyTy::BaseType *base_resolved = base_type->unify (other_base_type); + if (base_resolved == nullptr + || base_resolved->get_kind () == TypeKind::ERROR) + { + BaseRules::visit (type); + return; + } + + // rust is permissive about mutablity here you can always go from mutable to + // immutable but not the otherway round + bool mutability_ok = base->is_mutable () ? type.is_mutable () : true; + if (!mutability_ok) + { + BaseRules::visit (type); + return; + } + + resolved = new PointerType (base->get_ref (), base->get_ty_ref (), + TyVar (base_resolved->get_ref ()), + base->mutability ()); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + resolved->set_ref (type.get_ref ()); + } + +private: + BaseType *get_base () override { return base; } + + PointerType *base; +}; + +class ParamRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + ParamRules (ParamType *base) : BaseRules (base), base (base) {} + + // param types are a placeholder we shouldn't have cases where we unify + // against it. eg: struct foo { a: T }; When we invoke it we can do either: + // + // foo{ a: 123 }. + // Then this enforces the i32 type to be referenced on the + // field via an hirid. + // + // rust also allows for a = foo{a:123}; Where we can use an Inference Variable + // to handle the typing of the struct + BaseType *unify (BaseType *other) override final + { + if (!base->can_resolve ()) + return BaseRules::unify (other); + + auto lookup = base->resolve (); + return lookup->unify (other); + } + + void visit (ParamType &type) override + { + if (base->get_symbol ().compare (type.get_symbol ()) != 0) + { + BaseRules::visit (type); + return; + } + + resolved = type.clone (); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + } + +private: + BaseType *get_base () override { return base; } + + ParamType *base; +}; + +class StrRules : public BaseRules +{ + // FIXME we will need a enum for the StrType like ByteBuf etc.. + using Rust::TyTy::BaseRules::visit; + +public: + StrRules (StrType *base) : BaseRules (base), base (base) {} + + void visit (StrType &type) override { resolved = type.clone (); } + +private: + BaseType *get_base () override { return base; } + + StrType *base; +}; + +class NeverRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + NeverRules (NeverType *base) : BaseRules (base), base (base) {} + + void visit (NeverType &type) override { resolved = type.clone (); } + +private: + BaseType *get_base () override { return base; } + + NeverType *base; +}; + +class PlaceholderRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + PlaceholderRules (PlaceholderType *base) : BaseRules (base), base (base) {} + + BaseType *unify (BaseType *other) override final + { + if (!base->can_resolve ()) + return BaseRules::unify (other); + + BaseType *lookup = base->resolve (); + return lookup->unify (other); + } + + void visit (PlaceholderType &type) override + { + if (base->get_symbol ().compare (type.get_symbol ()) != 0) + { + BaseRules::visit (type); + return; + } + + resolved = type.clone (); + } + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + } + +private: + BaseType *get_base () override { return base; } + + PlaceholderType *base; +}; + +class DynamicRules : public BaseRules +{ + using Rust::TyTy::BaseRules::visit; + +public: + DynamicRules (DynamicObjectType *base) : BaseRules (base), base (base) {} + + void visit (InferType &type) override + { + if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + } + + void visit (DynamicObjectType &type) override + { + if (base->num_specified_bounds () != type.num_specified_bounds ()) + { + BaseRules::visit (type); + return; + } + + Location ref_locus = mappings->lookup_location (type.get_ref ()); + if (!base->bounds_compatible (type, ref_locus, true)) + { + BaseRules::visit (type); + return; + } + + resolved = base->clone (); + } + +private: + BaseType *get_base () override { return base; } + + DynamicObjectType *base; +}; + +} // namespace TyTy +} // namespace Rust + +#endif // RUST_TYTY_RULES diff --git a/gcc/rust/typecheck/rust-tyty-visitor.h b/gcc/rust/typecheck/rust-tyty-visitor.h new file mode 100644 index 00000000000..464e70d39d7 --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty-visitor.h @@ -0,0 +1,88 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_TYTY_VISITOR +#define RUST_TYTY_VISITOR + +#include "rust-tyty.h" + +namespace Rust { +namespace TyTy { + +class TyVisitor +{ +public: + virtual void visit (InferType &type) = 0; + virtual void visit (ADTType &type) = 0; + virtual void visit (TupleType &type) = 0; + virtual void visit (FnType &type) = 0; + virtual void visit (FnPtr &type) = 0; + virtual void visit (ArrayType &type) = 0; + virtual void visit (SliceType &type) = 0; + virtual void visit (BoolType &type) = 0; + virtual void visit (IntType &type) = 0; + virtual void visit (UintType &type) = 0; + virtual void visit (FloatType &type) = 0; + virtual void visit (USizeType &type) = 0; + virtual void visit (ISizeType &type) = 0; + virtual void visit (ErrorType &type) = 0; + virtual void visit (CharType &type) = 0; + virtual void visit (ReferenceType &type) = 0; + virtual void visit (PointerType &type) = 0; + virtual void visit (ParamType &type) = 0; + virtual void visit (StrType &type) = 0; + virtual void visit (NeverType &type) = 0; + virtual void visit (PlaceholderType &type) = 0; + virtual void visit (ProjectionType &type) = 0; + virtual void visit (DynamicObjectType &type) = 0; + virtual void visit (ClosureType &type) = 0; +}; + +class TyConstVisitor +{ +public: + virtual void visit (const InferType &type) = 0; + virtual void visit (const ADTType &type) = 0; + virtual void visit (const TupleType &type) = 0; + virtual void visit (const FnType &type) = 0; + virtual void visit (const FnPtr &type) = 0; + virtual void visit (const ArrayType &type) = 0; + virtual void visit (const SliceType &type) = 0; + virtual void visit (const BoolType &type) = 0; + virtual void visit (const IntType &type) = 0; + virtual void visit (const UintType &type) = 0; + virtual void visit (const FloatType &type) = 0; + virtual void visit (const USizeType &type) = 0; + virtual void visit (const ISizeType &type) = 0; + virtual void visit (const ErrorType &type) = 0; + virtual void visit (const CharType &type) = 0; + virtual void visit (const ReferenceType &type) = 0; + virtual void visit (const PointerType &type) = 0; + virtual void visit (const ParamType &type) = 0; + virtual void visit (const StrType &type) = 0; + virtual void visit (const NeverType &type) = 0; + virtual void visit (const PlaceholderType &type) = 0; + virtual void visit (const ProjectionType &type) = 0; + virtual void visit (const DynamicObjectType &type) = 0; + virtual void visit (const ClosureType &type) = 0; +}; + +} // namespace TyTy +} // namespace Rust + +#endif // RUST_TYTY_VISITOR diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc new file mode 100644 index 00000000000..3c2c6786940 --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty.cc @@ -0,0 +1,2885 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#include "rust-tyty.h" +#include "rust-tyty-visitor.h" +#include "rust-tyty-call.h" +#include "rust-hir-type-check-expr.h" +#include "rust-hir-type-check-type.h" +#include "rust-tyty-rules.h" +#include "rust-tyty-cmp.h" +#include "rust-hir-map.h" +#include "rust-substitution-mapper.h" +#include "rust-hir-trait-ref.h" +#include "rust-hir-type-bounds.h" + +namespace Rust { +namespace TyTy { + +std::string +TypeKindFormat::to_string (TypeKind kind) +{ + switch (kind) + { + case TypeKind::INFER: + return "Infer"; + + case TypeKind::ADT: + return "ADT"; + + case TypeKind::STR: + return "STR"; + + case TypeKind::REF: + return "REF"; + + case TypeKind::POINTER: + return "POINTER"; + + case TypeKind::PARAM: + return "PARAM"; + + case TypeKind::ARRAY: + return "ARRAY"; + + case TypeKind::SLICE: + return "SLICE"; + + case TypeKind::FNDEF: + return "FnDef"; + + case TypeKind::FNPTR: + return "FnPtr"; + + case TypeKind::TUPLE: + return "Tuple"; + + case TypeKind::BOOL: + return "Bool"; + + case TypeKind::CHAR: + return "Char"; + + case TypeKind::INT: + return "Int"; + + case TypeKind::UINT: + return "Uint"; + + case TypeKind::FLOAT: + return "Float"; + + case TypeKind::USIZE: + return "Usize"; + + case TypeKind::ISIZE: + return "Isize"; + + case TypeKind::NEVER: + return "Never"; + + case TypeKind::PLACEHOLDER: + return "Placeholder"; + + case TypeKind::PROJECTION: + return "Projection"; + + case TypeKind::DYNAMIC: + return "Dynamic"; + + case TypeKind::CLOSURE: + return "Closure"; + + case TypeKind::ERROR: + return "ERROR"; + } + gcc_unreachable (); +} + +bool +is_primitive_type_kind (TypeKind kind) +{ + switch (kind) + { + case TypeKind::BOOL: + case TypeKind::CHAR: + case TypeKind::INT: + case TypeKind::UINT: + case TypeKind::ISIZE: + case TypeKind::USIZE: + case TypeKind::FLOAT: + case TypeKind::NEVER: + case TypeKind::STR: + return true; + default: + return false; + } +} + +bool +BaseType::satisfies_bound (const TypeBoundPredicate &predicate) const +{ + const Resolver::TraitReference *query = predicate.get (); + for (auto &bound : specified_bounds) + { + const Resolver::TraitReference *item = bound.get (); + bool found = item->get_mappings ().get_defid () + == query->get_mappings ().get_defid (); + if (found) + return true; + } + + auto probed = Resolver::TypeBoundsProbe::Probe (this); + for (auto &b : probed) + { + const Resolver::TraitReference *bound = b.first; + bool found = bound->get_mappings ().get_defid () + == query->get_mappings ().get_defid (); + if (found) + return true; + } + + return false; +} + +bool +BaseType::bounds_compatible (const BaseType &other, Location locus, + bool emit_error) const +{ + std::vector> + unsatisfied_bounds; + for (auto &bound : get_specified_bounds ()) + { + if (!other.satisfies_bound (bound)) + unsatisfied_bounds.push_back (bound); + } + + // lets emit a single error for this + if (unsatisfied_bounds.size () > 0) + { + RichLocation r (locus); + std::string missing_preds; + for (size_t i = 0; i < unsatisfied_bounds.size (); i++) + { + const TypeBoundPredicate &pred = unsatisfied_bounds.at (i); + r.add_range (pred.get_locus ()); + missing_preds += pred.get_name (); + + bool have_next = (i + 1) < unsatisfied_bounds.size (); + if (have_next) + missing_preds += ", "; + } + + if (emit_error) + { + rust_error_at (r, + "bounds not satisfied for %s %<%s%> is not satisfied", + other.get_name ().c_str (), missing_preds.c_str ()); + // rust_assert (!emit_error); + } + } + + return unsatisfied_bounds.size () == 0; +} + +void +BaseType::inherit_bounds (const BaseType &other) +{ + inherit_bounds (other.get_specified_bounds ()); +} + +void +BaseType::inherit_bounds ( + const std::vector &specified_bounds) +{ + // FIXME + // 1. This needs to union the bounds + // 2. Do some checking for trait polarity to ensure compatibility + for (auto &bound : specified_bounds) + { + add_bound (bound); + } +} + +const BaseType * +BaseType::get_root () const +{ + // FIXME this needs to be it its own visitor class with a vector adjustments + const TyTy::BaseType *root = this; + if (get_kind () == TyTy::REF) + { + const ReferenceType *r = static_cast (root); + root = r->get_base ()->get_root (); + } + else if (get_kind () == TyTy::POINTER) + { + const PointerType *r = static_cast (root); + root = r->get_base ()->get_root (); + } + + // these are an unsize + else if (get_kind () == TyTy::SLICE) + { + const SliceType *r = static_cast (root); + root = r->get_element_type ()->get_root (); + } + // else if (get_kind () == TyTy::ARRAY) + // { + // const ArrayType *r = static_cast (root); + // root = r->get_element_type ()->get_root (); + // } + + return root; +} + +const BaseType * +BaseType::destructure () const +{ + int recurisve_ops = 0; + const BaseType *x = this; + while (true) + { + if (recurisve_ops++ >= rust_max_recursion_depth) + { + rust_error_at ( + Location (), + "% count exceeds limit of %i (use " + "% to increase the limit)", + rust_max_recursion_depth); + return new ErrorType (get_ref ()); + } + + switch (x->get_kind ()) + { + case TyTy::TypeKind::PARAM: { + const TyTy::ParamType *p = static_cast (x); + x = p->resolve (); + } + break; + + case TyTy::TypeKind::PLACEHOLDER: { + const TyTy::PlaceholderType *p + = static_cast (x); + rust_assert (p->can_resolve ()); + x = p->resolve (); + } + break; + + case TyTy::TypeKind::PROJECTION: { + const TyTy::ProjectionType *p + = static_cast (x); + x = p->get (); + } + break; + + default: + return x; + } + } + + return x; +} + +TyVar::TyVar (HirId ref) : ref (ref) +{ + // ensure this reference is defined within the context + auto context = Resolver::TypeCheckContext::get (); + BaseType *lookup = nullptr; + bool ok = context->lookup_type (ref, &lookup); + rust_assert (ok); +} + +BaseType * +TyVar::get_tyty () const +{ + auto context = Resolver::TypeCheckContext::get (); + BaseType *lookup = nullptr; + bool ok = context->lookup_type (ref, &lookup); + rust_assert (ok); + return lookup; +} + +TyVar +TyVar::get_implicit_infer_var (Location locus) +{ + auto mappings = Analysis::Mappings::get (); + auto context = Resolver::TypeCheckContext::get (); + + InferType *infer = new InferType (mappings->get_next_hir_id (), + InferType::InferTypeKind::GENERAL, locus); + context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (), + UNKNOWN_NODEID, + infer->get_ref (), + UNKNOWN_LOCAL_DEFID), + infer); + mappings->insert_location (infer->get_ref (), locus); + + return TyVar (infer->get_ref ()); +} + +TyVar +TyVar::subst_covariant_var (TyTy::BaseType *orig, TyTy::BaseType *subst) +{ + if (orig->get_kind () != TyTy::TypeKind::PARAM) + return TyVar (subst->get_ty_ref ()); + else if (subst->get_kind () == TyTy::TypeKind::PARAM) + { + TyTy::ParamType *p = static_cast (subst); + if (p->resolve ()->get_kind () == TyTy::TypeKind::PARAM) + { + return TyVar (subst->get_ty_ref ()); + } + } + + return TyVar (subst->get_ref ()); +} + +TyVar +TyVar::clone () const +{ + TyTy::BaseType *c = get_tyty ()->clone (); + return TyVar (c->get_ref ()); +} + +TyVar +TyVar::monomorphized_clone () const +{ + auto mappings = Analysis::Mappings::get (); + auto context = Resolver::TypeCheckContext::get (); + + // this needs a new hirid + TyTy::BaseType *c = get_tyty ()->monomorphized_clone (); + c->set_ref (mappings->get_next_hir_id ()); + + // insert it + context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (), + UNKNOWN_NODEID, c->get_ref (), + UNKNOWN_LOCAL_DEFID), + c); + + return TyVar (c->get_ref ()); +} + +TyWithLocation::TyWithLocation (BaseType *ty, Location locus) + : ty (ty), locus (locus) +{} + +TyWithLocation::TyWithLocation (BaseType *ty) : ty (ty) +{ + auto mappings = Analysis::Mappings::get (); + locus = mappings->lookup_location (ty->get_ref ()); +} + +void +InferType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +InferType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +InferType::as_string () const +{ + switch (infer_kind) + { + case GENERAL: + return "T?"; + case INTEGRAL: + return ""; + case FLOAT: + return ""; + } + return ""; +} + +BaseType * +InferType::unify (BaseType *other) +{ + InferRules r (this); + return r.unify (other); +} + +bool +InferType::can_eq (const BaseType *other, bool emit_errors) const +{ + InferCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +InferType::clone () const +{ + // clones for inference variables are special in that they _must_ exist within + // the type check context and we must ensure we don't loose the chain + // otherwise we will end up in the missing type annotations case + // + // This means we cannot simply take over the same reference we must generate a + // new ref just like the get_implicit_infer_var code then we can setup the + // chain of references accordingly to ensure we don't loose the ability to + // update the inference variables when we solve the type + + auto mappings = Analysis::Mappings::get (); + auto context = Resolver::TypeCheckContext::get (); + + InferType *clone + = new InferType (mappings->get_next_hir_id (), get_infer_kind (), + get_ident ().locus, get_combined_refs ()); + + context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (), + UNKNOWN_NODEID, + clone->get_ref (), + UNKNOWN_LOCAL_DEFID), + clone); + mappings->insert_location (clone->get_ref (), + mappings->lookup_location (get_ref ())); + + // setup the chain to reference this + clone->append_reference (get_ref ()); + + return clone; +} + +BaseType * +InferType::monomorphized_clone () const +{ + return clone (); +} + +bool +InferType::default_type (BaseType **type) const +{ + auto context = Resolver::TypeCheckContext::get (); + bool ok = false; + switch (infer_kind) + { + case GENERAL: + return false; + + case INTEGRAL: { + ok = context->lookup_builtin ("i32", type); + rust_assert (ok); + return ok; + } + + case FLOAT: { + ok = context->lookup_builtin ("f64", type); + rust_assert (ok); + return ok; + } + } + return false; +} + +void +ErrorType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ErrorType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ErrorType::as_string () const +{ + return ""; +} + +BaseType * +ErrorType::unify (BaseType *other) +{ + return this; +} + +bool +ErrorType::can_eq (const BaseType *other, bool emit_errors) const +{ + return get_kind () == other->get_kind (); +} + +BaseType * +ErrorType::clone () const +{ + return new ErrorType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + +BaseType * +ErrorType::monomorphized_clone () const +{ + return clone (); +} + +std::string +StructFieldType::as_string () const +{ + return name + ":" + get_field_type ()->debug_str (); +} + +bool +StructFieldType::is_equal (const StructFieldType &other) const +{ + bool names_eq = get_name ().compare (other.get_name ()) == 0; + + TyTy::BaseType *o = other.get_field_type (); + if (o->get_kind () == TypeKind::PARAM) + { + ParamType *op = static_cast (o); + o = op->resolve (); + } + + bool types_eq = get_field_type ()->is_equal (*o); + + return names_eq && types_eq; +} + +StructFieldType * +StructFieldType::clone () const +{ + return new StructFieldType (get_ref (), get_name (), + get_field_type ()->clone ()); +} + +StructFieldType * +StructFieldType::monomorphized_clone () const +{ + return new StructFieldType (get_ref (), get_name (), + get_field_type ()->monomorphized_clone ()); +} + +bool +SubstitutionParamMapping::need_substitution () const +{ + if (!param->can_resolve ()) + return true; + + auto resolved = param->resolve (); + return !resolved->is_concrete (); +} + +bool +SubstitutionParamMapping::fill_param_ty ( + SubstitutionArgumentMappings &subst_mappings, Location locus) +{ + SubstitutionArg arg = SubstitutionArg::error (); + bool ok = subst_mappings.get_argument_for_symbol (get_param_ty (), &arg); + if (!ok) + return true; + + TyTy::BaseType &type = *arg.get_tyty (); + if (type.get_kind () == TyTy::TypeKind::INFER) + { + type.inherit_bounds (*param); + } + else + { + if (!param->bounds_compatible (type, locus, true)) + return false; + } + + if (type.get_kind () == TypeKind::PARAM) + { + // delete param; + param = static_cast (type.clone ()); + } + else + { + // check the substitution is compatible with bounds + if (!param->bounds_compatible (type, locus, true)) + return false; + + // recursively pass this down to all HRTB's + for (auto &bound : param->get_specified_bounds ()) + bound.handle_substitions (subst_mappings); + + param->set_ty_ref (type.get_ref ()); + } + + return true; +} + +void +SubstitutionParamMapping::override_context () +{ + if (!param->can_resolve ()) + return; + + auto mappings = Analysis::Mappings::get (); + auto context = Resolver::TypeCheckContext::get (); + + context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (), + UNKNOWN_NODEID, + param->get_ref (), + UNKNOWN_LOCAL_DEFID), + param->resolve ()); +} + +SubstitutionArgumentMappings +SubstitutionRef::get_mappings_from_generic_args (HIR::GenericArgs &args) +{ + if (args.get_binding_args ().size () > 0) + { + RichLocation r (args.get_locus ()); + for (auto &binding : args.get_binding_args ()) + r.add_range (binding.get_locus ()); + + rust_error_at (r, "associated type bindings are not allowed here"); + return SubstitutionArgumentMappings::error (); + } + + // for inherited arguments + size_t offs = used_arguments.size (); + if (args.get_type_args ().size () + offs > substitutions.size ()) + { + RichLocation r (args.get_locus ()); + r.add_range (substitutions.front ().get_param_locus ()); + + rust_error_at ( + r, + "generic item takes at most %lu type arguments but %lu were supplied", + (unsigned long) substitutions.size (), + (unsigned long) args.get_type_args ().size ()); + return SubstitutionArgumentMappings::error (); + } + + if (args.get_type_args ().size () + offs < min_required_substitutions ()) + { + RichLocation r (args.get_locus ()); + r.add_range (substitutions.front ().get_param_locus ()); + + rust_error_at ( + r, + "generic item takes at least %lu type arguments but %lu were supplied", + (unsigned long) (min_required_substitutions () - offs), + (unsigned long) args.get_type_args ().size ()); + return SubstitutionArgumentMappings::error (); + } + + std::vector mappings = used_arguments.get_mappings (); + for (auto &arg : args.get_type_args ()) + { + BaseType *resolved = Resolver::TypeCheckType::Resolve (arg.get ()); + if (resolved == nullptr || resolved->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (args.get_locus (), "failed to resolve type arguments"); + return SubstitutionArgumentMappings::error (); + } + + SubstitutionArg subst_arg (&substitutions.at (offs), resolved); + offs++; + mappings.push_back (std::move (subst_arg)); + } + + // we must need to fill out defaults + size_t left_over + = num_required_substitutions () - min_required_substitutions (); + if (left_over > 0) + { + for (size_t offs = mappings.size (); offs < substitutions.size (); offs++) + { + SubstitutionParamMapping ¶m = substitutions.at (offs); + rust_assert (param.param_has_default_ty ()); + + BaseType *resolved = param.get_default_ty (); + if (resolved->get_kind () == TypeKind::ERROR) + return SubstitutionArgumentMappings::error (); + + // this resolved default might already contain default parameters + if (resolved->contains_type_parameters ()) + { + SubstitutionArgumentMappings intermediate (mappings, + args.get_locus ()); + resolved = Resolver::SubstMapperInternal::Resolve (resolved, + intermediate); + + if (resolved->get_kind () == TypeKind::ERROR) + return SubstitutionArgumentMappings::error (); + } + + SubstitutionArg subst_arg (¶m, resolved); + mappings.push_back (std::move (subst_arg)); + } + } + + return SubstitutionArgumentMappings (mappings, args.get_locus ()); +} + +SubstitutionArgumentMappings +SubstitutionRef::adjust_mappings_for_this ( + SubstitutionArgumentMappings &mappings) +{ + std::vector resolved_mappings; + for (size_t i = 0; i < substitutions.size (); i++) + { + auto &subst = substitutions.at (i); + + SubstitutionArg arg = SubstitutionArg::error (); + if (mappings.size () == substitutions.size ()) + { + mappings.get_argument_at (i, &arg); + } + else + { + if (subst.needs_substitution ()) + { + // get from passed in mappings + mappings.get_argument_for_symbol (subst.get_param_ty (), &arg); + } + else + { + // we should already have this somewhere + used_arguments.get_argument_for_symbol (subst.get_param_ty (), + &arg); + } + } + + bool ok = !arg.is_error (); + if (ok) + { + SubstitutionArg adjusted (&subst, arg.get_tyty ()); + resolved_mappings.push_back (std::move (adjusted)); + } + } + + if (resolved_mappings.empty ()) + return SubstitutionArgumentMappings::error (); + + return SubstitutionArgumentMappings (resolved_mappings, mappings.get_locus (), + mappings.get_subst_cb (), + mappings.trait_item_mode ()); +} + +bool +SubstitutionRef::are_mappings_bound (SubstitutionArgumentMappings &mappings) +{ + std::vector resolved_mappings; + for (size_t i = 0; i < substitutions.size (); i++) + { + auto &subst = substitutions.at (i); + + SubstitutionArg arg = SubstitutionArg::error (); + if (mappings.size () == substitutions.size ()) + { + mappings.get_argument_at (i, &arg); + } + else + { + if (subst.needs_substitution ()) + { + // get from passed in mappings + mappings.get_argument_for_symbol (subst.get_param_ty (), &arg); + } + else + { + // we should already have this somewhere + used_arguments.get_argument_for_symbol (subst.get_param_ty (), + &arg); + } + } + + bool ok = !arg.is_error (); + if (ok) + { + SubstitutionArg adjusted (&subst, arg.get_tyty ()); + resolved_mappings.push_back (std::move (adjusted)); + } + } + + return !resolved_mappings.empty (); +} + +// this function assumes that the mappings being passed are for the same type as +// this new substitution reference so ordering matters here +SubstitutionArgumentMappings +SubstitutionRef::solve_mappings_from_receiver_for_self ( + SubstitutionArgumentMappings &mappings) const +{ + std::vector resolved_mappings; + + rust_assert (mappings.size () == get_num_substitutions ()); + for (size_t i = 0; i < get_num_substitutions (); i++) + { + const SubstitutionParamMapping ¶m_mapping = substitutions.at (i); + SubstitutionArg &arg = mappings.get_mappings ().at (i); + + if (param_mapping.needs_substitution ()) + { + SubstitutionArg adjusted (¶m_mapping, arg.get_tyty ()); + resolved_mappings.push_back (std::move (adjusted)); + } + } + + return SubstitutionArgumentMappings (resolved_mappings, + mappings.get_locus ()); +} + +SubstitutionArgumentMappings +SubstitutionRef::solve_missing_mappings_from_this (SubstitutionRef &ref, + SubstitutionRef &to) +{ + rust_assert (!ref.needs_substitution ()); + rust_assert (needs_substitution ()); + rust_assert (get_num_substitutions () == ref.get_num_substitutions ()); + + Location locus = used_arguments.get_locus (); + std::vector resolved_mappings; + + std::map> substs; + for (size_t i = 0; i < get_num_substitutions (); i++) + { + SubstitutionParamMapping &a = substitutions.at (i); + SubstitutionParamMapping &b = ref.substitutions.at (i); + + if (a.need_substitution ()) + { + const BaseType *root = a.get_param_ty ()->resolve ()->get_root (); + rust_assert (root->get_kind () == TyTy::TypeKind::PARAM); + const ParamType *p = static_cast (root); + + substs[p->get_ty_ref ()] = {static_cast (p->clone ()), + b.get_param_ty ()->resolve ()}; + } + } + + for (auto it = substs.begin (); it != substs.end (); it++) + { + HirId param_id = it->first; + BaseType *arg = it->second.second; + + const SubstitutionParamMapping *associate_param = nullptr; + for (SubstitutionParamMapping &p : to.substitutions) + { + if (p.get_param_ty ()->get_ty_ref () == param_id) + { + associate_param = &p; + break; + } + } + + rust_assert (associate_param != nullptr); + SubstitutionArg argument (associate_param, arg); + resolved_mappings.push_back (std::move (argument)); + } + + return SubstitutionArgumentMappings (resolved_mappings, locus); +} + +bool +SubstitutionRef::monomorphize () +{ + auto context = Resolver::TypeCheckContext::get (); + for (const auto &subst : get_substs ()) + { + const TyTy::ParamType *pty = subst.get_param_ty (); + + if (!pty->can_resolve ()) + continue; + + const TyTy::BaseType *binding = pty->resolve (); + if (binding->get_kind () == TyTy::TypeKind::PARAM) + continue; + + for (const auto &bound : pty->get_specified_bounds ()) + { + const Resolver::TraitReference *specified_bound_ref = bound.get (); + + // setup any associated type mappings for the specified bonds and this + // type + auto candidates = Resolver::TypeBoundsProbe::Probe (binding); + + Resolver::AssociatedImplTrait *associated_impl_trait = nullptr; + for (auto &probed_bound : candidates) + { + const Resolver::TraitReference *bound_trait_ref + = probed_bound.first; + const HIR::ImplBlock *associated_impl = probed_bound.second; + + HirId impl_block_id + = associated_impl->get_mappings ().get_hirid (); + Resolver::AssociatedImplTrait *associated = nullptr; + bool found_impl_trait + = context->lookup_associated_trait_impl (impl_block_id, + &associated); + if (found_impl_trait) + { + bool found_trait + = specified_bound_ref->is_equal (*bound_trait_ref); + bool found_self + = associated->get_self ()->can_eq (binding, false); + if (found_trait && found_self) + { + associated_impl_trait = associated; + break; + } + } + } + + if (associated_impl_trait != nullptr) + { + associated_impl_trait->setup_associated_types (binding, bound); + } + } + } + + return true; +} + +void +ADTType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ADTType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ADTType::as_string () const +{ + std::string variants_buffer; + for (size_t i = 0; i < number_of_variants (); ++i) + { + TyTy::VariantDef *variant = variants.at (i); + variants_buffer += variant->as_string (); + if ((i + 1) < number_of_variants ()) + variants_buffer += ", "; + } + + return identifier + subst_as_string () + "{" + variants_buffer + "}"; +} + +BaseType * +ADTType::unify (BaseType *other) +{ + ADTRules r (this); + return r.unify (other); +} + +bool +ADTType::can_eq (const BaseType *other, bool emit_errors) const +{ + ADTCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +ADTType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + if (get_adt_kind () != other2.get_adt_kind ()) + return false; + + if (number_of_variants () != other2.number_of_variants ()) + return false; + + if (has_subsititions_defined () != other2.has_subsititions_defined ()) + return false; + + if (has_subsititions_defined ()) + { + if (get_num_substitutions () != other2.get_num_substitutions ()) + return false; + + for (size_t i = 0; i < get_num_substitutions (); i++) + { + const SubstitutionParamMapping &a = substitutions.at (i); + const SubstitutionParamMapping &b = other2.substitutions.at (i); + + const ParamType *aa = a.get_param_ty (); + const ParamType *bb = b.get_param_ty (); + BaseType *aaa = aa->resolve (); + BaseType *bbb = bb->resolve (); + if (!aaa->is_equal (*bbb)) + return false; + } + } + + for (size_t i = 0; i < number_of_variants (); i++) + { + const TyTy::VariantDef *a = get_variants ().at (i); + const TyTy::VariantDef *b = other2.get_variants ().at (i); + + if (!a->is_equal (*b)) + return false; + } + + return true; +} + +BaseType * +ADTType::clone () const +{ + std::vector cloned_variants; + for (auto &variant : variants) + cloned_variants.push_back (variant->clone ()); + + return new ADTType (get_ref (), get_ty_ref (), identifier, ident, + get_adt_kind (), cloned_variants, clone_substs (), + get_repr_options (), used_arguments, + get_combined_refs ()); +} + +BaseType * +ADTType::monomorphized_clone () const +{ + std::vector cloned_variants; + for (auto &variant : variants) + cloned_variants.push_back (variant->monomorphized_clone ()); + + return new ADTType (get_ref (), get_ty_ref (), identifier, ident, + get_adt_kind (), cloned_variants, clone_substs (), + get_repr_options (), used_arguments, + get_combined_refs ()); +} + +static bool +handle_substitions (SubstitutionArgumentMappings &subst_mappings, + StructFieldType *field) +{ + auto fty = field->get_field_type (); + bool is_param_ty = fty->get_kind () == TypeKind::PARAM; + if (is_param_ty) + { + ParamType *p = static_cast (fty); + + SubstitutionArg arg = SubstitutionArg::error (); + bool ok = subst_mappings.get_argument_for_symbol (p, &arg); + if (ok) + { + auto argt = arg.get_tyty (); + bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM; + bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER; + + if (arg_is_param || arg_is_concrete) + { + auto new_field = argt->clone (); + new_field->set_ref (fty->get_ref ()); + field->set_field_type (new_field); + } + else + { + field->get_field_type ()->set_ty_ref (argt->get_ref ()); + } + } + } + else if (fty->has_subsititions_defined () || fty->contains_type_parameters ()) + { + BaseType *concrete + = Resolver::SubstMapperInternal::Resolve (fty, subst_mappings); + + if (concrete->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (subst_mappings.get_locus (), + "Failed to resolve field substitution type: %s", + fty->as_string ().c_str ()); + return false; + } + + auto new_field = concrete->clone (); + new_field->set_ref (fty->get_ref ()); + field->set_field_type (new_field); + } + + return true; +} + +ADTType * +ADTType::handle_substitions (SubstitutionArgumentMappings subst_mappings) +{ + ADTType *adt = static_cast (clone ()); + adt->set_ty_ref (mappings->get_next_hir_id ()); + adt->used_arguments = subst_mappings; + + for (auto &sub : adt->get_substs ()) + { + SubstitutionArg arg = SubstitutionArg::error (); + bool ok + = subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg); + if (ok) + sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ()); + } + + for (auto &variant : adt->get_variants ()) + { + if (variant->is_dataless_variant ()) + continue; + + for (auto &field : variant->get_fields ()) + { + bool ok = ::Rust::TyTy::handle_substitions (subst_mappings, field); + if (!ok) + return adt; + } + } + + return adt; +} + +void +TupleType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +TupleType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +TupleType::as_string () const +{ + std::string fields_buffer; + for (const TyVar &field : get_fields ()) + { + fields_buffer += field.get_tyty ()->as_string (); + fields_buffer += ", "; + } + return "(" + fields_buffer + ")"; +} + +BaseType * +TupleType::get_field (size_t index) const +{ + return fields.at (index).get_tyty (); +} + +BaseType * +TupleType::unify (BaseType *other) +{ + TupleRules r (this); + return r.unify (other); +} + +bool +TupleType::can_eq (const BaseType *other, bool emit_errors) const +{ + TupleCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +TupleType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + if (num_fields () != other2.num_fields ()) + return false; + + for (size_t i = 0; i < num_fields (); i++) + { + if (!get_field (i)->is_equal (*other2.get_field (i))) + return false; + } + return true; +} + +BaseType * +TupleType::clone () const +{ + std::vector cloned_fields; + for (const auto &f : fields) + cloned_fields.push_back (f.clone ()); + + return new TupleType (get_ref (), get_ty_ref (), get_ident ().locus, + cloned_fields, get_combined_refs ()); +} + +BaseType * +TupleType::monomorphized_clone () const +{ + std::vector cloned_fields; + for (const auto &f : fields) + cloned_fields.push_back (f.monomorphized_clone ()); + + return new TupleType (get_ref (), get_ty_ref (), get_ident ().locus, + cloned_fields, get_combined_refs ()); +} + +TupleType * +TupleType::handle_substitions (SubstitutionArgumentMappings mappings) +{ + auto mappings_table = Analysis::Mappings::get (); + + TupleType *tuple = static_cast (clone ()); + tuple->set_ref (mappings_table->get_next_hir_id ()); + tuple->set_ty_ref (mappings_table->get_next_hir_id ()); + + for (size_t i = 0; i < tuple->fields.size (); i++) + { + TyVar &field = fields.at (i); + if (field.get_tyty ()->contains_type_parameters ()) + { + BaseType *concrete + = Resolver::SubstMapperInternal::Resolve (field.get_tyty (), + mappings); + tuple->fields[i] + = TyVar::subst_covariant_var (field.get_tyty (), concrete); + } + } + + return tuple; +} + +void +FnType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +FnType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +FnType::as_string () const +{ + std::string params_str = ""; + for (auto ¶m : params) + { + auto pattern = param.first; + auto ty = param.second; + params_str += pattern->as_string () + " " + ty->as_string (); + params_str += ","; + } + + std::string ret_str = type->as_string (); + return "fn" + subst_as_string () + " (" + params_str + ") -> " + ret_str; +} + +BaseType * +FnType::unify (BaseType *other) +{ + FnRules r (this); + return r.unify (other); +} + +bool +FnType::can_eq (const BaseType *other, bool emit_errors) const +{ + FnCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +FnType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + if (get_identifier ().compare (other2.get_identifier ()) != 0) + return false; + + if (!get_return_type ()->is_equal (*other2.get_return_type ())) + return false; + + if (has_subsititions_defined () != other2.has_subsititions_defined ()) + return false; + + if (has_subsititions_defined ()) + { + if (get_num_substitutions () != other2.get_num_substitutions ()) + return false; + + const FnType &ofn = static_cast (other); + for (size_t i = 0; i < get_num_substitutions (); i++) + { + const SubstitutionParamMapping &a = get_substs ().at (i); + const SubstitutionParamMapping &b = ofn.get_substs ().at (i); + + const ParamType *pa = a.get_param_ty (); + const ParamType *pb = b.get_param_ty (); + + if (!pa->is_equal (*pb)) + return false; + } + } + + if (num_params () != other2.num_params ()) + return false; + + for (size_t i = 0; i < num_params (); i++) + { + auto lhs = param_at (i).second; + auto rhs = other2.param_at (i).second; + if (!lhs->is_equal (*rhs)) + return false; + } + return true; +} + +BaseType * +FnType::clone () const +{ + std::vector> cloned_params; + for (auto &p : params) + cloned_params.push_back ({p.first, p.second->clone ()}); + + return new FnType (get_ref (), get_ty_ref (), get_id (), get_identifier (), + ident, flags, abi, std::move (cloned_params), + get_return_type ()->clone (), clone_substs (), + get_combined_refs ()); +} + +BaseType * +FnType::monomorphized_clone () const +{ + std::vector> cloned_params; + for (auto &p : params) + cloned_params.push_back ({p.first, p.second->monomorphized_clone ()}); + + return new FnType (get_ref (), get_ty_ref (), get_id (), get_identifier (), + ident, flags, abi, std::move (cloned_params), + get_return_type ()->clone (), clone_substs (), + get_combined_refs ()); +} + +FnType * +FnType::handle_substitions (SubstitutionArgumentMappings subst_mappings) +{ + FnType *fn = static_cast (clone ()); + fn->set_ty_ref (mappings->get_next_hir_id ()); + fn->used_arguments = subst_mappings; + + for (auto &sub : fn->get_substs ()) + { + SubstitutionArg arg = SubstitutionArg::error (); + + bool ok + = subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg); + if (ok) + { + sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ()); + } + } + + auto fty = fn->get_return_type (); + bool is_param_ty = fty->get_kind () == TypeKind::PARAM; + if (is_param_ty) + { + ParamType *p = static_cast (fty); + + SubstitutionArg arg = SubstitutionArg::error (); + bool ok = subst_mappings.get_argument_for_symbol (p, &arg); + if (ok) + { + auto argt = arg.get_tyty (); + bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM; + bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER; + + if (arg_is_param || arg_is_concrete) + { + auto new_field = argt->clone (); + new_field->set_ref (fty->get_ref ()); + fn->type = new_field; + } + else + { + fty->set_ty_ref (argt->get_ref ()); + } + } + } + else if (fty->needs_generic_substitutions () + || fty->contains_type_parameters ()) + { + BaseType *concrete + = Resolver::SubstMapperInternal::Resolve (fty, subst_mappings); + + if (concrete == nullptr || concrete->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (subst_mappings.get_locus (), + "Failed to resolve field substitution type: %s", + fty->as_string ().c_str ()); + return nullptr; + } + + auto new_field = concrete->clone (); + new_field->set_ref (fty->get_ref ()); + fn->type = new_field; + } + + for (auto ¶m : fn->get_params ()) + { + auto fty = param.second; + + bool is_param_ty = fty->get_kind () == TypeKind::PARAM; + if (is_param_ty) + { + ParamType *p = static_cast (fty); + + SubstitutionArg arg = SubstitutionArg::error (); + bool ok = subst_mappings.get_argument_for_symbol (p, &arg); + if (ok) + { + auto argt = arg.get_tyty (); + bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM; + bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER; + + if (arg_is_param || arg_is_concrete) + { + auto new_field = argt->clone (); + new_field->set_ref (fty->get_ref ()); + param.second = new_field; + } + else + { + fty->set_ty_ref (argt->get_ref ()); + } + } + } + else if (fty->has_subsititions_defined () + || fty->contains_type_parameters ()) + { + BaseType *concrete + = Resolver::SubstMapperInternal::Resolve (fty, subst_mappings); + + if (concrete == nullptr + || concrete->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (subst_mappings.get_locus (), + "Failed to resolve field substitution type: %s", + fty->as_string ().c_str ()); + return nullptr; + } + + auto new_field = concrete->clone (); + new_field->set_ref (fty->get_ref ()); + param.second = new_field; + } + } + + return fn; +} + +void +FnPtr::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +FnPtr::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +FnPtr::as_string () const +{ + std::string params_str; + + auto ¶ms = get_params (); + for (auto &p : params) + { + params_str += p.get_tyty ()->as_string () + " ,"; + } + + return "fnptr (" + params_str + ") -> " + get_return_type ()->as_string (); +} + +BaseType * +FnPtr::unify (BaseType *other) +{ + FnptrRules r (this); + return r.unify (other); +} + +bool +FnPtr::can_eq (const BaseType *other, bool emit_errors) const +{ + FnptrCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +FnPtr::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + auto this_ret_type = get_return_type (); + auto other_ret_type = other2.get_return_type (); + if (this_ret_type->is_equal (*other_ret_type)) + return false; + + if (num_params () != other2.num_params ()) + return false; + + for (size_t i = 0; i < num_params (); i++) + { + if (!param_at (i)->is_equal (*other2.param_at (i))) + return false; + } + return true; +} + +BaseType * +FnPtr::clone () const +{ + std::vector cloned_params; + for (auto &p : params) + cloned_params.push_back (TyVar (p.get_ref ())); + + return new FnPtr (get_ref (), get_ty_ref (), ident.locus, + std::move (cloned_params), result_type, + get_combined_refs ()); +} + +BaseType * +FnPtr::monomorphized_clone () const +{ + std::vector cloned_params; + for (auto &p : params) + cloned_params.push_back (p.monomorphized_clone ()); + + return new FnPtr (get_ref (), get_ty_ref (), ident.locus, + std::move (cloned_params), result_type, + get_combined_refs ()); +} + +void +ClosureType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ClosureType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ClosureType::as_string () const +{ + return "TODO"; +} + +BaseType * +ClosureType::unify (BaseType *other) +{ + ClosureRules r (this); + return r.unify (other); +} + +bool +ClosureType::can_eq (const BaseType *other, bool emit_errors) const +{ + ClosureCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +ClosureType::is_equal (const BaseType &other) const +{ + gcc_unreachable (); + return false; +} + +BaseType * +ClosureType::clone () const +{ + return new ClosureType (get_ref (), get_ty_ref (), ident, id, parameter_types, + result_type, clone_substs (), get_combined_refs ()); +} + +BaseType * +ClosureType::monomorphized_clone () const +{ + return clone (); +} + +ClosureType * +ClosureType::handle_substitions (SubstitutionArgumentMappings mappings) +{ + gcc_unreachable (); + return nullptr; +} + +void +ArrayType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ArrayType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ArrayType::as_string () const +{ + return "[" + get_element_type ()->as_string () + ":" + "CAPACITY" + "]"; +} + +BaseType * +ArrayType::unify (BaseType *other) +{ + ArrayRules r (this); + return r.unify (other); +} + +bool +ArrayType::can_eq (const BaseType *other, bool emit_errors) const +{ + ArrayCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +ArrayType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + + auto this_element_type = get_element_type (); + auto other_element_type = other2.get_element_type (); + + return this_element_type->is_equal (*other_element_type); +} + +BaseType * +ArrayType::get_element_type () const +{ + return element_type.get_tyty (); +} + +BaseType * +ArrayType::clone () const +{ + return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity_expr, + element_type, get_combined_refs ()); +} + +BaseType * +ArrayType::monomorphized_clone () const +{ + return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity_expr, + element_type.monomorphized_clone (), + get_combined_refs ()); +} + +ArrayType * +ArrayType::handle_substitions (SubstitutionArgumentMappings mappings) +{ + auto mappings_table = Analysis::Mappings::get (); + + ArrayType *ref = static_cast (clone ()); + ref->set_ty_ref (mappings_table->get_next_hir_id ()); + + // might be &T or &ADT so this needs to be recursive + auto base = ref->get_element_type (); + BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings); + ref->element_type = TyVar::subst_covariant_var (base, concrete); + + return ref; +} + +void +SliceType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +SliceType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +SliceType::as_string () const +{ + return "[" + get_element_type ()->as_string () + "]"; +} + +BaseType * +SliceType::unify (BaseType *other) +{ + SliceRules r (this); + return r.unify (other); +} + +bool +SliceType::can_eq (const BaseType *other, bool emit_errors) const +{ + SliceCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +SliceType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + + auto this_element_type = get_element_type (); + auto other_element_type = other2.get_element_type (); + + return this_element_type->is_equal (*other_element_type); +} + +BaseType * +SliceType::get_element_type () const +{ + return element_type.get_tyty (); +} + +BaseType * +SliceType::clone () const +{ + return new SliceType (get_ref (), get_ty_ref (), ident.locus, + element_type.clone (), get_combined_refs ()); +} + +BaseType * +SliceType::monomorphized_clone () const +{ + return new SliceType (get_ref (), get_ty_ref (), ident.locus, + element_type.monomorphized_clone (), + get_combined_refs ()); +} + +SliceType * +SliceType::handle_substitions (SubstitutionArgumentMappings mappings) +{ + auto mappings_table = Analysis::Mappings::get (); + + SliceType *ref = static_cast (clone ()); + ref->set_ty_ref (mappings_table->get_next_hir_id ()); + + // might be &T or &ADT so this needs to be recursive + auto base = ref->get_element_type (); + BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings); + ref->element_type = TyVar::subst_covariant_var (base, concrete); + + return ref; +} + +void +BoolType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +BoolType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +BoolType::as_string () const +{ + return "bool"; +} + +BaseType * +BoolType::unify (BaseType *other) +{ + BoolRules r (this); + return r.unify (other); +} + +bool +BoolType::can_eq (const BaseType *other, bool emit_errors) const +{ + BoolCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +BoolType::clone () const +{ + return new BoolType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + +BaseType * +BoolType::monomorphized_clone () const +{ + return clone (); +} + +void +IntType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +IntType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +IntType::as_string () const +{ + switch (int_kind) + { + case I8: + return "i8"; + case I16: + return "i16"; + case I32: + return "i32"; + case I64: + return "i64"; + case I128: + return "i128"; + } + gcc_unreachable (); + return "__unknown_int_type"; +} + +BaseType * +IntType::unify (BaseType *other) +{ + IntRules r (this); + return r.unify (other); +} + +bool +IntType::can_eq (const BaseType *other, bool emit_errors) const +{ + IntCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +IntType::clone () const +{ + return new IntType (get_ref (), get_ty_ref (), get_int_kind (), + get_combined_refs ()); +} + +BaseType * +IntType::monomorphized_clone () const +{ + return clone (); +} + +bool +IntType::is_equal (const BaseType &other) const +{ + if (!BaseType::is_equal (other)) + return false; + + const IntType &o = static_cast (other); + return get_int_kind () == o.get_int_kind (); +} + +void +UintType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +UintType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +UintType::as_string () const +{ + switch (uint_kind) + { + case U8: + return "u8"; + case U16: + return "u16"; + case U32: + return "u32"; + case U64: + return "u64"; + case U128: + return "u128"; + } + gcc_unreachable (); + return "__unknown_uint_type"; +} + +BaseType * +UintType::unify (BaseType *other) +{ + UintRules r (this); + return r.unify (other); +} + +bool +UintType::can_eq (const BaseType *other, bool emit_errors) const +{ + UintCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +UintType::clone () const +{ + return new UintType (get_ref (), get_ty_ref (), get_uint_kind (), + get_combined_refs ()); +} + +BaseType * +UintType::monomorphized_clone () const +{ + return clone (); +} + +bool +UintType::is_equal (const BaseType &other) const +{ + if (!BaseType::is_equal (other)) + return false; + + const UintType &o = static_cast (other); + return get_uint_kind () == o.get_uint_kind (); +} + +void +FloatType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +FloatType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +FloatType::as_string () const +{ + switch (float_kind) + { + case F32: + return "f32"; + case F64: + return "f64"; + } + gcc_unreachable (); + return "__unknown_float_type"; +} + +BaseType * +FloatType::unify (BaseType *other) +{ + FloatRules r (this); + return r.unify (other); +} + +bool +FloatType::can_eq (const BaseType *other, bool emit_errors) const +{ + FloatCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +FloatType::clone () const +{ + return new FloatType (get_ref (), get_ty_ref (), get_float_kind (), + get_combined_refs ()); +} + +BaseType * +FloatType::monomorphized_clone () const +{ + return clone (); +} + +bool +FloatType::is_equal (const BaseType &other) const +{ + if (!BaseType::is_equal (other)) + return false; + + const FloatType &o = static_cast (other); + return get_float_kind () == o.get_float_kind (); +} + +void +USizeType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +USizeType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +USizeType::as_string () const +{ + return "usize"; +} + +BaseType * +USizeType::unify (BaseType *other) +{ + USizeRules r (this); + return r.unify (other); +} + +bool +USizeType::can_eq (const BaseType *other, bool emit_errors) const +{ + USizeCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +USizeType::clone () const +{ + return new USizeType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + +BaseType * +USizeType::monomorphized_clone () const +{ + return clone (); +} + +void +ISizeType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ISizeType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ISizeType::as_string () const +{ + return "isize"; +} + +BaseType * +ISizeType::unify (BaseType *other) +{ + ISizeRules r (this); + return r.unify (other); +} + +bool +ISizeType::can_eq (const BaseType *other, bool emit_errors) const +{ + ISizeCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +ISizeType::clone () const +{ + return new ISizeType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + +BaseType * +ISizeType::monomorphized_clone () const +{ + return clone (); +} + +void +CharType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +CharType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +CharType::as_string () const +{ + return "char"; +} + +BaseType * +CharType::unify (BaseType *other) +{ + CharRules r (this); + return r.unify (other); +} + +bool +CharType::can_eq (const BaseType *other, bool emit_errors) const +{ + CharCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +CharType::clone () const +{ + return new CharType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + +BaseType * +CharType::monomorphized_clone () const +{ + return clone (); +} + +void +ReferenceType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ReferenceType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ReferenceType::as_string () const +{ + return std::string ("&") + (is_mutable () ? "mut" : "") + " " + + get_base ()->as_string (); +} + +BaseType * +ReferenceType::unify (BaseType *other) +{ + ReferenceRules r (this); + return r.unify (other); +} + +bool +ReferenceType::can_eq (const BaseType *other, bool emit_errors) const +{ + ReferenceCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +ReferenceType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + if (mutability () != other2.mutability ()) + return false; + + return get_base ()->is_equal (*other2.get_base ()); +} + +BaseType * +ReferenceType::get_base () const +{ + return base.get_tyty (); +} + +BaseType * +ReferenceType::clone () const +{ + return new ReferenceType (get_ref (), get_ty_ref (), base, mutability (), + get_combined_refs ()); +} + +BaseType * +ReferenceType::monomorphized_clone () const +{ + return new ReferenceType (get_ref (), get_ty_ref (), + base.monomorphized_clone (), mutability (), + get_combined_refs ()); +} + +ReferenceType * +ReferenceType::handle_substitions (SubstitutionArgumentMappings mappings) +{ + auto mappings_table = Analysis::Mappings::get (); + + ReferenceType *ref = static_cast (clone ()); + ref->set_ty_ref (mappings_table->get_next_hir_id ()); + + // might be &T or &ADT so this needs to be recursive + auto base = ref->get_base (); + BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings); + ref->base = TyVar::subst_covariant_var (base, concrete); + + return ref; +} + +void +PointerType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +PointerType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +PointerType::as_string () const +{ + return std::string ("* ") + (is_mutable () ? "mut" : "const") + " " + + get_base ()->as_string (); +} + +BaseType * +PointerType::unify (BaseType *other) +{ + PointerRules r (this); + return r.unify (other); +} + +bool +PointerType::can_eq (const BaseType *other, bool emit_errors) const +{ + PointerCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +PointerType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + auto other2 = static_cast (other); + if (mutability () != other2.mutability ()) + return false; + + return get_base ()->is_equal (*other2.get_base ()); +} + +BaseType * +PointerType::get_base () const +{ + return base.get_tyty (); +} + +BaseType * +PointerType::clone () const +{ + return new PointerType (get_ref (), get_ty_ref (), base, mutability (), + get_combined_refs ()); +} + +BaseType * +PointerType::monomorphized_clone () const +{ + return new PointerType (get_ref (), get_ty_ref (), + base.monomorphized_clone (), mutability (), + get_combined_refs ()); +} + +PointerType * +PointerType::handle_substitions (SubstitutionArgumentMappings mappings) +{ + auto mappings_table = Analysis::Mappings::get (); + + PointerType *ref = static_cast (clone ()); + ref->set_ty_ref (mappings_table->get_next_hir_id ()); + + // might be &T or &ADT so this needs to be recursive + auto base = ref->get_base (); + BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings); + ref->base = TyVar::subst_covariant_var (base, concrete); + + return ref; +} + +void +ParamType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ParamType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ParamType::as_string () const +{ + if (!can_resolve ()) + { + return get_symbol () + " REF: " + std::to_string (get_ref ()); + } + + BaseType *lookup = resolve (); + return get_symbol () + "=" + lookup->as_string (); +} + +std::string +ParamType::get_name () const +{ + if (!can_resolve ()) + return get_symbol (); + + return resolve ()->get_name (); +} + +BaseType * +ParamType::unify (BaseType *other) +{ + ParamRules r (this); + return r.unify (other); +} + +bool +ParamType::can_eq (const BaseType *other, bool emit_errors) const +{ + ParamCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +ParamType::clone () const +{ + return new ParamType (get_symbol (), ident.locus, get_ref (), get_ty_ref (), + param, get_specified_bounds (), get_combined_refs ()); +} + +BaseType * +ParamType::monomorphized_clone () const +{ + return resolve ()->clone (); +} + +std::string +ParamType::get_symbol () const +{ + return symbol; +} + +BaseType * +ParamType::resolve () const +{ + TyVar var (get_ty_ref ()); + BaseType *r = var.get_tyty (); + + while (r->get_kind () == TypeKind::PARAM) + { + ParamType *rr = static_cast (r); + if (!rr->can_resolve ()) + break; + + TyVar v (rr->get_ty_ref ()); + r = v.get_tyty (); + } + + if (r->get_kind () == TypeKind::PARAM && (r->get_ref () == r->get_ty_ref ())) + return TyVar (r->get_ty_ref ()).get_tyty (); + + return r; +} + +bool +ParamType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + { + if (!can_resolve ()) + return false; + + return resolve ()->is_equal (other); + } + + auto other2 = static_cast (other); + if (can_resolve () != other2.can_resolve ()) + return false; + + if (can_resolve ()) + return resolve ()->can_eq (other2.resolve (), false); + + return get_symbol ().compare (other2.get_symbol ()) == 0; +} + +ParamType * +ParamType::handle_substitions (SubstitutionArgumentMappings subst_mappings) +{ + SubstitutionArg arg = SubstitutionArg::error (); + bool ok = subst_mappings.get_argument_for_symbol (this, &arg); + if (!ok || arg.is_error ()) + return this; + + ParamType *p = static_cast (clone ()); + subst_mappings.on_param_subst (*p, arg); + + // there are two cases one where we substitute directly to a new PARAM and + // otherwise + if (arg.get_tyty ()->get_kind () == TyTy::TypeKind::PARAM) + { + p->set_ty_ref (arg.get_tyty ()->get_ref ()); + return p; + } + + // this is the new subst that this needs to pass + p->set_ref (mappings->get_next_hir_id ()); + p->set_ty_ref (arg.get_tyty ()->get_ref ()); + + return p; +} + +BaseType * +StrType::clone () const +{ + return new StrType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + +BaseType * +StrType::monomorphized_clone () const +{ + return clone (); +} + +void +StrType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +StrType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +StrType::as_string () const +{ + return "str"; +} + +BaseType * +StrType::unify (BaseType *other) +{ + StrRules r (this); + return r.unify (other); +} + +bool +StrType::can_eq (const BaseType *other, bool emit_errors) const +{ + StrCmp r (this, emit_errors); + return r.can_eq (other); +} + +bool +StrType::is_equal (const BaseType &other) const +{ + return get_kind () == other.get_kind (); +} + +void +NeverType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +NeverType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +NeverType::as_string () const +{ + return "!"; +} + +BaseType * +NeverType::unify (BaseType *other) +{ + NeverRules r (this); + return r.unify (other); +} + +bool +NeverType::can_eq (const BaseType *other, bool emit_errors) const +{ + NeverCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +NeverType::clone () const +{ + return new NeverType (get_ref (), get_ty_ref (), get_combined_refs ()); +} + +BaseType * +NeverType::monomorphized_clone () const +{ + return clone (); +} + +// placeholder type + +void +PlaceholderType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +PlaceholderType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +PlaceholderType::as_string () const +{ + return "as_string () : "") + + ">"; +} + +BaseType * +PlaceholderType::unify (BaseType *other) +{ + PlaceholderRules r (this); + return r.unify (other); +} + +bool +PlaceholderType::can_eq (const BaseType *other, bool emit_errors) const +{ + PlaceholderCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +PlaceholderType::clone () const +{ + return new PlaceholderType (get_symbol (), get_ref (), get_ty_ref (), + get_combined_refs ()); +} + +BaseType * +PlaceholderType::monomorphized_clone () const +{ + if (can_resolve ()) + return resolve ()->monomorphized_clone (); + + return clone (); +} + +void +PlaceholderType::set_associated_type (HirId ref) +{ + auto context = Resolver::TypeCheckContext::get (); + context->insert_associated_type_mapping (get_ty_ref (), ref); +} + +void +PlaceholderType::clear_associated_type () +{ + auto context = Resolver::TypeCheckContext::get (); + context->clear_associated_type_mapping (get_ty_ref ()); +} + +bool +PlaceholderType::can_resolve () const +{ + auto context = Resolver::TypeCheckContext::get (); + return context->lookup_associated_type_mapping (get_ty_ref (), nullptr); +} + +BaseType * +PlaceholderType::resolve () const +{ + auto context = Resolver::TypeCheckContext::get (); + + HirId mapping; + bool ok = context->lookup_associated_type_mapping (get_ty_ref (), &mapping); + rust_assert (ok); + + return TyVar (mapping).get_tyty (); +} + +bool +PlaceholderType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + { + if (!can_resolve ()) + return false; + + return resolve ()->is_equal (other); + } + + auto other2 = static_cast (other); + return get_symbol ().compare (other2.get_symbol ()) == 0; +} + +// Projection type + +void +ProjectionType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +ProjectionType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +ProjectionType::as_string () const +{ + return "as_string () + ">"; +} + +BaseType * +ProjectionType::unify (BaseType *other) +{ + return base->unify (other); +} + +bool +ProjectionType::can_eq (const BaseType *other, bool emit_errors) const +{ + return base->can_eq (other, emit_errors); +} + +BaseType * +ProjectionType::clone () const +{ + return new ProjectionType (get_ref (), get_ty_ref (), base->clone (), trait, + item, clone_substs (), used_arguments, + get_combined_refs ()); +} + +BaseType * +ProjectionType::monomorphized_clone () const +{ + return get ()->monomorphized_clone (); +} + +ProjectionType * +ProjectionType::handle_substitions (SubstitutionArgumentMappings subst_mappings) +{ + // // do we really need to substitute this? + // if (base->needs_generic_substitutions () || base->contains_type_parameters + // ()) + // { + // return this; + // } + + ProjectionType *projection = static_cast (clone ()); + projection->set_ty_ref (mappings->get_next_hir_id ()); + projection->used_arguments = subst_mappings; + + auto context = Resolver::TypeCheckContext::get (); + context->insert_implicit_type (projection->get_ty_ref (), projection); + + for (auto &sub : projection->get_substs ()) + { + SubstitutionArg arg = SubstitutionArg::error (); + bool ok + = subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg); + if (ok) + sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ()); + } + + auto fty = projection->base; + bool is_param_ty = fty->get_kind () == TypeKind::PARAM; + if (is_param_ty) + { + ParamType *p = static_cast (fty); + + SubstitutionArg arg = SubstitutionArg::error (); + bool ok = subst_mappings.get_argument_for_symbol (p, &arg); + if (ok) + { + auto argt = arg.get_tyty (); + bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM; + bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER; + + if (arg_is_param || arg_is_concrete) + { + auto new_field = argt->clone (); + new_field->set_ref (fty->get_ref ()); + projection->base = new_field; + } + else + { + fty->set_ty_ref (argt->get_ref ()); + } + } + } + else if (fty->needs_generic_substitutions () + || fty->contains_type_parameters ()) + { + BaseType *concrete + = Resolver::SubstMapperInternal::Resolve (fty, subst_mappings); + + if (concrete == nullptr || concrete->get_kind () == TyTy::TypeKind::ERROR) + { + rust_error_at (subst_mappings.get_locus (), + "Failed to resolve field substitution type: %s", + fty->as_string ().c_str ()); + return nullptr; + } + + projection->base = concrete; + } + + return projection; +} + +void +DynamicObjectType::accept_vis (TyVisitor &vis) +{ + vis.visit (*this); +} + +void +DynamicObjectType::accept_vis (TyConstVisitor &vis) const +{ + vis.visit (*this); +} + +std::string +DynamicObjectType::as_string () const +{ + return "dyn [" + raw_bounds_as_string () + "]"; +} + +BaseType * +DynamicObjectType::unify (BaseType *other) +{ + DynamicRules r (this); + return r.unify (other); +} + +bool +DynamicObjectType::can_eq (const BaseType *other, bool emit_errors) const +{ + DynamicCmp r (this, emit_errors); + return r.can_eq (other); +} + +BaseType * +DynamicObjectType::clone () const +{ + return new DynamicObjectType (get_ref (), get_ty_ref (), ident, + specified_bounds, get_combined_refs ()); +} + +BaseType * +DynamicObjectType::monomorphized_clone () const +{ + return clone (); +} + +std::string +DynamicObjectType::get_name () const +{ + return "dyn [" + raw_bounds_as_name () + "]"; +} + +bool +DynamicObjectType::is_equal (const BaseType &other) const +{ + if (get_kind () != other.get_kind ()) + return false; + + if (num_specified_bounds () != other.num_specified_bounds ()) + return false; + + return bounds_compatible (other, Location (), false); +} + +const std::vector< + std::pair> +DynamicObjectType::get_object_items () const +{ + std::vector< + std::pair> + items; + for (auto &bound : get_specified_bounds ()) + { + const Resolver::TraitReference *trait = bound.get (); + for (auto &item : trait->get_trait_items ()) + { + if (item.get_trait_item_type () + == Resolver::TraitItemReference::TraitItemType::FN + && item.is_object_safe ()) + items.push_back ({&item, &bound}); + } + + for (auto &super_trait : trait->get_super_traits ()) + { + for (auto &item : super_trait->get_trait_items ()) + { + if (item.get_trait_item_type () + == Resolver::TraitItemReference::TraitItemType::FN + && item.is_object_safe ()) + items.push_back ({&item, &bound}); + } + } + } + return items; +} + +} // namespace TyTy +} // namespace Rust diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h new file mode 100644 index 00000000000..c47921d44d7 --- /dev/null +++ b/gcc/rust/typecheck/rust-tyty.h @@ -0,0 +1,2533 @@ +// Copyright (C) 2020-2022 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +#ifndef RUST_TYTY +#define RUST_TYTY + +#include "rust-hir-map.h" +#include "rust-hir-full.h" +#include "rust-diagnostics.h" +#include "rust-abi.h" +#include "rust-common.h" +#include "rust-identifier.h" + +namespace Rust { + +namespace Resolver { +class TraitReference; +class TraitItemReference; +class AssociatedImplTrait; +} // namespace Resolver + +namespace TyTy { + +// https://rustc-dev-guide.rust-lang.org/type-inference.html#inference-variables +// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variants +enum TypeKind +{ + INFER, + ADT, + STR, + REF, + POINTER, + PARAM, + ARRAY, + SLICE, + FNDEF, + FNPTR, + TUPLE, + BOOL, + CHAR, + INT, + UINT, + FLOAT, + USIZE, + ISIZE, + NEVER, + PLACEHOLDER, + PROJECTION, + DYNAMIC, + CLOSURE, + // there are more to add... + ERROR +}; + +extern bool +is_primitive_type_kind (TypeKind kind); + +class TypeKindFormat +{ +public: + static std::string to_string (TypeKind kind); +}; + +class BaseType; +class TypeBoundPredicate; +class TypeBoundPredicateItem +{ +public: + TypeBoundPredicateItem (const TypeBoundPredicate *parent, + const Resolver::TraitItemReference *trait_item_ref) + : parent (parent), trait_item_ref (trait_item_ref) + {} + + static TypeBoundPredicateItem error () + { + return TypeBoundPredicateItem (nullptr, nullptr); + } + + bool is_error () const + { + return parent == nullptr || trait_item_ref == nullptr; + } + + BaseType *get_tyty_for_receiver (const TyTy::BaseType *receiver); + + const Resolver::TraitItemReference *get_raw_item () const; + + bool needs_implementation () const; + + const TypeBoundPredicate *get_parent () const { return parent; } + + Location get_locus () const; + +private: + const TypeBoundPredicate *parent; + const Resolver::TraitItemReference *trait_item_ref; +}; + +class TypeBoundsMappings +{ +protected: + TypeBoundsMappings (std::vector specified_bounds); + +public: + std::vector &get_specified_bounds (); + + const std::vector &get_specified_bounds () const; + + size_t num_specified_bounds () const; + + std::string raw_bounds_as_string () const; + + std::string bounds_as_string () const; + + std::string raw_bounds_as_name () const; + +protected: + void add_bound (TypeBoundPredicate predicate); + + std::vector specified_bounds; +}; + +class TyVisitor; +class TyConstVisitor; +class BaseType : public TypeBoundsMappings +{ +public: + virtual ~BaseType () {} + + HirId get_ref () const { return ref; } + + void set_ref (HirId id) + { + if (id != ref) + append_reference (ref); + ref = id; + } + + HirId get_ty_ref () const { return ty_ref; } + + void set_ty_ref (HirId id) { ty_ref = id; } + + virtual void accept_vis (TyVisitor &vis) = 0; + + virtual void accept_vis (TyConstVisitor &vis) const = 0; + + virtual std::string as_string () const = 0; + + virtual std::string get_name () const = 0; + + // Unify two types. Returns a pointer to the newly-created unified ty, or + // nullptr if the two ty cannot be unified. The caller is responsible for + // releasing the memory of the returned ty. + virtual BaseType *unify (BaseType *other) = 0; + + // similar to unify but does not actually perform type unification but + // determines whether they are compatible. Consider the following + // + // fn foo() -> T { ... } + // fn foo() -> i32 { ... } + // + // when the function has been substituted they can be considered equal. + // + // It can also be used to optional emit errors for trait item compatibility + // checks + virtual bool can_eq (const BaseType *other, bool emit_errors) const = 0; + + // Check value equality between two ty. Type inference rules are ignored. Two + // ty are considered equal if they're of the same kind, and + // 1. (For ADTs, arrays, tuples, refs) have the same underlying ty + // 2. (For functions) have the same signature + virtual bool is_equal (const BaseType &other) const + { + return get_kind () == other.get_kind (); + } + + bool satisfies_bound (const TypeBoundPredicate &predicate) const; + + bool bounds_compatible (const BaseType &other, Location locus, + bool emit_error) const; + + void inherit_bounds (const BaseType &other); + + void inherit_bounds ( + const std::vector &specified_bounds); + + virtual bool is_unit () const { return false; } + + virtual bool is_concrete () const = 0; + + TypeKind get_kind () const { return kind; } + + /* Returns a pointer to a clone of this. The caller is responsible for + * releasing the memory of the returned ty. */ + virtual BaseType *clone () const = 0; + + // TODO + virtual BaseType *monomorphized_clone () const = 0; + + // get_combined_refs returns the chain of node refs involved in unification + std::set get_combined_refs () const { return combined; } + + void append_reference (HirId id) { combined.insert (id); } + + virtual bool supports_substitutions () const { return false; } + + virtual bool has_subsititions_defined () const { return false; } + + virtual bool can_substitute () const + { + return supports_substitutions () && has_subsititions_defined (); + } + + virtual bool needs_generic_substitutions () const { return false; } + + bool contains_type_parameters () const { return !is_concrete (); } + + std::string mappings_str () const + { + std::string buffer = "Ref: " + std::to_string (get_ref ()) + + " TyRef: " + std::to_string (get_ty_ref ()); + buffer += "["; + for (auto &ref : combined) + buffer += std::to_string (ref) + ","; + buffer += "]"; + return "(" + buffer + ")"; + } + + std::string debug_str () const + { + return TypeKindFormat::to_string (get_kind ()) + ":" + as_string () + ":" + + mappings_str () + ":" + bounds_as_string (); + } + + void debug () const + { + rust_debug ("[%p] %s", static_cast (this), + debug_str ().c_str ()); + } + + // FIXME this will eventually go away + const BaseType *get_root () const; + + // This will get the monomorphized type from Params, Placeholders or + // Projections if available or error + const BaseType *destructure () const; + + const RustIdent &get_ident () const { return ident; } + + Location get_locus () const { return ident.locus; } + +protected: + BaseType (HirId ref, HirId ty_ref, TypeKind kind, RustIdent ident, + std::set refs = std::set ()) + : TypeBoundsMappings ({}), kind (kind), ref (ref), ty_ref (ty_ref), + combined (refs), ident (ident), mappings (Analysis::Mappings::get ()) + {} + + BaseType (HirId ref, HirId ty_ref, TypeKind kind, RustIdent ident, + std::vector specified_bounds, + std::set refs = std::set ()) + : TypeBoundsMappings (specified_bounds), kind (kind), ref (ref), + ty_ref (ty_ref), combined (refs), ident (ident), + mappings (Analysis::Mappings::get ()) + {} + + TypeKind kind; + HirId ref; + HirId ty_ref; + std::set combined; + RustIdent ident; + + Analysis::Mappings *mappings; +}; + +// this is a placeholder for types that can change like inference variables +class TyVar +{ +public: + explicit TyVar (HirId ref); + + HirId get_ref () const { return ref; } + + BaseType *get_tyty () const; + + TyVar clone () const; + + TyVar monomorphized_clone () const; + + static TyVar get_implicit_infer_var (Location locus); + + static TyVar subst_covariant_var (TyTy::BaseType *orig, + TyTy::BaseType *subst); + +private: + HirId ref; +}; + +class TyWithLocation +{ +public: + TyWithLocation (BaseType *ty, Location locus); + TyWithLocation (BaseType *ty); + + BaseType *get_ty () const { return ty; } + Location get_locus () const { return locus; } + +private: + BaseType *ty; + Location locus; +}; + +class InferType : public BaseType +{ +public: + enum InferTypeKind + { + GENERAL, + INTEGRAL, + FLOAT + }; + + InferType (HirId ref, InferTypeKind infer_kind, Location locus, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::INFER, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + infer_kind (infer_kind) + {} + + InferType (HirId ref, HirId ty_ref, InferTypeKind infer_kind, Location locus, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::INFER, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + infer_kind (infer_kind) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + InferTypeKind get_infer_kind () const { return infer_kind; } + + std::string get_name () const override final { return as_string (); } + + bool default_type (BaseType **type) const; + + bool is_concrete () const final override { return true; } + +private: + InferTypeKind infer_kind; +}; + +class ErrorType : public BaseType +{ +public: + ErrorType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::ERROR, + {Resolver::CanonicalPath::create_empty (), Location ()}, refs) + {} + + ErrorType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::ERROR, + {Resolver::CanonicalPath::create_empty (), Location ()}, refs) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + bool is_unit () const override { return true; } + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + std::string get_name () const override final { return as_string (); } + + bool is_concrete () const final override { return false; } +}; + +class SubstitutionArgumentMappings; +class ParamType : public BaseType +{ +public: + ParamType (std::string symbol, Location locus, HirId ref, + HIR::GenericParam ¶m, + std::vector specified_bounds, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::PARAM, + {Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol), + locus}, + specified_bounds, refs), + symbol (symbol), param (param) + {} + + ParamType (std::string symbol, Location locus, HirId ref, HirId ty_ref, + HIR::GenericParam ¶m, + std::vector specified_bounds, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::PARAM, + {Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol), + locus}, + specified_bounds, refs), + symbol (symbol), param (param) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + std::string get_symbol () const; + + HIR::GenericParam &get_generic_param () { return param; } + + bool can_resolve () const { return get_ref () != get_ty_ref (); } + + BaseType *resolve () const; + + std::string get_name () const override final; + + bool is_equal (const BaseType &other) const override; + + bool is_concrete () const override final + { + auto r = resolve (); + if (r == this) + return false; + + return r->is_concrete (); + } + + ParamType *handle_substitions (SubstitutionArgumentMappings mappings); + +private: + std::string symbol; + HIR::GenericParam ¶m; +}; + +class StructFieldType +{ +public: + StructFieldType (HirId ref, std::string name, BaseType *ty) + : ref (ref), name (name), ty (ty) + {} + + HirId get_ref () const { return ref; } + + std::string as_string () const; + + bool is_equal (const StructFieldType &other) const; + + std::string get_name () const { return name; } + + BaseType *get_field_type () const { return ty; } + + void set_field_type (BaseType *fty) { ty = fty; } + + StructFieldType *clone () const; + + StructFieldType *monomorphized_clone () const; + + bool is_concrete () const { return ty->is_concrete (); } + + void debug () const { rust_debug ("%s", as_string ().c_str ()); } + +private: + HirId ref; + std::string name; + BaseType *ty; +}; + +class TupleType : public BaseType +{ +public: + TupleType (HirId ref, Location locus, + std::vector fields = std::vector (), + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::TUPLE, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + fields (fields) + {} + + TupleType (HirId ref, HirId ty_ref, Location locus, + std::vector fields = std::vector (), + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::TUPLE, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + fields (fields) + {} + + static TupleType *get_unit_type (HirId ref) + { + return new TupleType (ref, Linemap::predeclared_location ()); + } + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + bool is_unit () const override { return this->fields.empty (); } + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + size_t num_fields () const { return fields.size (); } + + BaseType *get_field (size_t index) const; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_concrete () const override final + { + for (size_t i = 0; i < num_fields (); i++) + { + if (!get_field (i)->is_concrete ()) + return false; + } + return true; + } + + const std::vector &get_fields () const { return fields; } + + std::string get_name () const override final { return as_string (); } + + TupleType *handle_substitions (SubstitutionArgumentMappings mappings); + +private: + std::vector fields; +}; + +class SubstitutionParamMapping +{ +public: + SubstitutionParamMapping (const HIR::TypeParam &generic, ParamType *param) + : generic (generic), param (param) + {} + + SubstitutionParamMapping (const SubstitutionParamMapping &other) + : generic (other.generic), param (other.param) + {} + + std::string as_string () const + { + if (param == nullptr) + return "nullptr"; + + return param->get_name (); + } + + bool fill_param_ty (SubstitutionArgumentMappings &subst_mappings, + Location locus); + + SubstitutionParamMapping clone () const + { + return SubstitutionParamMapping (generic, static_cast ( + param->clone ())); + } + + ParamType *get_param_ty () { return param; } + + const ParamType *get_param_ty () const { return param; } + + const HIR::TypeParam &get_generic_param () { return generic; }; + + // this is used for the backend to override the HirId ref of the param to + // what the concrete type is for the rest of the context + void override_context (); + + bool needs_substitution () const + { + return !(get_param_ty ()->is_concrete ()); + } + + Location get_param_locus () const { return generic.get_locus (); } + + bool param_has_default_ty () const { return generic.has_type (); } + + BaseType *get_default_ty () const + { + TyVar var (generic.get_type_mappings ().get_hirid ()); + return var.get_tyty (); + } + + bool need_substitution () const; + +private: + const HIR::TypeParam &generic; + ParamType *param; +}; + +class SubstitutionArg +{ +public: + SubstitutionArg (const SubstitutionParamMapping *param, BaseType *argument) + : param (param), argument (argument) + {} + + // FIXME + // the copy constructors need removed - they are unsafe see + // TypeBoundPredicate + SubstitutionArg (const SubstitutionArg &other) + : param (other.param), argument (other.argument) + {} + + SubstitutionArg &operator= (const SubstitutionArg &other) + { + param = other.param; + argument = other.argument; + return *this; + } + + BaseType *get_tyty () { return argument; } + + const BaseType *get_tyty () const { return argument; } + + const SubstitutionParamMapping *get_param_mapping () const { return param; } + + static SubstitutionArg error () { return SubstitutionArg (nullptr, nullptr); } + + bool is_error () const { return param == nullptr || argument == nullptr; } + + bool is_conrete () const + { + if (argument != nullptr) + return true; + + if (argument->get_kind () == TyTy::TypeKind::PARAM) + return false; + + return argument->is_concrete (); + } + + std::string as_string () const + { + return param->as_string () + + (argument != nullptr ? ":" + argument->as_string () : ""); + } + +private: + const SubstitutionParamMapping *param; + BaseType *argument; +}; + +typedef std::function + ParamSubstCb; +class SubstitutionArgumentMappings +{ +public: + SubstitutionArgumentMappings (std::vector mappings, + Location locus, + ParamSubstCb param_subst_cb = nullptr, + bool trait_item_flag = false) + : mappings (mappings), locus (locus), param_subst_cb (param_subst_cb), + trait_item_flag (trait_item_flag) + {} + + SubstitutionArgumentMappings (const SubstitutionArgumentMappings &other) + : mappings (other.mappings), locus (other.locus), + param_subst_cb (other.param_subst_cb), + trait_item_flag (other.trait_item_flag) + {} + + SubstitutionArgumentMappings & + operator= (const SubstitutionArgumentMappings &other) + { + mappings = other.mappings; + locus = other.locus; + param_subst_cb = other.param_subst_cb; + trait_item_flag = other.trait_item_flag; + + return *this; + } + + static SubstitutionArgumentMappings error () + { + return SubstitutionArgumentMappings ({}, Location (), nullptr, false); + } + + bool is_error () const { return mappings.size () == 0; } + + bool get_argument_for_symbol (const ParamType *param_to_find, + SubstitutionArg *argument) + { + for (auto &mapping : mappings) + { + const SubstitutionParamMapping *param = mapping.get_param_mapping (); + const ParamType *p = param->get_param_ty (); + + if (p->get_symbol ().compare (param_to_find->get_symbol ()) == 0) + { + *argument = mapping; + return true; + } + } + return false; + } + + bool get_argument_at (size_t index, SubstitutionArg *argument) + { + if (index > mappings.size ()) + return false; + + *argument = mappings.at (index); + return true; + } + + // is_concrete means if the used args is non error, ie: non empty this will + // verify if actual real types have been put in place of are they still + // ParamTy + bool is_concrete () const + { + for (auto &mapping : mappings) + { + if (!mapping.is_conrete ()) + return false; + } + return true; + } + + Location get_locus () const { return locus; } + + size_t size () const { return mappings.size (); } + + bool is_empty () const { return size () == 0; } + + std::vector &get_mappings () { return mappings; } + + const std::vector &get_mappings () const { return mappings; } + + std::string as_string () const + { + std::string buffer; + for (auto &mapping : mappings) + { + buffer += mapping.as_string () + ", "; + } + return "<" + buffer + ">"; + } + + void on_param_subst (const ParamType &p, const SubstitutionArg &a) const + { + if (param_subst_cb == nullptr) + return; + + param_subst_cb (p, a); + } + + ParamSubstCb get_subst_cb () const { return param_subst_cb; } + + bool trait_item_mode () const { return trait_item_flag; } + +private: + std::vector mappings; + Location locus; + ParamSubstCb param_subst_cb; + bool trait_item_flag; +}; + +class SubstitutionRef +{ +public: + SubstitutionRef (std::vector substitutions, + SubstitutionArgumentMappings arguments) + : substitutions (substitutions), used_arguments (arguments) + {} + + bool has_substitutions () const { return substitutions.size () > 0; } + + std::string subst_as_string () const + { + std::string buffer; + for (size_t i = 0; i < substitutions.size (); i++) + { + const SubstitutionParamMapping &sub = substitutions.at (i); + buffer += sub.as_string (); + + if ((i + 1) < substitutions.size ()) + buffer += ", "; + } + + return buffer.empty () ? "" : "<" + buffer + ">"; + } + + size_t get_num_substitutions () const { return substitutions.size (); } + + std::vector &get_substs () { return substitutions; } + + const std::vector &get_substs () const + { + return substitutions; + } + + std::vector clone_substs () const + { + std::vector clone; + + for (auto &sub : substitutions) + clone.push_back (sub.clone ()); + + return clone; + } + + void override_context () + { + for (auto &sub : substitutions) + { + sub.override_context (); + } + } + + bool needs_substitution () const + { + for (auto &sub : substitutions) + { + if (sub.need_substitution ()) + return true; + } + return false; + } + + bool was_substituted () const { return !needs_substitution (); } + + SubstitutionArgumentMappings get_substitution_arguments () const + { + return used_arguments; + } + + // this is the count of type params that are not substituted fuly + size_t num_required_substitutions () const + { + size_t n = 0; + for (auto &p : substitutions) + { + if (p.needs_substitution ()) + n++; + } + return n; + } + + // this is the count of type params that need substituted taking into account + // possible defaults + size_t min_required_substitutions () const + { + size_t n = 0; + for (auto &p : substitutions) + { + if (p.needs_substitution () && !p.param_has_default_ty ()) + n++; + } + return n; + } + + // We are trying to subst into Struct Foo {} + // in the case of Foo{...} + // + // the substitions we have here define X,Y but the arguments have no bindings + // so its a matter of ordering + SubstitutionArgumentMappings + get_mappings_from_generic_args (HIR::GenericArgs &args); + + // Recursive substitutions + // Foo { a:A, b: B}; Bar {a:X, b: Foo} + // + // we have bindings for X Y Z and need to propagate the binding Y,Z into Foo + // Which binds to A,B + SubstitutionArgumentMappings + adjust_mappings_for_this (SubstitutionArgumentMappings &mappings); + + // Are the mappings here actually bound to this type. For example imagine the + // case: + // + // struct Foo(T); + // impl Foo { + // fn test(self) { ... } + // } + // + // In this case we have a generic ADT of Foo and an impl block of a generic T + // on Foo for the Self type. When we it comes to path resolution we can have: + // + // Foo::::test() + // + // This means the first segment of Foo:: returns the ADT Foo not the + // Self ADT bound to the T from the impl block. This means when it comes to + // the next segment of test which resolves to the function we need to check + // wether the arguments in the struct definition of foo can be bound here + // before substituting the previous segments type here. This functions acts as + // a guard for the solve_mappings_from_receiver_for_self to handle the case + // where arguments are not bound. This is important for this next case: + // + // struct Baz(A, B); + // impl Baz { + // fn test(a: X) -> X { + // a + // } + // } + // + // In this case Baz has been already substituted for the impl's Self to become + // ADT so that the function test only has 1 generic argument of X. + // The path for this will be: + // + // Baz::test::<_>(123) + // + // So the first segment here will be Baz<_, _> to try and infer the arguments + // which will be taken from the impl's Self type in this case since it is + // already substituted and like the previous case the check to see if we need + // to inherit the previous segments generic arguments takes place but the + // generic arguments are not bound to this type as they have already been + // substituted. + // + // Its important to remember from the first example the FnType actually looks + // like: + // + // fn test(self :Foo(T)) + // + // As the generic parameters are "bound" to each of the items in the impl + // block. So this check is about wether the arguments we have here can + // actually be bound to this type. + bool are_mappings_bound (SubstitutionArgumentMappings &mappings); + + // struct Foo(A, B); + // + // impl Foo; + // -> fn test(self, a: X) -> X + // + // We might invoke this via: + // + // a = Foo(123, 456f32); + // b = a.test::(false); + // + // we need to figure out relevant generic arguemts for self to apply to the + // fntype + SubstitutionArgumentMappings solve_mappings_from_receiver_for_self ( + SubstitutionArgumentMappings &mappings) const; + + // TODO comment + SubstitutionArgumentMappings + solve_missing_mappings_from_this (SubstitutionRef &ref, SubstitutionRef &to); + + // TODO comment + BaseType *infer_substitions (Location locus) + { + std::vector args; + std::map argument_mappings; + for (auto &p : get_substs ()) + { + if (p.needs_substitution ()) + { + const std::string &symbol = p.get_param_ty ()->get_symbol (); + auto it = argument_mappings.find (symbol); + if (it == argument_mappings.end ()) + { + TyVar infer_var = TyVar::get_implicit_infer_var (locus); + args.push_back (SubstitutionArg (&p, infer_var.get_tyty ())); + argument_mappings[symbol] = infer_var.get_tyty (); + } + else + { + args.push_back (SubstitutionArg (&p, it->second)); + } + } + else + { + args.push_back ( + SubstitutionArg (&p, p.get_param_ty ()->resolve ())); + } + } + + SubstitutionArgumentMappings infer_arguments (std::move (args), locus); + return handle_substitions (std::move (infer_arguments)); + } + + // TODO comment + bool monomorphize (); + + // TODO comment + virtual BaseType *handle_substitions (SubstitutionArgumentMappings mappings) + = 0; + + SubstitutionArgumentMappings get_used_arguments () const + { + return used_arguments; + } + +protected: + std::vector substitutions; + SubstitutionArgumentMappings used_arguments; +}; + +class TypeBoundPredicate : public SubstitutionRef +{ +public: + TypeBoundPredicate (const Resolver::TraitReference &trait_reference, + Location locus); + + TypeBoundPredicate (DefId reference, + std::vector substitutions, + Location locus); + + TypeBoundPredicate (const TypeBoundPredicate &other); + + TypeBoundPredicate &operator= (const TypeBoundPredicate &other); + + static TypeBoundPredicate error (); + + std::string as_string () const; + + std::string as_name () const; + + const Resolver::TraitReference *get () const; + + Location get_locus () const { return locus; } + + std::string get_name () const; + + // check that this predicate is object-safe see: + // https://doc.rust-lang.org/reference/items/traits.html#object-safety + bool is_object_safe (bool emit_error, Location locus) const; + + void apply_generic_arguments (HIR::GenericArgs *generic_args); + + bool contains_item (const std::string &search) const; + + TypeBoundPredicateItem + lookup_associated_item (const std::string &search) const; + + TypeBoundPredicateItem + lookup_associated_item (const Resolver::TraitItemReference *ref) const; + + // WARNING THIS WILL ALWAYS RETURN NULLPTR + BaseType * + handle_substitions (SubstitutionArgumentMappings mappings) override final; + + bool is_error () const; + + bool requires_generic_args () const; + +private: + DefId reference; + Location locus; + bool error_flag; +}; + +// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.VariantDef.html +class VariantDef +{ +public: + enum VariantType + { + NUM, + TUPLE, + STRUCT + }; + + static std::string variant_type_string (VariantType type) + { + switch (type) + { + case NUM: + return "enumeral"; + case TUPLE: + return "tuple"; + case STRUCT: + return "struct"; + } + gcc_unreachable (); + return ""; + } + + VariantDef (HirId id, std::string identifier, RustIdent ident, + HIR::Expr *discriminant) + : id (id), identifier (identifier), ident (ident), + discriminant (discriminant) + + { + type = VariantType::NUM; + fields = {}; + } + + VariantDef (HirId id, std::string identifier, RustIdent ident, + VariantType type, HIR::Expr *discriminant, + std::vector fields) + : id (id), identifier (identifier), ident (ident), type (type), + discriminant (discriminant), fields (fields) + { + rust_assert ( + (type == VariantType::NUM && fields.empty ()) + || (type == VariantType::TUPLE || type == VariantType::STRUCT)); + } + + VariantDef (const VariantDef &other) + : id (other.id), identifier (other.identifier), ident (other.ident), + type (other.type), discriminant (other.discriminant), + fields (other.fields) + {} + + VariantDef &operator= (const VariantDef &other) + { + id = other.id; + identifier = other.identifier; + type = other.type; + discriminant = other.discriminant; + fields = other.fields; + ident = other.ident; + + return *this; + } + + static VariantDef &get_error_node () + { + static VariantDef node + = VariantDef (UNKNOWN_HIRID, "", + {Resolver::CanonicalPath::create_empty (), + Linemap::unknown_location ()}, + nullptr); + + return node; + } + + bool is_error () const { return get_id () == UNKNOWN_HIRID; } + + HirId get_id () const { return id; } + + VariantType get_variant_type () const { return type; } + bool is_data_variant () const { return type != VariantType::NUM; } + bool is_dataless_variant () const { return type == VariantType::NUM; } + + std::string get_identifier () const { return identifier; } + + size_t num_fields () const { return fields.size (); } + StructFieldType *get_field_at_index (size_t index) + { + rust_assert (index < fields.size ()); + return fields.at (index); + } + + std::vector &get_fields () + { + rust_assert (type != NUM); + return fields; + } + + bool lookup_field (const std::string &lookup, StructFieldType **field_lookup, + size_t *index) const + { + size_t i = 0; + for (auto &field : fields) + { + if (field->get_name ().compare (lookup) == 0) + { + if (index != nullptr) + *index = i; + + if (field_lookup != nullptr) + *field_lookup = field; + + return true; + } + i++; + } + return false; + } + + HIR::Expr *get_discriminant () const + { + rust_assert (discriminant != nullptr); + return discriminant; + } + + std::string as_string () const + { + if (type == VariantType::NUM) + return identifier + " = " + discriminant->as_string (); + + std::string buffer; + for (size_t i = 0; i < fields.size (); ++i) + { + buffer += fields.at (i)->as_string (); + if ((i + 1) < fields.size ()) + buffer += ", "; + } + + if (type == VariantType::TUPLE) + return identifier + " (" + buffer + ")"; + else + return identifier + " {" + buffer + "}"; + } + + bool is_equal (const VariantDef &other) const + { + if (type != other.type) + return false; + + if (identifier.compare (other.identifier) != 0) + return false; + + if (discriminant != other.discriminant) + return false; + + if (fields.size () != other.fields.size ()) + return false; + + for (size_t i = 0; i < fields.size (); i++) + { + if (!fields.at (i)->is_equal (*other.fields.at (i))) + return false; + } + + return true; + } + + VariantDef *clone () const + { + std::vector cloned_fields; + for (auto &f : fields) + cloned_fields.push_back ((StructFieldType *) f->clone ()); + + return new VariantDef (id, identifier, ident, type, discriminant, + cloned_fields); + } + + VariantDef *monomorphized_clone () const + { + std::vector cloned_fields; + for (auto &f : fields) + cloned_fields.push_back ((StructFieldType *) f->monomorphized_clone ()); + + return new VariantDef (id, identifier, ident, type, discriminant, + cloned_fields); + } + + const RustIdent &get_ident () const { return ident; } + +private: + HirId id; + std::string identifier; + RustIdent ident; + VariantType type; + // can either be a structure or a discriminant value + HIR::Expr *discriminant; + std::vector fields; +}; + +class ADTType : public BaseType, public SubstitutionRef +{ +public: + enum ADTKind + { + STRUCT_STRUCT, + TUPLE_STRUCT, + UNION, + ENUM + }; + + // Representation options, specified via attributes e.g. #[repr(packed)] + struct ReprOptions + { + // bool is_c; + // bool is_transparent; + //... + + // For align and pack: 0 = unspecified. Nonzero = byte alignment. + // It is an error for both to be nonzero, this should be caught when + // parsing the #[repr] attribute. + unsigned char align = 0; + unsigned char pack = 0; + }; + + ADTType (HirId ref, std::string identifier, RustIdent ident, ADTKind adt_kind, + std::vector variants, + std::vector subst_refs, + SubstitutionArgumentMappings generic_arguments + = SubstitutionArgumentMappings::error (), + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::ADT, ident, refs), + SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)), + identifier (identifier), variants (variants), adt_kind (adt_kind) + {} + + ADTType (HirId ref, HirId ty_ref, std::string identifier, RustIdent ident, + ADTKind adt_kind, std::vector variants, + std::vector subst_refs, + SubstitutionArgumentMappings generic_arguments + = SubstitutionArgumentMappings::error (), + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::ADT, ident, refs), + SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)), + identifier (identifier), variants (variants), adt_kind (adt_kind) + {} + + ADTType (HirId ref, HirId ty_ref, std::string identifier, RustIdent ident, + ADTKind adt_kind, std::vector variants, + std::vector subst_refs, ReprOptions repr, + SubstitutionArgumentMappings generic_arguments + = SubstitutionArgumentMappings::error (), + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::ADT, ident, refs), + SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)), + identifier (identifier), variants (variants), adt_kind (adt_kind), + repr (repr) + {} + + ADTKind get_adt_kind () const { return adt_kind; } + ReprOptions get_repr_options () const { return repr; } + + bool is_struct_struct () const { return adt_kind == STRUCT_STRUCT; } + bool is_tuple_struct () const { return adt_kind == TUPLE_STRUCT; } + bool is_union () const { return adt_kind == UNION; } + bool is_enum () const { return adt_kind == ENUM; } + + bool is_unit () const override + { + if (number_of_variants () == 0) + return true; + + if (number_of_variants () == 1) + return variants.at (0)->num_fields () == 0; + + return false; + } + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + std::string get_identifier () const { return identifier; } + + std::string get_name () const override final + { + return identifier + subst_as_string (); + } + + bool is_concrete () const override final + { + for (auto &variant : variants) + { + for (auto &field : variant->get_fields ()) + { + if (!field->is_concrete ()) + return false; + } + } + return true; + } + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool needs_generic_substitutions () const override final + { + return needs_substitution (); + } + + bool supports_substitutions () const override final { return true; } + + bool has_subsititions_defined () const override final + { + return has_substitutions (); + } + + size_t number_of_variants () const { return variants.size (); } + + std::vector &get_variants () { return variants; } + const std::vector &get_variants () const { return variants; } + + bool lookup_variant (const std::string &lookup, + VariantDef **found_variant) const + { + for (auto &variant : variants) + { + if (variant->get_identifier ().compare (lookup) == 0) + { + *found_variant = variant; + return true; + } + } + return false; + } + + bool lookup_variant_by_id (HirId id, VariantDef **found_variant, + int *index = nullptr) const + { + int i = 0; + for (auto &variant : variants) + { + if (variant->get_id () == id) + { + if (index != nullptr) + *index = i; + + *found_variant = variant; + return true; + } + i++; + } + return false; + } + + ADTType * + handle_substitions (SubstitutionArgumentMappings mappings) override final; + +private: + std::string identifier; + std::vector variants; + ADTType::ADTKind adt_kind; + ReprOptions repr; +}; + +class FnType : public BaseType, public SubstitutionRef +{ +public: + static const uint8_t FNTYPE_DEFAULT_FLAGS = 0x00; + static const uint8_t FNTYPE_IS_METHOD_FLAG = 0x01; + static const uint8_t FNTYPE_IS_EXTERN_FLAG = 0x02; + static const uint8_t FNTYPE_IS_VARADIC_FLAG = 0X04; + + FnType (HirId ref, DefId id, std::string identifier, RustIdent ident, + uint8_t flags, ABI abi, + std::vector> params, + BaseType *type, std::vector subst_refs, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::FNDEF, ident, refs), + SubstitutionRef (std::move (subst_refs), + SubstitutionArgumentMappings::error ()), + params (std::move (params)), type (type), flags (flags), + identifier (identifier), id (id), abi (abi) + { + LocalDefId local_def_id = id.localDefId; + rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID); + } + + FnType (HirId ref, HirId ty_ref, DefId id, std::string identifier, + RustIdent ident, uint8_t flags, ABI abi, + std::vector> params, + BaseType *type, std::vector subst_refs, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::FNDEF, ident, refs), + SubstitutionRef (std::move (subst_refs), + SubstitutionArgumentMappings::error ()), + params (params), type (type), flags (flags), identifier (identifier), + id (id), abi (abi) + { + LocalDefId local_def_id = id.localDefId; + rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID); + } + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + std::string get_identifier () const { return identifier; } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + size_t num_params () const { return params.size (); } + + bool is_method () const + { + if (num_params () == 0) + return false; + + return (flags & FNTYPE_IS_METHOD_FLAG) != 0; + } + + bool is_extern () const { return (flags & FNTYPE_IS_EXTERN_FLAG) != 0; } + + bool is_varadic () const { return (flags & FNTYPE_IS_VARADIC_FLAG) != 0; } + + DefId get_id () const { return id; } + + // get the Self type for the method + BaseType *get_self_type () const + { + rust_assert (is_method ()); + return param_at (0).second; + } + + bool is_concrete () const override final + { + for (const auto ¶m : params) + { + const BaseType *p = param.second; + if (!p->is_concrete ()) + return false; + } + return get_return_type ()->is_concrete (); + } + + std::vector> &get_params () + { + return params; + } + + const std::vector> &get_params () const + { + return params; + } + + std::pair ¶m_at (size_t idx) + { + return params.at (idx); + } + + const std::pair ¶m_at (size_t idx) const + { + return params.at (idx); + } + + BaseType *get_return_type () const { return type; } + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool needs_generic_substitutions () const override final + { + return needs_substitution (); + } + + bool supports_substitutions () const override final { return true; } + + bool has_subsititions_defined () const override final + { + return has_substitutions (); + } + + FnType * + handle_substitions (SubstitutionArgumentMappings mappings) override final; + + ABI get_abi () const { return abi; } + +private: + std::vector> params; + BaseType *type; + uint8_t flags; + std::string identifier; + DefId id; + ABI abi; +}; + +class FnPtr : public BaseType +{ +public: + FnPtr (HirId ref, Location locus, std::vector params, + TyVar result_type, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::FNPTR, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + params (std::move (params)), result_type (result_type) + {} + + FnPtr (HirId ref, HirId ty_ref, Location locus, std::vector params, + TyVar result_type, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::FNPTR, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + params (params), result_type (result_type) + {} + + std::string get_name () const override final { return as_string (); } + + BaseType *get_return_type () const { return result_type.get_tyty (); } + + size_t num_params () const { return params.size (); } + + BaseType *param_at (size_t idx) const { return params.at (idx).get_tyty (); } + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + void iterate_params (std::function cb) const + { + for (auto &p : params) + { + if (!cb (p.get_tyty ())) + return; + } + } + + std::vector &get_params () { return params; } + const std::vector &get_params () const { return params; } + + bool is_concrete () const override final + { + for (auto &p : params) + { + if (!p.get_tyty ()->is_concrete ()) + return false; + } + return result_type.get_tyty ()->is_concrete (); + } + +private: + std::vector params; + TyVar result_type; +}; + +class ClosureType : public BaseType, public SubstitutionRef +{ +public: + ClosureType (HirId ref, DefId id, RustIdent ident, + std::vector parameter_types, TyVar result_type, + std::vector subst_refs, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::CLOSURE, ident, refs), + SubstitutionRef (std::move (subst_refs), + SubstitutionArgumentMappings::error ()), + parameter_types (std::move (parameter_types)), + result_type (std::move (result_type)), id (id) + { + LocalDefId local_def_id = id.localDefId; + rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID); + } + + ClosureType (HirId ref, HirId ty_ref, RustIdent ident, DefId id, + std::vector parameter_types, TyVar result_type, + std::vector subst_refs, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::CLOSURE, ident, refs), + SubstitutionRef (std::move (subst_refs), + SubstitutionArgumentMappings::error ()), + parameter_types (std::move (parameter_types)), + result_type (std::move (result_type)), id (id) + { + LocalDefId local_def_id = id.localDefId; + rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID); + } + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_concrete () const override final + { + for (auto ¶m : parameter_types) + { + auto p = param.get_tyty (); + if (!p->is_concrete ()) + return false; + } + return result_type.get_tyty ()->is_concrete (); + } + + bool needs_generic_substitutions () const override final + { + return needs_substitution (); + } + + bool supports_substitutions () const override final { return true; } + + bool has_subsititions_defined () const override final + { + return has_substitutions (); + } + + ClosureType * + handle_substitions (SubstitutionArgumentMappings mappings) override final; + +private: + std::vector parameter_types; + TyVar result_type; + DefId id; +}; + +class ArrayType : public BaseType +{ +public: + ArrayType (HirId ref, Location locus, HIR::Expr &capacity_expr, TyVar base, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::ARRAY, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + element_type (base), capacity_expr (capacity_expr) + {} + + ArrayType (HirId ref, HirId ty_ref, Location locus, HIR::Expr &capacity_expr, + TyVar base, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::ARRAY, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + element_type (base), capacity_expr (capacity_expr) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *get_element_type () const; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_concrete () const final override + { + return get_element_type ()->is_concrete (); + } + + HIR::Expr &get_capacity_expr () const { return capacity_expr; } + + ArrayType *handle_substitions (SubstitutionArgumentMappings mappings); + +private: + TyVar element_type; + HIR::Expr &capacity_expr; +}; + +class SliceType : public BaseType +{ +public: + SliceType (HirId ref, Location locus, TyVar base, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::SLICE, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + element_type (base) + {} + + SliceType (HirId ref, HirId ty_ref, Location locus, TyVar base, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::SLICE, + {Resolver::CanonicalPath::create_empty (), locus}, refs), + element_type (base) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *get_element_type () const; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_concrete () const final override + { + return get_element_type ()->is_concrete (); + } + + SliceType *handle_substitions (SubstitutionArgumentMappings mappings); + +private: + TyVar element_type; +}; + +class BoolType : public BaseType +{ +public: + BoolType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::BOOL, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + BoolType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::BOOL, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + bool is_concrete () const override final { return true; } +}; + +class IntType : public BaseType +{ +public: + enum IntKind + { + I8, + I16, + I32, + I64, + I128 + }; + + IntType (HirId ref, IntKind kind, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::INT, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + int_kind (kind) + {} + + IntType (HirId ref, HirId ty_ref, IntKind kind, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::INT, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + int_kind (kind) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + IntKind get_int_kind () const { return int_kind; } + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_equal (const BaseType &other) const override; + bool is_concrete () const override final { return true; } + +private: + IntKind int_kind; +}; + +class UintType : public BaseType +{ +public: + enum UintKind + { + U8, + U16, + U32, + U64, + U128 + }; + + UintType (HirId ref, UintKind kind, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::UINT, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + uint_kind (kind) + {} + + UintType (HirId ref, HirId ty_ref, UintKind kind, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::UINT, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + uint_kind (kind) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + UintKind get_uint_kind () const { return uint_kind; } + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_equal (const BaseType &other) const override; + bool is_concrete () const override final { return true; } + +private: + UintKind uint_kind; +}; + +class FloatType : public BaseType +{ +public: + enum FloatKind + { + F32, + F64 + }; + + FloatType (HirId ref, FloatKind kind, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::FLOAT, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + float_kind (kind) + {} + + FloatType (HirId ref, HirId ty_ref, FloatKind kind, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::FLOAT, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + float_kind (kind) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + FloatKind get_float_kind () const { return float_kind; } + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_equal (const BaseType &other) const override; + bool is_concrete () const override final { return true; } + +private: + FloatKind float_kind; +}; + +class USizeType : public BaseType +{ +public: + USizeType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::USIZE, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + USizeType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::USIZE, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + bool is_concrete () const override final { return true; } +}; + +class ISizeType : public BaseType +{ +public: + ISizeType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::ISIZE, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + ISizeType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::ISIZE, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + bool is_concrete () const override final { return true; } +}; + +class CharType : public BaseType +{ +public: + CharType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::CHAR, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + CharType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::CHAR, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final { return as_string (); } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + bool is_concrete () const override final { return true; } +}; + +class StrType : public BaseType +{ +public: + StrType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::STR, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + StrType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::STR, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + std::string get_name () const override final { return as_string (); } + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + bool is_concrete () const override final { return true; } +}; + +class ReferenceType : public BaseType +{ +public: + ReferenceType (HirId ref, TyVar base, Mutability mut, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::REF, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + base (base), mut (mut) + {} + + ReferenceType (HirId ref, HirId ty_ref, TyVar base, Mutability mut, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::REF, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + base (base), mut (mut) + {} + + BaseType *get_base () const; + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final + { + return "&" + get_base ()->get_name (); + } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_concrete () const override final + { + return get_base ()->is_concrete (); + } + + ReferenceType *handle_substitions (SubstitutionArgumentMappings mappings); + + Mutability mutability () const { return mut; } + + bool is_mutable () const { return mut == Mutability::Mut; } + + bool is_dyn_object () const + { + return is_dyn_slice_type () || is_dyn_str_type (); + } + + bool is_dyn_slice_type (const TyTy::SliceType **slice = nullptr) const + { + const TyTy::BaseType *element = get_base ()->destructure (); + if (element->get_kind () != TyTy::TypeKind::SLICE) + return false; + if (slice == nullptr) + return true; + + *slice = static_cast (element); + return true; + } + + bool is_dyn_str_type (const TyTy::StrType **str = nullptr) const + { + const TyTy::BaseType *element = get_base ()->destructure (); + if (element->get_kind () != TyTy::TypeKind::STR) + return false; + if (str == nullptr) + return true; + + *str = static_cast (element); + return true; + } + +private: + TyVar base; + Mutability mut; +}; + +class PointerType : public BaseType +{ +public: + PointerType (HirId ref, TyVar base, Mutability mut, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::POINTER, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + base (base), mut (mut) + {} + + PointerType (HirId ref, HirId ty_ref, TyVar base, Mutability mut, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::POINTER, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + base (base), mut (mut) + {} + + BaseType *get_base () const; + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + std::string get_name () const override final + { + return "*" + get_base ()->get_name (); + } + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + bool is_concrete () const override final + { + return get_base ()->is_concrete (); + } + + PointerType *handle_substitions (SubstitutionArgumentMappings mappings); + + Mutability mutability () const { return mut; } + + bool is_mutable () const { return mut == Mutability::Mut; } + + bool is_const () const { return mut == Mutability::Imm; } + + bool is_dyn_object () const + { + return is_dyn_slice_type () || is_dyn_str_type (); + } + + bool is_dyn_slice_type (const TyTy::SliceType **slice = nullptr) const + { + const TyTy::BaseType *element = get_base ()->destructure (); + if (element->get_kind () != TyTy::TypeKind::SLICE) + return false; + if (slice == nullptr) + return true; + + *slice = static_cast (element); + return true; + } + + bool is_dyn_str_type (const TyTy::StrType **str = nullptr) const + { + const TyTy::BaseType *element = get_base ()->destructure (); + if (element->get_kind () != TyTy::TypeKind::STR) + return false; + if (str == nullptr) + return true; + + *str = static_cast (element); + return true; + } + +private: + TyVar base; + Mutability mut; +}; + +// https://doc.rust-lang.org/std/primitive.never.html +// +// Since the `!` type is really complicated and it is even still unstable +// in rustc, only fairly limited support for this type is introduced here. +// Unification between `!` and ANY other type (including ``) is simply +// not allowed. If it is needed, it should be handled manually. For example, +// unifying `!` with other types is very necessary when resolving types of +// `if/else` expressions. +// +// See related discussion at https://github.com/Rust-GCC/gccrs/pull/364 +class NeverType : public BaseType +{ +public: + NeverType (HirId ref, std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::NEVER, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + NeverType (HirId ref, HirId ty_ref, std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::NEVER, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + std::string get_name () const override final { return as_string (); } + + bool is_unit () const override { return true; } + bool is_concrete () const override final { return true; } +}; + +// used at the type in associated types in traits +// see: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +class PlaceholderType : public BaseType +{ +public: + PlaceholderType (std::string symbol, HirId ref, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::PLACEHOLDER, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + symbol (symbol) + {} + + PlaceholderType (std::string symbol, HirId ref, HirId ty_ref, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::PLACEHOLDER, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + symbol (symbol) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + std::string get_name () const override final { return as_string (); } + + bool is_unit () const override + { + rust_assert (can_resolve ()); + return resolve ()->is_unit (); + } + + std::string get_symbol () const { return symbol; } + + void set_associated_type (HirId ref); + + void clear_associated_type (); + + bool can_resolve () const; + + BaseType *resolve () const; + + bool is_equal (const BaseType &other) const override; + + bool is_concrete () const override final + { + if (!can_resolve ()) + return true; + + return resolve ()->is_concrete (); + } + +private: + std::string symbol; +}; + +class ProjectionType : public BaseType, public SubstitutionRef +{ +public: + ProjectionType (HirId ref, BaseType *base, + const Resolver::TraitReference *trait, DefId item, + std::vector subst_refs, + SubstitutionArgumentMappings generic_arguments + = SubstitutionArgumentMappings::error (), + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::PROJECTION, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)), + base (base), trait (trait), item (item) + {} + + ProjectionType (HirId ref, HirId ty_ref, BaseType *base, + const Resolver::TraitReference *trait, DefId item, + std::vector subst_refs, + SubstitutionArgumentMappings generic_arguments + = SubstitutionArgumentMappings::error (), + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::PROJECTION, + {Resolver::CanonicalPath::create_empty (), + Linemap::predeclared_location ()}, + refs), + SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)), + base (base), trait (trait), item (item) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + std::string get_name () const override final { return as_string (); } + + bool is_unit () const override { return false; } + + bool needs_generic_substitutions () const override final + { + return needs_substitution (); + } + + bool supports_substitutions () const override final { return true; } + + bool has_subsititions_defined () const override final + { + return has_substitutions (); + } + + const BaseType *get () const { return base; } + BaseType *get () { return base; } + + bool is_concrete () const override final { return base->is_concrete (); } + + ProjectionType * + handle_substitions (SubstitutionArgumentMappings mappings) override final; + +private: + BaseType *base; + const Resolver::TraitReference *trait; + DefId item; +}; + +class DynamicObjectType : public BaseType +{ +public: + DynamicObjectType (HirId ref, RustIdent ident, + std::vector specified_bounds, + std::set refs = std::set ()) + : BaseType (ref, ref, TypeKind::DYNAMIC, ident, specified_bounds, refs) + {} + + DynamicObjectType (HirId ref, HirId ty_ref, RustIdent ident, + std::vector specified_bounds, + std::set refs = std::set ()) + : BaseType (ref, ty_ref, TypeKind::DYNAMIC, ident, specified_bounds, refs) + {} + + void accept_vis (TyVisitor &vis) override; + void accept_vis (TyConstVisitor &vis) const override; + + std::string as_string () const override; + + BaseType *unify (BaseType *other) override; + bool can_eq (const BaseType *other, bool emit_errors) const override final; + + bool is_equal (const BaseType &other) const override; + + BaseType *clone () const final override; + BaseType *monomorphized_clone () const final override; + + std::string get_name () const override final; + + bool is_concrete () const override final { return true; } + + // this returns a flat list of items including super trait bounds + const std::vector< + std::pair> + get_object_items () const; +}; + +} // namespace TyTy +} // namespace Rust + +#endif // RUST_TYTY -- 2.25.1