#ifndef RUST_AST_EXPR_H #define RUST_AST_EXPR_H #include "rust-ast.h" #include "rust-path.h" #include "rust-macro.h" #include "rust-operators.h" namespace Rust { namespace AST { /* TODO: if GCC moves to C++17 or allows boost, replace some boolean * "has_whatever" pairs with * optional types (std::optional or boost::optional)? */ // Loop label expression AST node used with break and continue expressions // TODO: inline? class LoopLabel /*: public Node*/ { Lifetime label; // or type LIFETIME_OR_LABEL location_t locus; NodeId node_id; public: std::string as_string () const; LoopLabel (Lifetime loop_label, location_t locus = UNDEF_LOCATION) : label (std::move (loop_label)), locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} // Returns whether the LoopLabel is in an error state. bool is_error () const { return label.is_error (); } // Creates an error state LoopLabel. static LoopLabel error () { return LoopLabel (Lifetime::error ()); } location_t get_locus () const { return locus; } Lifetime &get_lifetime () { return label; } NodeId get_node_id () const { return node_id; } }; // AST node for an expression with an accompanying block - abstract class ExprWithBlock : public Expr { protected: // pure virtual clone implementation virtual ExprWithBlock *clone_expr_with_block_impl () const = 0; // prevent having to define multiple clone expressions ExprWithBlock *clone_expr_impl () const final override { return clone_expr_with_block_impl (); } bool is_expr_without_block () const final override { return false; }; public: // Unique pointer custom clone function std::unique_ptr clone_expr_with_block () const { return std::unique_ptr (clone_expr_with_block_impl ()); } }; // Literals? Or literal base? class LiteralExpr : public ExprWithoutBlock { std::vector outer_attrs; Literal literal; location_t locus; public: std::string as_string () const override { return literal.as_string (); } Literal::LitType get_lit_type () const { return literal.get_lit_type (); } LiteralExpr (std::string value_as_string, Literal::LitType type, PrimitiveCoreType type_hint, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), literal (std::move (value_as_string), type, type_hint), locus (locus) {} LiteralExpr (Literal literal, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), literal (std::move (literal)), locus (locus) {} // Unique pointer custom clone function std::unique_ptr clone_literal_expr () const { return std::unique_ptr (clone_literal_expr_impl ()); } location_t get_locus () const override final { return locus; } bool is_literal () const override final { return true; } Literal get_literal () const { return literal; } void accept_vis (ASTVisitor &vis) override; // Invalid if literal is in error state, so base stripping on that. void mark_for_strip () override { literal = Literal::create_error (); } bool is_marked_for_strip () const override { return literal.is_error (); } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ LiteralExpr *clone_expr_without_block_impl () const final override { return clone_literal_expr_impl (); } /* not virtual as currently no subclasses of LiteralExpr, but could be in * future */ /*virtual*/ LiteralExpr *clone_literal_expr_impl () const { return new LiteralExpr (*this); } }; // Literal expression attribute body (non-macro attribute) class AttrInputLiteral : public AttrInput { LiteralExpr literal_expr; public: AttrInputLiteral (LiteralExpr lit_expr) : literal_expr (std::move (lit_expr)) {} std::string as_string () const override { return " = " + literal_expr.as_string (); } void accept_vis (ASTVisitor &vis) override; /* this can never be a cfg predicate - cfg and cfg_attr require a token-tree * cfg */ bool check_cfg_predicate (const Session &) const override { return false; } bool is_meta_item () const override { return false; } LiteralExpr &get_literal () { return literal_expr; } AttrInputType get_attr_input_type () const final override { return AttrInput::AttrInputType::LITERAL; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ AttrInputLiteral *clone_attr_input_impl () const override { return new AttrInputLiteral (*this); } }; // Like an AttrInputLiteral, but stores a MacroInvocation class AttrInputMacro : public AttrInput { std::unique_ptr macro; public: AttrInputMacro (std::unique_ptr macro) : macro (std::move (macro)) {} AttrInputMacro (const AttrInputMacro &oth); AttrInputMacro (AttrInputMacro &&oth) : macro (std::move (oth.macro)) {} void operator= (const AttrInputMacro &oth); void operator= (AttrInputMacro &&oth) { macro = std::move (oth.macro); } std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; // assuming this can't be a cfg predicate bool check_cfg_predicate (const Session &) const override { return false; } // assuming this is like AttrInputLiteral bool is_meta_item () const override { return false; } std::unique_ptr &get_macro () { return macro; } AttrInputType get_attr_input_type () const final override { return AttrInput::AttrInputType::MACRO; } protected: AttrInputMacro *clone_attr_input_impl () const override { return new AttrInputMacro (*this); } }; /* literal expr only meta item inner - TODO possibly replace with inheritance of * LiteralExpr itself? */ class MetaItemLitExpr : public MetaItemInner { LiteralExpr lit_expr; public: MetaItemLitExpr (LiteralExpr lit_expr) : lit_expr (std::move (lit_expr)) {} std::string as_string () const override { return lit_expr.as_string (); } location_t get_locus () const override { return lit_expr.get_locus (); } LiteralExpr get_literal () const { return lit_expr; } LiteralExpr &get_literal () { return lit_expr; } void accept_vis (ASTVisitor &vis) override; bool check_cfg_predicate (const Session &session) const override; MetaItemInner::Kind get_kind () override { return MetaItemInner::Kind::LitExpr; } protected: // Use covariance to implement clone function as returning this type MetaItemLitExpr *clone_meta_item_inner_impl () const override { return new MetaItemLitExpr (*this); } }; // more generic meta item "path = lit" form class MetaItemPathLit : public MetaItem { SimplePath path; LiteralExpr lit; public: MetaItemPathLit (SimplePath path, LiteralExpr lit_expr) : path (std::move (path)), lit (std::move (lit_expr)) {} SimplePath get_path () const { return path; } SimplePath &get_path () { return path; } LiteralExpr get_literal () const { return lit; } LiteralExpr &get_literal () { return lit; } std::string as_string () const override { return path.as_string () + " = " + lit.as_string (); } MetaItem::ItemKind get_item_kind () const override { return MetaItem::ItemKind::PathLit; } // There are two Locations in MetaItemPathLit (path and lit_expr), // we have no idea use which of them, just simply return UNKNOWN_LOCATION // now. // Maybe we will figure out when we really need the location in the future. location_t get_locus () const override { return UNKNOWN_LOCATION; } void accept_vis (ASTVisitor &vis) override; bool check_cfg_predicate (const Session &session) const override; /* TODO: return true if "ident" is defined and value of it is "lit", return * false otherwise */ Attribute to_attribute () const override; protected: // Use covariance to implement clone function as returning this type MetaItemPathLit *clone_meta_item_inner_impl () const override { return new MetaItemPathLit (*this); } }; /* Represents an expression using unary or binary operators as AST node. Can be * overloaded. */ class OperatorExpr : public ExprWithoutBlock { // TODO: create binary and unary operator subclasses? public: location_t locus; protected: /* Variables must be protected to allow derived classes to use them as first * class citizens */ std::vector outer_attrs; std::unique_ptr main_or_left_expr; // Constructor (only for initialisation of expr purposes) OperatorExpr (std::unique_ptr main_or_left_expr, std::vector outer_attribs, location_t locus) : locus (locus), outer_attrs (std::move (outer_attribs)), main_or_left_expr (std::move (main_or_left_expr)) {} // Copy constructor (only for initialisation of expr purposes) OperatorExpr (OperatorExpr const &other) : locus (other.locus), outer_attrs (other.outer_attrs) { // guard to prevent null dereference (only required if error state) if (other.main_or_left_expr != nullptr) main_or_left_expr = other.main_or_left_expr->clone_expr (); } // Overload assignment operator to deep copy expr OperatorExpr &operator= (OperatorExpr const &other) { ExprWithoutBlock::operator= (other); locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.main_or_left_expr != nullptr) main_or_left_expr = other.main_or_left_expr->clone_expr (); else main_or_left_expr = nullptr; return *this; } // move constructors OperatorExpr (OperatorExpr &&other) = default; OperatorExpr &operator= (OperatorExpr &&other) = default; public: location_t get_locus () const override final { return locus; } // Invalid if expr is null, so base stripping on that. void mark_for_strip () override { main_or_left_expr = nullptr; } bool is_marked_for_strip () const override { return main_or_left_expr == nullptr; } 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); } }; /* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be * overloaded. */ class BorrowExpr : public OperatorExpr { bool is_mut; bool double_borrow; public: std::string as_string () const override; BorrowExpr (std::unique_ptr borrow_lvalue, bool is_mut_borrow, bool is_double_borrow, std::vector outer_attribs, location_t locus) : OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs), locus), is_mut (is_mut_borrow), double_borrow (is_double_borrow) {} void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_borrowed_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } bool get_is_mut () const { return is_mut; } bool get_is_double_borrow () const { return double_borrow; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ BorrowExpr *clone_expr_without_block_impl () const override { return new BorrowExpr (*this); } }; // Unary prefix * deference operator class DereferenceExpr : public OperatorExpr { public: std::string as_string () const override; // Constructor calls OperatorExpr's protected constructor DereferenceExpr (std::unique_ptr deref_lvalue, std::vector outer_attribs, location_t locus) : OperatorExpr (std::move (deref_lvalue), std::move (outer_attribs), locus) {} void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_dereferenced_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ DereferenceExpr *clone_expr_without_block_impl () const override { return new DereferenceExpr (*this); } }; // Unary postfix ? error propogation operator. Cannot be overloaded. class ErrorPropagationExpr : public OperatorExpr { public: std::string as_string () const override; // Constructor calls OperatorExpr's protected constructor ErrorPropagationExpr (std::unique_ptr potential_error_value, std::vector outer_attribs, location_t locus) : OperatorExpr (std::move (potential_error_value), std::move (outer_attribs), locus) {} void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_propagating_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ErrorPropagationExpr *clone_expr_without_block_impl () const override { return new ErrorPropagationExpr (*this); } }; // Unary prefix - or ! negation or NOT operators. class NegationExpr : public OperatorExpr { public: using ExprType = NegationOperator; private: /* Note: overload negation via std::ops::Neg and not via std::ops::Not * Negation only works for signed integer and floating-point types, NOT only * works for boolean and integer types (via bitwise NOT) */ ExprType expr_type; public: std::string as_string () const override; ExprType get_expr_type () const { return expr_type; } // Constructor calls OperatorExpr's protected constructor NegationExpr (std::unique_ptr negated_value, ExprType expr_kind, std::vector outer_attribs, location_t locus) : OperatorExpr (std::move (negated_value), std::move (outer_attribs), locus), expr_type (expr_kind) {} void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_negated_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ NegationExpr *clone_expr_without_block_impl () const override { return new NegationExpr (*this); } }; // Infix binary operators. +, -, *, /, %, &, |, ^, <<, >> class ArithmeticOrLogicalExpr : public OperatorExpr { public: using ExprType = ArithmeticOrLogicalOperator; private: // Note: overloading trait specified in comments ExprType expr_type; std::unique_ptr right_expr; public: std::string as_string () const override; ExprType get_expr_type () const { return expr_type; } // Constructor calls OperatorExpr's protected constructor ArithmeticOrLogicalExpr (std::unique_ptr left_value, std::unique_ptr right_value, ExprType expr_kind, location_t locus) : OperatorExpr (std::move (left_value), std::vector (), locus), expr_type (expr_kind), right_expr (std::move (right_value)) {} // outer attributes not allowed // Copy constructor - probably required due to unique pointer ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other) : OperatorExpr (other), expr_type (other.expr_type), right_expr (other.right_expr->clone_expr ()) {} // Overload assignment operator ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr const &other) { OperatorExpr::operator= (other); // main_or_left_expr = other.main_or_left_expr->clone_expr(); right_expr = other.right_expr->clone_expr (); expr_type = other.expr_type; return *this; } // move constructors ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr &&other) = default; ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_left_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } std::unique_ptr &get_left_expr_ptr () { rust_assert (main_or_left_expr != nullptr); return main_or_left_expr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_right_expr () { rust_assert (right_expr != nullptr); return *right_expr; } std::unique_ptr &get_right_expr_ptr () { rust_assert (right_expr != nullptr); return right_expr; } void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); } void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override { return new ArithmeticOrLogicalExpr (*this); } }; // Infix binary comparison operators. ==, !=, <, <=, >, >= class ComparisonExpr : public OperatorExpr { public: using ExprType = ComparisonOperator; private: // Note: overloading trait specified in comments ExprType expr_type; std::unique_ptr right_expr; public: std::string as_string () const override; ExprType get_expr_type () const { return expr_type; } // Constructor requires pointers for polymorphism ComparisonExpr (std::unique_ptr left_value, std::unique_ptr right_value, ExprType comparison_kind, location_t locus) : OperatorExpr (std::move (left_value), std::vector (), locus), expr_type (comparison_kind), right_expr (std::move (right_value)) {} // outer attributes not allowed // Copy constructor also calls OperatorExpr's protected constructor ComparisonExpr (ComparisonExpr const &other) : OperatorExpr (other), expr_type (other.expr_type), right_expr (other.right_expr->clone_expr ()) {} // Overload assignment operator to deep copy ComparisonExpr &operator= (ComparisonExpr const &other) { OperatorExpr::operator= (other); // main_or_left_expr = other.main_or_left_expr->clone_expr(); right_expr = other.right_expr->clone_expr (); expr_type = other.expr_type; // outer_attrs = other.outer_attrs; return *this; } // move constructors ComparisonExpr (ComparisonExpr &&other) = default; ComparisonExpr &operator= (ComparisonExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_left_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } std::unique_ptr &get_left_expr_ptr () { rust_assert (main_or_left_expr != nullptr); return main_or_left_expr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_right_expr () { rust_assert (right_expr != nullptr); return *right_expr; } std::unique_ptr &get_right_expr_ptr () { rust_assert (right_expr != nullptr); return right_expr; } ExprType get_kind () { return expr_type; } /* TODO: implement via a function call to std::cmp::PartialEq::eq(&op1, &op2) * maybe? */ protected: /* Use covariance to implement clone function as returning this object rather * than base */ ComparisonExpr *clone_expr_without_block_impl () const override { return new ComparisonExpr (*this); } }; // Infix binary lazy boolean logical operators && and ||. class LazyBooleanExpr : public OperatorExpr { public: using ExprType = LazyBooleanOperator; private: ExprType expr_type; std::unique_ptr right_expr; public: // Constructor calls OperatorExpr's protected constructor LazyBooleanExpr (std::unique_ptr left_bool_expr, std::unique_ptr right_bool_expr, ExprType expr_kind, location_t locus) : OperatorExpr (std::move (left_bool_expr), std::vector (), locus), expr_type (expr_kind), right_expr (std::move (right_bool_expr)) {} // outer attributes not allowed // Copy constructor also calls OperatorExpr's protected constructor LazyBooleanExpr (LazyBooleanExpr const &other) : OperatorExpr (other), expr_type (other.expr_type), right_expr (other.right_expr->clone_expr ()) {} // Overload assignment operator to deep copy LazyBooleanExpr &operator= (LazyBooleanExpr const &other) { OperatorExpr::operator= (other); // main_or_left_expr = other.main_or_left_expr->clone_expr(); right_expr = other.right_expr->clone_expr (); expr_type = other.expr_type; return *this; } // move constructors LazyBooleanExpr (LazyBooleanExpr &&other) = default; LazyBooleanExpr &operator= (LazyBooleanExpr &&other) = default; std::string as_string () const override; ExprType get_expr_type () const { return expr_type; } void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_left_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } std::unique_ptr &get_left_expr_ptr () { rust_assert (main_or_left_expr != nullptr); return main_or_left_expr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_right_expr () { rust_assert (right_expr != nullptr); return *right_expr; } std::unique_ptr &get_right_expr_ptr () { rust_assert (right_expr != nullptr); return right_expr; } ExprType get_kind () { return expr_type; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ LazyBooleanExpr *clone_expr_without_block_impl () const override { return new LazyBooleanExpr (*this); } }; // Binary infix "as" cast expression. class TypeCastExpr : public OperatorExpr { std::unique_ptr type_to_convert_to; // Note: only certain type casts allowed, outlined in reference public: std::string as_string () const override; // Constructor requires calling protected constructor of OperatorExpr TypeCastExpr (std::unique_ptr expr_to_cast, std::unique_ptr type_to_cast_to, location_t locus) : OperatorExpr (std::move (expr_to_cast), std::vector (), locus), type_to_convert_to (std::move (type_to_cast_to)) {} // outer attributes not allowed // Copy constructor also requires calling protected constructor TypeCastExpr (TypeCastExpr const &other) : OperatorExpr (other), type_to_convert_to (other.type_to_convert_to->clone_type_no_bounds ()) {} // Overload assignment operator to deep copy TypeCastExpr &operator= (TypeCastExpr const &other) { OperatorExpr::operator= (other); // main_or_left_expr = other.main_or_left_expr->clone_expr(); type_to_convert_to = other.type_to_convert_to->clone_type_no_bounds (); return *this; } // move constructors TypeCastExpr (TypeCastExpr &&other) = default; TypeCastExpr &operator= (TypeCastExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_casted_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } // TODO: is this better? Or is a "vis_block" better? TypeNoBounds &get_type_to_cast_to () { rust_assert (type_to_convert_to != nullptr); return *type_to_convert_to; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ TypeCastExpr *clone_expr_without_block_impl () const override { return new TypeCastExpr (*this); } }; // Binary assignment expression. class AssignmentExpr : public OperatorExpr { std::unique_ptr right_expr; public: std::string as_string () const override; // Call OperatorExpr constructor to initialise left_expr AssignmentExpr (std::unique_ptr value_to_assign_to, std::unique_ptr value_to_assign, std::vector outer_attribs, location_t locus) : OperatorExpr (std::move (value_to_assign_to), std::move (outer_attribs), locus), right_expr (std::move (value_to_assign)) {} // outer attributes not allowed // Call OperatorExpr constructor in copy constructor, as well as clone AssignmentExpr (AssignmentExpr const &other) : OperatorExpr (other), right_expr (other.right_expr->clone_expr ()) {} // Overload assignment operator to clone unique_ptr right_expr AssignmentExpr &operator= (AssignmentExpr const &other) { OperatorExpr::operator= (other); // main_or_left_expr = other.main_or_left_expr->clone_expr(); right_expr = other.right_expr->clone_expr (); // outer_attrs = other.outer_attrs; return *this; } // move constructors AssignmentExpr (AssignmentExpr &&other) = default; AssignmentExpr &operator= (AssignmentExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); } void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); } // TODO: is this better? Or is a "vis_block" better? Expr &get_left_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } std::unique_ptr &get_left_expr_ptr () { rust_assert (main_or_left_expr != nullptr); return main_or_left_expr; } std::unique_ptr &get_right_expr_ptr () { rust_assert (right_expr != nullptr); return right_expr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_right_expr () { rust_assert (right_expr != nullptr); return *right_expr; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ AssignmentExpr *clone_expr_without_block_impl () const override { return new AssignmentExpr (*this); } }; /* Binary infix compound assignment (arithmetic or logic then assignment) * expressions. */ class CompoundAssignmentExpr : public OperatorExpr { public: using ExprType = CompoundAssignmentOperator; private: // Note: overloading trait specified in comments ExprType expr_type; std::unique_ptr right_expr; public: std::string as_string () const override; ExprType get_expr_type () const { return expr_type; } // Use pointers in constructor to enable polymorphism CompoundAssignmentExpr (std::unique_ptr value_to_assign_to, std::unique_ptr value_to_assign, ExprType expr_kind, location_t locus) : OperatorExpr (std::move (value_to_assign_to), std::vector (), locus), expr_type (expr_kind), right_expr (std::move (value_to_assign)) {} // outer attributes not allowed // Have clone in copy constructor CompoundAssignmentExpr (CompoundAssignmentExpr const &other) : OperatorExpr (other), expr_type (other.expr_type), right_expr (other.right_expr->clone_expr ()) {} // Overload assignment operator to clone CompoundAssignmentExpr &operator= (CompoundAssignmentExpr const &other) { OperatorExpr::operator= (other); // main_or_left_expr = other.main_or_left_expr->clone_expr(); right_expr = other.right_expr->clone_expr (); expr_type = other.expr_type; // outer_attrs = other.outer_attrs; return *this; } // move constructors CompoundAssignmentExpr (CompoundAssignmentExpr &&other) = default; CompoundAssignmentExpr &operator= (CompoundAssignmentExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_left_expr () { rust_assert (main_or_left_expr != nullptr); return *main_or_left_expr; } std::unique_ptr &get_left_expr_ptr () { rust_assert (main_or_left_expr != nullptr); return main_or_left_expr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_right_expr () { rust_assert (right_expr != nullptr); return *right_expr; } std::unique_ptr &get_right_expr_ptr () { rust_assert (right_expr != nullptr); return right_expr; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ CompoundAssignmentExpr *clone_expr_without_block_impl () const override { return new CompoundAssignmentExpr (*this); } }; // Expression in parentheses (i.e. like literally just any 3 + (2 * 6)) class GroupedExpr : public ExprWithoutBlock { std::vector outer_attrs; std::vector inner_attrs; std::unique_ptr expr_in_parens; location_t locus; public: std::string as_string () const override; const std::vector &get_inner_attrs () const { return inner_attrs; } std::vector &get_inner_attrs () { return inner_attrs; } 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); } GroupedExpr (std::unique_ptr parenthesised_expr, std::vector inner_attribs, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), inner_attrs (std::move (inner_attribs)), expr_in_parens (std::move (parenthesised_expr)), locus (locus) {} // Copy constructor includes clone for expr_in_parens GroupedExpr (GroupedExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), inner_attrs (other.inner_attrs), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.expr_in_parens != nullptr) expr_in_parens = other.expr_in_parens->clone_expr (); } // Overloaded assignment operator to clone expr_in_parens GroupedExpr &operator= (GroupedExpr const &other) { ExprWithoutBlock::operator= (other); inner_attrs = other.inner_attrs; locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.expr_in_parens != nullptr) expr_in_parens = other.expr_in_parens->clone_expr (); else expr_in_parens = nullptr; return *this; } // move constructors GroupedExpr (GroupedExpr &&other) = default; GroupedExpr &operator= (GroupedExpr &&other) = default; location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if inner expr is null, so base stripping on that. void mark_for_strip () override { expr_in_parens = nullptr; } bool is_marked_for_strip () const override { return expr_in_parens == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_expr_in_parens () { rust_assert (expr_in_parens != nullptr); return *expr_in_parens; } std::unique_ptr &get_expr_in_parens_ptr () { rust_assert (expr_in_parens != nullptr); return expr_in_parens; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ GroupedExpr *clone_expr_without_block_impl () const override { return new GroupedExpr (*this); } }; // Base array initialisation internal element representation thing (abstract) // aka ArrayElements class ArrayElems { public: virtual ~ArrayElems () {} // Unique pointer custom clone ArrayElems function std::unique_ptr clone_array_elems () const { return std::unique_ptr (clone_array_elems_impl ()); } virtual std::string as_string () const = 0; virtual void accept_vis (ASTVisitor &vis) = 0; NodeId get_node_id () const { return node_id; } protected: ArrayElems () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} // pure virtual clone implementation virtual ArrayElems *clone_array_elems_impl () const = 0; NodeId node_id; }; // Value array elements class ArrayElemsValues : public ArrayElems { std::vector > values; location_t locus; public: ArrayElemsValues (std::vector > elems, location_t locus) : ArrayElems (), values (std::move (elems)), locus (locus) {} // copy constructor with vector clone ArrayElemsValues (ArrayElemsValues const &other) { values.reserve (other.values.size ()); for (const auto &e : other.values) values.push_back (e->clone_expr ()); } // overloaded assignment operator with vector clone ArrayElemsValues &operator= (ArrayElemsValues const &other) { values.reserve (other.values.size ()); for (const auto &e : other.values) values.push_back (e->clone_expr ()); return *this; } // move constructors ArrayElemsValues (ArrayElemsValues &&other) = default; ArrayElemsValues &operator= (ArrayElemsValues &&other) = default; std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector > &get_values () const { return values; } std::vector > &get_values () { return values; } size_t get_num_values () const { return values.size (); } protected: ArrayElemsValues *clone_array_elems_impl () const override { return new ArrayElemsValues (*this); } }; // Copied array element and number of copies class ArrayElemsCopied : public ArrayElems { std::unique_ptr elem_to_copy; std::unique_ptr num_copies; location_t locus; public: // Constructor requires pointers for polymorphism ArrayElemsCopied (std::unique_ptr copied_elem, std::unique_ptr copy_amount, location_t locus) : ArrayElems (), elem_to_copy (std::move (copied_elem)), num_copies (std::move (copy_amount)), locus (locus) {} // Copy constructor required due to unique_ptr - uses custom clone ArrayElemsCopied (ArrayElemsCopied const &other) : elem_to_copy (other.elem_to_copy->clone_expr ()), num_copies (other.num_copies->clone_expr ()) {} // Overloaded assignment operator for deep copying ArrayElemsCopied &operator= (ArrayElemsCopied const &other) { elem_to_copy = other.elem_to_copy->clone_expr (); num_copies = other.num_copies->clone_expr (); return *this; } // move constructors ArrayElemsCopied (ArrayElemsCopied &&other) = default; ArrayElemsCopied &operator= (ArrayElemsCopied &&other) = default; std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_elem_to_copy () { rust_assert (elem_to_copy != nullptr); return *elem_to_copy; } // TODO: is this better? Or is a "vis_block" better? Expr &get_num_copies () { rust_assert (num_copies != nullptr); return *num_copies; } protected: ArrayElemsCopied *clone_array_elems_impl () const override { return new ArrayElemsCopied (*this); } }; // Array definition-ish expression class ArrayExpr : public ExprWithoutBlock { std::vector outer_attrs; std::vector inner_attrs; std::unique_ptr internal_elements; location_t locus; // TODO: find another way to store this to save memory? bool marked_for_strip = false; public: std::string as_string () const override; const std::vector &get_inner_attrs () const { return inner_attrs; } std::vector &get_inner_attrs () { return inner_attrs; } 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); } // Constructor requires ArrayElems pointer ArrayExpr (std::unique_ptr array_elems, std::vector inner_attribs, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), inner_attrs (std::move (inner_attribs)), internal_elements (std::move (array_elems)), locus (locus) { rust_assert (internal_elements != nullptr); } // Copy constructor requires cloning ArrayElems for polymorphism to hold ArrayExpr (ArrayExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), inner_attrs (other.inner_attrs), locus (other.locus), marked_for_strip (other.marked_for_strip) { internal_elements = other.internal_elements->clone_array_elems (); rust_assert (internal_elements != nullptr); } // Overload assignment operator to clone internal_elements ArrayExpr &operator= (ArrayExpr const &other) { ExprWithoutBlock::operator= (other); inner_attrs = other.inner_attrs; locus = other.locus; marked_for_strip = other.marked_for_strip; outer_attrs = other.outer_attrs; internal_elements = other.internal_elements->clone_array_elems (); rust_assert (internal_elements != nullptr); return *this; } // move constructors ArrayExpr (ArrayExpr &&other) = default; ArrayExpr &operator= (ArrayExpr &&other) = default; location_t 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: is this better? Or is a "vis_block" better? std::unique_ptr &get_array_elems () { rust_assert (internal_elements != nullptr); return internal_elements; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ArrayExpr *clone_expr_without_block_impl () const override { return new ArrayExpr (*this); } }; // Aka IndexExpr (also applies to slices) /* Apparently a[b] is equivalent to *std::ops::Index::index(&a, b) or * *std::ops::Index::index_mut(&mut a, b) */ /* Also apparently deref operations on a will be repeatedly applied to find an * implementation */ class ArrayIndexExpr : public ExprWithoutBlock { std::vector outer_attrs; std::unique_ptr array_expr; std::unique_ptr index_expr; location_t locus; public: std::string as_string () const override; ArrayIndexExpr (std::unique_ptr array_expr, std::unique_ptr array_index_expr, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), array_expr (std::move (array_expr)), index_expr (std::move (array_index_expr)), locus (locus) {} // Copy constructor requires special cloning due to unique_ptr ArrayIndexExpr (ArrayIndexExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.array_expr != nullptr) array_expr = other.array_expr->clone_expr (); if (other.index_expr != nullptr) index_expr = other.index_expr->clone_expr (); } // Overload assignment operator to clone unique_ptrs ArrayIndexExpr &operator= (ArrayIndexExpr const &other) { ExprWithoutBlock::operator= (other); outer_attrs = other.outer_attrs; locus = other.locus; // guard to prevent null dereference (only required if error state) if (other.array_expr != nullptr) array_expr = other.array_expr->clone_expr (); else array_expr = nullptr; if (other.index_expr != nullptr) index_expr = other.index_expr->clone_expr (); else index_expr = nullptr; return *this; } // move constructors ArrayIndexExpr (ArrayIndexExpr &&other) = default; ArrayIndexExpr &operator= (ArrayIndexExpr &&other) = default; location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if either expr is null, so base stripping on that. void mark_for_strip () override { array_expr = nullptr; index_expr = nullptr; } bool is_marked_for_strip () const override { return array_expr == nullptr && index_expr == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_array_expr () { rust_assert (array_expr != nullptr); return *array_expr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_index_expr () { rust_assert (index_expr != nullptr); return *index_expr; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ArrayIndexExpr *clone_expr_without_block_impl () const override { return new ArrayIndexExpr (*this); } }; // AST representation of a tuple class TupleExpr : public ExprWithoutBlock { std::vector outer_attrs; std::vector inner_attrs; std::vector > tuple_elems; location_t locus; // TODO: find another way to store this to save memory? bool marked_for_strip = false; public: std::string as_string () const override; const std::vector &get_inner_attrs () const { return inner_attrs; } std::vector &get_inner_attrs () { return inner_attrs; } 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); } TupleExpr (std::vector > tuple_elements, std::vector inner_attribs, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), inner_attrs (std::move (inner_attribs)), tuple_elems (std::move (tuple_elements)), locus (locus) {} // copy constructor with vector clone TupleExpr (TupleExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), inner_attrs (other.inner_attrs), locus (other.locus), marked_for_strip (other.marked_for_strip) { tuple_elems.reserve (other.tuple_elems.size ()); for (const auto &e : other.tuple_elems) tuple_elems.push_back (e->clone_expr ()); } // overloaded assignment operator to vector clone TupleExpr &operator= (TupleExpr const &other) { ExprWithoutBlock::operator= (other); outer_attrs = other.outer_attrs; inner_attrs = other.inner_attrs; locus = other.locus; marked_for_strip = other.marked_for_strip; tuple_elems.reserve (other.tuple_elems.size ()); for (const auto &e : other.tuple_elems) tuple_elems.push_back (e->clone_expr ()); return *this; } // move constructors TupleExpr (TupleExpr &&other) = default; TupleExpr &operator= (TupleExpr &&other) = default; /* Note: syntactically, can disambiguate single-element tuple from parens with * comma, i.e. (0,) rather than (0) */ location_t 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: this mutable getter seems really dodgy. Think up better way. const std::vector > &get_tuple_elems () const { return tuple_elems; } std::vector > &get_tuple_elems () { return tuple_elems; } bool is_unit () const { return tuple_elems.size () == 0; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ TupleExpr *clone_expr_without_block_impl () const override { return new TupleExpr (*this); } }; // aka TupleIndexingExpr // AST representation of a tuple indexing expression class TupleIndexExpr : public ExprWithoutBlock { std::vector outer_attrs; std::unique_ptr tuple_expr; // TupleIndex is a decimal int literal with no underscores or suffix TupleIndex tuple_index; location_t locus; // i.e. pair.0 public: std::string as_string () const override; TupleIndex get_tuple_index () const { return tuple_index; } TupleIndexExpr (std::unique_ptr tuple_expr, TupleIndex index, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus) {} // Copy constructor requires a clone for tuple_expr TupleIndexExpr (TupleIndexExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), tuple_index (other.tuple_index), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.tuple_expr != nullptr) tuple_expr = other.tuple_expr->clone_expr (); } // Overload assignment operator in order to clone TupleIndexExpr &operator= (TupleIndexExpr const &other) { ExprWithoutBlock::operator= (other); tuple_index = other.tuple_index; locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.tuple_expr != nullptr) tuple_expr = other.tuple_expr->clone_expr (); else tuple_expr = nullptr; return *this; } // move constructors TupleIndexExpr (TupleIndexExpr &&other) = default; TupleIndexExpr &operator= (TupleIndexExpr &&other) = default; location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if tuple expr is null, so base stripping on that. void mark_for_strip () override { tuple_expr = nullptr; } bool is_marked_for_strip () const override { return tuple_expr == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_tuple_expr () { rust_assert (tuple_expr != nullptr); return *tuple_expr; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ TupleIndexExpr *clone_expr_without_block_impl () const override { return new TupleIndexExpr (*this); } }; // Base struct/tuple/union value creator AST node (abstract) class StructExpr : public ExprWithoutBlock { std::vector outer_attrs; PathInExpression struct_name; protected: // Protected constructor to allow initialising struct_name StructExpr (PathInExpression struct_path, std::vector outer_attribs) : outer_attrs (std::move (outer_attribs)), struct_name (std::move (struct_path)) {} public: const PathInExpression &get_struct_name () const { return struct_name; } PathInExpression &get_struct_name () { return struct_name; } std::string as_string () const override; // Invalid if path is empty, so base stripping on that. void mark_for_strip () override { struct_name = PathInExpression::create_error (); } bool is_marked_for_strip () const override { return struct_name.is_error (); } 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); } }; // Actual AST node of the struct creator (with no fields). Not abstract! class StructExprStruct : public StructExpr { std::vector inner_attrs; location_t locus; public: std::string as_string () const override; const std::vector &get_inner_attrs () const { return inner_attrs; } std::vector &get_inner_attrs () { return inner_attrs; } // Constructor has to call protected constructor of base class StructExprStruct (PathInExpression struct_path, std::vector inner_attribs, std::vector outer_attribs, location_t locus) : StructExpr (std::move (struct_path), std::move (outer_attribs)), inner_attrs (std::move (inner_attribs)), locus (locus) {} location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; protected: /* Use covariance to implement clone function as returning this object rather * than base */ StructExprStruct *clone_expr_without_block_impl () const override { return new StructExprStruct (*this); } }; /* AST node representing expression used to fill a struct's fields from another * struct */ struct StructBase { private: std::unique_ptr base_struct; location_t locus; public: StructBase (std::unique_ptr base_struct_ptr, location_t locus) : base_struct (std::move (base_struct_ptr)), locus (locus) {} // Copy constructor requires clone StructBase (StructBase const &other) { /* HACK: gets around base_struct pointer being null (e.g. if no struct base * exists) */ if (other.base_struct != nullptr) base_struct = other.base_struct->clone_expr (); } // Destructor ~StructBase () = default; // Overload assignment operator to clone base_struct StructBase &operator= (StructBase const &other) { // prevent null pointer dereference if (other.base_struct != nullptr) base_struct = other.base_struct->clone_expr (); else base_struct = nullptr; return *this; } // move constructors StructBase (StructBase &&other) = default; StructBase &operator= (StructBase &&other) = default; // Returns a null expr-ed StructBase - error state static StructBase error () { return StructBase (nullptr, UNDEF_LOCATION); } // Returns whether StructBase is in error state bool is_invalid () const { return base_struct == nullptr; } std::string as_string () const; // TODO: is this better? Or is a "vis_block" better? Expr &get_base_struct () { rust_assert (base_struct != nullptr); return *base_struct; } }; /* Base AST node for a single struct expression field (in struct instance * creation) - abstract */ class StructExprField { public: virtual ~StructExprField () {} // Unique pointer custom clone function std::unique_ptr clone_struct_expr_field () const { return std::unique_ptr (clone_struct_expr_field_impl ()); } virtual std::string as_string () const = 0; virtual void accept_vis (ASTVisitor &vis) = 0; virtual location_t get_locus () const = 0; NodeId get_node_id () const { return node_id; } protected: // pure virtual clone implementation virtual StructExprField *clone_struct_expr_field_impl () const = 0; StructExprField () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} NodeId node_id; }; // Identifier-only variant of StructExprField AST node class StructExprFieldIdentifier : public StructExprField { Identifier field_name; location_t locus; public: StructExprFieldIdentifier (Identifier field_identifier, location_t locus) : StructExprField (), field_name (std::move (field_identifier)), locus (locus) {} std::string as_string () const override { return field_name.as_string (); } location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; Identifier get_field_name () const { return field_name; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ StructExprFieldIdentifier *clone_struct_expr_field_impl () const override { return new StructExprFieldIdentifier (*this); } }; /* Base AST node for a single struct expression field with an assigned value - * abstract */ class StructExprFieldWithVal : public StructExprField { std::unique_ptr value; protected: StructExprFieldWithVal (std::unique_ptr field_value) : StructExprField (), value (std::move (field_value)) {} // Copy constructor requires clone StructExprFieldWithVal (StructExprFieldWithVal const &other) : value (other.value->clone_expr ()) {} // Overload assignment operator to clone unique_ptr StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other) { value = other.value->clone_expr (); return *this; } // move constructors StructExprFieldWithVal (StructExprFieldWithVal &&other) = default; StructExprFieldWithVal &operator= (StructExprFieldWithVal &&other) = default; public: std::string as_string () const override; // TODO: is this better? Or is a "vis_block" better? Expr &get_value () { rust_assert (value != nullptr); return *value; } }; // Identifier and value variant of StructExprField AST node class StructExprFieldIdentifierValue : public StructExprFieldWithVal { Identifier field_name; location_t locus; public: StructExprFieldIdentifierValue (Identifier field_identifier, std::unique_ptr field_value, location_t locus) : StructExprFieldWithVal (std::move (field_value)), field_name (std::move (field_identifier)), locus (locus) {} std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; std::string get_field_name () const { return field_name.as_string (); } location_t get_locus () const override final { return locus; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override { return new StructExprFieldIdentifierValue (*this); } }; // Tuple index and value variant of StructExprField AST node class StructExprFieldIndexValue : public StructExprFieldWithVal { TupleIndex index; location_t locus; public: StructExprFieldIndexValue (TupleIndex tuple_index, std::unique_ptr field_value, location_t locus) : StructExprFieldWithVal (std::move (field_value)), index (tuple_index), locus (locus) {} std::string as_string () const override; void accept_vis (ASTVisitor &vis) override; TupleIndex get_index () const { return index; } location_t get_locus () const override final { return locus; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ StructExprFieldIndexValue *clone_struct_expr_field_impl () const override { return new StructExprFieldIndexValue (*this); } }; // AST node of a struct creator with fields class StructExprStructFields : public StructExprStruct { // std::vector fields; std::vector > fields; // bool has_struct_base; StructBase struct_base; public: std::string as_string () const override; bool has_struct_base () const { return !struct_base.is_invalid (); } // Constructor for StructExprStructFields when no struct base is used StructExprStructFields ( PathInExpression struct_path, std::vector > expr_fields, location_t locus, StructBase base_struct = StructBase::error (), std::vector inner_attribs = std::vector (), std::vector outer_attribs = std::vector ()) : StructExprStruct (std::move (struct_path), std::move (inner_attribs), std::move (outer_attribs), locus), fields (std::move (expr_fields)), struct_base (std::move (base_struct)) {} // copy constructor with vector clone StructExprStructFields (StructExprStructFields const &other) : StructExprStruct (other), struct_base (other.struct_base) { fields.reserve (other.fields.size ()); for (const auto &e : other.fields) fields.push_back (e->clone_struct_expr_field ()); } // overloaded assignment operator with vector clone StructExprStructFields &operator= (StructExprStructFields const &other) { StructExprStruct::operator= (other); struct_base = other.struct_base; fields.reserve (other.fields.size ()); for (const auto &e : other.fields) fields.push_back (e->clone_struct_expr_field ()); return *this; } // move constructors StructExprStructFields (StructExprStructFields &&other) = default; StructExprStructFields &operator= (StructExprStructFields &&other) = default; 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; } StructBase &get_struct_base () { return struct_base; } const StructBase &get_struct_base () const { return struct_base; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ StructExprStructFields *clone_expr_without_block_impl () const override { return new StructExprStructFields (*this); } }; // AST node of the functional update struct creator /* TODO: remove and replace with StructExprStructFields, except with empty * vector of fields? */ class StructExprStructBase : public StructExprStruct { StructBase struct_base; public: std::string as_string () const override; StructExprStructBase (PathInExpression struct_path, StructBase base_struct, std::vector inner_attribs, std::vector outer_attribs, location_t locus) : StructExprStruct (std::move (struct_path), std::move (inner_attribs), std::move (outer_attribs), locus), struct_base (std::move (base_struct)) {} void accept_vis (ASTVisitor &vis) override; StructBase &get_struct_base () { return struct_base; } const StructBase &get_struct_base () const { return struct_base; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ StructExprStructBase *clone_expr_without_block_impl () const override { return new StructExprStructBase (*this); } }; // Forward decl for Function - used in CallExpr class Function; // Function call expression AST node class CallExpr : public ExprWithoutBlock { std::vector outer_attrs; std::unique_ptr function; std::vector > params; location_t locus; public: Function *fndeclRef; std::string as_string () const override; CallExpr (std::unique_ptr function_expr, std::vector > function_params, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), function (std::move (function_expr)), params (std::move (function_params)), locus (locus) {} // copy constructor requires clone CallExpr (CallExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.function != nullptr) function = other.function->clone_expr (); params.reserve (other.params.size ()); for (const auto &e : other.params) params.push_back (e->clone_expr ()); } // Overload assignment operator to clone CallExpr &operator= (CallExpr const &other) { ExprWithoutBlock::operator= (other); locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.function != nullptr) function = other.function->clone_expr (); else function = nullptr; params.reserve (other.params.size ()); for (const auto &e : other.params) params.push_back (e->clone_expr ()); return *this; } // move constructors CallExpr (CallExpr &&other) = default; CallExpr &operator= (CallExpr &&other) = default; // Returns whether function call has parameters. bool has_params () const { return !params.empty (); } location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if function expr is null, so base stripping on that. void mark_for_strip () override { function = nullptr; } bool is_marked_for_strip () const override { return function == nullptr; } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector > &get_params () const { return params; } std::vector > &get_params () { return params; } // TODO: is this better? Or is a "vis_block" better? Expr &get_function_expr () { rust_assert (function != nullptr); return *function; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ CallExpr *clone_expr_without_block_impl () const override { return new CallExpr (*this); } }; // Method call expression AST node class MethodCallExpr : public ExprWithoutBlock { std::vector outer_attrs; std::unique_ptr receiver; PathExprSegment method_name; std::vector > params; location_t locus; public: std::string as_string () const override; MethodCallExpr (std::unique_ptr call_receiver, PathExprSegment method_path, std::vector > method_params, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), receiver (std::move (call_receiver)), method_name (std::move (method_path)), params (std::move (method_params)), locus (locus) {} // copy constructor required due to cloning MethodCallExpr (MethodCallExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), method_name (other.method_name), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.receiver != nullptr) receiver = other.receiver->clone_expr (); params.reserve (other.params.size ()); for (const auto &e : other.params) params.push_back (e->clone_expr ()); } // Overload assignment operator to clone receiver object MethodCallExpr &operator= (MethodCallExpr const &other) { ExprWithoutBlock::operator= (other); method_name = other.method_name; locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.receiver != nullptr) receiver = other.receiver->clone_expr (); else receiver = nullptr; params.reserve (other.params.size ()); for (const auto &e : other.params) params.push_back (e->clone_expr ()); return *this; } // move constructors MethodCallExpr (MethodCallExpr &&other) = default; MethodCallExpr &operator= (MethodCallExpr &&other) = default; location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if receiver expr is null, so base stripping on that. void mark_for_strip () override { receiver = nullptr; } bool is_marked_for_strip () const override { return receiver == nullptr; } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector > &get_params () const { return params; } std::vector > &get_params () { return params; } // TODO: is this better? Or is a "vis_block" better? Expr &get_receiver_expr () { rust_assert (receiver != nullptr); return *receiver; } const PathExprSegment &get_method_name () const { return method_name; } PathExprSegment &get_method_name () { return method_name; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ MethodCallExpr *clone_expr_without_block_impl () const override { return new MethodCallExpr (*this); } }; // aka FieldExpression // Struct or union field access expression AST node class FieldAccessExpr : public ExprWithoutBlock { std::vector outer_attrs; std::unique_ptr receiver; Identifier field; location_t locus; public: std::string as_string () const override; FieldAccessExpr (std::unique_ptr field_access_receiver, Identifier field_name, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), receiver (std::move (field_access_receiver)), field (std::move (field_name)), locus (locus) {} // Copy constructor required due to unique_ptr cloning FieldAccessExpr (FieldAccessExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), field (other.field), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.receiver != nullptr) receiver = other.receiver->clone_expr (); } // Overload assignment operator to clone unique_ptr FieldAccessExpr &operator= (FieldAccessExpr const &other) { ExprWithoutBlock::operator= (other); field = other.field; locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.receiver != nullptr) receiver = other.receiver->clone_expr (); else receiver = nullptr; return *this; } // move constructors FieldAccessExpr (FieldAccessExpr &&other) = default; FieldAccessExpr &operator= (FieldAccessExpr &&other) = default; location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if receiver expr is null, so base stripping on that. void mark_for_strip () override { receiver = nullptr; } bool is_marked_for_strip () const override { return receiver == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_receiver_expr () { rust_assert (receiver != nullptr); return *receiver; } Identifier get_field_name () const { return field; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ FieldAccessExpr *clone_expr_without_block_impl () const override { return new FieldAccessExpr (*this); } }; // Closure parameter data structure struct ClosureParam { private: std::vector outer_attrs; std::unique_ptr pattern; std::unique_ptr type; location_t locus; public: // Returns whether the type of the parameter has been given. bool has_type_given () const { return type != nullptr; } bool has_outer_attrs () const { return !outer_attrs.empty (); } // Constructor for closure parameter ClosureParam (std::unique_ptr param_pattern, location_t locus, std::unique_ptr param_type = nullptr, std::vector outer_attrs = {}) : outer_attrs (std::move (outer_attrs)), pattern (std::move (param_pattern)), type (std::move (param_type)), locus (locus) {} // Copy constructor required due to cloning as a result of unique_ptrs ClosureParam (ClosureParam const &other) : outer_attrs (other.outer_attrs) { // guard to protect from null pointer dereference if (other.pattern != nullptr) pattern = other.pattern->clone_pattern (); if (other.type != nullptr) type = other.type->clone_type (); } ~ClosureParam () = default; // Assignment operator must be overloaded to clone as well ClosureParam &operator= (ClosureParam const &other) { outer_attrs = other.outer_attrs; // guard to protect from null pointer dereference if (other.pattern != nullptr) pattern = other.pattern->clone_pattern (); else pattern = nullptr; if (other.type != nullptr) type = other.type->clone_type (); else type = nullptr; return *this; } // move constructors ClosureParam (ClosureParam &&other) = default; ClosureParam &operator= (ClosureParam &&other) = default; // Returns whether closure parameter is in an error state. bool is_error () const { return pattern == nullptr; } // Creates an error state closure parameter. static ClosureParam create_error () { return ClosureParam (nullptr, UNDEF_LOCATION); } std::string as_string () const; const std::vector &get_outer_attrs () const { return outer_attrs; } std::vector &get_outer_attrs () { return outer_attrs; } Pattern &get_pattern () { rust_assert (pattern != nullptr); return *pattern; } Type &get_type () { rust_assert (has_type_given ()); return *type; } std::unique_ptr &get_type_ptr () { rust_assert (has_type_given ()); return type; } location_t get_locus () const { return locus; } }; // Base closure definition expression AST node - abstract class ClosureExpr : public ExprWithoutBlock { std::vector outer_attrs; bool has_move; std::vector params; // may be empty location_t locus; protected: ClosureExpr (std::vector closure_params, bool has_move, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), has_move (has_move), params (std::move (closure_params)), locus (locus) {} public: std::string as_string () const override; location_t get_locus () const override final { return locus; } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector &get_params () const { return params; } std::vector &get_params () { return params; } 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); } bool get_has_move () const { return has_move; } }; // Represents a non-type-specified closure expression AST node class ClosureExprInner : public ClosureExpr { std::unique_ptr closure_inner; public: std::string as_string () const override; // Constructor for a ClosureExprInner ClosureExprInner (std::unique_ptr closure_inner_expr, std::vector closure_params, location_t locus, bool is_move = false, std::vector outer_attribs = std::vector ()) : ClosureExpr (std::move (closure_params), is_move, std::move (outer_attribs), locus), closure_inner (std::move (closure_inner_expr)) {} // Copy constructor must be defined to allow copying via cloning of unique_ptr ClosureExprInner (ClosureExprInner const &other) : ClosureExpr (other) { // guard to prevent null dereference (only required if error state) if (other.closure_inner != nullptr) closure_inner = other.closure_inner->clone_expr (); } // Overload assignment operator to clone closure_inner ClosureExprInner &operator= (ClosureExprInner const &other) { ClosureExpr::operator= (other); // params = other.params; // has_move = other.has_move; // outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.closure_inner != nullptr) closure_inner = other.closure_inner->clone_expr (); else closure_inner = nullptr; return *this; } // move constructors ClosureExprInner (ClosureExprInner &&other) = default; ClosureExprInner &operator= (ClosureExprInner &&other) = default; void accept_vis (ASTVisitor &vis) override; // Invalid if inner expr is null, so base stripping on that. void mark_for_strip () override { closure_inner = nullptr; } bool is_marked_for_strip () const override { return closure_inner == nullptr; } Expr &get_definition_expr () { rust_assert (closure_inner != nullptr); return *closure_inner; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ClosureExprInner *clone_expr_without_block_impl () const override { return new ClosureExprInner (*this); } }; // A block AST node class BlockExpr : public ExprWithBlock { std::vector outer_attrs; std::vector inner_attrs; std::vector > statements; std::unique_ptr expr; LoopLabel label; location_t start_locus; location_t end_locus; bool marked_for_strip = false; public: std::string as_string () const override; // Returns whether the block contains statements. bool has_statements () const { return !statements.empty (); } // Returns whether the block contains a final expression. bool has_tail_expr () const { return expr != nullptr; } BlockExpr (std::vector > block_statements, std::unique_ptr block_expr, std::vector inner_attribs, std::vector outer_attribs, LoopLabel label, location_t start_locus, location_t end_locus) : outer_attrs (std::move (outer_attribs)), inner_attrs (std::move (inner_attribs)), statements (std::move (block_statements)), expr (std::move (block_expr)), label (std::move (label)), start_locus (start_locus), end_locus (end_locus) {} // Copy constructor with clone BlockExpr (BlockExpr const &other) : ExprWithBlock (other), outer_attrs (other.outer_attrs), inner_attrs (other.inner_attrs), label (other.label), start_locus (other.start_locus), end_locus (other.end_locus), marked_for_strip (other.marked_for_strip) { // guard to protect from null pointer dereference if (other.expr != nullptr) expr = other.expr->clone_expr (); statements.reserve (other.statements.size ()); for (const auto &e : other.statements) statements.push_back (e->clone_stmt ()); } // Overloaded assignment operator to clone pointer BlockExpr &operator= (BlockExpr const &other) { ExprWithBlock::operator= (other); inner_attrs = other.inner_attrs; start_locus = other.start_locus; end_locus = other.end_locus; marked_for_strip = other.marked_for_strip; outer_attrs = other.outer_attrs; // guard to protect from null pointer dereference if (other.expr != nullptr) expr = other.expr->clone_expr (); else expr = nullptr; statements.reserve (other.statements.size ()); for (const auto &e : other.statements) statements.push_back (e->clone_stmt ()); return *this; } // move constructors BlockExpr (BlockExpr &&other) = default; BlockExpr &operator= (BlockExpr &&other) = default; // Unique pointer custom clone function std::unique_ptr clone_block_expr () const { return std::unique_ptr (clone_block_expr_impl ()); } location_t get_locus () const override final { return start_locus; } location_t get_start_locus () const { return start_locus; } location_t get_end_locus () const { return end_locus; } void accept_vis (ASTVisitor &vis) override; // Can be completely empty, so have to have a separate flag. void mark_for_strip () override { marked_for_strip = true; } bool is_marked_for_strip () const override { return marked_for_strip; } size_t num_statements () const { return statements.size (); } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector &get_inner_attrs () const { return inner_attrs; } std::vector &get_inner_attrs () { return inner_attrs; } const std::vector > &get_statements () const { return statements; } std::vector > &get_statements () { return statements; } // TODO: is this better? Or is a "vis_block" better? Expr &get_tail_expr () { rust_assert (has_tail_expr ()); return *expr; } std::unique_ptr &get_tail_expr_ptr () { rust_assert (has_tail_expr ()); return expr; } std::unique_ptr take_tail_expr () { rust_assert (has_tail_expr ()); return std::move (expr); } void set_tail_expr (std::unique_ptr expr) { this->expr = std::move (expr); } // Removes the tail expression from the block. void strip_tail_expr () { expr = nullptr; } // Normalizes a trailing statement without a semicolon to a tail expression. void normalize_tail_expr (); void try_convert_last_stmt (); 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); } bool has_label () { return !label.is_error (); } LoopLabel &get_label () { return label; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ BlockExpr *clone_expr_with_block_impl () const final override { return clone_block_expr_impl (); } /* This is the base method as not an abstract class - not virtual but could be * in future if required. */ /*virtual*/ BlockExpr *clone_block_expr_impl () const { return new BlockExpr (*this); } }; // Represents a type-specified closure expression AST node class ClosureExprInnerTyped : public ClosureExpr { // TODO: spec says typenobounds std::unique_ptr return_type; std::unique_ptr expr; // only used because may be polymorphic in future public: std::string as_string () const override; // Constructor potentially with a move ClosureExprInnerTyped (std::unique_ptr closure_return_type, std::unique_ptr closure_expr, std::vector closure_params, location_t locus, bool is_move = false, std::vector outer_attribs = std::vector ()) : ClosureExpr (std::move (closure_params), is_move, std::move (outer_attribs), locus), return_type (std::move (closure_return_type)), expr (std::move (closure_expr)) {} // Copy constructor requires cloning ClosureExprInnerTyped (ClosureExprInnerTyped const &other) : ClosureExpr (other) { // guard to prevent null dereference (only required if error state) if (other.expr != nullptr) expr = other.expr->clone_block_expr (); if (other.return_type != nullptr) return_type = other.return_type->clone_type (); } // Overload assignment operator to clone unique_ptrs ClosureExprInnerTyped &operator= (ClosureExprInnerTyped const &other) { ClosureExpr::operator= (other); // params = other.params; // has_move = other.has_move; // outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.expr != nullptr) expr = other.expr->clone_block_expr (); else expr = nullptr; if (other.return_type != nullptr) return_type = other.return_type->clone_type (); else return_type = nullptr; return *this; } // move constructors ClosureExprInnerTyped (ClosureExprInnerTyped &&other) = default; ClosureExprInnerTyped &operator= (ClosureExprInnerTyped &&other) = default; void accept_vis (ASTVisitor &vis) override; /* Invalid if inner expr is null, so base stripping on that. Technically, * type should also not be null. */ void mark_for_strip () override { expr = nullptr; } bool is_marked_for_strip () const override { return expr == nullptr; } // TODO: is this better? Or is a "vis_block" better? BlockExpr &get_definition_block () { rust_assert (expr != nullptr); return *expr; } // TODO: is this better? Or is a "vis_block" better? Type &get_return_type () { rust_assert (return_type != nullptr); return *return_type; } std::unique_ptr &get_return_type_ptr () { rust_assert (return_type != nullptr); return return_type; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ClosureExprInnerTyped *clone_expr_without_block_impl () const override { return new ClosureExprInnerTyped (*this); } }; // AST node representing continue expression within loops class ContinueExpr : public ExprWithoutBlock { std::vector outer_attrs; Lifetime label; location_t locus; // TODO: find another way to store this to save memory? bool marked_for_strip = false; public: std::string as_string () const override; // Returns true if the continue expr has a label. bool has_label () const { return !label.is_error (); } // Constructor for a ContinueExpr with a label. ContinueExpr (Lifetime label, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), label (std::move (label)), locus (locus) {} location_t 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; } 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); } Lifetime &get_label () { return label; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ContinueExpr *clone_expr_without_block_impl () const override { return new ContinueExpr (*this); } }; // TODO: merge "break" and "continue"? Or even merge in "return"? // AST node representing break expression within loops class BreakExpr : public ExprWithoutBlock { std::vector outer_attrs; LoopLabel label; std::unique_ptr break_expr; location_t 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 the break expression has a label or not. bool has_label () const { return !label.is_error (); } /* Returns whether the break expression has an expression used in the break or * not. */ bool has_break_expr () const { return break_expr != nullptr; } // Constructor for a break expression BreakExpr (LoopLabel break_label, std::unique_ptr expr_in_break, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)), break_expr (std::move (expr_in_break)), locus (locus) {} // Copy constructor defined to use clone for unique pointer BreakExpr (BreakExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), label (other.label), locus (other.locus), marked_for_strip (other.marked_for_strip) { // guard to protect from null pointer dereference if (other.break_expr != nullptr) break_expr = other.break_expr->clone_expr (); } // Overload assignment operator to clone unique pointer BreakExpr &operator= (BreakExpr const &other) { ExprWithoutBlock::operator= (other); label = other.label; locus = other.locus; marked_for_strip = other.marked_for_strip; outer_attrs = other.outer_attrs; // guard to protect from null pointer dereference if (other.break_expr != nullptr) break_expr = other.break_expr->clone_expr (); else break_expr = nullptr; return *this; } // move constructors BreakExpr (BreakExpr &&other) = default; BreakExpr &operator= (BreakExpr &&other) = default; location_t 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: is this better? Or is a "vis_block" better? Expr &get_break_expr () { rust_assert (has_break_expr ()); return *break_expr; } 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); } LoopLabel &get_label () { return label; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ BreakExpr *clone_expr_without_block_impl () const override { return new BreakExpr (*this); } }; // Base range expression AST node object - abstract class RangeExpr : public ExprWithoutBlock { location_t locus; protected: // outer attributes not allowed before range expressions RangeExpr (location_t locus) : locus (locus) {} public: location_t get_locus () const override final { return locus; } std::vector &get_outer_attrs () override final { // RangeExpr cannot have any outer attributes rust_assert (false); } // should never be called - error if called void set_outer_attrs (std::vector /* new_attrs */) override { rust_assert (false); } }; // Range from (inclusive) and to (exclusive) expression AST node object // aka RangeExpr; constructs a std::ops::Range object class RangeFromToExpr : public RangeExpr { std::unique_ptr from; std::unique_ptr to; public: std::string as_string () const override; RangeFromToExpr (std::unique_ptr range_from, std::unique_ptr range_to, location_t locus) : RangeExpr (locus), from (std::move (range_from)), to (std::move (range_to)) {} // Copy constructor with cloning RangeFromToExpr (RangeFromToExpr const &other) : RangeExpr (other) { // guard to prevent null dereference (only required if error state) if (other.from != nullptr) from = other.from->clone_expr (); if (other.to != nullptr) to = other.to->clone_expr (); } // Overload assignment operator to clone unique pointers RangeFromToExpr &operator= (RangeFromToExpr const &other) { RangeExpr::operator= (other); // guard to prevent null dereference (only required if error state) if (other.from != nullptr) from = other.from->clone_expr (); else from = nullptr; if (other.to != nullptr) to = other.to->clone_expr (); else to = nullptr; return *this; } // move constructors RangeFromToExpr (RangeFromToExpr &&other) = default; RangeFromToExpr &operator= (RangeFromToExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // Invalid if either expr is null, so base stripping on that. void mark_for_strip () override { from = nullptr; to = nullptr; } bool is_marked_for_strip () const override { return from == nullptr && to == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_from_expr () { rust_assert (from != nullptr); return *from; } // TODO: is this better? Or is a "vis_block" better? Expr &get_to_expr () { rust_assert (to != nullptr); return *to; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ RangeFromToExpr *clone_expr_without_block_impl () const override { return new RangeFromToExpr (*this); } }; // Range from (inclusive) expression AST node object // constructs a std::ops::RangeFrom object class RangeFromExpr : public RangeExpr { std::unique_ptr from; public: std::string as_string () const override; RangeFromExpr (std::unique_ptr range_from, location_t locus) : RangeExpr (locus), from (std::move (range_from)) {} // Copy constructor with clone RangeFromExpr (RangeFromExpr const &other) : RangeExpr (other) { // guard to prevent null dereference (only required if error state) if (other.from != nullptr) from = other.from->clone_expr (); } // Overload assignment operator to clone unique_ptr RangeFromExpr &operator= (RangeFromExpr const &other) { RangeExpr::operator= (other); // guard to prevent null dereference (only required if error state) if (other.from != nullptr) from = other.from->clone_expr (); else from = nullptr; return *this; } // move constructors RangeFromExpr (RangeFromExpr &&other) = default; RangeFromExpr &operator= (RangeFromExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // Invalid if expr is null, so base stripping on that. void mark_for_strip () override { from = nullptr; } bool is_marked_for_strip () const override { return from == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_from_expr () { rust_assert (from != nullptr); return *from; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ RangeFromExpr *clone_expr_without_block_impl () const override { return new RangeFromExpr (*this); } }; // Range to (exclusive) expression AST node object // constructs a std::ops::RangeTo object class RangeToExpr : public RangeExpr { std::unique_ptr to; public: std::string as_string () const override; // outer attributes not allowed RangeToExpr (std::unique_ptr range_to, location_t locus) : RangeExpr (locus), to (std::move (range_to)) {} // Copy constructor with clone RangeToExpr (RangeToExpr const &other) : RangeExpr (other) { // guard to prevent null dereference (only required if error state) if (other.to != nullptr) to = other.to->clone_expr (); } // Overload assignment operator to clone unique_ptr RangeToExpr &operator= (RangeToExpr const &other) { RangeExpr::operator= (other); // guard to prevent null dereference (only required if error state) if (other.to != nullptr) to = other.to->clone_expr (); else to = nullptr; return *this; } // move constructors RangeToExpr (RangeToExpr &&other) = default; RangeToExpr &operator= (RangeToExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // Invalid if expr is null, so base stripping on that. void mark_for_strip () override { to = nullptr; } bool is_marked_for_strip () const override { return to == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_to_expr () { rust_assert (to != nullptr); return *to; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ RangeToExpr *clone_expr_without_block_impl () const override { return new RangeToExpr (*this); } }; // Full range expression AST node object // constructs a std::ops::RangeFull object class RangeFullExpr : public RangeExpr { // TODO: find another way to store this to save memory? bool marked_for_strip = false; public: std::string as_string () const override; RangeFullExpr (location_t locus) : RangeExpr (locus) {} // outer attributes not allowed 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; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ RangeFullExpr *clone_expr_without_block_impl () const override { return new RangeFullExpr (*this); } }; // Range from (inclusive) and to (inclusive) expression AST node object // aka RangeInclusiveExpr; constructs a std::ops::RangeInclusive object class RangeFromToInclExpr : public RangeExpr { std::unique_ptr from; std::unique_ptr to; public: std::string as_string () const override; RangeFromToInclExpr (std::unique_ptr range_from, std::unique_ptr range_to, location_t locus) : RangeExpr (locus), from (std::move (range_from)), to (std::move (range_to)) {} // outer attributes not allowed // Copy constructor with clone RangeFromToInclExpr (RangeFromToInclExpr const &other) : RangeExpr (other) { // guard to prevent null dereference (only required if error state) if (other.from != nullptr) from = other.from->clone_expr (); if (other.to != nullptr) to = other.to->clone_expr (); } // Overload assignment operator to use clone RangeFromToInclExpr &operator= (RangeFromToInclExpr const &other) { RangeExpr::operator= (other); // guard to prevent null dereference (only required if error state) if (other.from != nullptr) from = other.from->clone_expr (); else from = nullptr; if (other.to != nullptr) to = other.to->clone_expr (); else to = nullptr; return *this; } // move constructors RangeFromToInclExpr (RangeFromToInclExpr &&other) = default; RangeFromToInclExpr &operator= (RangeFromToInclExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // Invalid if either expr is null, so base stripping on that. void mark_for_strip () override { from = nullptr; to = nullptr; } bool is_marked_for_strip () const override { return from == nullptr && to == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_from_expr () { rust_assert (from != nullptr); return *from; } // TODO: is this better? Or is a "vis_block" better? Expr &get_to_expr () { rust_assert (to != nullptr); return *to; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ RangeFromToInclExpr *clone_expr_without_block_impl () const override { return new RangeFromToInclExpr (*this); } }; // Range to (inclusive) expression AST node object // aka RangeToInclusiveExpr; constructs a std::ops::RangeToInclusive object class RangeToInclExpr : public RangeExpr { std::unique_ptr to; public: std::string as_string () const override; RangeToInclExpr (std::unique_ptr range_to, location_t locus) : RangeExpr (locus), to (std::move (range_to)) {} // outer attributes not allowed // Copy constructor with clone RangeToInclExpr (RangeToInclExpr const &other) : RangeExpr (other) { // guard to prevent null dereference (only required if error state) if (other.to != nullptr) to = other.to->clone_expr (); } // Overload assignment operator to clone pointer RangeToInclExpr &operator= (RangeToInclExpr const &other) { RangeExpr::operator= (other); // guard to prevent null dereference (only required if error state) if (other.to != nullptr) to = other.to->clone_expr (); else to = nullptr; return *this; } // move constructors RangeToInclExpr (RangeToInclExpr &&other) = default; RangeToInclExpr &operator= (RangeToInclExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // Invalid if expr is null, so base stripping on that. void mark_for_strip () override { to = nullptr; } bool is_marked_for_strip () const override { return to == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_to_expr () { rust_assert (to != nullptr); return *to; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ RangeToInclExpr *clone_expr_without_block_impl () const override { return new RangeToInclExpr (*this); } }; // Return expression AST node representation class ReturnExpr : public ExprWithoutBlock { std::vector outer_attrs; std::unique_ptr return_expr; location_t 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 the object has an expression returned (i.e. not void return * type). */ bool has_returned_expr () const { return return_expr != nullptr; } // Constructor for ReturnExpr. ReturnExpr (std::unique_ptr returned_expr, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), return_expr (std::move (returned_expr)), locus (locus) {} // Copy constructor with clone ReturnExpr (ReturnExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), locus (other.locus), marked_for_strip (other.marked_for_strip) { // guard to protect from null pointer dereference if (other.return_expr != nullptr) return_expr = other.return_expr->clone_expr (); } // Overloaded assignment operator to clone return_expr pointer ReturnExpr &operator= (ReturnExpr const &other) { ExprWithoutBlock::operator= (other); locus = other.locus; marked_for_strip = other.marked_for_strip; outer_attrs = other.outer_attrs; // guard to protect from null pointer dereference if (other.return_expr != nullptr) return_expr = other.return_expr->clone_expr (); else return_expr = nullptr; return *this; } // move constructors ReturnExpr (ReturnExpr &&other) = default; ReturnExpr &operator= (ReturnExpr &&other) = default; location_t 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: is this better? Or is a "vis_block" better? Expr &get_returned_expr () { rust_assert (return_expr != nullptr); return *return_expr; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ReturnExpr *clone_expr_without_block_impl () const override { return new ReturnExpr (*this); } }; // Forward decl - defined in rust-macro.h class MacroInvocation; // An unsafe block AST node class UnsafeBlockExpr : public ExprWithBlock { std::vector outer_attrs; // Or just have it extend BlockExpr std::unique_ptr expr; location_t locus; public: std::string as_string () const override; UnsafeBlockExpr (std::unique_ptr block_expr, std::vector outer_attribs, location_t locus) : outer_attrs (std::move (outer_attribs)), expr (std::move (block_expr)), locus (locus) {} // Copy constructor with clone UnsafeBlockExpr (UnsafeBlockExpr const &other) : ExprWithBlock (other), outer_attrs (other.outer_attrs), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.expr != nullptr) expr = other.expr->clone_block_expr (); } // Overloaded assignment operator to clone UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other) { ExprWithBlock::operator= (other); locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.expr != nullptr) expr = other.expr->clone_block_expr (); else expr = nullptr; return *this; } // move constructors UnsafeBlockExpr (UnsafeBlockExpr &&other) = default; UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default; location_t 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 { expr = nullptr; } bool is_marked_for_strip () const override { return expr == nullptr; } // TODO: is this better? Or is a "vis_block" better? BlockExpr &get_block_expr () { rust_assert (expr != nullptr); return *expr; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ UnsafeBlockExpr *clone_expr_with_block_impl () const override { return new UnsafeBlockExpr (*this); } }; // Base loop expression AST node - aka LoopExpr class BaseLoopExpr : public ExprWithBlock { protected: // protected to allow subclasses better use of them std::vector outer_attrs; LoopLabel loop_label; std::unique_ptr loop_block; private: location_t locus; protected: // Constructor for BaseLoopExpr BaseLoopExpr (std::unique_ptr loop_block, location_t locus, LoopLabel loop_label = LoopLabel::error (), std::vector outer_attribs = std::vector ()) : outer_attrs (std::move (outer_attribs)), loop_label (std::move (loop_label)), loop_block (std::move (loop_block)), locus (locus) {} // Copy constructor for BaseLoopExpr with clone BaseLoopExpr (BaseLoopExpr const &other) : ExprWithBlock (other), outer_attrs (other.outer_attrs), loop_label (other.loop_label), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.loop_block != nullptr) loop_block = other.loop_block->clone_block_expr (); } // Overloaded assignment operator to clone BaseLoopExpr &operator= (BaseLoopExpr const &other) { ExprWithBlock::operator= (other); loop_label = other.loop_label; locus = other.locus; outer_attrs = other.outer_attrs; // guard to prevent null dereference (only required if error state) if (other.loop_block != nullptr) loop_block = other.loop_block->clone_block_expr (); else loop_block = nullptr; return *this; } // move constructors BaseLoopExpr (BaseLoopExpr &&other) = default; BaseLoopExpr &operator= (BaseLoopExpr &&other) = default; public: bool has_loop_label () const { return !loop_label.is_error (); } LoopLabel &get_loop_label () { return loop_label; } location_t get_locus () const override final { return locus; } // Invalid if loop block is null, so base stripping on that. void mark_for_strip () override { loop_block = nullptr; } bool is_marked_for_strip () const override { return loop_block == nullptr; } // TODO: is this better? Or is a "vis_block" better? BlockExpr &get_loop_block () { rust_assert (loop_block != nullptr); return *loop_block; } 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); } }; // 'Loop' expression (i.e. the infinite loop) AST node class LoopExpr : public BaseLoopExpr { public: std::string as_string () const override; // Constructor for LoopExpr LoopExpr (std::unique_ptr loop_block, location_t locus, LoopLabel loop_label = LoopLabel::error (), std::vector outer_attribs = std::vector ()) : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), std::move (outer_attribs)) {} void accept_vis (ASTVisitor &vis) override; protected: /* Use covariance to implement clone function as returning this object rather * than base */ LoopExpr *clone_expr_with_block_impl () const override { return new LoopExpr (*this); } }; // While loop expression AST node (predicate loop) class WhileLoopExpr : public BaseLoopExpr { std::unique_ptr condition; public: std::string as_string () const override; // Constructor for while loop with loop label WhileLoopExpr (std::unique_ptr loop_condition, std::unique_ptr loop_block, location_t locus, LoopLabel loop_label = LoopLabel::error (), std::vector outer_attribs = std::vector ()) : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), std::move (outer_attribs)), condition (std::move (loop_condition)) {} // Copy constructor with clone WhileLoopExpr (WhileLoopExpr const &other) : BaseLoopExpr (other), condition (other.condition->clone_expr ()) {} // Overloaded assignment operator to clone WhileLoopExpr &operator= (WhileLoopExpr const &other) { BaseLoopExpr::operator= (other); condition = other.condition->clone_expr (); // loop_block = other.loop_block->clone_block_expr(); // loop_label = other.loop_label; // outer_attrs = other.outer_attrs; return *this; } // move constructors WhileLoopExpr (WhileLoopExpr &&other) = default; WhileLoopExpr &operator= (WhileLoopExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_predicate_expr () { rust_assert (condition != nullptr); return *condition; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ WhileLoopExpr *clone_expr_with_block_impl () const override { return new WhileLoopExpr (*this); } }; // While let loop expression AST node (predicate pattern loop) class WhileLetLoopExpr : public BaseLoopExpr { // MatchArmPatterns patterns; std::vector > match_arm_patterns; // inlined std::unique_ptr scrutinee; public: std::string as_string () const override; // Constructor with a loop label WhileLetLoopExpr (std::vector > match_arm_patterns, std::unique_ptr scrutinee, std::unique_ptr loop_block, location_t locus, LoopLabel loop_label = LoopLabel::error (), std::vector outer_attribs = std::vector ()) : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label), std::move (outer_attribs)), match_arm_patterns (std::move (match_arm_patterns)), scrutinee (std::move (scrutinee)) {} // Copy constructor with clone WhileLetLoopExpr (WhileLetLoopExpr const &other) : BaseLoopExpr (other), /*match_arm_patterns(other.match_arm_patterns),*/ scrutinee ( other.scrutinee->clone_expr ()) { match_arm_patterns.reserve (other.match_arm_patterns.size ()); for (const auto &e : other.match_arm_patterns) match_arm_patterns.push_back (e->clone_pattern ()); } // Overloaded assignment operator to clone pointers WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other) { BaseLoopExpr::operator= (other); // match_arm_patterns = other.match_arm_patterns; scrutinee = other.scrutinee->clone_expr (); // loop_block = other.loop_block->clone_block_expr(); // loop_label = other.loop_label; // outer_attrs = other.outer_attrs; match_arm_patterns.reserve (other.match_arm_patterns.size ()); for (const auto &e : other.match_arm_patterns) match_arm_patterns.push_back (e->clone_pattern ()); return *this; } // move constructors WhileLetLoopExpr (WhileLetLoopExpr &&other) = default; WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_scrutinee_expr () { rust_assert (scrutinee != nullptr); return *scrutinee; } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector > &get_patterns () const { return match_arm_patterns; } std::vector > &get_patterns () { return match_arm_patterns; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ WhileLetLoopExpr *clone_expr_with_block_impl () const override { return new WhileLetLoopExpr (*this); } }; // For loop expression AST node (iterator loop) class ForLoopExpr : public BaseLoopExpr { std::unique_ptr pattern; std::unique_ptr iterator_expr; public: std::string as_string () const override; // Constructor with loop label ForLoopExpr (std::unique_ptr loop_pattern, std::unique_ptr iterator_expr, std::unique_ptr loop_body, location_t locus, LoopLabel loop_label = LoopLabel::error (), std::vector outer_attribs = std::vector ()) : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label), std::move (outer_attribs)), pattern (std::move (loop_pattern)), iterator_expr (std::move (iterator_expr)) {} // Copy constructor with clone ForLoopExpr (ForLoopExpr const &other) : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()), iterator_expr (other.iterator_expr->clone_expr ()) {} // Overloaded assignment operator to clone ForLoopExpr &operator= (ForLoopExpr const &other) { BaseLoopExpr::operator= (other); pattern = other.pattern->clone_pattern (); iterator_expr = other.iterator_expr->clone_expr (); /*loop_block = other.loop_block->clone_block_expr(); loop_label = other.loop_label; outer_attrs = other.outer_attrs;*/ return *this; } // move constructors ForLoopExpr (ForLoopExpr &&other) = default; ForLoopExpr &operator= (ForLoopExpr &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? Expr &get_iterator_expr () { rust_assert (iterator_expr != nullptr); return *iterator_expr; } // TODO: is this better? Or is a "vis_block" better? Pattern &get_pattern () { rust_assert (pattern != nullptr); return *pattern; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ ForLoopExpr *clone_expr_with_block_impl () const override { return new ForLoopExpr (*this); } }; // forward decl for IfExpr class IfLetExpr; // Base if expression with no "else" or "if let" AST node class IfExpr : public ExprWithBlock { std::vector outer_attrs; std::unique_ptr condition; std::unique_ptr if_block; location_t locus; public: std::string as_string () const override; IfExpr (std::unique_ptr condition, std::unique_ptr if_block, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), condition (std::move (condition)), if_block (std::move (if_block)), locus (locus) {} // outer attributes are never allowed on IfExprs // Copy constructor with clone IfExpr (IfExpr const &other) : ExprWithBlock (other), outer_attrs (other.outer_attrs), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.condition != nullptr) condition = other.condition->clone_expr (); if (other.if_block != nullptr) if_block = other.if_block->clone_block_expr (); } // Overloaded assignment operator to clone expressions IfExpr &operator= (IfExpr const &other) { ExprWithBlock::operator= (other); outer_attrs = other.outer_attrs; locus = other.locus; // guard to prevent null dereference (only required if error state) if (other.condition != nullptr) condition = other.condition->clone_expr (); else condition = nullptr; if (other.if_block != nullptr) if_block = other.if_block->clone_block_expr (); else if_block = nullptr; return *this; } // move constructors IfExpr (IfExpr &&other) = default; IfExpr &operator= (IfExpr &&other) = default; // Unique pointer custom clone function std::unique_ptr clone_if_expr () const { return std::unique_ptr (clone_if_expr_impl ()); } /* Note that multiple "else if"s are handled via nested ASTs rather than a * vector of else ifs - i.e. not like a switch statement. TODO - is this a * better approach? or does it not parse correctly and have downsides? */ location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; void vis_if_condition (ASTVisitor &vis) { condition->accept_vis (vis); } void vis_if_block (ASTVisitor &vis) { if_block->accept_vis (vis); } // TODO: is this better? Or is a "vis_block" better? Expr &get_condition_expr () { rust_assert (condition != nullptr); return *condition; } std::unique_ptr &get_condition_expr_ptr () { rust_assert (condition != nullptr); return condition; } // TODO: is this better? Or is a "vis_block" better? BlockExpr &get_if_block () { rust_assert (if_block != nullptr); return *if_block; } // Invalid if if block or condition is null, so base stripping on that. void mark_for_strip () override { if_block = nullptr; condition = nullptr; } bool is_marked_for_strip () const override { return if_block == nullptr && condition == nullptr; } void set_outer_attrs (std::vector new_attrs) override { outer_attrs = std::move (new_attrs); } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector &get_outer_attrs () const { return outer_attrs; } std::vector &get_outer_attrs () override { return outer_attrs; } protected: // Base clone function but still concrete as concrete base class virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); } /* Use covariance to implement clone function as returning this object rather * than base */ IfExpr *clone_expr_with_block_impl () const final override { return clone_if_expr_impl (); } }; // If expression with an ending "else" expression AST node (trailing) class IfExprConseqElse : public IfExpr { std::unique_ptr else_block; public: std::string as_string () const override; IfExprConseqElse (std::unique_ptr condition, std::unique_ptr if_block, std::unique_ptr else_block, std::vector outer_attrs, location_t locus) : IfExpr (std::move (condition), std::move (if_block), std::move (outer_attrs), locus), else_block (std::move (else_block)) {} // again, outer attributes not allowed // Copy constructor with clone IfExprConseqElse (IfExprConseqElse const &other) : IfExpr (other), else_block (other.else_block->clone_expr_with_block ()) {} // Overloaded assignment operator with cloning IfExprConseqElse &operator= (IfExprConseqElse const &other) { IfExpr::operator= (other); // condition = other.condition->clone_expr(); // if_block = other.if_block->clone_block_expr(); else_block = other.else_block->clone_expr_with_block (); return *this; } // move constructors IfExprConseqElse (IfExprConseqElse &&other) = default; IfExprConseqElse &operator= (IfExprConseqElse &&other) = default; void accept_vis (ASTVisitor &vis) override; void vis_else_block (ASTVisitor &vis) { else_block->accept_vis (vis); } // TODO: is this better? Or is a "vis_block" better? ExprWithBlock &get_else_block () { rust_assert (else_block != nullptr); return *else_block; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ IfExprConseqElse *clone_if_expr_impl () const override { return new IfExprConseqElse (*this); } }; // Basic "if let" expression AST node with no else class IfLetExpr : public ExprWithBlock { std::vector outer_attrs; std::vector > match_arm_patterns; // inlined std::unique_ptr value; std::unique_ptr if_block; location_t locus; public: std::string as_string () const override; IfLetExpr (std::vector > match_arm_patterns, std::unique_ptr value, std::unique_ptr if_block, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), match_arm_patterns (std::move (match_arm_patterns)), value (std::move (value)), if_block (std::move (if_block)), locus (locus) {} // copy constructor with clone IfLetExpr (IfLetExpr const &other) : ExprWithBlock (other), outer_attrs (other.outer_attrs), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.value != nullptr) value = other.value->clone_expr (); if (other.if_block != nullptr) if_block = other.if_block->clone_block_expr (); match_arm_patterns.reserve (other.match_arm_patterns.size ()); for (const auto &e : other.match_arm_patterns) match_arm_patterns.push_back (e->clone_pattern ()); } // overload assignment operator to clone IfLetExpr &operator= (IfLetExpr const &other) { ExprWithBlock::operator= (other); outer_attrs = other.outer_attrs; locus = other.locus; // guard to prevent null dereference (only required if error state) if (other.value != nullptr) value = other.value->clone_expr (); else value = nullptr; if (other.if_block != nullptr) if_block = other.if_block->clone_block_expr (); else if_block = nullptr; match_arm_patterns.reserve (other.match_arm_patterns.size ()); for (const auto &e : other.match_arm_patterns) match_arm_patterns.push_back (e->clone_pattern ()); return *this; } // move constructors IfLetExpr (IfLetExpr &&other) = default; IfLetExpr &operator= (IfLetExpr &&other) = default; // Unique pointer custom clone function std::unique_ptr clone_if_let_expr () const { return std::unique_ptr (clone_if_let_expr_impl ()); } location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if block or value is null, so base stripping on that. void mark_for_strip () override { if_block = nullptr; value = nullptr; } bool is_marked_for_strip () const override { return if_block == nullptr && value == nullptr; } // TODO: is this better? Or is a "vis_block" better? Expr &get_value_expr () { rust_assert (value != nullptr); return *value; } std::unique_ptr &get_value_expr_ptr () { rust_assert (value != nullptr); return value; } // TODO: is this better? Or is a "vis_block" better? BlockExpr &get_if_block () { rust_assert (if_block != nullptr); return *if_block; } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector > &get_patterns () const { return match_arm_patterns; } std::vector > &get_patterns () { return match_arm_patterns; } void set_outer_attrs (std::vector new_attrs) override { outer_attrs = std::move (new_attrs); } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector &get_outer_attrs () const { return outer_attrs; } std::vector &get_outer_attrs () override { return outer_attrs; } protected: /* Use covariance to implement clone function as returning this object rather * than base (or rather this or any derived object) */ IfLetExpr *clone_expr_with_block_impl () const final override { return clone_if_let_expr_impl (); } // Base clone function but still concrete as concrete base class virtual IfLetExpr *clone_if_let_expr_impl () const { return new IfLetExpr (*this); } }; /* AST node representing "if let" expression with an "else" expression at the * end */ class IfLetExprConseqElse : public IfLetExpr { std::unique_ptr else_block; public: std::string as_string () const override; IfLetExprConseqElse ( std::vector > match_arm_patterns, std::unique_ptr value, std::unique_ptr if_block, std::unique_ptr else_block, std::vector outer_attrs, location_t locus) : IfLetExpr (std::move (match_arm_patterns), std::move (value), std::move (if_block), std::move (outer_attrs), locus), else_block (std::move (else_block)) {} // outer attributes not allowed // copy constructor with clone IfLetExprConseqElse (IfLetExprConseqElse const &other) : IfLetExpr (other), else_block (other.else_block->clone_expr_with_block ()) {} // overload assignment operator to clone IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other) { IfLetExpr::operator= (other); // match_arm_patterns = other.match_arm_patterns; // value = other.value->clone_expr(); // if_block = other.if_block->clone_block_expr(); else_block = other.else_block->clone_expr_with_block (); // outer_attrs = other.outer_attrs; return *this; } // move constructors IfLetExprConseqElse (IfLetExprConseqElse &&other) = default; IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default; void accept_vis (ASTVisitor &vis) override; // TODO: is this better? Or is a "vis_block" better? ExprWithBlock &get_else_block () { rust_assert (else_block != nullptr); return *else_block; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ IfLetExprConseqElse *clone_if_let_expr_impl () const override { return new IfLetExprConseqElse (*this); } }; // Match arm expression struct MatchArm { private: std::vector outer_attrs; // MatchArmPatterns patterns; std::vector > match_arm_patterns; // inlined // bool has_match_arm_guard; // inlined from MatchArmGuard std::unique_ptr guard_expr; location_t locus; public: // Returns whether the MatchArm has a match arm guard expression bool has_match_arm_guard () const { return guard_expr != nullptr; } // Constructor for match arm with a guard expression MatchArm (std::vector > match_arm_patterns, location_t locus, std::unique_ptr guard_expr = nullptr, std::vector outer_attrs = std::vector ()) : outer_attrs (std::move (outer_attrs)), match_arm_patterns (std::move (match_arm_patterns)), guard_expr (std::move (guard_expr)), locus (locus) {} // Copy constructor with clone MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs) { // guard to protect from null pointer dereference if (other.guard_expr != nullptr) guard_expr = other.guard_expr->clone_expr (); match_arm_patterns.reserve (other.match_arm_patterns.size ()); for (const auto &e : other.match_arm_patterns) match_arm_patterns.push_back (e->clone_pattern ()); locus = other.locus; } ~MatchArm () = default; // Overload assignment operator to clone MatchArm &operator= (MatchArm const &other) { outer_attrs = other.outer_attrs; if (other.guard_expr != nullptr) guard_expr = other.guard_expr->clone_expr (); else guard_expr = nullptr; match_arm_patterns.reserve (other.match_arm_patterns.size ()); for (const auto &e : other.match_arm_patterns) match_arm_patterns.push_back (e->clone_pattern ()); return *this; } // move constructors MatchArm (MatchArm &&other) = default; MatchArm &operator= (MatchArm &&other) = default; // Returns whether match arm is in an error state. bool is_error () const { return match_arm_patterns.empty (); } // Creates a match arm in an error state. static MatchArm create_error () { location_t locus = UNDEF_LOCATION; return MatchArm (std::vector > (), locus); } std::string as_string () const; // TODO: is this better? Or is a "vis_block" better? Expr &get_guard_expr () { rust_assert (has_match_arm_guard ()); return *guard_expr; } std::unique_ptr &get_guard_expr_ptr () { rust_assert (has_match_arm_guard ()); return guard_expr; } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector &get_outer_attrs () const { return outer_attrs; } std::vector &get_outer_attrs () { return outer_attrs; } const std::vector > &get_patterns () const { return match_arm_patterns; } std::vector > &get_patterns () { return match_arm_patterns; } location_t get_locus () const { return locus; } }; /* A "match case" - a correlated match arm and resulting expression. Not * abstract. */ struct MatchCase { private: MatchArm arm; std::unique_ptr expr; NodeId node_id; /* TODO: does whether trailing comma exists need to be stored? currently * assuming it is only syntactical and has no effect on meaning. */ public: MatchCase (MatchArm arm, std::unique_ptr expr) : arm (std::move (arm)), expr (std::move (expr)), node_id (Analysis::Mappings::get ()->get_next_node_id ()) {} MatchCase (const MatchCase &other) : arm (other.arm), expr (other.expr->clone_expr ()), node_id (other.node_id) {} MatchCase &operator= (const MatchCase &other) { arm = other.arm; expr = other.expr->clone_expr (); node_id = other.node_id; return *this; } MatchCase (MatchCase &&other) = default; MatchCase &operator= (MatchCase &&other) = default; ~MatchCase () = default; std::string as_string () const; // TODO: is this better? Or is a "vis_block" better? Expr &get_expr () { rust_assert (expr != nullptr); return *expr; } std::unique_ptr &get_expr_ptr () { rust_assert (expr != nullptr); return expr; } // TODO: is this better? Or is a "vis_block" better? MatchArm &get_arm () { rust_assert (!arm.is_error ()); return arm; } NodeId get_node_id () const { return node_id; } }; // Match expression AST node class MatchExpr : public ExprWithBlock { std::vector outer_attrs; std::unique_ptr branch_value; std::vector inner_attrs; std::vector match_arms; location_t locus; public: std::string as_string () const override; // Returns whether the match expression has any match arms. bool has_match_arms () const { return !match_arms.empty (); } MatchExpr (std::unique_ptr branch_value, std::vector match_arms, std::vector inner_attrs, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), branch_value (std::move (branch_value)), inner_attrs (std::move (inner_attrs)), match_arms (std::move (match_arms)), locus (locus) {} // Copy constructor requires clone due to unique_ptr MatchExpr (MatchExpr const &other) : ExprWithBlock (other), outer_attrs (other.outer_attrs), inner_attrs (other.inner_attrs), match_arms (other.match_arms), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.branch_value != nullptr) branch_value = other.branch_value->clone_expr (); } // Overloaded assignment operator to clone due to unique_ptr MatchExpr &operator= (MatchExpr const &other) { ExprWithBlock::operator= (other); inner_attrs = other.inner_attrs; match_arms = other.match_arms; outer_attrs = other.outer_attrs; locus = other.locus; // guard to prevent null dereference (only required if error state) if (other.branch_value != nullptr) branch_value = other.branch_value->clone_expr (); else branch_value = nullptr; return *this; } // move constructors MatchExpr (MatchExpr &&other) = default; MatchExpr &operator= (MatchExpr &&other) = default; location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if branch value is null, so base stripping on that. void mark_for_strip () override { branch_value = nullptr; } bool is_marked_for_strip () const override { return branch_value == nullptr; } // TODO: this mutable getter seems really dodgy. Think up better way. const std::vector &get_inner_attrs () const { return inner_attrs; } std::vector &get_inner_attrs () { return inner_attrs; } 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); } // TODO: is this better? Or is a "vis_block" better? Expr &get_scrutinee_expr () { rust_assert (branch_value != nullptr); return *branch_value; } const std::vector &get_match_cases () const { return match_arms; } std::vector &get_match_cases () { return match_arms; } protected: /* Use covariance to implement clone function as returning this object rather * than base */ MatchExpr *clone_expr_with_block_impl () const override { return new MatchExpr (*this); } }; // Await expression AST node (pseudo-member variable access) class AwaitExpr : public ExprWithoutBlock { std::vector outer_attrs; std::unique_ptr awaited_expr; location_t locus; public: // TODO: ensure outer attributes are actually allowed AwaitExpr (std::unique_ptr awaited_expr, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), awaited_expr (std::move (awaited_expr)), locus (locus) {} // copy constructor with clone AwaitExpr (AwaitExpr const &other) : ExprWithoutBlock (other), outer_attrs (other.outer_attrs), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.awaited_expr != nullptr) awaited_expr = other.awaited_expr->clone_expr (); } // overloaded assignment operator with clone AwaitExpr &operator= (AwaitExpr const &other) { ExprWithoutBlock::operator= (other); outer_attrs = other.outer_attrs; locus = other.locus; // guard to prevent null dereference (only required if error state) if (other.awaited_expr != nullptr) awaited_expr = other.awaited_expr->clone_expr (); else awaited_expr = nullptr; return *this; } // move constructors AwaitExpr (AwaitExpr &&other) = default; AwaitExpr &operator= (AwaitExpr &&other) = default; std::string as_string () const override; location_t get_locus () const override final { return locus; } void accept_vis (ASTVisitor &vis) override; // Invalid if awaited expr is null, so base stripping on that. void mark_for_strip () override { awaited_expr = nullptr; } bool is_marked_for_strip () const override { return awaited_expr == nullptr; } // TODO: is this better? Or is a "vis_block" better? std::unique_ptr &get_awaited_expr () { rust_assert (awaited_expr != nullptr); return awaited_expr; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ AwaitExpr *clone_expr_without_block_impl () const override { return new AwaitExpr (*this); } }; // Async block expression AST node (block expr that evaluates to a future) class AsyncBlockExpr : public ExprWithBlock { // TODO: should this extend BlockExpr rather than be a composite of it? std::vector outer_attrs; bool has_move; std::unique_ptr block_expr; location_t locus; public: AsyncBlockExpr (std::unique_ptr block_expr, bool has_move, std::vector outer_attrs, location_t locus) : outer_attrs (std::move (outer_attrs)), has_move (has_move), block_expr (std::move (block_expr)), locus (locus) {} // copy constructor with clone AsyncBlockExpr (AsyncBlockExpr const &other) : ExprWithBlock (other), outer_attrs (other.outer_attrs), has_move (other.has_move), locus (other.locus) { // guard to prevent null dereference (only required if error state) if (other.block_expr != nullptr) block_expr = other.block_expr->clone_block_expr (); } // overloaded assignment operator to clone AsyncBlockExpr &operator= (AsyncBlockExpr const &other) { ExprWithBlock::operator= (other); outer_attrs = other.outer_attrs; has_move = other.has_move; locus = other.locus; // guard to prevent null dereference (only required if error state) if (other.block_expr != nullptr) block_expr = other.block_expr->clone_block_expr (); else block_expr = nullptr; return *this; } // move constructors AsyncBlockExpr (AsyncBlockExpr &&other) = default; AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default; std::string as_string () const override; bool get_has_move () { return has_move; } location_t 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 { block_expr = nullptr; } bool is_marked_for_strip () const override { return block_expr == nullptr; } // TODO: is this better? Or is a "vis_block" better? std::unique_ptr &get_block_expr () { rust_assert (block_expr != nullptr); return block_expr; } 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); } protected: /* Use covariance to implement clone function as returning this object rather * than base */ AsyncBlockExpr *clone_expr_with_block_impl () const override { return new AsyncBlockExpr (*this); } }; // Inline-assembly specific options enum class InlineAsmOptions { PURE = 1 << 0, NOMEM = 1 << 1, READONLY = 1 << 2, PRESERVES_FLAGS = 1 << 3, NORETURN = 1 << 4, NOSTACK = 1 << 5, ATT_SYNTAX = 1 << 6, RAW = 1 << 7, MAY_UNWIND = 1 << 8, }; struct AnonConst { NodeId id; std::unique_ptr value; }; struct InlineAsmRegOrRegClass { enum Type { Reg, RegClass, }; struct Reg { std::string Symbol; }; struct RegClass { std::string Symbol; }; Identifier name; location_t locus; }; struct InlineAsmOperand { enum RegisterType { In, Out, InOut, SplitInOut, Const, Sym, }; struct In { InlineAsmRegOrRegClass reg; std::unique_ptr expr; }; struct Out { InlineAsmRegOrRegClass reg; bool late; std::unique_ptr expr; // can be null }; struct InOut { InlineAsmRegOrRegClass reg; bool late; std::unique_ptr expr; // this can't be null }; struct SplitInOut { InlineAsmRegOrRegClass reg; bool late; std::unique_ptr in_expr; std::unique_ptr out_expr; // could be null }; struct Const { AnonConst anon_const; }; struct Sym { std::unique_ptr sym; }; location_t locus; }; struct InlineAsmPlaceHolder { size_t operand_idx; char modifier; // can be null location_t locus; }; struct InlineAsmTemplatePiece { bool is_placeholder; union { std::string string; InlineAsmPlaceHolder placeholder; }; }; struct TupleClobber { // as gccrs still doesn't contain a symbol class I have put them as strings std::string symbol; location_t loc; }; struct TupleTemplateStr { // as gccrs still doesn't contain a symbol class I have put them as strings std::string symbol; std::string optional_symbol; location_t loc; }; // Inline Assembly Node class InlineAsm : public ExprWithoutBlock { public: std::vector template_; std::vector template_strs; std::vector operands; TupleClobber clobber_abi; InlineAsmOptions options; std::vector line_spans; }; } // namespace AST } // namespace Rust #endif