From patchwork Tue Dec 6 10:13:59 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arthur Cohen X-Patchwork-Id: 61528 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2FF13382D3EE for ; Tue, 6 Dec 2022 10:23:28 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x434.google.com (mail-wr1-x434.google.com [IPv6:2a00:1450:4864:20::434]) by sourceware.org (Postfix) with ESMTPS id 07B0C395C415 for ; Tue, 6 Dec 2022 10:12:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 07B0C395C415 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com Received: by mail-wr1-x434.google.com with SMTP id q7so22750523wrr.8 for ; Tue, 06 Dec 2022 02:12:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=content-transfer-encoding:mime-version:reply-to:references :in-reply-to:message-id:date:subject:cc:to:from:from:to:cc:subject :date:message-id:reply-to; bh=NQBCHk7YrHZq+9/UPTmk8GRDzd8SiJVj/LOEKA/Y6uA=; b=KT0MeAwpZ7cvoaFttr6e9QsZfRaocolqhcZV4MfxyTV/zrQOG/sAf+xaZCW2GLK0Fw 6wAqsbzNTEGbos79ZJ/7VaRqg73AjUO5wuFN6iOcLpNnLSXqrKtgj2afV5imLtkfRgqa lzHtl0v/FvtOHfwmjME+QepfcGp1SIsSbMYXlCGa8TNBuKTT8NWWPltS3gKxYhBF4Orx SaevrApNCzu41e//J+uFF8N2QnHfzRQucUxLTWmBugoCjzG6i+j3BmD+JMNMJ1QNgsNm wF0i81rRbRGuuWJoNpD3+1QXvh8/9kXKwZ0L/ld4aCUTc1tIcWwafbtBAjq7JchAkBU+ QLDw== 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:subject:date:message-id:reply-to; bh=NQBCHk7YrHZq+9/UPTmk8GRDzd8SiJVj/LOEKA/Y6uA=; b=M+AMSy5dFmMWBkZOyw7/rnCaHECt0CXoyAQjFyfaJd3jY9Jt9upP2H6Z0oha8bcZk5 NkbZoDMHHFfdmmvY1m6qKWe1mKx89uFnl8HAo3grXBFHOPxLm/DEjXAmQ6fVu1aqw9Xu R9uqIbR6m0QNX5icPwY7+hhQ6GebbkH9gBon7Bw20aXDa58SCNtNzHCXAB6Sp6Xi5gmN V+XGPEeHlVMTduJP7zBrI0T448g4zmw0yd2ICe/qXLyWUNlxtf+gWOWNuDDRYhuYkBt6 L+viT1osMyWSKUs5PGxoOUJrtItjtHm/cVgj9XBFMzIB3s7Td15uMAF+xdGHXotn1F+g h6Kg== X-Gm-Message-State: ANoB5pmjnJAFzAKyUa70UgAc+Rc+YXpkZ/EjYR/3xlW40yazzVBKgYkz yoz90dQj2yeuOWb4ZO+kSyflMLbwy2jNrtT0ng== X-Google-Smtp-Source: AA0mqf4TZ6mnQVi/lURyuu/t4QtKpwxYNC8rmlzNCle7tStTkU9uFty1PKZvw2y1eKRSN/xfF630JA== X-Received: by 2002:a5d:6244:0:b0:236:73b2:f026 with SMTP id m4-20020a5d6244000000b0023673b2f026mr53295466wrv.396.1670321548094; Tue, 06 Dec 2022 02:12:28 -0800 (PST) Received: from platypus.lan ([2001:861:5e4c:3bb0:6424:328a:1734:3249]) by smtp.googlemail.com with ESMTPSA id r10-20020a05600c458a00b003cfd4a50d5asm27052699wmo.34.2022.12.06.02.12.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Dec 2022 02:12:27 -0800 (PST) From: arthur.cohen@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, Philip Herron Subject: [PATCH Rust front-end v4 27/46] gccrs: Add type resolution and trait solving pass Date: Tue, 6 Dec 2022 11:13:59 +0100 Message-Id: <20221206101417.778807-28-arthur.cohen@embecosm.com> X-Mailer: git-send-email 2.38.1 In-Reply-To: <20221206101417.778807-1-arthur.cohen@embecosm.com> References: <20221206101417.778807-1-arthur.cohen@embecosm.com> MIME-Version: 1.0 X-Spam-Status: No, score=-18.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: arthur.cohen@embecosm.com Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" From: Philip Herron This serves to handle parts of the Rust type-system. Namely, the type resolution (similar to type-checking) and the trait solving algorithms (which ensure Rust's type contracts are upheld throughout the codebase). --- gcc/rust/typecheck/rust-hir-trait-resolve.cc | 599 +++++++ gcc/rust/typecheck/rust-hir-trait-resolve.h | 87 + .../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 ++++ gcc/rust/typecheck/rust-tyty-visitor.h | 88 + 28 files changed, 8337 insertions(+) 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-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-tyty-visitor.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-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-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