// Copyright (C) 2020-2024 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #ifndef RUST_AST_BASE_H #define RUST_AST_BASE_H // Base for AST used in gccrs, basically required by all specific ast things #include "rust-system.h" #include "rust-hir-map.h" #include "rust-token.h" #include "rust-location.h" #include "rust-diagnostics.h" #include "rust-keyword-values.h" namespace Rust { // TODO: remove typedefs and make actual types for these typedef int TupleIndex; struct Session; struct MacroExpander; class Identifier { public: // Create dummy identifier Identifier () : ident (""), loc (UNDEF_LOCATION) {} // Create identifier with dummy location Identifier (std::string ident, location_t loc = UNDEF_LOCATION) : ident (ident), loc (loc) {} // Create identifier from token Identifier (const_TokenPtr token) : ident (token->get_str ()), loc (token->get_locus ()) {} Identifier (const Identifier &) = default; Identifier (Identifier &&) = default; Identifier &operator= (const Identifier &) = default; Identifier &operator= (Identifier &&) = default; location_t get_locus () const { return loc; } const std::string &as_string () const { return ident; } bool empty () const { return ident.empty (); } private: std::string ident; location_t loc; }; std::ostream & operator<< (std::ostream &os, Identifier const &i); namespace AST { // foward decl: ast visitor class ASTVisitor; using AttrVec = std::vector; // The available kinds of AST Nodes enum class Kind { UNKNOWN, MODULE, MACRO_RULES_DEFINITION, MACRO_INVOCATION, IDENTIFIER, }; class Visitable { public: virtual ~Visitable () = default; virtual void accept_vis (ASTVisitor &vis) = 0; }; // Abstract base class for all AST elements class Node : public Visitable { public: /** * Get the kind of Node this is. This is used to differentiate various AST * elements with very little overhead when extracting the derived type * through static casting is not necessary. */ // FIXME: Mark this as `= 0` in the future to make sure every node // implements it virtual Kind get_ast_kind () const { return Kind::UNKNOWN; } }; // Delimiter types - used in macros and whatever. enum DelimType { PARENS, SQUARE, CURLY }; // forward decl for use in token tree method class Token; // A tree of tokens (or a single token) - abstract base class class TokenTree : public Visitable { public: virtual ~TokenTree () {} // Unique pointer custom clone function std::unique_ptr clone_token_tree () const { return std::unique_ptr (clone_token_tree_impl ()); } virtual std::string as_string () const = 0; /* Converts token tree to a flat token stream. Tokens must be pointer to * avoid mutual dependency with Token. */ virtual std::vector> to_token_stream () const = 0; protected: // pure virtual clone implementation virtual TokenTree *clone_token_tree_impl () const = 0; }; // Abstract base class for a macro match class MacroMatch : public Visitable { public: enum MacroMatchType { Fragment, Repetition, Matcher, Tok }; virtual ~MacroMatch () {} virtual std::string as_string () const = 0; virtual location_t get_match_locus () const = 0; // Unique pointer custom clone function std::unique_ptr clone_macro_match () const { return std::unique_ptr (clone_macro_match_impl ()); } virtual MacroMatchType get_macro_match_type () const = 0; protected: // pure virtual clone implementation virtual MacroMatch *clone_macro_match_impl () const = 0; }; // A token is a kind of token tree (except delimiter tokens) class Token : public TokenTree, public MacroMatch { // A token is a kind of token tree (except delimiter tokens) // A token is a kind of MacroMatch (except $ and delimiter tokens) #if 0 // TODO: improve member variables - current ones are the same as lexer token // Token kind. TokenId token_id; // Token location. location_t locus; // Associated text (if any) of token. std::string str; // Token type hint (if any). PrimitiveCoreType type_hint; #endif const_TokenPtr tok_ref; /* new idea: wrapper around const_TokenPtr used for heterogeneuous storage * in token trees. rather than convert back and forth when parsing macros, * just wrap it. */ public: // Unique pointer custom clone function std::unique_ptr clone_token () const { return std::unique_ptr (clone_token_impl ()); } #if 0 /* constructor from general text - avoid using if lexer const_TokenPtr is * available */ Token (TokenId token_id, location_t locus, std::string str, PrimitiveCoreType type_hint) : token_id (token_id), locus (locus), str (std::move (str)), type_hint (type_hint) {} #endif // not doable with new implementation - will have to make a const_TokenPtr // Constructor from lexer const_TokenPtr #if 0 /* TODO: find workaround for std::string being nullptr - probably have to * introduce new method in lexer Token, or maybe make conversion method * there */ Token (const_TokenPtr lexer_token_ptr) : token_id (lexer_token_ptr->get_id ()), locus (lexer_token_ptr->get_locus ()), str (""), type_hint (lexer_token_ptr->get_type_hint ()) { // FIXME: change to "should have str" later? if (lexer_token_ptr->has_str ()) { str = lexer_token_ptr->get_str (); // DEBUG rust_debug ("ast token created with str '%s'", str.c_str ()); } else { // FIXME: is this returning correct thing? str = lexer_token_ptr->get_token_description (); // DEBUG rust_debug ("ast token created with string '%s'", str.c_str ()); } // DEBUG if (lexer_token_ptr->should_have_str () && !lexer_token_ptr->has_str ()) { rust_debug ( "BAD: for token '%s', should have string but does not!", lexer_token_ptr->get_token_description ()); } } #endif Token (const_TokenPtr lexer_tok_ptr) : tok_ref (std::move (lexer_tok_ptr)) {} bool is_string_lit () const { switch (get_id ()) { case STRING_LITERAL: case BYTE_STRING_LITERAL: return true; default: return false; } } std::string as_string () const override; location_t get_match_locus () const override { return tok_ref->get_locus (); }; void accept_vis (ASTVisitor &vis) override; // Return copy of itself but in token stream form. std::vector> to_token_stream () const override; TokenId get_id () const { return tok_ref->get_id (); } const std::string &get_str () const { return tok_ref->get_str (); } location_t get_locus () const { return tok_ref->get_locus (); } PrimitiveCoreType get_type_hint () const { return tok_ref->get_type_hint (); } // Get a new token pointer copy. const_TokenPtr get_tok_ptr () const { return tok_ref; } MacroMatchType get_macro_match_type () const override { return MacroMatchType::Tok; } protected: // No virtual for now as not polymorphic but can be in future /*virtual*/ Token *clone_token_impl () const { return new Token (*this); } /* Use covariance to implement clone function as returning this object * rather than base */ Token *clone_token_tree_impl () const final override { return clone_token_impl (); } /* Use covariance to implement clone function as returning this object * rather than base */ Token *clone_macro_match_impl () const final override { return clone_token_impl (); } }; // A literal - value with a type. Used in LiteralExpr and LiteralPattern. struct Literal { public: enum LitType { CHAR, STRING, BYTE, BYTE_STRING, INT, FLOAT, BOOL, ERROR }; private: /* TODO: maybe make subclasses of each type of literal with their typed * values (or generics) */ std::string value_as_string; LitType type; PrimitiveCoreType type_hint; public: std::string as_string () const { return value_as_string; } LitType get_lit_type () const { return type; } PrimitiveCoreType get_type_hint () const { return type_hint; } Literal (std::string value_as_string, LitType type, PrimitiveCoreType type_hint) : value_as_string (std::move (value_as_string)), type (type), type_hint (type_hint) {} static Literal create_error () { return Literal ("", ERROR, PrimitiveCoreType::CORETYPE_UNKNOWN); } // Returns whether literal is in an invalid state. bool is_error () const { return type == ERROR; } }; /* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr * to be defined */ class AttrInputLiteral; /* TODO: move applicable stuff into here or just don't include it because * nothing uses it A segment of a path (maybe) */ class PathSegment { public: virtual ~PathSegment () {} virtual std::string as_string () const = 0; // TODO: add visitor here? }; // A segment of a simple path without generic or type arguments class SimplePathSegment : public PathSegment { std::string segment_name; location_t locus; NodeId node_id; // only allow identifiers, "super", "self", "crate", or "$crate" public: // TODO: put checks in constructor to enforce this rule? SimplePathSegment (std::string segment_name, location_t locus) : segment_name (std::move (segment_name)), locus (locus), node_id (Analysis::Mappings::get ().get_next_node_id ()) {} /* Returns whether simple path segment is in an invalid state (currently, if * empty). */ bool is_error () const { return segment_name.empty (); } // Creates an error SimplePathSegment static SimplePathSegment create_error () { return SimplePathSegment (std::string (""), UNDEF_LOCATION); } std::string as_string () const override; location_t get_locus () const { return locus; } NodeId get_node_id () const { return node_id; } const std::string &get_segment_name () const { return segment_name; } bool is_super_path_seg () const { return as_string ().compare (Values::Keywords::SUPER) == 0; } bool is_crate_path_seg () const { return as_string ().compare (Values::Keywords::CRATE) == 0; } bool is_lower_self_seg () const { return as_string ().compare (Values::Keywords::SELF) == 0; } bool is_big_self () const { return as_string ().compare (Values::Keywords::SELF_ALIAS) == 0; } }; // A simple path without generic or type arguments class SimplePath { bool opening_scope_resolution; std::vector segments; location_t locus; NodeId node_id; public: // Constructor SimplePath (std::vector path_segments, bool has_opening_scope_resolution = false, location_t locus = UNDEF_LOCATION) : opening_scope_resolution (has_opening_scope_resolution), segments (std::move (path_segments)), locus (locus), node_id (Analysis::Mappings::get ().get_next_node_id ()) {} SimplePath (Identifier ident) : opening_scope_resolution (false), segments ({SimplePathSegment (ident.as_string (), ident.get_locus ())}), locus (ident.get_locus ()), node_id (Analysis::Mappings::get ().get_next_node_id ()) {} // Creates an empty SimplePath. static SimplePath create_empty () { return SimplePath (std::vector ()); } // Returns whether the SimplePath is empty, i.e. has path segments. bool is_empty () const { return segments.empty (); } const std::string as_string () const; bool has_opening_scope_resolution () const { return opening_scope_resolution; } location_t get_locus () const { return locus; } NodeId get_node_id () const { return node_id; } // does this need visitor if not polymorphic? probably not // path-to-string comparison operator bool operator== (const std::string &rhs) const { return !opening_scope_resolution && segments.size () == 1 && segments[0].as_string () == rhs; } /* Creates a single-segment SimplePath from a string. This will not check to * ensure that this is a valid identifier in path, so be careful. Also, this * will have no location data. * TODO have checks? */ static SimplePath from_str (std::string str, location_t locus) { std::vector single_segments = {AST::SimplePathSegment (std::move (str), locus)}; return SimplePath (std::move (single_segments), false, locus); } const std::vector &get_segments () const { return segments; } std::vector &get_segments () { return segments; } const SimplePathSegment &get_final_segment () const { return segments.back (); } }; // path-to-string inverse comparison operator inline bool operator!= (const SimplePath &lhs, const std::string &rhs) { return !(lhs == rhs); } // forward decl for Attribute class AttrInput; // 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_t locus; // should this store location info? public: // Creates a Visibility - TODO make constructor protected or private? Visibility (VisType vis_type, SimplePath in_path, location_t 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_CRATE; } // Returns whether visibility is public or not. bool is_public () const { return vis_type != PRIV && !is_error (); } location_t get_locus () const { return locus; } // empty? // Creates an error visibility. static Visibility create_error () { return Visibility (PUB_IN_PATH, SimplePath::create_empty (), UNDEF_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_t 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_t crate_tok_location, location_t crate_vis_location) { return Visibility (PUB_CRATE, SimplePath::from_str (Values::Keywords::CRATE, crate_tok_location), crate_vis_location); } // Creates a public visibility with self-relative paths static Visibility create_self (location_t self_tok_location, location_t self_vis_location) { return Visibility (PUB_SELF, SimplePath::from_str (Values::Keywords::SELF, self_tok_location), self_vis_location); } // Creates a public visibility with parent module-relative paths static Visibility create_super (location_t super_tok_location, location_t super_vis_location) { return Visibility (PUB_SUPER, SimplePath::from_str (Values::Keywords::SUPER, super_tok_location), super_vis_location); } // Creates a private visibility static Visibility create_private () { return Visibility (PRIV, SimplePath::create_empty (), UNDEF_LOCATION); } // Creates a public visibility with a given path or whatever. static Visibility create_in_path (SimplePath in_path, location_t 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); } }; // aka Attr // Attribute AST representation struct Attribute { private: SimplePath path; // bool has_attr_input; std::unique_ptr attr_input; location_t locus; bool inner_attribute; // TODO: maybe a variable storing whether attr input is parsed or not public: // Returns whether Attribute has AttrInput bool has_attr_input () const { return attr_input != nullptr; } // Constructor has pointer AttrInput for polymorphism reasons Attribute (SimplePath path, std::unique_ptr input, location_t locus = UNDEF_LOCATION, bool inner_attribute = false) : path (std::move (path)), attr_input (std::move (input)), locus (locus), inner_attribute (inner_attribute) {} bool is_derive () const; std::vector> get_traits_to_derive (); // default destructor ~Attribute () = default; // no point in being defined inline as requires virtual call anyway Attribute (const Attribute &other); // no point in being defined inline as requires virtual call anyway Attribute &operator= (const Attribute &other); // default move semantics Attribute (Attribute &&other) = default; Attribute &operator= (Attribute &&other) = default; // Unique pointer custom clone function std::unique_ptr clone_attribute () const { return std::unique_ptr (clone_attribute_impl ()); } // Creates an empty attribute (which is invalid) static Attribute create_empty () { return Attribute (SimplePath::create_empty (), nullptr); } // Returns whether the attribute is considered an "empty" attribute. bool is_empty () const { return attr_input == nullptr && path.is_empty (); } location_t get_locus () const { return locus; } AttrInput &get_attr_input () const { return *attr_input; } /* e.g.: #![crate_type = "lib"] #[test] #[cfg(target_os = "linux")] #[allow(non_camel_case_types)] #![allow(unused_variables)] */ // Full built-in attribute list: /* cfg * cfg_attr * test * ignore * should_panic * derive * macro_export * macro_use * proc_macro * proc_macro_derive * proc_macro_attribute * allow * warn * deny * forbid * deprecated * must_use * link * link_name * no_link * repr * crate_type * no_main * export_name * link_section * no_mangle * used * crate_name * inline * cold * no_builtins * target_feature * doc * no_std * no_implicit_prelude * path * recursion_limit * type_length_limit * panic_handler * global_allocator * windows_subsystem * feature */ std::string as_string () const; bool is_inner_attribute () const { return inner_attribute; } // no visitor pattern as not currently polymorphic const SimplePath &get_path () const { return path; } SimplePath &get_path () { return path; } // Call to parse attribute body to meta item syntax. void parse_attr_to_meta_item (); /* Determines whether cfg predicate is true and item with attribute should * not be stripped. Attribute body must already be parsed to meta item. */ bool check_cfg_predicate (const Session &session) const; // Returns whether body has been parsed to meta item form or not. bool is_parsed_to_meta_item () const; /* Returns any attributes generated from cfg_attr attributes. Attribute body * must already be parsed to meta item. */ std::vector separate_cfg_attrs () const; protected: // not virtual as currently no subclasses of Attribute, but could be in // future /*virtual*/ Attribute *clone_attribute_impl () const { return new Attribute (*this); } }; // Attribute body - abstract base class class AttrInput : public Visitable { public: enum AttrInputType { LITERAL, MACRO, META_ITEM, TOKEN_TREE, }; virtual ~AttrInput () {} // Unique pointer custom clone function std::unique_ptr clone_attr_input () const { return std::unique_ptr (clone_attr_input_impl ()); } virtual std::string as_string () const = 0; virtual bool check_cfg_predicate (const Session &session) const = 0; // Parse attribute input to meta item, if possible virtual AttrInput *parse_to_meta_item () const { return nullptr; } virtual std::vector separate_cfg_attrs () const { return {}; } // Returns whether attr input has been parsed to meta item syntax. virtual bool is_meta_item () const = 0; virtual AttrInputType get_attr_input_type () const = 0; protected: // pure virtual clone implementation virtual AttrInput *clone_attr_input_impl () const = 0; }; // Forward decl - defined in rust-macro.h class MetaNameValueStr; // abstract base meta item inner class class MetaItemInner : public Visitable { protected: // pure virtual as MetaItemInner virtual MetaItemInner *clone_meta_item_inner_impl () const = 0; public: enum class Kind { LitExpr, MetaItem, }; // Unique pointer custom clone function std::unique_ptr clone_meta_item_inner () const { return std::unique_ptr (clone_meta_item_inner_impl ()); } virtual Kind get_kind () = 0; virtual ~MetaItemInner (); virtual location_t get_locus () const = 0; virtual std::string as_string () const = 0; /* HACK: used to simplify parsing - creates a copy of that type, or returns * null */ virtual std::unique_ptr to_meta_name_value_str () const; // HACK: used to simplify parsing - same thing virtual SimplePath to_path_item () const { return SimplePath::create_empty (); } virtual Attribute to_attribute () const { return Attribute::create_empty (); } virtual bool check_cfg_predicate (const Session &session) const = 0; virtual bool is_key_value_pair () const { return false; } }; // Container used to store MetaItems as AttrInput (bridge-ish kinda thing) class AttrInputMetaItemContainer : public AttrInput { std::vector> items; public: AttrInputMetaItemContainer (std::vector> items) : items (std::move (items)) {} // copy constructor with vector clone AttrInputMetaItemContainer (const AttrInputMetaItemContainer &other) { items.reserve (other.items.size ()); for (const auto &e : other.items) items.push_back (e->clone_meta_item_inner ()); } // copy assignment operator with vector clone AttrInputMetaItemContainer & operator= (const AttrInputMetaItemContainer &other) { AttrInput::operator= (other); items.reserve (other.items.size ()); for (const auto &e : other.items) items.push_back (e->clone_meta_item_inner ()); return *this; } // default move constructors AttrInputMetaItemContainer (AttrInputMetaItemContainer &&other) = default; AttrInputMetaItemContainer &operator= (AttrInputMetaItemContainer &&other) = default; std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; bool check_cfg_predicate (const Session &session) const override; AttrInputType get_attr_input_type () const final override { return AttrInput::AttrInputType::META_ITEM; } // Clones this object. std::unique_ptr clone_attr_input_meta_item_container () const { return std::unique_ptr ( clone_attr_input_meta_item_container_impl ()); } std::vector separate_cfg_attrs () const override; bool is_meta_item () const override { return true; } // TODO: this mutable getter seems dodgy std::vector> &get_items () { return items; } const std::vector> &get_items () const { return items; } protected: // Use covariance to implement clone function as returning this type AttrInputMetaItemContainer *clone_attr_input_impl () const final override { return clone_attr_input_meta_item_container_impl (); } AttrInputMetaItemContainer *clone_attr_input_meta_item_container_impl () const { return new AttrInputMetaItemContainer (*this); } }; // A token tree with delimiters class DelimTokenTree : public TokenTree, public AttrInput { DelimType delim_type; std::vector> token_trees; location_t locus; protected: DelimTokenTree *clone_delim_tok_tree_impl () const { return new DelimTokenTree (*this); } /* Use covariance to implement clone function as returning a DelimTokenTree * object */ DelimTokenTree *clone_attr_input_impl () const final override { return clone_delim_tok_tree_impl (); } /* Use covariance to implement clone function as returning a DelimTokenTree * object */ DelimTokenTree *clone_token_tree_impl () const final override { return clone_delim_tok_tree_impl (); } public: DelimTokenTree (DelimType delim_type, std::vector> token_trees = std::vector> (), location_t locus = UNDEF_LOCATION) : delim_type (delim_type), token_trees (std::move (token_trees)), locus (locus) {} // Copy constructor with vector clone DelimTokenTree (DelimTokenTree const &other) : delim_type (other.delim_type), locus (other.locus) { token_trees.clear (); token_trees.reserve (other.token_trees.size ()); for (const auto &e : other.token_trees) token_trees.push_back (e->clone_token_tree ()); } // overloaded assignment operator with vector clone DelimTokenTree &operator= (DelimTokenTree const &other) { delim_type = other.delim_type; locus = other.locus; token_trees.clear (); token_trees.reserve (other.token_trees.size ()); for (const auto &e : other.token_trees) token_trees.push_back (e->clone_token_tree ()); return *this; } // move constructors DelimTokenTree (DelimTokenTree &&other) = default; DelimTokenTree &operator= (DelimTokenTree &&other) = default; static DelimTokenTree create_empty () { return DelimTokenTree (PARENS); } std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; bool check_cfg_predicate (const Session &) const override { // this should never be called - should be converted first rust_assert (false); return false; } AttrInputMetaItemContainer *parse_to_meta_item () const override; std::vector> to_token_stream () const override; std::unique_ptr clone_delim_token_tree () const { return std::unique_ptr (clone_delim_tok_tree_impl ()); } bool is_meta_item () const override { return false; } AttrInputType get_attr_input_type () const final override { return AttrInput::AttrInputType::TOKEN_TREE; } std::vector> &get_token_trees () { return token_trees; } const std::vector> &get_token_trees () const { return token_trees; } DelimType get_delim_type () const { return delim_type; } }; /* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr * to be defined */ class AttrInputLiteral; // abstract base meta item class class MetaItem : public MetaItemInner { public: enum class ItemKind { Path, Word, NameValueStr, PathLit, Seq, ListPaths, ListNameValueStr, }; MetaItemInner::Kind get_kind () override { return MetaItemInner::Kind::MetaItem; } virtual ItemKind get_item_kind () const = 0; }; // Forward decl - defined in rust-expr.h class MetaItemLitExpr; // Forward decl - defined in rust-expr.h class MetaItemPathLit; // Forward decl - defined in rust-macro.h class MetaItemPath; // Forward decl - defined in rust-macro.h class MetaItemSeq; // Forward decl - defined in rust-macro.h class MetaWord; // Forward decl - defined in rust-macro.h class MetaListPaths; // Forward decl - defined in rust-macro.h class MetaListNameValueStr; /* Base statement abstract class. Note that most "statements" are not allowed * in top-level module scope - only a subclass of statements called "items" * are. */ class Stmt : public Node { public: enum class Kind { Empty, Item, Let, Expr, MacroInvocation, }; // Unique pointer custom clone function std::unique_ptr clone_stmt () const { return std::unique_ptr (clone_stmt_impl ()); } virtual ~Stmt () {} virtual std::string as_string () const = 0; virtual location_t get_locus () const = 0; virtual void mark_for_strip () = 0; virtual bool is_marked_for_strip () const = 0; NodeId get_node_id () const { return node_id; } virtual Kind get_stmt_kind () = 0; // TODO: Can we remove these two? virtual bool is_item () const = 0; virtual bool is_expr () const { return false; } virtual void add_semicolon () {} protected: Stmt () : node_id (Analysis::Mappings::get ().get_next_node_id ()) {} // Clone function implementation as pure virtual method virtual Stmt *clone_stmt_impl () const = 0; NodeId node_id; }; // Rust "item" AST node (declaration of top-level/module-level allowed stuff) class Item : public Stmt { public: // Unique pointer custom clone function std::unique_ptr clone_item () const { return std::unique_ptr (clone_item_impl ()); } /* Adds crate names to the vector passed by reference, if it can * (polymorphism). TODO: remove, unused. */ virtual void add_crate_name (std::vector &names ATTRIBUTE_UNUSED) const {} Stmt::Kind get_stmt_kind () final { return Stmt::Kind::Item; } // FIXME: ARTHUR: Is it okay to have removed that final? Is it *required* // behavior that we have items that can also be expressions? bool is_item () const override { return true; } virtual std::vector &get_outer_attrs () = 0; virtual const std::vector &get_outer_attrs () const = 0; virtual bool has_outer_attrs () const { return !get_outer_attrs ().empty (); } protected: // Clone function implementation as pure virtual method virtual Item *clone_item_impl () const = 0; /* Save having to specify two clone methods in derived classes by making * statement clone return item clone. Hopefully won't affect performance too * much. */ Item *clone_stmt_impl () const final override { return clone_item_impl (); } }; // 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 () override { return outer_attrs; } const std::vector &get_outer_attrs () const override { return outer_attrs; } }; // forward decl of ExprWithoutBlock class ExprWithoutBlock; // Base expression AST node - abstract class Expr : public Node { public: // Unique pointer custom clone function std::unique_ptr clone_expr () const { return std::unique_ptr (clone_expr_impl ()); } /* TODO: public methods that could be useful: * - get_type() - returns type of expression. set_type() may also be useful * for some? * - evaluate() - evaluates expression if constant? can_evaluate()? */ virtual std::string as_string () const = 0; virtual ~Expr () {} virtual location_t get_locus () const = 0; virtual bool is_literal () const { return false; } // HACK: strictly not needed, but faster than full downcast clone virtual bool is_expr_without_block () const = 0; virtual void mark_for_strip () = 0; virtual bool is_marked_for_strip () const = 0; virtual NodeId get_node_id () const { return node_id; } virtual void set_node_id (NodeId id) { node_id = id; } virtual std::vector &get_outer_attrs () = 0; // TODO: think of less hacky way to implement this kind of thing // Sets outer attributes. virtual void set_outer_attrs (std::vector) = 0; protected: // Constructor Expr () : node_id (Analysis::Mappings::get ().get_next_node_id ()) {} // Clone function implementation as pure virtual method virtual Expr *clone_expr_impl () const = 0; NodeId node_id; }; // AST node for an expression without an accompanying block - abstract class ExprWithoutBlock : public Expr { protected: // pure virtual clone implementation virtual ExprWithoutBlock *clone_expr_without_block_impl () const = 0; /* Save having to specify two clone methods in derived classes by making * expr clone return exprwithoutblock clone. Hopefully won't affect * performance too much. */ ExprWithoutBlock *clone_expr_impl () const final override { return clone_expr_without_block_impl (); } bool is_expr_without_block () const final override { return true; }; public: // Unique pointer custom clone function std::unique_ptr clone_expr_without_block () const { return std::unique_ptr (clone_expr_without_block_impl ()); } }; /* HACK: IdentifierExpr, delete when figure out identifier vs expr problem in * Pratt parser */ /* Alternatively, identifiers could just be represented as single-segment * paths */ class IdentifierExpr : public ExprWithoutBlock { std::vector outer_attrs; Identifier ident; location_t locus; public: IdentifierExpr (Identifier ident, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), ident (std::move (ident)), locus (locus) {} std::string as_string () const override { return ident.as_string (); } location_t get_locus () const override final { return locus; } Identifier get_ident () const { return ident; } void accept_vis (ASTVisitor &vis) override; // Clones this object. std::unique_ptr clone_identifier_expr () const { return std::unique_ptr (clone_identifier_expr_impl ()); } // "Error state" if ident is empty, so base stripping on this. void mark_for_strip () override { ident = {""}; } bool is_marked_for_strip () const override { return ident.empty (); } const std::vector &get_outer_attrs () const { return outer_attrs; } std::vector &get_outer_attrs () override { return outer_attrs; } void set_outer_attrs (std::vector new_attrs) override { outer_attrs = std::move (new_attrs); } Kind get_ast_kind () const override { return Kind::IDENTIFIER; } protected: // Clone method implementation IdentifierExpr *clone_expr_without_block_impl () const final override { return clone_identifier_expr_impl (); } IdentifierExpr *clone_identifier_expr_impl () const { return new IdentifierExpr (*this); } }; // Pattern base AST node class Pattern : public Visitable { public: enum class Kind { Literal, Identifier, Wildcard, Rest, Range, Reference, Struct, TupleStruct, Tuple, Grouped, Slice, Alt, Path, MacroInvocation, }; // Unique pointer custom clone function std::unique_ptr clone_pattern () const { return std::unique_ptr (clone_pattern_impl ()); } virtual Kind get_pattern_kind () = 0; // possible virtual methods: is_refutable() virtual ~Pattern () {} virtual std::string as_string () const = 0; // as only one kind of pattern can be stripped, have default of nothing virtual void mark_for_strip () {} virtual bool is_marked_for_strip () const { return false; } virtual location_t get_locus () const = 0; virtual NodeId get_node_id () const = 0; protected: // Clone pattern implementation as pure virtual method virtual Pattern *clone_pattern_impl () const = 0; }; // forward decl for Type class TraitBound; // Base class for types as represented in AST - abstract class Type : public Node { public: // Unique pointer custom clone function std::unique_ptr clone_type () const { return std::unique_ptr (clone_type_impl ()); } // virtual destructor virtual ~Type () {} virtual std::string as_string () const = 0; /* HACK: convert to trait bound. Virtual method overriden by classes that * enable this. */ virtual TraitBound *to_trait_bound (bool) const { return nullptr; } /* as pointer, shouldn't require definition beforehand, only forward * declaration. */ // as only two kinds of types can be stripped, have default of nothing virtual void mark_for_strip () {} virtual bool is_marked_for_strip () const { return false; } virtual location_t get_locus () const = 0; NodeId get_node_id () const { return node_id; } protected: Type () : node_id (Analysis::Mappings::get ().get_next_node_id ()) {} // Clone function implementation as pure virtual method virtual Type *clone_type_impl () const = 0; NodeId node_id; }; // A type without parentheses? - abstract class TypeNoBounds : public Type { public: // Unique pointer custom clone function std::unique_ptr clone_type_no_bounds () const { return std::unique_ptr (clone_type_no_bounds_impl ()); } protected: // Clone function implementation as pure virtual method virtual TypeNoBounds *clone_type_no_bounds_impl () const = 0; /* Save having to specify two clone methods in derived classes by making * type clone return typenobounds clone. Hopefully won't affect performance * too much. */ TypeNoBounds *clone_type_impl () const final override { return clone_type_no_bounds_impl (); } TypeNoBounds () : Type () {} }; /* Abstract base class representing a type param bound - Lifetime and * TraitBound extends it */ class TypeParamBound : public Visitable { public: virtual ~TypeParamBound () {} // Unique pointer custom clone function std::unique_ptr clone_type_param_bound () const { return std::unique_ptr (clone_type_param_bound_impl ()); } virtual std::string as_string () const = 0; NodeId get_node_id () const { return node_id; } virtual location_t get_locus () const = 0; protected: // Clone function implementation as pure virtual method virtual TypeParamBound *clone_type_param_bound_impl () const = 0; TypeParamBound (NodeId node_id) : node_id (node_id) {} NodeId node_id; }; // Represents a lifetime (and is also a kind of type param bound) class Lifetime : public TypeParamBound { public: enum LifetimeType { NAMED, // corresponds to LIFETIME_OR_LABEL STATIC, // corresponds to 'static WILDCARD // corresponds to '_ }; private: LifetimeType lifetime_type; std::string lifetime_name; location_t locus; NodeId node_id; public: // Constructor Lifetime (LifetimeType type, std::string name = std::string (), location_t locus = UNDEF_LOCATION) : TypeParamBound (Analysis::Mappings::get ().get_next_node_id ()), lifetime_type (type), lifetime_name (std::move (name)), locus (locus) {} Lifetime (NodeId id, LifetimeType type, std::string name = std::string (), location_t locus = UNDEF_LOCATION) : TypeParamBound (id), lifetime_type (type), lifetime_name (std::move (name)), locus (locus) {} // Creates an "error" lifetime. static Lifetime error () { return Lifetime (NAMED, ""); } static Lifetime elided () { return Lifetime (WILDCARD, ""); } // Returns true if the lifetime is in an error state. bool is_error () const { return lifetime_type == NAMED && lifetime_name.empty (); } std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; LifetimeType get_lifetime_type () { return lifetime_type; } location_t get_locus () const override final { return locus; } std::string get_lifetime_name () const { return lifetime_name; } protected: /* Use covariance to implement clone function as returning this object * rather than base */ Lifetime *clone_type_param_bound_impl () const override { return new Lifetime (node_id, lifetime_type, lifetime_name, locus); } }; /* Base generic parameter in AST. Abstract - can be represented by a Lifetime * or Type param */ class GenericParam : public Visitable { public: enum class Kind { Lifetime, Type, Const, }; virtual ~GenericParam () {} // Unique pointer custom clone function std::unique_ptr clone_generic_param () const { return std::unique_ptr (clone_generic_param_impl ()); } virtual std::string as_string () const = 0; virtual location_t get_locus () const = 0; virtual Kind get_kind () const = 0; NodeId get_node_id () { return node_id; } protected: GenericParam () : node_id (Analysis::Mappings::get ().get_next_node_id ()) {} GenericParam (NodeId node_id) : node_id (node_id) {} // Clone function implementation as pure virtual method virtual GenericParam *clone_generic_param_impl () const = 0; NodeId node_id; }; // A lifetime generic parameter (as opposed to a type generic parameter) class LifetimeParam : public GenericParam { Lifetime lifetime; std::vector lifetime_bounds; Attribute outer_attr; location_t locus; public: Lifetime get_lifetime () const { return lifetime; } Lifetime &get_lifetime () { return lifetime; } Attribute &get_outer_attribute () { return outer_attr; } // Returns whether the lifetime param has any lifetime bounds. bool has_lifetime_bounds () const { return !lifetime_bounds.empty (); } std::vector &get_lifetime_bounds () { return lifetime_bounds; } // Returns whether the lifetime param has an outer attribute. bool has_outer_attribute () const { return !outer_attr.is_empty (); } // Creates an error state lifetime param. static LifetimeParam create_error () { return LifetimeParam (Lifetime::error (), {}, Attribute::create_empty (), UNDEF_LOCATION); } // Returns whether the lifetime param is in an error state. bool is_error () const { return lifetime.is_error (); } // Constructor LifetimeParam (Lifetime lifetime, std::vector lifetime_bounds, Attribute outer_attr, location_t locus) : lifetime (std::move (lifetime)), lifetime_bounds (std::move (lifetime_bounds)), outer_attr (std::move (outer_attr)), locus (locus) {} std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; location_t get_locus () const override final { return locus; } Kind get_kind () const override final { return Kind::Lifetime; } protected: /* Use covariance to implement clone function as returning this object * rather than base */ LifetimeParam *clone_generic_param_impl () const override { return new LifetimeParam (*this); } }; class AssociatedItem : public Visitable { protected: // Clone function implementation as pure virtual method virtual AssociatedItem *clone_associated_item_impl () const = 0; public: virtual ~AssociatedItem () {} std::unique_ptr clone_associated_item () const { return std::unique_ptr (clone_associated_item_impl ()); } virtual std::string as_string () const = 0; virtual void mark_for_strip () = 0; virtual bool is_marked_for_strip () const = 0; virtual location_t get_locus () const = 0; }; // Item used in trait declarations - abstract base class class TraitItem : public AssociatedItem { protected: TraitItem (location_t locus) : node_id (Analysis::Mappings::get ().get_next_node_id ()), vis (Visibility::create_private ()), locus (locus) {} TraitItem (Visibility vis, location_t locus) : node_id (Analysis::Mappings::get ().get_next_node_id ()), vis (vis), locus (locus) {} // Clone function implementation as pure virtual method virtual TraitItem *clone_associated_item_impl () const override = 0; NodeId node_id; Visibility vis; location_t locus; public: // Unique pointer custom clone function std::unique_ptr clone_trait_item () const { return std::unique_ptr (clone_associated_item_impl ()); } NodeId get_node_id () const { return node_id; } location_t get_locus () const override { return locus; } }; // Abstract base class for an item used inside an extern block class ExternalItem : public Visitable { public: ExternalItem () : node_id (Analysis::Mappings::get ().get_next_node_id ()) {} ExternalItem (NodeId node_id) : node_id (node_id) {} virtual ~ExternalItem () {} // 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 = 0; virtual void mark_for_strip () = 0; virtual bool is_marked_for_strip () const = 0; virtual NodeId get_node_id () const { return node_id; } protected: // Clone function implementation as pure virtual method virtual ExternalItem *clone_external_item_impl () const = 0; NodeId node_id; }; /* Data structure to store the data used in macro invocations and macro * invocations with semicolons. */ struct MacroInvocData { private: SimplePath path; DelimTokenTree token_tree; // One way of parsing the macro. Probably not applicable for all macros. std::vector> parsed_items; bool parsed_to_meta_item = false; MacroExpander *expander = nullptr; public: std::string as_string () const; MacroInvocData (SimplePath path, DelimTokenTree token_tree) : path (std::move (path)), token_tree (std::move (token_tree)) {} // Copy constructor with vector clone MacroInvocData (const MacroInvocData &other) : path (other.path), token_tree (other.token_tree), parsed_to_meta_item (other.parsed_to_meta_item) { parsed_items.reserve (other.parsed_items.size ()); for (const auto &e : other.parsed_items) parsed_items.push_back (e->clone_meta_item_inner ()); } // Copy assignment operator with vector clone MacroInvocData &operator= (const MacroInvocData &other) { path = other.path; token_tree = other.token_tree; parsed_to_meta_item = other.parsed_to_meta_item; expander = other.expander; parsed_items.reserve (other.parsed_items.size ()); for (const auto &e : other.parsed_items) parsed_items.push_back (e->clone_meta_item_inner ()); return *this; } // Move constructors MacroInvocData (MacroInvocData &&other) = default; MacroInvocData &operator= (MacroInvocData &&other) = default; // Invalid if path is empty, so base stripping on that. void mark_for_strip () { path = SimplePath::create_empty (); } bool is_marked_for_strip () const { return path.is_empty (); } // Returns whether the macro has been parsed already. bool is_parsed () const { return parsed_to_meta_item; } // TODO: update on other ways of parsing it // TODO: this mutable getter seems kinda dodgy DelimTokenTree &get_delim_tok_tree () { return token_tree; } const DelimTokenTree &get_delim_tok_tree () const { return token_tree; } // Set the delim token tree of a macro invocation void set_delim_tok_tree (DelimTokenTree tree) { token_tree = tree; } // TODO: this mutable getter seems kinda dodgy SimplePath &get_path () { return path; } const SimplePath &get_path () const { return path; } void set_expander (MacroExpander *new_expander) { expander = new_expander; } MacroExpander *get_expander () { rust_assert (expander); return expander; } void set_meta_item_output (std::vector> new_items) { parsed_items = std::move (new_items); } // TODO: mutable getter seems kinda dodgy std::vector> &get_meta_items () { return parsed_items; } const std::vector> &get_meta_items () const { return parsed_items; } }; class SingleASTNode : public Visitable { public: enum NodeType { EXPRESSION, ITEM, STMT, EXTERN, ASSOC_ITEM, TYPE, }; private: NodeType kind; // FIXME make this a union std::unique_ptr expr; std::unique_ptr item; std::unique_ptr stmt; std::unique_ptr external_item; std::unique_ptr assoc_item; std::unique_ptr type; public: SingleASTNode (std::unique_ptr expr) : kind (EXPRESSION), expr (std::move (expr)) {} SingleASTNode (std::unique_ptr item) : kind (ITEM), item (std::move (item)) {} SingleASTNode (std::unique_ptr stmt) : kind (STMT), stmt (std::move (stmt)) {} SingleASTNode (std::unique_ptr item) : kind (EXTERN), external_item (std::move (item)) {} SingleASTNode (std::unique_ptr item) : kind (ASSOC_ITEM), assoc_item (std::move (item)) {} SingleASTNode (std::unique_ptr type) : kind (TYPE), type (std::move (type)) {} SingleASTNode (SingleASTNode const &other); SingleASTNode operator= (SingleASTNode const &other); SingleASTNode (SingleASTNode &&other) = default; SingleASTNode &operator= (SingleASTNode &&other) = default; NodeType get_kind () const { return kind; } std::unique_ptr &get_expr () { rust_assert (kind == EXPRESSION); return expr; } std::unique_ptr &get_item () { rust_assert (kind == ITEM); return item; } std::unique_ptr &get_stmt () { rust_assert (kind == STMT); return stmt; } /** * Access the inner nodes and take ownership of them. * You can only call these functions once per node */ std::unique_ptr take_stmt () { rust_assert (!is_error ()); return std::move (stmt); } std::unique_ptr take_expr () { rust_assert (!is_error ()); return std::move (expr); } std::unique_ptr take_item () { rust_assert (!is_error ()); return std::move (item); } std::unique_ptr take_trait_item () { rust_assert (!is_error ()); return std::unique_ptr ( static_cast (assoc_item.release ())); } std::unique_ptr take_external_item () { rust_assert (!is_error ()); return std::move (external_item); } std::unique_ptr take_assoc_item () { rust_assert (!is_error ()); return std::move (assoc_item); } std::unique_ptr take_impl_item () { return take_assoc_item (); } std::unique_ptr take_trait_impl_item () { return take_assoc_item (); } std::unique_ptr take_type () { rust_assert (!is_error ()); return std::move (type); } void accept_vis (ASTVisitor &vis) override; bool is_error (); std::string as_string () const; }; // A crate AST object - holds all the data for a single compilation unit struct Crate { std::vector inner_attrs; // dodgy spacing required here /* TODO: is it better to have a vector of items here or a module (implicit * top-level one)? */ std::vector> items; NodeId node_id; public: // Constructor Crate (std::vector> items, std::vector inner_attrs) : inner_attrs (std::move (inner_attrs)), items (std::move (items)), node_id (Analysis::Mappings::get ().get_next_node_id ()) {} // Copy constructor with vector clone Crate (Crate const &other) : inner_attrs (other.inner_attrs), node_id (other.node_id) { items.reserve (other.items.size ()); for (const auto &e : other.items) items.push_back (e->clone_item ()); } ~Crate () = default; // Overloaded assignment operator with vector clone Crate &operator= (Crate const &other) { inner_attrs = other.inner_attrs; node_id = other.node_id; items.reserve (other.items.size ()); for (const auto &e : other.items) items.push_back (e->clone_item ()); return *this; } // Move constructors Crate (Crate &&other) = default; Crate &operator= (Crate &&other) = default; // Get crate representation as string (e.g. for debugging). std::string as_string () const; // Delete all crate information, e.g. if fails cfg. void strip_crate () { inner_attrs.clear (); inner_attrs.shrink_to_fit (); items.clear (); items.shrink_to_fit (); // TODO: is this the best way to do this? } NodeId get_node_id () const { return node_id; } const std::vector &get_inner_attrs () const { return inner_attrs; } std::vector &get_inner_attrs () { return inner_attrs; } std::vector> take_items () { return std::move (items); } void set_items (std::vector> &&new_items) { items = std::move (new_items); } }; // Base path expression AST node - abstract class PathExpr : public ExprWithoutBlock { }; } // namespace AST } // namespace Rust namespace std { template <> struct less { bool operator() (const Rust::Identifier &lhs, const Rust::Identifier &rhs) const { return lhs.as_string () < rhs.as_string (); } }; } // namespace std #endif