// Copyright (C) 2020-2023 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #ifndef RUST_AST_ITEM_H #define RUST_AST_ITEM_H #include "rust-ast.h" #include "rust-path.h" #include "rust-common.h" #include "rust-expr.h" namespace Rust { namespace AST { // forward decls 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 (); } std::vector &get_for_lifetimes () { return for_lifetimes; } // 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; Location locus; // should this store location info? public: // Creates a Visibility - TODO make constructor protected or private? Visibility (VisType vis_type, SimplePath in_path, Location locus) : vis_type (vis_type), in_path (std::move (in_path)), locus (locus) {} VisType get_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 (); } Location get_locus () const { return locus; } // empty? // Creates an error visibility. static Visibility create_error () { return Visibility (PUB_IN_PATH, SimplePath::create_empty (), Location ()); } // 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. // empty? static Visibility create_public (Location pub_vis_location) { return Visibility (PUB, SimplePath::create_empty (), pub_vis_location); } // Creates a public visibility with crate-relative paths static Visibility create_crate (Location crate_tok_location, Location crate_vis_location) { return Visibility (PUB_CRATE, SimplePath::from_str ("crate", crate_tok_location), crate_vis_location); } // Creates a public visibility with self-relative paths static Visibility create_self (Location self_tok_location, Location self_vis_location) { return Visibility (PUB_SELF, SimplePath::from_str ("self", self_tok_location), self_vis_location); } // Creates a public visibility with parent module-relative paths static Visibility create_super (Location super_tok_location, Location super_vis_location) { return Visibility (PUB_SUPER, SimplePath::from_str ("super", super_tok_location), super_vis_location); } // Creates a private visibility static Visibility create_private () { return Visibility (PRIV, SimplePath::create_empty (), Location ()); } // Creates a public visibility with a given path or whatever. static Visibility create_in_path (SimplePath in_path, Location in_path_vis_location) { return Visibility (PUB_IN_PATH, std::move (in_path), in_path_vis_location); } 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; } Visibility &get_visibility () { return vis; } 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; } Visibility &get_visibility () { return visibility; } 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; } Visibility &get_visibility () { return visibility; } 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; 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 (locus), outer_attrs (std::move (outer_attrs)), decl (std::move (decl)), block_expr (std::move (block_expr)) {} // Copy constructor with clone TraitItemFunc (TraitItemFunc const &other) : TraitItem (other.locus), outer_attrs (other.outer_attrs), decl (other.decl) { 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; 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; 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 (locus), outer_attrs (std::move (outer_attrs)), decl (std::move (decl)), block_expr (std::move (block_expr)) {} // Copy constructor with clone TraitItemMethod (TraitItemMethod const &other) : TraitItem (other.locus), outer_attrs (other.outer_attrs), decl (other.decl) { 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; 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; 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 (locus), outer_attrs (std::move (outer_attrs)), name (std::move (name)), type (std::move (type)), expr (std::move (expr)) {} // Copy constructor with clones TraitItemConst (TraitItemConst const &other) : TraitItem (other.locus), outer_attrs (other.outer_attrs), name (other.name) { 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 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 (locus), outer_attrs (std::move (outer_attrs)), name (std::move (name)), type_param_bounds (std::move (type_param_bounds)) {} // Copy constructor with vector clone TraitItemType (TraitItemType const &other) : TraitItem (other.locus), outer_attrs (other.outer_attrs), name (other.name) { 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; 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; } Visibility &get_visibility () { return visibility; } 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); } }; class MacroRulesDefinition; } // namespace AST } // namespace Rust #endif