From patchwork Tue Dec 6 10:13:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arthur Cohen X-Patchwork-Id: 61511 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 D0A7E382CD60 for ; Tue, 6 Dec 2022 10:18:30 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-wr1-x433.google.com (mail-wr1-x433.google.com [IPv6:2a00:1450:4864:20::433]) by sourceware.org (Postfix) with ESMTPS id 754CF396D82C for ; Tue, 6 Dec 2022 10:12:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 754CF396D82C 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-x433.google.com with SMTP id q7so22748634wrr.8 for ; Tue, 06 Dec 2022 02:12:02 -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=s31+94ibpaypK+HnIO+osDZnfVQoRGVFeQv57fpzhN4=; b=YSWY+V+cimWuQt0t5fc/V7FgetrCtcbg0uXnHYjkhe1AFifUDIkd+VACKbqiEeg9jS PWjn3heRWcPAuKVnsM84ns1R/ur/598GST7irCC6x5JKkymBO5GvRoavgtj2sghO49yp B53/2+HxbKXtUmGgd6idyVroctnvz1CvvB4iEe9yv1CubO2UK5QZF2yjUCokQSWRw3j6 QVW5kPPLglPPKK+koG1wHzAXjKGNwUwg1LwWUDshe+AjviToKa/nNxLg/24nj2VaIJuL 5ZR1p1z/w2pLxlBD1co4SMGwm18qFTMjiOXTHOw45oMcNxd8vurzbVl+ZZEvaFzHcX7j vqow== 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=s31+94ibpaypK+HnIO+osDZnfVQoRGVFeQv57fpzhN4=; b=DV6BSUbI05p6PLjCDYQdg1+u/L8Py68XPTrEunKAYPylTVsfnlQJFry4ZhUzCqotWi 5zMi7l2UUBXcvOu3ArcrmetyG//vKQMlfg5oag0vix7ApGx6q5ec+mdxPuJ+O5VsOd45 PFx8LZ0XVorYwUAwM1aM/bazENz1A1LnqqlX1oE/cL2zMdEAYTRBf9XCtSXJUrmxFE0W GOB3Wffhy3JPHkl6oiyEtT1db29SZpNVue896pl1ItKmDFny4iUtHmv89HdGGIdn1axi t1Xg64A69NLztGbUwPk/k5aw+P6FrhB2oCbWMq9Q7y9zEFLwB3OoGBTpsxxlt9KaQori kLvw== X-Gm-Message-State: ANoB5plOMeoDQdLn6/8V70mc5zL9gWMCll0ksEgTNCfi44LsvM51wque 7gFGn7S9Pw7f3GNerHvtCHWj6d7LBe5stJQpaA== X-Google-Smtp-Source: AA0mqf6GlMRWMsRbfEL7npXff7B4fbBA7zcyQgerJGXf0SJMe8ogqjTPTJldAFQPapT+9sSxKR0tIg== X-Received: by 2002:a5d:6244:0:b0:236:73b2:f026 with SMTP id m4-20020a5d6244000000b0023673b2f026mr53294308wrv.396.1670321519310; Tue, 06 Dec 2022 02:11:59 -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.11.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Dec 2022 02:11:58 -0800 (PST) From: arthur.cohen@embecosm.com To: gcc-patches@gcc.gnu.org Cc: gcc-rust@gcc.gnu.org, The Other , Philip Herron Subject: [PATCH Rust front-end v4 09/46] gccrs: Add definitions of Rust Items in AST data structures Date: Tue, 6 Dec 2022 11:13:41 +0100 Message-Id: <20221206101417.778807-10-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=-27.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: The Other This adds the proper definitions of our AST Item nodes. Co-authored-by: Philip Herron --- gcc/rust/ast/rust-item.h | 4382 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 4382 insertions(+) create mode 100644 gcc/rust/ast/rust-item.h diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h new file mode 100644 index 00000000000..4987674cba1 --- /dev/null +++ b/gcc/rust/ast/rust-item.h @@ -0,0 +1,4382 @@ +// 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_AST_ITEM_H +#define RUST_AST_ITEM_H + +#include "rust-ast.h" +#include "rust-path.h" +#include "rust-common.h" + +namespace Rust { +namespace AST { +// forward decls +class BlockExpr; +class TypePath; + +// TODO: inline? +/*struct AbiName { + std::string abi_name; + // Technically is meant to be STRING_LITERAL + + public: + // Returns whether abi name is empty, i.e. doesn't exist. + bool is_empty() const { + return abi_name.empty(); + } + + AbiName(std::string name) : abi_name(std::move(name)) {} + + // Empty AbiName constructor + AbiName() {} +};*/ + +// A type generic parameter (as opposed to a lifetime generic parameter) +class TypeParam : public GenericParam +{ + // bool has_outer_attribute; + // std::unique_ptr outer_attr; + Attribute outer_attr; + + Identifier type_representation; + + // bool has_type_param_bounds; + // TypeParamBounds type_param_bounds; + std::vector> + type_param_bounds; // inlined form + + // bool has_type; + std::unique_ptr type; + + Location locus; + +public: + Identifier get_type_representation () const { return type_representation; } + + // Returns whether the type of the type param has been specified. + bool has_type () const { return type != nullptr; } + + // Returns whether the type param has type param bounds. + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + // Returns whether the type param has an outer attribute. + bool has_outer_attribute () const { return !outer_attr.is_empty (); } + + TypeParam (Identifier type_representation, Location locus = Location (), + std::vector> type_param_bounds + = std::vector> (), + std::unique_ptr type = nullptr, + Attribute outer_attr = Attribute::create_empty ()) + : GenericParam (Analysis::Mappings::get ()->get_next_node_id ()), + outer_attr (std::move (outer_attr)), + type_representation (std::move (type_representation)), + type_param_bounds (std::move (type_param_bounds)), + type (std::move (type)), locus (locus) + {} + + // Copy constructor uses clone + TypeParam (TypeParam const &other) + : GenericParam (other.node_id), outer_attr (other.outer_attr), + type_representation (other.type_representation), locus (other.locus) + { + // guard to prevent null pointer dereference + if (other.type != nullptr) + type = other.type->clone_type (); + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // Overloaded assignment operator to clone + TypeParam &operator= (TypeParam const &other) + { + type_representation = other.type_representation; + outer_attr = other.outer_attr; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null pointer dereference + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // move constructors + TypeParam (TypeParam &&other) = default; + TypeParam &operator= (TypeParam &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + Kind get_kind () const override final { return Kind::Type; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + // TODO: mutable getter seems kinda dodgy + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + +protected: + // Clone function implementation as virtual method + TypeParam *clone_generic_param_impl () const override + { + return new TypeParam (*this); + } +}; + +/* "where" clause item base. Abstract - use LifetimeWhereClauseItem, + * TypeBoundWhereClauseItem */ +class WhereClauseItem +{ +public: + virtual ~WhereClauseItem () {} + + // Unique pointer custom clone function + std::unique_ptr clone_where_clause_item () const + { + return std::unique_ptr (clone_where_clause_item_impl ()); + } + + virtual std::string as_string () const = 0; + + virtual void accept_vis (ASTVisitor &vis) = 0; + + virtual NodeId get_node_id () const = 0; + +protected: + // Clone function implementation as pure virtual method + virtual WhereClauseItem *clone_where_clause_item_impl () const = 0; +}; + +// A lifetime where clause item +class LifetimeWhereClauseItem : public WhereClauseItem +{ + Lifetime lifetime; + std::vector lifetime_bounds; + Location locus; + NodeId node_id; + +public: + LifetimeWhereClauseItem (Lifetime lifetime, + std::vector lifetime_bounds, + Location locus) + : lifetime (std::move (lifetime)), + lifetime_bounds (std::move (lifetime_bounds)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + NodeId get_node_id () const override final { return node_id; } + + Lifetime &get_lifetime () { return lifetime; } + + std::vector &get_lifetime_bounds () { return lifetime_bounds; } + + Location get_locus () const { return locus; } + +protected: + // Clone function implementation as (not pure) virtual method + LifetimeWhereClauseItem *clone_where_clause_item_impl () const override + { + return new LifetimeWhereClauseItem (*this); + } +}; + +// A type bound where clause item +class TypeBoundWhereClauseItem : public WhereClauseItem +{ + std::vector for_lifetimes; + std::unique_ptr bound_type; + std::vector> type_param_bounds; + NodeId node_id; + Location locus; + +public: + // Returns whether the item has ForLifetimes + bool has_for_lifetimes () const { return !for_lifetimes.empty (); } + + // Returns whether the item has type param bounds + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + TypeBoundWhereClauseItem ( + std::vector for_lifetimes, std::unique_ptr bound_type, + std::vector> type_param_bounds, + Location locus) + : for_lifetimes (std::move (for_lifetimes)), + bound_type (std::move (bound_type)), + type_param_bounds (std::move (type_param_bounds)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor requires clone + TypeBoundWhereClauseItem (TypeBoundWhereClauseItem const &other) + : for_lifetimes (other.for_lifetimes), + bound_type (other.bound_type->clone_type ()) + { + node_id = other.node_id; + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // Overload assignment operator to clone + TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem const &other) + { + node_id = other.node_id; + for_lifetimes = other.for_lifetimes; + bound_type = other.bound_type->clone_type (); + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // move constructors + TypeBoundWhereClauseItem (TypeBoundWhereClauseItem &&other) = default; + TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem &&other) + = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + std::unique_ptr &get_type () + { + rust_assert (bound_type != nullptr); + return bound_type; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + + NodeId get_node_id () const override final { return node_id; } + + Location get_locus () const { return locus; } + +protected: + // Clone function implementation as (not pure) virtual method + TypeBoundWhereClauseItem *clone_where_clause_item_impl () const override + { + return new TypeBoundWhereClauseItem (*this); + } +}; + +// A where clause +struct WhereClause +{ +private: + std::vector> where_clause_items; + NodeId node_id; + +public: + WhereClause (std::vector> where_clause_items) + : where_clause_items (std::move (where_clause_items)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // copy constructor with vector clone + WhereClause (WhereClause const &other) + { + node_id = other.node_id; + where_clause_items.reserve (other.where_clause_items.size ()); + for (const auto &e : other.where_clause_items) + where_clause_items.push_back (e->clone_where_clause_item ()); + } + + // overloaded assignment operator with vector clone + WhereClause &operator= (WhereClause const &other) + { + node_id = other.node_id; + where_clause_items.reserve (other.where_clause_items.size ()); + for (const auto &e : other.where_clause_items) + where_clause_items.push_back (e->clone_where_clause_item ()); + + return *this; + } + + // move constructors + WhereClause (WhereClause &&other) = default; + WhereClause &operator= (WhereClause &&other) = default; + + // Creates a WhereClause with no items. + static WhereClause create_empty () + { + return WhereClause (std::vector> ()); + } + + // Returns whether the WhereClause has no items. + bool is_empty () const { return where_clause_items.empty (); } + + std::string as_string () const; + + NodeId get_node_id () const { return node_id; } + + // TODO: this mutable getter seems kinda dodgy + std::vector> &get_items () + { + return where_clause_items; + } + const std::vector> &get_items () const + { + return where_clause_items; + } +}; + +// A self parameter in a method +struct SelfParam +{ +private: + bool has_ref; + bool is_mut; + // bool has_lifetime; // only possible if also ref + Lifetime lifetime; + + // bool has_type; // only possible if not ref + std::unique_ptr type; + + NodeId node_id; + + Location locus; + + // Unrestricted constructor used for error state + SelfParam (Lifetime lifetime, bool has_ref, bool is_mut, Type *type) + : has_ref (has_ref), is_mut (is_mut), lifetime (std::move (lifetime)), + type (type), node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + // this is ok as no outside classes can ever call this + + // TODO: self param can have outer attributes + +public: + // Returns whether the self-param has a type field. + bool has_type () const { return type != nullptr; } + + // Returns whether the self-param has a valid lifetime. + bool has_lifetime () const { return !lifetime.is_error (); } + + // Returns whether the self-param is in an error state. + bool is_error () const + { + return (has_type () && has_lifetime ()) || (has_lifetime () && !has_ref); + // not having either is not an error + } + + // Creates an error state self-param. + static SelfParam create_error () + { + // cannot have no ref but have a lifetime at the same time + return SelfParam (Lifetime (Lifetime::STATIC), false, false, nullptr); + } + + // Type-based self parameter (not ref, no lifetime) + SelfParam (std::unique_ptr type, bool is_mut, Location locus) + : has_ref (false), is_mut (is_mut), lifetime (Lifetime::error ()), + type (std::move (type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Lifetime-based self parameter (is ref, no type) + SelfParam (Lifetime lifetime, bool is_mut, Location locus) + : has_ref (true), is_mut (is_mut), lifetime (std::move (lifetime)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor requires clone + SelfParam (SelfParam const &other) + : has_ref (other.has_ref), is_mut (other.is_mut), lifetime (other.lifetime), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), + locus (other.locus) + { + node_id = other.node_id; + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // Overload assignment operator to use clone + SelfParam &operator= (SelfParam const &other) + { + is_mut = other.is_mut; + has_ref = other.has_ref; + lifetime = other.lifetime; + locus = other.locus; + node_id = other.node_id; + + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + SelfParam (SelfParam &&other) = default; + SelfParam &operator= (SelfParam &&other) = default; + + std::string as_string () const; + + Location get_locus () const { return locus; } + + bool get_has_ref () const { return has_ref; }; + bool get_is_mut () const { return is_mut; } + + Lifetime get_lifetime () const { return lifetime; } + + NodeId get_node_id () const { return node_id; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (has_type ()); + return type; + } +}; + +// Qualifiers for function, i.e. const, unsafe, extern etc. +struct FunctionQualifiers +{ +private: + AsyncConstStatus const_status; + bool has_unsafe; + bool has_extern; + std::string extern_abi; + Location locus; + +public: + FunctionQualifiers (Location locus, AsyncConstStatus const_status, + bool has_unsafe, bool has_extern = false, + std::string extern_abi = std::string ()) + : const_status (const_status), has_unsafe (has_unsafe), + has_extern (has_extern), extern_abi (std::move (extern_abi)), + locus (locus) + { + if (!this->extern_abi.empty ()) + { + // having extern is required; not having it is an implementation error + rust_assert (has_extern); + } + } + + std::string as_string () const; + + AsyncConstStatus get_const_status () const { return const_status; } + bool is_unsafe () const { return has_unsafe; } + bool is_extern () const { return has_extern; } + std::string get_extern_abi () const { return extern_abi; } + bool has_abi () const { return !extern_abi.empty (); } + + Location get_locus () const { return locus; } +}; + +// A function parameter +struct FunctionParam +{ +private: + std::vector outer_attrs; + Location locus; + std::unique_ptr param_name; + std::unique_ptr type; + +public: + FunctionParam (std::unique_ptr param_name, + std::unique_ptr param_type, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), locus (locus), + param_name (std::move (param_name)), type (std::move (param_type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // Copy constructor uses clone + FunctionParam (FunctionParam const &other) + : locus (other.locus), node_id (other.node_id) + { + // guard to prevent nullptr dereference + if (other.param_name != nullptr) + param_name = other.param_name->clone_pattern (); + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // Overload assignment operator to use clone + FunctionParam &operator= (FunctionParam const &other) + { + locus = other.locus; + node_id = other.node_id; + + // guard to prevent nullptr dereference + if (other.param_name != nullptr) + param_name = other.param_name->clone_pattern (); + else + param_name = nullptr; + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + FunctionParam (FunctionParam &&other) = default; + FunctionParam &operator= (FunctionParam &&other) = default; + + // Returns whether FunctionParam is in an invalid state. + bool is_error () const { return param_name == nullptr || type == nullptr; } + + // Creates an error FunctionParam. + static FunctionParam create_error () + { + return FunctionParam (nullptr, nullptr, {}, Location ()); + } + + std::string as_string () const; + + Location get_locus () const { return locus; } + + // TODO: seems kinda dodgy. Think of better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_pattern () + { + rust_assert (param_name != nullptr); + return param_name; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + NodeId get_node_id () const { return node_id; } + +protected: + NodeId node_id; +}; + +// Visibility of item - if the item has it, then it is some form of public +struct Visibility +{ +public: + enum VisType + { + PRIV, + PUB, + PUB_CRATE, + PUB_SELF, + PUB_SUPER, + PUB_IN_PATH + }; + +private: + VisType vis_type; + // Only assigned if vis_type is IN_PATH + SimplePath in_path; + + // should this store location info? + +public: + // Creates a Visibility - TODO make constructor protected or private? + Visibility (VisType vis_type, SimplePath in_path) + : vis_type (vis_type), in_path (std::move (in_path)) + {} + + VisType get_public_vis_type () const { return vis_type; } + + // Returns whether visibility is in an error state. + bool is_error () const + { + return vis_type == PUB_IN_PATH && in_path.is_empty (); + } + + // Returns whether a visibility has a path + bool has_path () const { return !(is_error ()) && vis_type == PUB_IN_PATH; } + + // Returns whether visibility is public or not. + bool is_public () const { return vis_type != PRIV && !is_error (); } + + // Creates an error visibility. + static Visibility create_error () + { + return Visibility (PUB_IN_PATH, SimplePath::create_empty ()); + } + + // Unique pointer custom clone function + /*std::unique_ptr clone_visibility() const { + return std::unique_ptr(clone_visibility_impl()); + }*/ + + /* TODO: think of a way to only allow valid Visibility states - polymorphism + * is one idea but may be too resource-intensive. */ + + // Creates a public visibility with no further features/arguments. + static Visibility create_public () + { + return Visibility (PUB, SimplePath::create_empty ()); + } + + // Creates a public visibility with crate-relative paths + static Visibility create_crate (Location crate_tok_location) + { + return Visibility (PUB_CRATE, + SimplePath::from_str ("crate", crate_tok_location)); + } + + // Creates a public visibility with self-relative paths + static Visibility create_self (Location self_tok_location) + { + return Visibility (PUB_SELF, + SimplePath::from_str ("self", self_tok_location)); + } + + // Creates a public visibility with parent module-relative paths + static Visibility create_super (Location super_tok_location) + { + return Visibility (PUB_SUPER, + SimplePath::from_str ("super", super_tok_location)); + } + + // Creates a private visibility + static Visibility create_private () + { + return Visibility (PRIV, SimplePath::create_empty ()); + } + + // Creates a public visibility with a given path or whatever. + static Visibility create_in_path (SimplePath in_path) + { + return Visibility (PUB_IN_PATH, std::move (in_path)); + } + + std::string as_string () const; + const SimplePath &get_path () const { return in_path; } + SimplePath &get_path () { return in_path; } + +protected: + // Clone function implementation - not currently virtual but may be if + // polymorphism used + /*virtual*/ Visibility *clone_visibility_impl () const + { + return new Visibility (*this); + } +}; + +// A method (function belonging to a type) +class Method : public InherentImplItem, public TraitImplItem +{ + std::vector outer_attrs; + Visibility vis; + FunctionQualifiers qualifiers; + Identifier method_name; + std::vector> generic_params; + SelfParam self_param; + std::vector function_params; + std::unique_ptr return_type; + WhereClause where_clause; + std::unique_ptr function_body; + Location locus; + NodeId node_id; + +public: + // Returns whether the method is in an error state. + bool is_error () const + { + return function_body == nullptr || method_name.empty () + || self_param.is_error (); + } + + // Creates an error state method. + static Method create_error () + { + return Method ("", FunctionQualifiers (Location (), NONE, true), + std::vector> (), + SelfParam::create_error (), std::vector (), + nullptr, WhereClause::create_empty (), nullptr, + Visibility::create_error (), std::vector (), {}); + } + + // Returns whether the method has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether the method has parameters. + bool has_params () const { return !function_params.empty (); } + + // Returns whether the method has a return type (void otherwise). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether the where clause exists (i.e. has items) + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether method has a non-default visibility. + bool has_visibility () const { return !vis.is_error (); } + + // Mega-constructor with all possible fields + Method (Identifier method_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + SelfParam self_param, std::vector function_params, + std::unique_ptr return_type, WhereClause where_clause, + std::unique_ptr function_body, Visibility vis, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), vis (std::move (vis)), + qualifiers (std::move (qualifiers)), + method_name (std::move (method_name)), + generic_params (std::move (generic_params)), + self_param (std::move (self_param)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)), + function_body (std::move (function_body)), locus (locus), + node_id (Analysis::Mappings::get ()->get_next_node_id ()) + {} + + // TODO: add constructor with less fields + + // Copy constructor with clone + Method (Method const &other) + : outer_attrs (other.outer_attrs), vis (other.vis), + qualifiers (other.qualifiers), method_name (other.method_name), + self_param (other.self_param), function_params (other.function_params), + where_clause (other.where_clause), locus (other.locus) + { + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + node_id = other.node_id; + } + + // Overloaded assignment operator to clone + Method &operator= (Method const &other) + { + method_name = other.method_name; + outer_attrs = other.outer_attrs; + vis = other.vis; + qualifiers = other.qualifiers; + self_param = other.self_param; + function_params = other.function_params; + where_clause = other.where_clause; + locus = other.locus; + + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + else + function_body = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + node_id = other.node_id; + + return *this; + } + + // move constructors + Method (Method &&other) = default; + Method &operator= (Method &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if block is null, so base stripping on that. + void mark_for_strip () override { function_body = nullptr; } + bool is_marked_for_strip () const override + { + return function_body == nullptr; + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () + { + rust_assert (function_body != nullptr); + return function_body; + } + + SelfParam &get_self_param () { return self_param; } + const SelfParam &get_self_param () const { return self_param; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + Identifier get_method_name () const { return method_name; } + + NodeId get_node_id () const { return node_id; } + + Location get_locus () const override final { return locus; } + + FunctionQualifiers get_qualifiers () { return qualifiers; } + + const Visibility &get_visibility () const { return vis; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Method *clone_inherent_impl_item_impl () const final override + { + return clone_method_impl (); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + Method *clone_trait_impl_item_impl () const final override + { + return clone_method_impl (); + } + + /*virtual*/ Method *clone_method_impl () const { return new Method (*this); } +}; + +// Item that supports visibility - abstract base class +class VisItem : public Item +{ + Visibility visibility; + std::vector outer_attrs; + +protected: + // Visibility constructor + VisItem (Visibility visibility, + std::vector outer_attrs = std::vector ()) + : visibility (std::move (visibility)), outer_attrs (std::move (outer_attrs)) + {} + + // Visibility copy constructor + VisItem (VisItem const &other) + : visibility (other.visibility), outer_attrs (other.outer_attrs) + {} + + // Overload assignment operator to clone + VisItem &operator= (VisItem const &other) + { + visibility = other.visibility; + outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + VisItem (VisItem &&other) = default; + VisItem &operator= (VisItem &&other) = default; + +public: + /* Does the item have some kind of public visibility (non-default + * visibility)? */ + bool has_visibility () const { return visibility.is_public (); } + + std::string as_string () const override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + Visibility &get_visibility () { return visibility; } + const Visibility &get_visibility () const { return visibility; } + + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } +}; + +// Rust module item - abstract base class +class Module : public VisItem +{ +public: + // Type of the current module. A module can be either loaded or unloaded, + // meaning that the items of the module can already be present or not. For + // example, the following module would be loaded: `mod foo { fn bar() {} }`. + // However, the module would be unloaded if it refers to an external file (i.e + // `mod foo;`) and then become loaded upon expansion. + enum ModuleKind + { + LOADED, + UNLOADED, + }; + + Identifier get_name () const { return module_name; } + +private: + Identifier module_name; + Location locus; + ModuleKind kind; + + // Name of the file including the module + std::string outer_filename; + // bool has_inner_attrs; + std::vector inner_attrs; + // bool has_items; + std::vector> items; + // Names of including inline modules (immediate parent is last in the list) + std::vector module_scope; + + // Filename the module refers to. Empty string on LOADED modules or if an + // error occured when dealing with UNLOADED modules + std::string module_file; + + void clone_items (const std::vector> &other_items) + { + items.reserve (other_items.size ()); + for (const auto &e : other_items) + items.push_back (e->clone_item ()); + } + +public: + // Returns whether the module has items in its body. + bool has_items () const { return !items.empty (); } + + // Returns whether the module has any inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + // Unloaded module constructor + Module (Identifier module_name, Visibility visibility, + std::vector outer_attrs, Location locus, + std::string outer_filename, std::vector module_scope) + : VisItem (std::move (visibility), std::move (outer_attrs)), + module_name (module_name), locus (locus), kind (ModuleKind::UNLOADED), + outer_filename (outer_filename), inner_attrs (std::vector ()), + items (std::vector> ()), + module_scope (std::move (module_scope)) + {} + + // Loaded module constructor, with items + Module (Identifier name, Location locus, + std::vector> items, + Visibility visibility = Visibility::create_error (), + std::vector inner_attrs = std::vector (), + std::vector outer_attrs = std::vector ()) + : VisItem (std::move (visibility), std::move (outer_attrs)), + module_name (name), locus (locus), kind (ModuleKind::LOADED), + outer_filename (std::string ()), inner_attrs (std::move (inner_attrs)), + items (std::move (items)) + {} + + // Copy constructor with vector clone + Module (Module const &other) + : VisItem (other), module_name (other.module_name), locus (other.locus), + kind (other.kind), inner_attrs (other.inner_attrs), + module_scope (other.module_scope) + { + // We need to check whether we are copying a loaded module or an unloaded + // one. In the second case, clear the `items` vector. + if (other.kind == LOADED) + clone_items (other.items); + else + items.clear (); + } + + // Overloaded assignment operator with vector clone + Module &operator= (Module const &other) + { + VisItem::operator= (other); + + module_name = other.module_name; + locus = other.locus; + kind = other.kind; + inner_attrs = other.inner_attrs; + module_scope = other.module_scope; + + // Likewise, we need to clear the `items` vector in case the other module is + // unloaded + if (kind == LOADED) + clone_items (other.items); + else + items.clear (); + + return *this; + } + + // Search for the filename associated with an external module, storing it in + // module_file + void process_file_path (); + // Load the items contained in an external module + void load_items (); + + void accept_vis (ASTVisitor &vis) override; + + /* Override that runs the function recursively on all items contained within + * the module. */ + void add_crate_name (std::vector &names) const override; + + // Returns the kind of the module + enum ModuleKind get_kind () const { return kind; } + + // TODO: think of better way to do this - mutable getter seems dodgy + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector> &get_items () const { return items; } + std::vector> &get_items () { return items; } + + // move constructors + Module (Module &&other) = default; + Module &operator= (Module &&other) = default; + + std::string as_string () const override; + + Location get_locus () const override final { return locus; } + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { module_name = ""; } + bool is_marked_for_strip () const override { return module_name.empty (); } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Module *clone_item_impl () const override { return new Module (*this); } +}; + +// Rust extern crate declaration AST node +class ExternCrate : public VisItem +{ + // this is either an identifier or "self", with self parsed to string + std::string referenced_crate; + // bool has_as_clause; + // AsClause as_clause; + // this is either an identifier or "_", with _ parsed to string + std::string as_clause_name; + + Location locus; + + /* e.g. + "extern crate foo as _" + "extern crate foo" + "extern crate std as cool_std" */ +public: + std::string as_string () const override; + + // Returns whether extern crate declaration has an as clause. + bool has_as_clause () const { return !as_clause_name.empty (); } + + /* Returns whether extern crate declaration references the current crate + * (i.e. self). */ + bool references_self () const { return referenced_crate == "self"; } + + // Constructor + ExternCrate (std::string referenced_crate, Visibility visibility, + std::vector outer_attrs, Location locus, + std::string as_clause_name = std::string ()) + : VisItem (std::move (visibility), std::move (outer_attrs)), + referenced_crate (std::move (referenced_crate)), + as_clause_name (std::move (as_clause_name)), locus (locus) + {} + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + const std::string &get_referenced_crate () const { return referenced_crate; } + const std::string &get_as_clause () const { return as_clause_name; } + + // Override that adds extern crate name in decl to passed list of names. + void add_crate_name (std::vector &names) const override + { + names.push_back (referenced_crate); + } + + // Invalid if crate name is empty, so base stripping on that. + void mark_for_strip () override { referenced_crate = ""; } + bool is_marked_for_strip () const override + { + return referenced_crate.empty (); + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternCrate *clone_item_impl () const override + { + return new ExternCrate (*this); + } +}; + +// The path-ish thing referred to in a use declaration - abstract base class +class UseTree +{ + Location locus; + +public: + enum Kind + { + Glob, + Rebind, + List, + }; + + virtual ~UseTree () {} + + // Overload assignment operator to clone + UseTree &operator= (UseTree const &other) + { + locus = other.locus; + + return *this; + } + + UseTree (const UseTree &other) = default; + + // move constructors + UseTree (UseTree &&other) = default; + UseTree &operator= (UseTree &&other) = default; + + // Unique pointer custom clone function + std::unique_ptr clone_use_tree () const + { + return std::unique_ptr (clone_use_tree_impl ()); + } + + virtual std::string as_string () const = 0; + virtual Kind get_kind () const = 0; + + Location get_locus () const { return locus; } + + virtual void accept_vis (ASTVisitor &vis) = 0; + +protected: + // Clone function implementation as pure virtual method + virtual UseTree *clone_use_tree_impl () const = 0; + + UseTree (Location locus) : locus (locus) {} +}; + +// Use tree with a glob (wildcard) operator +class UseTreeGlob : public UseTree +{ +public: + enum PathType + { + NO_PATH, + GLOBAL, + PATH_PREFIXED + }; + +private: + PathType glob_type; + SimplePath path; + +public: + UseTreeGlob (PathType glob_type, SimplePath path, Location locus) + : UseTree (locus), glob_type (glob_type), path (std::move (path)) + { + if (this->glob_type != PATH_PREFIXED) + { + // compiler implementation error if there is a path with a + // non-path-prefixed use tree glob + rust_assert (!has_path ()); + } + // TODO: do path-prefixed paths also have to have a path? If so, have an + // assert for that too. + } + + /* Returns whether has path. Should be made redundant by PathType + * PATH_PREFIXED. */ + bool has_path () const { return !path.is_empty (); } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Kind get_kind () const override { return Glob; } + + SimplePath get_path () const + { + rust_assert (has_path ()); + return path; + } + + /* TODO: find way to ensure only PATH_PREFIXED glob_type has path - factory + * methods? */ +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + UseTreeGlob *clone_use_tree_impl () const override + { + return new UseTreeGlob (*this); + } +}; + +// Use tree with a list of paths with a common prefix +class UseTreeList : public UseTree +{ +public: + enum PathType + { + NO_PATH, + GLOBAL, + PATH_PREFIXED + }; + +private: + PathType path_type; + SimplePath path; + + std::vector> trees; + +public: + UseTreeList (PathType path_type, SimplePath path, + std::vector> trees, Location locus) + : UseTree (locus), path_type (path_type), path (std::move (path)), + trees (std::move (trees)) + { + if (this->path_type != PATH_PREFIXED) + { + // compiler implementation error if there is a path with a + // non-path-prefixed use tree glob + rust_assert (!has_path ()); + } + // TODO: do path-prefixed paths also have to have a path? If so, have an + // assert for that too. + } + + // copy constructor with vector clone + UseTreeList (UseTreeList const &other) + : UseTree (other), path_type (other.path_type), path (other.path) + { + trees.reserve (other.trees.size ()); + for (const auto &e : other.trees) + trees.push_back (e->clone_use_tree ()); + } + + // overloaded assignment operator with vector clone + UseTreeList &operator= (UseTreeList const &other) + { + UseTree::operator= (other); + path_type = other.path_type; + path = other.path; + + trees.reserve (other.trees.size ()); + for (const auto &e : other.trees) + trees.push_back (e->clone_use_tree ()); + + return *this; + } + + // move constructors + UseTreeList (UseTreeList &&other) = default; + UseTreeList &operator= (UseTreeList &&other) = default; + + // Returns whether has path. Should be made redundant by path_type. + bool has_path () const { return !path.is_empty (); } + + // Returns whether has inner tree elements. + bool has_trees () const { return !trees.empty (); } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Kind get_kind () const override { return List; } + SimplePath get_path () const + { + rust_assert (has_path ()); + return path; + } + + const std::vector> &get_trees () const + { + return trees; + } + + // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory + // methods? +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + UseTreeList *clone_use_tree_impl () const override + { + return new UseTreeList (*this); + } +}; + +// Use tree where it rebinds the module name as something else +class UseTreeRebind : public UseTree +{ +public: + enum NewBindType + { + NONE, + IDENTIFIER, + WILDCARD + }; + +private: + SimplePath path; + + NewBindType bind_type; + Identifier identifier; // only if NewBindType is IDENTIFIER + +public: + UseTreeRebind (NewBindType bind_type, SimplePath path, Location locus, + Identifier identifier = std::string ()) + : UseTree (locus), path (std::move (path)), bind_type (bind_type), + identifier (std::move (identifier)) + {} + + // Returns whether has path (this should always be true). + bool has_path () const { return !path.is_empty (); } + + // Returns whether has identifier (or, rather, is allowed to). + bool has_identifier () const { return bind_type == IDENTIFIER; } + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + Kind get_kind () const override { return Rebind; } + + SimplePath get_path () const + { + rust_assert (has_path ()); + return path; + } + + const Identifier &get_identifier () const + { + rust_assert (has_identifier ()); + return identifier; + } + + // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory + // methods? +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + virtual UseTreeRebind *clone_use_tree_impl () const override + { + return new UseTreeRebind (*this); + } +}; + +// Rust use declaration (i.e. for modules) AST node +class UseDeclaration : public VisItem +{ + std::unique_ptr use_tree; + Location locus; + +public: + std::string as_string () const override; + + UseDeclaration (std::unique_ptr use_tree, Visibility visibility, + std::vector outer_attrs, Location locus) + : VisItem (std::move (visibility), std::move (outer_attrs)), + use_tree (std::move (use_tree)), locus (locus) + {} + + // Copy constructor with clone + UseDeclaration (UseDeclaration const &other) + : VisItem (other), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.use_tree != nullptr) + use_tree = other.use_tree->clone_use_tree (); + } + + // Overloaded assignment operator to clone + UseDeclaration &operator= (UseDeclaration const &other) + { + VisItem::operator= (other); + // visibility = other.visibility->clone_visibility(); + // outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.use_tree != nullptr) + use_tree = other.use_tree->clone_use_tree (); + else + use_tree = nullptr; + + return *this; + } + + // move constructors + UseDeclaration (UseDeclaration &&other) = default; + UseDeclaration &operator= (UseDeclaration &&other) = default; + + Location get_locus () const override final { return locus; } + const std::unique_ptr &get_tree () const { return use_tree; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if use tree is null, so base stripping on that. + void mark_for_strip () override { use_tree = nullptr; } + bool is_marked_for_strip () const override { return use_tree == nullptr; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + UseDeclaration *clone_item_impl () const override + { + return new UseDeclaration (*this); + } +}; + +class LetStmt; + +// Rust function declaration AST node +class Function : public VisItem, public InherentImplItem, public TraitImplItem +{ + FunctionQualifiers qualifiers; + Identifier function_name; + std::vector> generic_params; + std::vector function_params; + std::unique_ptr return_type; + WhereClause where_clause; + std::unique_ptr function_body; + Location locus; + +public: + std::string as_string () const override; + + // Returns whether function has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether function has regular parameters. + bool has_function_params () const { return !function_params.empty (); } + + // Returns whether function has return type - if not, it is void. + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether function has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Mega-constructor with all possible fields + Function (Identifier function_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + std::vector function_params, + std::unique_ptr return_type, WhereClause where_clause, + std::unique_ptr function_body, Visibility vis, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + qualifiers (std::move (qualifiers)), + function_name (std::move (function_name)), + generic_params (std::move (generic_params)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)), + function_body (std::move (function_body)), locus (locus) + {} + + // TODO: add constructor with less fields + + // Copy constructor with clone + Function (Function const &other) + : VisItem (other), qualifiers (other.qualifiers), + function_name (other.function_name), + function_params (other.function_params), + where_clause (other.where_clause), locus (other.locus) + { + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator to clone + Function &operator= (Function const &other) + { + VisItem::operator= (other); + function_name = other.function_name; + qualifiers = other.qualifiers; + function_params = other.function_params; + where_clause = other.where_clause; + // visibility = other.visibility->clone_visibility(); + // outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (always required) + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + // guard to prevent null dereference (only required if error state) + if (other.function_body != nullptr) + function_body = other.function_body->clone_block_expr (); + else + function_body = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Function (Function &&other) = default; + Function &operator= (Function &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if block is null, so base stripping on that. + void mark_for_strip () override { function_body = nullptr; } + bool is_marked_for_strip () const override + { + return function_body == nullptr; + } + + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () + { + rust_assert (function_body != nullptr); + return function_body; + } + + const FunctionQualifiers &get_qualifiers () const { return qualifiers; } + + Identifier get_function_name () const { return function_name; } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Function *clone_item_impl () const override { return new Function (*this); } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + Function *clone_inherent_impl_item_impl () const override + { + return new Function (*this); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + Function *clone_trait_impl_item_impl () const override + { + return new Function (*this); + } +}; + +// Rust type alias (i.e. typedef) AST node +class TypeAlias : public VisItem, public TraitImplItem +{ + Identifier new_type_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::unique_ptr existing_type; + + Location locus; + +public: + std::string as_string () const override; + + // Returns whether type alias has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether type alias has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Mega-constructor with all possible fields + TypeAlias (Identifier new_type_name, + std::vector> generic_params, + WhereClause where_clause, std::unique_ptr existing_type, + Visibility vis, std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + new_type_name (std::move (new_type_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), + existing_type (std::move (existing_type)), locus (locus) + {} + + // Copy constructor + TypeAlias (TypeAlias const &other) + : VisItem (other), new_type_name (other.new_type_name), + where_clause (other.where_clause), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.existing_type != nullptr) + existing_type = other.existing_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator to clone + TypeAlias &operator= (TypeAlias const &other) + { + VisItem::operator= (other); + new_type_name = other.new_type_name; + where_clause = other.where_clause; + // visibility = other.visibility->clone_visibility(); + // outer_attrs = other.outer_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.existing_type != nullptr) + existing_type = other.existing_type->clone_type (); + else + existing_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + TypeAlias (TypeAlias &&other) = default; + TypeAlias &operator= (TypeAlias &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if existing type is null, so base stripping on that. + void mark_for_strip () override { existing_type = nullptr; } + bool is_marked_for_strip () const override + { + return existing_type == nullptr; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type_aliased () + { + rust_assert (existing_type != nullptr); + return existing_type; + } + + Identifier get_new_type_name () const { return new_type_name; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + TypeAlias *clone_item_impl () const override { return new TypeAlias (*this); } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + TypeAlias *clone_trait_impl_item_impl () const override + { + return new TypeAlias (*this); + } +}; + +// Rust base struct declaration AST node - abstract base class +class Struct : public VisItem +{ +protected: + // protected to enable access by derived classes - allows better as_string + Identifier struct_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + +private: + Location locus; + +public: + // Returns whether struct has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether struct has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Location get_locus () const override final { return locus; } + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { struct_name = ""; } + bool is_marked_for_strip () const override { return struct_name.empty (); } + + Identifier get_struct_name () const { return struct_name; } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + Identifier get_identifier () const { return struct_name; } + +protected: + Struct (Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, Visibility vis, Location locus, + std::vector outer_attrs = std::vector ()) + : VisItem (std::move (vis), std::move (outer_attrs)), + struct_name (std::move (struct_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), locus (locus) + {} + + // Copy constructor with vector clone + Struct (Struct const &other) + : VisItem (other), struct_name (other.struct_name), + where_clause (other.where_clause), locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator with vector clone + Struct &operator= (Struct const &other) + { + VisItem::operator= (other); + struct_name = other.struct_name; + where_clause = other.where_clause; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Struct (Struct &&other) = default; + Struct &operator= (Struct &&other) = default; +}; + +// A single field in a struct +struct StructField +{ +private: + // bool has_outer_attributes; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier field_name; + std::unique_ptr field_type; + + NodeId node_id; + + Location locus; + +public: + // Returns whether struct field has any outer attributes. + bool has_outer_attributes () const { return !outer_attrs.empty (); } + + // Returns whether struct field has a non-private (non-default) visibility. + bool has_visibility () const { return !visibility.is_error (); } + + StructField (Identifier field_name, std::unique_ptr field_type, + Visibility vis, Location locus, + std::vector outer_attrs = std::vector ()) + : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)), + field_name (std::move (field_name)), field_type (std::move (field_type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor + StructField (StructField const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + field_name (other.field_name), node_id (other.node_id), + locus (other.locus) + { + // guard to prevent null dereference + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + } + + ~StructField () = default; + + // Overloaded assignment operator to clone + StructField &operator= (StructField const &other) + { + field_name = other.field_name; + visibility = other.visibility; + outer_attrs = other.outer_attrs; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + else + field_type = nullptr; + + return *this; + } + + // move constructors + StructField (StructField &&other) = default; + StructField &operator= (StructField &&other) = default; + + // Returns whether struct field is in an error state. + bool is_error () const + { + return field_name.empty () && field_type == nullptr; + // this should really be an or since neither are allowed + } + + // Creates an error state struct field. + static StructField create_error () + { + return StructField (std::string (""), nullptr, Visibility::create_error (), + Location ()); + } + + std::string as_string () const; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + Identifier get_field_name () const { return field_name; } + + Location get_locus () const { return locus; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_field_type () + { + rust_assert (field_type != nullptr); + return field_type; + } + + const Visibility &get_visibility () const { return visibility; } + + NodeId get_node_id () const { return node_id; } +}; + +// Rust struct declaration with true struct type AST node +class StructStruct : public Struct +{ + std::vector fields; + bool is_unit; + +public: + std::string as_string () const override; + + // Mega-constructor with all possible fields + StructStruct (std::vector fields, Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, bool is_unit, Visibility vis, + std::vector outer_attrs, Location locus) + : Struct (std::move (struct_name), std::move (generic_params), + std::move (where_clause), std::move (vis), locus, + std::move (outer_attrs)), + fields (std::move (fields)), is_unit (is_unit) + {} + + // Unit struct constructor + StructStruct (Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, Visibility vis, + std::vector outer_attrs, Location locus) + : Struct (std::move (struct_name), std::move (generic_params), + std::move (where_clause), std::move (vis), locus, + std::move (outer_attrs)), + is_unit (true) + {} + + /* Returns whether the struct is a unit struct - struct defined without + * fields. This is important because it also means an implicit constant of its + * type is defined. */ + bool is_unit_struct () const { return is_unit; } + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_fields () { return fields; } + const std::vector &get_fields () const { return fields; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + StructStruct *clone_item_impl () const override + { + return new StructStruct (*this); + } +}; + +// A single field in a tuple +struct TupleField +{ +private: + // bool has_outer_attributes; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + std::unique_ptr field_type; + + NodeId node_id; + + Location locus; + +public: + // Returns whether tuple field has outer attributes. + bool has_outer_attributes () const { return !outer_attrs.empty (); } + + /* Returns whether tuple field has a non-default visibility (i.e. a public + * one) */ + bool has_visibility () const { return !visibility.is_error (); } + + // Complete constructor + TupleField (std::unique_ptr field_type, Visibility vis, Location locus, + std::vector outer_attrs = std::vector ()) + : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)), + field_type (std::move (field_type)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor with clone + TupleField (TupleField const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + node_id (other.node_id), locus (other.locus) + { + // guard to prevent null dereference (only required if error) + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + } + + ~TupleField () = default; + + // Overloaded assignment operator to clone + TupleField &operator= (TupleField const &other) + { + visibility = other.visibility; + outer_attrs = other.outer_attrs; + node_id = other.node_id; + locus = other.locus; + + // guard to prevent null dereference (only required if error) + if (other.field_type != nullptr) + field_type = other.field_type->clone_type (); + else + field_type = nullptr; + + return *this; + } + + // move constructors + TupleField (TupleField &&other) = default; + TupleField &operator= (TupleField &&other) = default; + + // Returns whether tuple field is in an error state. + bool is_error () const { return field_type == nullptr; } + + // Creates an error state tuple field. + static TupleField create_error () + { + return TupleField (nullptr, Visibility::create_error (), Location ()); + } + + std::string as_string () const; + + NodeId get_node_id () const { return node_id; } + + const Visibility &get_visibility () const { return visibility; } + + Location get_locus () const { return locus; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_field_type () + { + rust_assert (field_type != nullptr); + return field_type; + } +}; + +// Rust tuple declared using struct keyword AST node +class TupleStruct : public Struct +{ + std::vector fields; + +public: + std::string as_string () const override; + + // Mega-constructor with all possible fields + TupleStruct (std::vector fields, Identifier struct_name, + std::vector> generic_params, + WhereClause where_clause, Visibility vis, + std::vector outer_attrs, Location locus) + : Struct (std::move (struct_name), std::move (generic_params), + std::move (where_clause), std::move (vis), locus, + std::move (outer_attrs)), + fields (std::move (fields)) + {} + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_fields () { return fields; } + const std::vector &get_fields () const { return fields; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + TupleStruct *clone_item_impl () const override + { + return new TupleStruct (*this); + } +}; + +/* An item used in an "enum" tagged union - not abstract: base represents a + * name-only enum. EnumItems (variants) syntactically allow a Visibility + * annotation. */ +class EnumItem : public VisItem +{ + Identifier variant_name; + + Location locus; + +public: + virtual ~EnumItem () {} + + EnumItem (Identifier variant_name, Visibility vis, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + variant_name (std::move (variant_name)), locus (locus) + {} + + // Unique pointer custom clone function + std::unique_ptr clone_enum_item () const + { + return std::unique_ptr (clone_item_impl ()); + } + + virtual std::string as_string () const; + + // not pure virtual as not abstract + virtual void accept_vis (ASTVisitor &vis); + + Location get_locus () const { return locus; } + + Identifier get_identifier () const { return variant_name; } + + // Based on idea that name is never empty. + void mark_for_strip () { variant_name = ""; } + bool is_marked_for_strip () const { return variant_name.empty (); } + +protected: + EnumItem *clone_item_impl () const override { return new EnumItem (*this); } +}; + +// A tuple item used in an "enum" tagged union +class EnumItemTuple : public EnumItem +{ + // bool has_tuple_fields; + std::vector tuple_fields; + +public: + // Returns whether tuple enum item has tuple fields. + bool has_tuple_fields () const { return !tuple_fields.empty (); } + + EnumItemTuple (Identifier variant_name, Visibility vis, + std::vector tuple_fields, + std::vector outer_attrs, Location locus) + : EnumItem (std::move (variant_name), std::move (vis), + std::move (outer_attrs), locus), + tuple_fields (std::move (tuple_fields)) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_tuple_fields () { return tuple_fields; } + const std::vector &get_tuple_fields () const + { + return tuple_fields; + } + +protected: + // Clone function implementation as (not pure) virtual method + EnumItemTuple *clone_item_impl () const override + { + return new EnumItemTuple (*this); + } +}; + +// A struct item used in an "enum" tagged union +class EnumItemStruct : public EnumItem +{ + // bool has_struct_fields; + std::vector struct_fields; + +public: + // Returns whether struct enum item has struct fields. + bool has_struct_fields () const { return !struct_fields.empty (); } + + EnumItemStruct (Identifier variant_name, Visibility vis, + std::vector struct_fields, + std::vector outer_attrs, Location locus) + : EnumItem (std::move (variant_name), std::move (vis), + std::move (outer_attrs), locus), + struct_fields (std::move (struct_fields)) + {} + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_struct_fields () { return struct_fields; } + const std::vector &get_struct_fields () const + { + return struct_fields; + } + +protected: + // Clone function implementation as (not pure) virtual method + EnumItemStruct *clone_item_impl () const override + { + return new EnumItemStruct (*this); + } +}; + +// A discriminant (numbered enum) item used in an "enum" tagged union +class EnumItemDiscriminant : public EnumItem +{ + std::unique_ptr expression; + +public: + EnumItemDiscriminant (Identifier variant_name, Visibility vis, + std::unique_ptr expr, + std::vector outer_attrs, Location locus) + : EnumItem (std::move (variant_name), std::move (vis), + std::move (outer_attrs), locus), + expression (std::move (expr)) + {} + + // Copy constructor with clone + EnumItemDiscriminant (EnumItemDiscriminant const &other) + : EnumItem (other), expression (other.expression->clone_expr ()) + {} + + // Overloaded assignment operator to clone + EnumItemDiscriminant &operator= (EnumItemDiscriminant const &other) + { + EnumItem::operator= (other); + expression = other.expression->clone_expr (); + // variant_name = other.variant_name; + // outer_attrs = other.outer_attrs; + + return *this; + } + + // move constructors + EnumItemDiscriminant (EnumItemDiscriminant &&other) = default; + EnumItemDiscriminant &operator= (EnumItemDiscriminant &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (expression != nullptr); + return expression; + } + +protected: + // Clone function implementation as (not pure) virtual method + EnumItemDiscriminant *clone_item_impl () const override + { + return new EnumItemDiscriminant (*this); + } +}; + +// AST node for Rust "enum" - tagged union +class Enum : public VisItem +{ + Identifier enum_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::vector> items; + + Location locus; + +public: + std::string as_string () const override; + + // Returns whether "enum" has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether "enum" has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + /* Returns whether enum is a "zero-variant" (no possible variant) enum, + * which cannot be instantiated. */ + bool is_zero_variant () const { return items.empty (); } + + // Mega-constructor + Enum (Identifier enum_name, Visibility vis, + std::vector> generic_params, + WhereClause where_clause, std::vector> items, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + enum_name (std::move (enum_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), items (std::move (items)), + locus (locus) + {} + + // TODO: constructor with less arguments + + // Copy constructor with vector clone + Enum (Enum const &other) + : VisItem (other), enum_name (other.enum_name), + where_clause (other.where_clause), locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_enum_item ()); + } + + // Overloaded assignment operator with vector clone + Enum &operator= (Enum const &other) + { + VisItem::operator= (other); + enum_name = other.enum_name; + where_clause = other.where_clause; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + items.reserve (other.items.size ()); + for (const auto &e : other.items) + items.push_back (e->clone_enum_item ()); + + return *this; + } + + // Move constructors + Enum (Enum &&other) = default; + Enum &operator= (Enum &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + Identifier get_identifier () const { return enum_name; } + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { enum_name = ""; } + bool is_marked_for_strip () const override { return enum_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector> &get_variants () { return items; } + const std::vector> &get_variants () const + { + return items; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Enum *clone_item_impl () const override { return new Enum (*this); } +}; + +// Rust untagged union used for C compat AST node +class Union : public VisItem +{ + Identifier union_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::vector variants; + + Location locus; + +public: + std::string as_string () const override; + + // Returns whether union has generic params. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether union has where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Union (Identifier union_name, Visibility vis, + std::vector> generic_params, + WhereClause where_clause, std::vector variants, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + union_name (std::move (union_name)), + generic_params (std::move (generic_params)), + where_clause (std::move (where_clause)), variants (std::move (variants)), + locus (locus) + {} + + // copy constructor with vector clone + Union (Union const &other) + : VisItem (other), union_name (other.union_name), + where_clause (other.where_clause), variants (other.variants), + locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // overloaded assignment operator with vector clone + Union &operator= (Union const &other) + { + VisItem::operator= (other); + union_name = other.union_name; + where_clause = other.where_clause; + variants = other.variants; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Union (Union &&other) = default; + Union &operator= (Union &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { union_name = ""; } + bool is_marked_for_strip () const override { return union_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_variants () { return variants; } + const std::vector &get_variants () const { return variants; } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + Identifier get_identifier () const { return union_name; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Union *clone_item_impl () const override { return new Union (*this); } +}; + +/* "Constant item" AST node - used for constant, compile-time expressions + * within module scope (like constexpr) */ +class ConstantItem : public VisItem, + public InherentImplItem, + public TraitImplItem +{ + // either has an identifier or "_" - maybe handle in identifier? + // bool identifier_is_underscore; + // if no identifier declared, identifier will be "_" + std::string identifier; + + std::unique_ptr type; + std::unique_ptr const_expr; + + Location locus; + +public: + std::string as_string () const override; + + ConstantItem (std::string ident, Visibility vis, std::unique_ptr type, + std::unique_ptr const_expr, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + identifier (std::move (ident)), type (std::move (type)), + const_expr (std::move (const_expr)), locus (locus) + {} + + ConstantItem (ConstantItem const &other) + : VisItem (other), identifier (other.identifier), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + if (other.const_expr != nullptr) + const_expr = other.const_expr->clone_expr (); + } + + // Overload assignment operator to clone + ConstantItem &operator= (ConstantItem const &other) + { + VisItem::operator= (other); + identifier = other.identifier; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + if (other.const_expr != nullptr) + const_expr = other.const_expr->clone_expr (); + else + const_expr = nullptr; + + return *this; + } + + // move constructors + ConstantItem (ConstantItem &&other) = default; + ConstantItem &operator= (ConstantItem &&other) = default; + + /* Returns whether constant item is an "unnamed" (wildcard underscore used + * as identifier) constant. */ + bool is_unnamed () const { return identifier == "_"; } + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if type or expression are null, so base stripping on that. + void mark_for_strip () override + { + type = nullptr; + const_expr = nullptr; + } + bool is_marked_for_strip () const override + { + return type == nullptr && const_expr == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (const_expr != nullptr); + return const_expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + std::string get_identifier () const { return identifier; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ConstantItem *clone_item_impl () const override + { + return new ConstantItem (*this); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + ConstantItem *clone_inherent_impl_item_impl () const override + { + return new ConstantItem (*this); + } + + /* Use covariance to implement clone function as returning this object + * rather than base */ + ConstantItem *clone_trait_impl_item_impl () const override + { + return new ConstantItem (*this); + } +}; + +/* Static item AST node - items within module scope with fixed storage + * duration? */ +class StaticItem : public VisItem +{ + bool has_mut; + Identifier name; + std::unique_ptr type; + std::unique_ptr expr; + Location locus; + +public: + std::string as_string () const override; + + StaticItem (Identifier name, bool is_mut, std::unique_ptr type, + std::unique_ptr expr, Visibility vis, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), has_mut (is_mut), + name (std::move (name)), type (std::move (type)), expr (std::move (expr)), + locus (locus) + {} + + // Copy constructor with clone + StaticItem (StaticItem const &other) + : VisItem (other), has_mut (other.has_mut), name (other.name), + locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + } + + // Overloaded assignment operator to clone + StaticItem &operator= (StaticItem const &other) + { + VisItem::operator= (other); + name = other.name; + has_mut = other.has_mut; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + else + expr = nullptr; + + return *this; + } + + // move constructors + StaticItem (StaticItem &&other) = default; + StaticItem &operator= (StaticItem &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if type or expression are null, so base stripping on that. + void mark_for_strip () override + { + type = nullptr; + expr = nullptr; + } + bool is_marked_for_strip () const override + { + return type == nullptr && expr == nullptr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (expr != nullptr); + return expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + bool is_mutable () const { return has_mut; } + + Identifier get_identifier () const { return name; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + StaticItem *clone_item_impl () const override + { + return new StaticItem (*this); + } +}; + +// Function declaration in traits +struct TraitFunctionDecl +{ +private: + // TODO: delete and replace with Function decl item? no as no body in this. + FunctionQualifiers qualifiers; + Identifier function_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_params; + // FunctionParams function_params; + std::vector function_params; // inlined + + // bool has_return_type; + std::unique_ptr return_type; + + // bool has_where_clause; + WhereClause where_clause; + + // should this store location info? + +public: + // Returns whether function decl has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether function decl has regular parameters. + bool has_params () const { return !function_params.empty (); } + + // Returns whether function has return type (otherwise is void). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether function has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Identifier get_identifier () const { return function_name; } + + // Mega-constructor + TraitFunctionDecl (Identifier function_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + std::vector function_params, + std::unique_ptr return_type, + WhereClause where_clause) + : qualifiers (std::move (qualifiers)), + function_name (std::move (function_name)), + generic_params (std::move (generic_params)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)) + {} + + // Copy constructor with clone + TraitFunctionDecl (TraitFunctionDecl const &other) + : qualifiers (other.qualifiers), function_name (other.function_name), + function_params (other.function_params), where_clause (other.where_clause) + { + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + ~TraitFunctionDecl () = default; + + // Overloaded assignment operator with clone + TraitFunctionDecl &operator= (TraitFunctionDecl const &other) + { + function_name = other.function_name; + qualifiers = other.qualifiers; + function_params = other.function_params; + where_clause = other.where_clause; + + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + TraitFunctionDecl (TraitFunctionDecl &&other) = default; + TraitFunctionDecl &operator= (TraitFunctionDecl &&other) = default; + + std::string as_string () const; + + // Invalid if function name is empty, so base stripping on that. + void mark_for_strip () { function_name = ""; } + bool is_marked_for_strip () const { return function_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () { return return_type; } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + FunctionQualifiers get_qualifiers () { return qualifiers; } +}; + +// Actual trait item function declaration within traits +class TraitItemFunc : public TraitItem +{ + std::vector outer_attrs; + TraitFunctionDecl decl; + std::unique_ptr block_expr; + Location locus; + +public: + // Returns whether function has a definition or is just a declaration. + bool has_definition () const { return block_expr != nullptr; } + + TraitItemFunc (TraitFunctionDecl decl, std::unique_ptr block_expr, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + decl (std::move (decl)), block_expr (std::move (block_expr)), + locus (locus) + {} + + // Copy constructor with clone + TraitItemFunc (TraitItemFunc const &other) + : outer_attrs (other.outer_attrs), decl (other.decl), locus (other.locus) + { + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + } + + // Overloaded assignment operator to clone + TraitItemFunc &operator= (TraitItemFunc const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + decl = other.decl; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + else + block_expr = nullptr; + + return *this; + } + + // move constructors + TraitItemFunc (TraitItemFunc &&other) = default; + TraitItemFunc &operator= (TraitItemFunc &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if trait decl is empty, so base stripping on that. + void mark_for_strip () override { decl.mark_for_strip (); } + bool is_marked_for_strip () const override + { + return decl.is_marked_for_strip (); + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () { return block_expr; } + + // TODO: is this better? Or is a "vis_block" better? + TraitFunctionDecl &get_trait_function_decl () + { + // TODO: maybe only allow access if not marked for strip? + return decl; + } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemFunc *clone_trait_item_impl () const override + { + return new TraitItemFunc (*this); + } +}; + +// Method declaration within traits +struct TraitMethodDecl +{ +private: + // TODO: delete and replace with Function decl item? no as no body. + FunctionQualifiers qualifiers; + Identifier function_name; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + SelfParam self_param; + + // bool has_params; + // FunctionParams function_params; + std::vector function_params; // inlined + + // bool has_return_type; + std::unique_ptr return_type; + + // bool has_where_clause; + WhereClause where_clause; + + // should this store location info? + +public: + // Returns whether method decl has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether method decl has regular parameters. + bool has_params () const { return !function_params.empty (); } + + // Returns whether method has return type (otherwise is void). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether method has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + Identifier get_identifier () const { return function_name; } + + // Mega-constructor + TraitMethodDecl (Identifier function_name, FunctionQualifiers qualifiers, + std::vector> generic_params, + SelfParam self_param, + std::vector function_params, + std::unique_ptr return_type, WhereClause where_clause) + : qualifiers (std::move (qualifiers)), + function_name (std::move (function_name)), + generic_params (std::move (generic_params)), + self_param (std::move (self_param)), + function_params (std::move (function_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)) + {} + + // Copy constructor with clone + TraitMethodDecl (TraitMethodDecl const &other) + : qualifiers (other.qualifiers), function_name (other.function_name), + self_param (other.self_param), function_params (other.function_params), + where_clause (other.where_clause) + { + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + ~TraitMethodDecl () = default; + + // Overloaded assignment operator with clone + TraitMethodDecl &operator= (TraitMethodDecl const &other) + { + function_name = other.function_name; + qualifiers = other.qualifiers; + self_param = other.self_param; + function_params = other.function_params; + where_clause = other.where_clause; + + // guard to prevent nullptr dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + TraitMethodDecl (TraitMethodDecl &&other) = default; + TraitMethodDecl &operator= (TraitMethodDecl &&other) = default; + + std::string as_string () const; + + // Invalid if method name is empty, so base stripping on that. + void mark_for_strip () { function_name = ""; } + bool is_marked_for_strip () const { return function_name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_function_params () { return function_params; } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () { return return_type; } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + SelfParam &get_self_param () { return self_param; } + const SelfParam &get_self_param () const { return self_param; } + + FunctionQualifiers get_qualifiers () { return qualifiers; } +}; + +// Actual trait item method declaration within traits +class TraitItemMethod : public TraitItem +{ + std::vector outer_attrs; + TraitMethodDecl decl; + std::unique_ptr block_expr; + Location locus; + +public: + // Returns whether method has a definition or is just a declaration. + bool has_definition () const { return block_expr != nullptr; } + + TraitItemMethod (TraitMethodDecl decl, std::unique_ptr block_expr, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + decl (std::move (decl)), block_expr (std::move (block_expr)), + locus (locus) + {} + + // Copy constructor with clone + TraitItemMethod (TraitItemMethod const &other) + : outer_attrs (other.outer_attrs), decl (other.decl), locus (other.locus) + { + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + } + + // Overloaded assignment operator to clone + TraitItemMethod &operator= (TraitItemMethod const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + decl = other.decl; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.block_expr != nullptr) + block_expr = other.block_expr->clone_block_expr (); + else + block_expr = nullptr; + + return *this; + } + + // move constructors + TraitItemMethod (TraitItemMethod &&other) = default; + TraitItemMethod &operator= (TraitItemMethod &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if trait decl is empty, so base stripping on that. + void mark_for_strip () override { decl.mark_for_strip (); } + bool is_marked_for_strip () const override + { + return decl.is_marked_for_strip (); + } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + TraitMethodDecl &get_trait_method_decl () + { + // TODO: maybe only allow access if not marked for strip? + return decl; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_definition () { return block_expr; } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemMethod *clone_trait_item_impl () const override + { + return new TraitItemMethod (*this); + } +}; + +// Constant item within traits +class TraitItemConst : public TraitItem +{ + std::vector outer_attrs; + Identifier name; + std::unique_ptr type; + + // bool has_expression; + std::unique_ptr expr; + + Location locus; + +public: + // Whether the constant item has an associated expression. + bool has_expression () const { return expr != nullptr; } + + TraitItemConst (Identifier name, std::unique_ptr type, + std::unique_ptr expr, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + name (std::move (name)), type (std::move (type)), expr (std::move (expr)), + locus (locus) + {} + + // Copy constructor with clones + TraitItemConst (TraitItemConst const &other) + : outer_attrs (other.outer_attrs), name (other.name), locus (other.locus) + { + node_id = other.node_id; + + // guard to prevent null dereference + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + + // guard to prevent null dereference (only for error state) + if (other.type != nullptr) + type = other.type->clone_type (); + } + + // Overloaded assignment operator to clone + TraitItemConst &operator= (TraitItemConst const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + name = other.name; + locus = other.locus; + node_id = other.node_id; + + // guard to prevent null dereference + if (other.expr != nullptr) + expr = other.expr->clone_expr (); + else + expr = nullptr; + + // guard to prevent null dereference (only for error state) + if (other.type != nullptr) + type = other.type->clone_type (); + else + type = nullptr; + + return *this; + } + + // move constructors + TraitItemConst (TraitItemConst &&other) = default; + TraitItemConst &operator= (TraitItemConst &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if type is null, so base stripping on that. + void mark_for_strip () override { type = nullptr; } + bool is_marked_for_strip () const override { return type == nullptr; } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + bool has_expr () const { return expr != nullptr; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_expr () + { + rust_assert (has_expr ()); + return expr; + } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (type != nullptr); + return type; + } + + Identifier get_identifier () const { return name; } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemConst *clone_trait_item_impl () const override + { + return new TraitItemConst (*this); + } +}; + +// Type items within traits +class TraitItemType : public TraitItem +{ + std::vector outer_attrs; + + Identifier name; + + // bool has_type_param_bounds; + // TypeParamBounds type_param_bounds; + std::vector> + type_param_bounds; // inlined form + + Location locus; + +public: + // Returns whether trait item type has type param bounds. + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + TraitItemType (Identifier name, + std::vector> type_param_bounds, + std::vector outer_attrs, Location locus) + : TraitItem (), outer_attrs (std::move (outer_attrs)), + name (std::move (name)), + type_param_bounds (std::move (type_param_bounds)), locus (locus) + {} + + // Copy constructor with vector clone + TraitItemType (TraitItemType const &other) + : outer_attrs (other.outer_attrs), name (other.name), locus (other.locus) + { + node_id = other.node_id; + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + } + + // Overloaded assignment operator with vector clone + TraitItemType &operator= (TraitItemType const &other) + { + TraitItem::operator= (other); + outer_attrs = other.outer_attrs; + name = other.name; + locus = other.locus; + node_id = other.node_id; + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + return *this; + } + + // default move constructors + TraitItemType (TraitItemType &&other) = default; + TraitItemType &operator= (TraitItemType &&other) = default; + + std::string as_string () const override; + + Location get_locus () const { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if name is empty, so base stripping on that. + void mark_for_strip () override { name = ""; } + bool is_marked_for_strip () const override { return name.empty (); } + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: mutable getter seems kinda dodgy + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + + Identifier get_identifier () const { return name; } + +protected: + // Clone function implementation as (not pure) virtual method + TraitItemType *clone_trait_item_impl () const override + { + return new TraitItemType (*this); + } +}; + +// Rust trait item declaration AST node +class Trait : public VisItem +{ + bool has_unsafe; + Identifier name; + std::vector> generic_params; + std::vector> type_param_bounds; + WhereClause where_clause; + std::vector inner_attrs; + std::vector> trait_items; + Location locus; + +public: + std::string as_string () const override; + + // Returns whether trait has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether trait has type parameter bounds. + bool has_type_param_bounds () const { return !type_param_bounds.empty (); } + + // Returns whether trait has where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether trait has trait items. + bool has_trait_items () const { return !trait_items.empty (); } + + // Returns whether trait has inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + Identifier get_identifier () const { return name; } + + bool is_unsafe () const { return has_unsafe; } + + // Mega-constructor + Trait (Identifier name, bool is_unsafe, + std::vector> generic_params, + std::vector> type_param_bounds, + WhereClause where_clause, + std::vector> trait_items, Visibility vis, + std::vector outer_attrs, std::vector inner_attrs, + Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + has_unsafe (is_unsafe), name (std::move (name)), + generic_params (std::move (generic_params)), + type_param_bounds (std::move (type_param_bounds)), + where_clause (std::move (where_clause)), + inner_attrs (std::move (inner_attrs)), + trait_items (std::move (trait_items)), locus (locus) + {} + + // Copy constructor with vector clone + Trait (Trait const &other) + : VisItem (other), has_unsafe (other.has_unsafe), name (other.name), + where_clause (other.where_clause), inner_attrs (other.inner_attrs), + locus (other.locus) + { + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + trait_items.reserve (other.trait_items.size ()); + for (const auto &e : other.trait_items) + trait_items.push_back (e->clone_trait_item ()); + } + + // Overloaded assignment operator with vector clone + Trait &operator= (Trait const &other) + { + VisItem::operator= (other); + name = other.name; + has_unsafe = other.has_unsafe; + where_clause = other.where_clause; + inner_attrs = other.inner_attrs; + locus = other.locus; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + type_param_bounds.reserve (other.type_param_bounds.size ()); + for (const auto &e : other.type_param_bounds) + type_param_bounds.push_back (e->clone_type_param_bound ()); + + trait_items.reserve (other.trait_items.size ()); + for (const auto &e : other.trait_items) + trait_items.push_back (e->clone_trait_item ()); + + return *this; + } + + // default move constructors + Trait (Trait &&other) = default; + Trait &operator= (Trait &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Invalid if trait name is empty, so base stripping on that. + void mark_for_strip () override { name = ""; } + bool is_marked_for_strip () const override { return name.empty (); } + + // TODO: think of better way to do this + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + const std::vector> &get_trait_items () const + { + return trait_items; + } + std::vector> &get_trait_items () + { + return trait_items; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + std::vector> &get_type_param_bounds () + { + return type_param_bounds; + } + const std::vector> & + get_type_param_bounds () const + { + return type_param_bounds; + } + + WhereClause &get_where_clause () { return where_clause; } + + void insert_implict_self (std::unique_ptr &¶m) + { + std::vector> new_list; + new_list.reserve (generic_params.size () + 1); + + new_list.push_back (std::move (param)); + for (auto &p : generic_params) + { + new_list.push_back (std::move (p)); + } + + generic_params = std::move (new_list); + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + Trait *clone_item_impl () const override { return new Trait (*this); } +}; + +// Implementation item declaration AST node - abstract base class +class Impl : public VisItem +{ + // must be protected to allow subclasses to access them properly +protected: + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + std::unique_ptr trait_type; + + // bool has_where_clause; + WhereClause where_clause; + + // bool has_inner_attrs; + std::vector inner_attrs; + +private: + // doesn't really need to be protected as write access probably not needed + Location locus; + +public: + // Returns whether impl has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether impl has where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether impl has inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + Location get_locus () const override final { return locus; } + + // Invalid if trait type is null, so base stripping on that. + void mark_for_strip () override { trait_type = nullptr; } + bool is_marked_for_strip () const override { return trait_type == nullptr; } + + // TODO: think of better way to do this + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (trait_type != nullptr); + return trait_type; + } + +protected: + // Mega-constructor + Impl (std::vector> generic_params, + std::unique_ptr trait_type, WhereClause where_clause, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), + generic_params (std::move (generic_params)), + trait_type (std::move (trait_type)), + where_clause (std::move (where_clause)), + inner_attrs (std::move (inner_attrs)), locus (locus) + {} + + // Copy constructor + Impl (Impl const &other) + : VisItem (other), where_clause (other.where_clause), + inner_attrs (other.inner_attrs), locus (other.locus) + { + // guard to prevent null dereference (only required if error state) + if (other.trait_type != nullptr) + trait_type = other.trait_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Assignment operator overload with cloning + Impl &operator= (Impl const &other) + { + VisItem::operator= (other); + where_clause = other.where_clause; + inner_attrs = other.inner_attrs; + locus = other.locus; + + // guard to prevent null dereference (only required if error state) + if (other.trait_type != nullptr) + trait_type = other.trait_type->clone_type (); + else + trait_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + Impl (Impl &&other) = default; + Impl &operator= (Impl &&other) = default; +}; + +// Regular "impl foo" impl block declaration AST node +class InherentImpl : public Impl +{ + // bool has_impl_items; + std::vector> impl_items; + +public: + std::string as_string () const override; + + // Returns whether inherent impl block has inherent impl items. + bool has_impl_items () const { return !impl_items.empty (); } + + // Mega-constructor + InherentImpl (std::vector> impl_items, + std::vector> generic_params, + std::unique_ptr trait_type, WhereClause where_clause, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : Impl (std::move (generic_params), std::move (trait_type), + std::move (where_clause), std::move (vis), std::move (inner_attrs), + std::move (outer_attrs), locus), + impl_items (std::move (impl_items)) + {} + + // Copy constructor with vector clone + InherentImpl (InherentImpl const &other) : Impl (other) + { + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_inherent_impl_item ()); + } + + // Overloaded assignment operator with vector clone + InherentImpl &operator= (InherentImpl const &other) + { + Impl::operator= (other); + + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_inherent_impl_item ()); + + return *this; + } + + // default move constructors + InherentImpl (InherentImpl &&other) = default; + InherentImpl &operator= (InherentImpl &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + // TODO: think of better way to do this + const std::vector> &get_impl_items () const + { + return impl_items; + } + std::vector> &get_impl_items () + { + return impl_items; + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + InherentImpl *clone_item_impl () const override + { + return new InherentImpl (*this); + } +}; + +// The "impl footrait for foo" impl block declaration AST node +class TraitImpl : public Impl +{ + bool has_unsafe; + bool has_exclam; + TypePath trait_path; + + // bool has_impl_items; + std::vector> impl_items; + +public: + std::string as_string () const override; + + // Returns whether trait impl has impl items. + bool has_impl_items () const { return !impl_items.empty (); } + + // Mega-constructor + TraitImpl (TypePath trait_path, bool is_unsafe, bool has_exclam, + std::vector> impl_items, + std::vector> generic_params, + std::unique_ptr trait_type, WhereClause where_clause, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : Impl (std::move (generic_params), std::move (trait_type), + std::move (where_clause), std::move (vis), std::move (inner_attrs), + std::move (outer_attrs), locus), + has_unsafe (is_unsafe), has_exclam (has_exclam), + trait_path (std::move (trait_path)), impl_items (std::move (impl_items)) + {} + + // Copy constructor with vector clone + TraitImpl (TraitImpl const &other) + : Impl (other), has_unsafe (other.has_unsafe), + has_exclam (other.has_exclam), trait_path (other.trait_path) + { + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_trait_impl_item ()); + } + + // Overloaded assignment operator with vector clone + TraitImpl &operator= (TraitImpl const &other) + { + Impl::operator= (other); + trait_path = other.trait_path; + has_unsafe = other.has_unsafe; + has_exclam = other.has_exclam; + + impl_items.reserve (other.impl_items.size ()); + for (const auto &e : other.impl_items) + impl_items.push_back (e->clone_trait_impl_item ()); + + return *this; + } + + // move constructors + TraitImpl (TraitImpl &&other) = default; + TraitImpl &operator= (TraitImpl &&other) = default; + + void accept_vis (ASTVisitor &vis) override; + + bool is_unsafe () const { return has_unsafe; }; + bool is_exclam () const { return has_exclam; } + + // TODO: think of better way to do this + const std::vector> &get_impl_items () const + { + return impl_items; + } + std::vector> &get_impl_items () + { + return impl_items; + } + + // TODO: is this better? Or is a "vis_block" better? + TypePath &get_trait_path () + { + // TODO: assert that trait path is not empty? + return trait_path; + } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + TraitImpl *clone_item_impl () const override { return new TraitImpl (*this); } +}; + +#if 0 +// Abstract base class for an item used inside an extern block +class ExternalItem +{ + // bool has_outer_attrs; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier item_name; + Location locus; + +public: + virtual ~ExternalItem () {} + + /* TODO: spec syntax rules state that "MacroInvocationSemi" can be used as + * ExternalItem, but text body isn't so clear. Adding MacroInvocationSemi + * support would require a lot of refactoring. */ + + // Returns whether item has outer attributes. + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether item has non-default visibility. + bool has_visibility () const { return !visibility.is_error (); } + + // Unique pointer custom clone function + std::unique_ptr clone_external_item () const + { + return std::unique_ptr (clone_external_item_impl ()); + } + + virtual std::string as_string () const; + + Location get_locus () const override final { return locus; } + + virtual void accept_vis (ASTVisitor &vis) = 0; + + // TODO: make virtual? Would be more flexible. + // Based on idea that name should never be empty. + void mark_for_strip () { item_name = ""; }; + bool is_marked_for_strip () const { return item_name.empty (); }; + +protected: + ExternalItem (Identifier item_name, Visibility vis, + std::vector outer_attrs, Location locus) + : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)), + item_name (std::move (item_name)), locus (locus) + {} + + // Copy constructor + ExternalItem (ExternalItem const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + item_name (other.item_name), locus (other.locus) + {} + + // Overloaded assignment operator to clone + ExternalItem &operator= (ExternalItem const &other) + { + item_name = other.item_name; + visibility = other.visibility; + outer_attrs = other.outer_attrs; + locus = other.locus; + + return *this; + } + + // move constructors + ExternalItem (ExternalItem &&other) = default; + ExternalItem &operator= (ExternalItem &&other) = default; + + // Clone function implementation as pure virtual method + virtual ExternalItem *clone_external_item_impl () const = 0; + + // possibly make this public if required + std::string get_item_name () const { return item_name; } +}; +#endif + +// A static item used in an extern block +class ExternalStaticItem : public ExternalItem +{ + // bool has_outer_attrs; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier item_name; + Location locus; + + bool has_mut; + std::unique_ptr item_type; + +public: + ExternalStaticItem (Identifier item_name, std::unique_ptr item_type, + bool is_mut, Visibility vis, + std::vector outer_attrs, Location locus) + : ExternalItem (), outer_attrs (std::move (outer_attrs)), + visibility (std::move (vis)), item_name (std::move (item_name)), + locus (locus), has_mut (is_mut), item_type (std::move (item_type)) + {} + + // Copy constructor + ExternalStaticItem (ExternalStaticItem const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + item_name (other.item_name), locus (other.locus), has_mut (other.has_mut) + { + node_id = other.node_id; + // guard to prevent null dereference (only required if error state) + if (other.item_type != nullptr) + item_type = other.item_type->clone_type (); + } + + // Overloaded assignment operator to clone + ExternalStaticItem &operator= (ExternalStaticItem const &other) + { + node_id = other.node_id; + outer_attrs = other.outer_attrs; + visibility = other.visibility; + item_name = other.item_name; + locus = other.locus; + has_mut = other.has_mut; + + // guard to prevent null dereference (only required if error state) + if (other.item_type != nullptr) + item_type = other.item_type->clone_type (); + else + item_type = nullptr; + + return *this; + } + + // move constructors + ExternalStaticItem (ExternalStaticItem &&other) = default; + ExternalStaticItem &operator= (ExternalStaticItem &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // Returns whether item has outer attributes. + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether item has non-default visibility. + bool has_visibility () const { return !visibility.is_error (); } + + Location get_locus () const { return locus; } + + // Based on idea that type should never be null. + void mark_for_strip () override { item_type = nullptr; }; + bool is_marked_for_strip () const override { return item_type == nullptr; }; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (item_type != nullptr); + return item_type; + } + + Identifier get_identifier () const { return item_name; } + + const Visibility &get_visibility () const { return visibility; } + + bool is_mut () const { return has_mut; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternalStaticItem *clone_external_item_impl () const override + { + return new ExternalStaticItem (*this); + } +}; + +// A named function parameter used in external functions +struct NamedFunctionParam +{ +private: + // bool has_name; // otherwise is _ + std::string name; + + std::unique_ptr param_type; + + // seemingly new since writing this node + std::vector outer_attrs; + + NodeId node_id; + Location locus; + +public: + /* Returns whether the named function parameter has a name (i.e. name is not + * '_'). */ + bool has_name () const { return name != "_"; } + + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether the named function parameter is in an error state. + bool is_error () const + { + // also if identifier is "" but that is probably more costly to compute + return param_type == nullptr; + } + + std::string get_name () const { return name; } + + // Creates an error state named function parameter. + static NamedFunctionParam create_error () + { + return NamedFunctionParam ("", nullptr, {}, Location ()); + } + + NamedFunctionParam (std::string name, std::unique_ptr param_type, + std::vector outer_attrs, Location locus) + : name (std::move (name)), param_type (std::move (param_type)), + outer_attrs (std::move (outer_attrs)), + node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus) + {} + + // Copy constructor + NamedFunctionParam (NamedFunctionParam const &other) + : name (other.name), outer_attrs (other.outer_attrs) + { + node_id = other.node_id; + // guard to prevent null dereference (only required if error state) + if (other.param_type != nullptr) + param_type = other.param_type->clone_type (); + } + + ~NamedFunctionParam () = default; + + // Overloaded assignment operator to clone + NamedFunctionParam &operator= (NamedFunctionParam const &other) + { + node_id = other.node_id; + name = other.name; + // has_name = other.has_name; + outer_attrs = other.outer_attrs; + + // guard to prevent null dereference (only required if error state) + if (other.param_type != nullptr) + param_type = other.param_type->clone_type (); + else + param_type = nullptr; + + return *this; + } + + // move constructors + NamedFunctionParam (NamedFunctionParam &&other) = default; + NamedFunctionParam &operator= (NamedFunctionParam &&other) = default; + + std::string as_string () const; + + // Based on idea that nane should never be empty. + void mark_for_strip () { param_type = nullptr; }; + bool is_marked_for_strip () const { return is_error (); }; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_type () + { + rust_assert (param_type != nullptr); + return param_type; + } + + NodeId get_node_id () const { return node_id; } +}; + +// A function item used in an extern block +class ExternalFunctionItem : public ExternalItem +{ + // bool has_outer_attrs; + std::vector outer_attrs; + + // bool has_visibility; + Visibility visibility; + + Identifier item_name; + Location locus; + + // bool has_generics; + // Generics generic_params; + std::vector> generic_params; // inlined + + // bool has_return_type; + // FunctionReturnType return_type; + std::unique_ptr return_type; // inlined + + // bool has_where_clause; + WhereClause where_clause; + + std::vector function_params; + bool has_variadics; + std::vector variadic_outer_attrs; + +public: + // Returns whether item has generic parameters. + bool has_generics () const { return !generic_params.empty (); } + + // Returns whether item has a return type (otherwise void). + bool has_return_type () const { return return_type != nullptr; } + + // Returns whether item has a where clause. + bool has_where_clause () const { return !where_clause.is_empty (); } + + // Returns whether item has outer attributes. + bool has_outer_attrs () const { return !outer_attrs.empty (); } + + // Returns whether item has non-default visibility. + bool has_visibility () const { return !visibility.is_error (); } + + // Returns whether item has variadic parameters. + bool is_variadic () const { return has_variadics; } + + // Returns whether item has outer attributes on its variadic parameters. + bool has_variadic_outer_attrs () const + { + return !variadic_outer_attrs.empty (); + } + + Location get_locus () const { return locus; } + + const Visibility &get_visibility () const { return visibility; } + + ExternalFunctionItem ( + Identifier item_name, + std::vector> generic_params, + std::unique_ptr return_type, WhereClause where_clause, + std::vector function_params, bool has_variadics, + std::vector variadic_outer_attrs, Visibility vis, + std::vector outer_attrs, Location locus) + : ExternalItem (), outer_attrs (std::move (outer_attrs)), + visibility (std::move (vis)), item_name (std::move (item_name)), + locus (locus), generic_params (std::move (generic_params)), + return_type (std::move (return_type)), + where_clause (std::move (where_clause)), + function_params (std::move (function_params)), + has_variadics (has_variadics), + variadic_outer_attrs (std::move (variadic_outer_attrs)) + { + // TODO: assert that if has variadic outer attrs, then has_variadics is + // true? + } + + // Copy constructor with clone + ExternalFunctionItem (ExternalFunctionItem const &other) + : outer_attrs (other.outer_attrs), visibility (other.visibility), + item_name (other.item_name), locus (other.locus), + where_clause (other.where_clause), + function_params (other.function_params), + has_variadics (other.has_variadics), + variadic_outer_attrs (other.variadic_outer_attrs) + { + node_id = other.node_id; + // guard to prevent null pointer dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + } + + // Overloaded assignment operator with clone + ExternalFunctionItem &operator= (ExternalFunctionItem const &other) + { + outer_attrs = other.outer_attrs; + visibility = other.visibility; + item_name = other.item_name; + locus = other.locus; + where_clause = other.where_clause; + function_params = other.function_params; + has_variadics = other.has_variadics; + variadic_outer_attrs = other.variadic_outer_attrs; + node_id = other.node_id; + + // guard to prevent null pointer dereference + if (other.return_type != nullptr) + return_type = other.return_type->clone_type (); + else + return_type = nullptr; + + generic_params.reserve (other.generic_params.size ()); + for (const auto &e : other.generic_params) + generic_params.push_back (e->clone_generic_param ()); + + return *this; + } + + // move constructors + ExternalFunctionItem (ExternalFunctionItem &&other) = default; + ExternalFunctionItem &operator= (ExternalFunctionItem &&other) = default; + + std::string as_string () const override; + + void accept_vis (ASTVisitor &vis) override; + + // Based on idea that nane should never be empty. + void mark_for_strip () override { item_name = ""; }; + bool is_marked_for_strip () const override { return item_name.empty (); }; + + // TODO: this mutable getter seems really dodgy. Think up better way. + std::vector &get_outer_attrs () { return outer_attrs; } + const std::vector &get_outer_attrs () const { return outer_attrs; } + + std::vector &get_function_params () + { + return function_params; + } + const std::vector &get_function_params () const + { + return function_params; + } + + std::vector> &get_generic_params () + { + return generic_params; + } + const std::vector> &get_generic_params () const + { + return generic_params; + } + + // TODO: is this better? Or is a "vis_block" better? + WhereClause &get_where_clause () { return where_clause; } + + // TODO: is this better? Or is a "vis_block" better? + std::unique_ptr &get_return_type () + { + rust_assert (has_return_type ()); + return return_type; + } + + Identifier get_identifier () const { return item_name; }; + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternalFunctionItem *clone_external_item_impl () const override + { + return new ExternalFunctionItem (*this); + } +}; + +// An extern block AST node +class ExternBlock : public VisItem +{ + // bool has_abi; + std::string abi; + + // bool has_inner_attrs; + std::vector inner_attrs; + + // bool has_extern_items; + std::vector> extern_items; + + Location locus; + + // TODO: find another way to store this to save memory? + bool marked_for_strip = false; + +public: + std::string as_string () const override; + + // Returns whether extern block has inner attributes. + bool has_inner_attrs () const { return !inner_attrs.empty (); } + + // Returns whether extern block has extern items. + bool has_extern_items () const { return !extern_items.empty (); } + + // Returns whether extern block has ABI name. + bool has_abi () const { return !abi.empty (); } + + std::string get_abi () const { return abi; } + + ExternBlock (std::string abi, + std::vector> extern_items, + Visibility vis, std::vector inner_attrs, + std::vector outer_attrs, Location locus) + : VisItem (std::move (vis), std::move (outer_attrs)), abi (std::move (abi)), + inner_attrs (std::move (inner_attrs)), + extern_items (std::move (extern_items)), locus (locus) + {} + + // Copy constructor with vector clone + ExternBlock (ExternBlock const &other) + : VisItem (other), abi (other.abi), inner_attrs (other.inner_attrs), + locus (other.locus), marked_for_strip (other.marked_for_strip) + { + extern_items.reserve (other.extern_items.size ()); + for (const auto &e : other.extern_items) + extern_items.push_back (e->clone_external_item ()); + } + + // Overloaded assignment operator with vector clone + ExternBlock &operator= (ExternBlock const &other) + { + VisItem::operator= (other); + abi = other.abi; + inner_attrs = other.inner_attrs; + locus = other.locus; + marked_for_strip = other.marked_for_strip; + + extern_items.reserve (other.extern_items.size ()); + for (const auto &e : other.extern_items) + extern_items.push_back (e->clone_external_item ()); + + return *this; + } + + // move constructors + ExternBlock (ExternBlock &&other) = default; + ExternBlock &operator= (ExternBlock &&other) = default; + + Location get_locus () const override final { return locus; } + + void accept_vis (ASTVisitor &vis) override; + + // Can't think of any invalid invariants, so store boolean. + void mark_for_strip () override { marked_for_strip = true; } + bool is_marked_for_strip () const override { return marked_for_strip; } + + // TODO: think of better way to do this + const std::vector> &get_extern_items () const + { + return extern_items; + } + std::vector> &get_extern_items () + { + return extern_items; + } + + // TODO: think of better way to do this + const std::vector &get_inner_attrs () const { return inner_attrs; } + std::vector &get_inner_attrs () { return inner_attrs; } + +protected: + /* Use covariance to implement clone function as returning this object + * rather than base */ + ExternBlock *clone_item_impl () const override + { + return new ExternBlock (*this); + } +}; + +// Replaced with forward decls - defined in "rust-macro.h" +class MacroItem; +class MacroRulesDefinition; +} // namespace AST +} // namespace Rust + +#endif