LCOV - code coverage report
Current view: top level - gcc/rust/ast - rust-expr.h (source / functions) Coverage Total Hit
Test: gcc.info Lines: 83.2 % 1692 1407
Test Date: 2026-02-28 14:20:25 Functions: 79.5 % 572 455
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #ifndef RUST_AST_EXPR_H
       2              : #define RUST_AST_EXPR_H
       3              : 
       4              : #include "optional.h"
       5              : #include "rust-ast.h"
       6              : #include "rust-common.h"
       7              : #include "rust-path.h"
       8              : #include "rust-macro.h"
       9              : #include "rust-operators.h"
      10              : 
      11              : namespace Rust {
      12              : namespace AST {
      13              : /* TODO: if GCC moves to C++17 or allows boost, replace some boolean
      14              :  * "has_whatever" pairs with
      15              :  * optional types (std::optional or boost::optional)? */
      16              : 
      17              : // Loop label expression AST node used with break and continue expressions
      18              : // TODO: inline?
      19          549 : class LoopLabel /*: public Visitable*/
      20              : {
      21              :   Lifetime label; // or type LIFETIME_OR_LABEL
      22              :   location_t locus;
      23              : 
      24              :   NodeId node_id;
      25              : 
      26              : public:
      27              :   std::string as_string () const;
      28              : 
      29           63 :   LoopLabel (Lifetime loop_label, location_t locus = UNDEF_LOCATION)
      30           63 :     : label (std::move (loop_label)), locus (locus),
      31           63 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
      32           63 :   {}
      33              : 
      34              :   // Returns whether the LoopLabel is in an error state.
      35           81 :   location_t get_locus () const { return locus; }
      36              : 
      37         1026 :   Lifetime &get_lifetime () { return label; }
      38              : 
      39           36 :   NodeId get_node_id () const { return node_id; }
      40              : };
      41              : 
      42              : // AST node for an expression with an accompanying block - abstract
      43        48431 : class ExprWithBlock : public Expr
      44              : {
      45              : protected:
      46              :   // pure virtual clone implementation
      47              :   virtual ExprWithBlock *clone_expr_with_block_impl () const = 0;
      48              : 
      49              :   // prevent having to define multiple clone expressions
      50        13468 :   ExprWithBlock *clone_expr_impl () const final override
      51              :   {
      52        13468 :     return clone_expr_with_block_impl ();
      53              :   }
      54              : 
      55        17850 :   bool is_expr_without_block () const final override { return false; };
      56              : 
      57              : public:
      58              :   // Unique pointer custom clone function
      59         1689 :   std::unique_ptr<ExprWithBlock> clone_expr_with_block () const
      60              :   {
      61         1689 :     return std::unique_ptr<ExprWithBlock> (clone_expr_with_block_impl ());
      62              :   }
      63              : };
      64              : 
      65              : // Literals? Or literal base?
      66              : class LiteralExpr : public ExprWithoutBlock
      67              : {
      68              :   std::vector<Attribute> outer_attrs;
      69              :   Literal literal;
      70              :   location_t locus;
      71              : 
      72              : public:
      73         6686 :   std::string as_string () const override { return literal.as_string (); }
      74              : 
      75         6636 :   Literal::LitType get_lit_type () const { return literal.get_lit_type (); }
      76              : 
      77        37576 :   LiteralExpr (std::string value_as_string, Literal::LitType type,
      78              :                PrimitiveCoreType type_hint, std::vector<Attribute> outer_attrs,
      79              :                location_t locus)
      80        75152 :     : outer_attrs (std::move (outer_attrs)),
      81        37576 :       literal (std::move (value_as_string), type, type_hint), locus (locus)
      82        37576 :   {}
      83              : 
      84            0 :   LiteralExpr (Literal literal, std::vector<Attribute> outer_attrs,
      85              :                location_t locus)
      86            0 :     : outer_attrs (std::move (outer_attrs)), literal (std::move (literal)),
      87            0 :       locus (locus)
      88            0 :   {}
      89              : 
      90              :   // Unique pointer custom clone function
      91              :   std::unique_ptr<LiteralExpr> clone_literal_expr () const
      92              :   {
      93              :     return std::unique_ptr<LiteralExpr> (clone_literal_expr_impl ());
      94              :   }
      95              : 
      96        42904 :   location_t get_locus () const override final { return locus; }
      97              : 
      98          234 :   bool is_literal () const override final { return true; }
      99              : 
     100        22219 :   Literal get_literal () const { return literal; }
     101              : 
     102              :   void accept_vis (ASTVisitor &vis) override;
     103              : 
     104              :   // Invalid if literal is in error state, so base stripping on that.
     105            0 :   void mark_for_strip () override { literal = Literal::create_error (); }
     106       611604 :   bool is_marked_for_strip () const override { return literal.is_error (); }
     107              : 
     108              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
     109      2537635 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
     110              : 
     111            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
     112              :   {
     113            0 :     outer_attrs = std::move (new_attrs);
     114            0 :   }
     115              : 
     116        18685 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Literal; }
     117              : 
     118              : protected:
     119              :   /* Use covariance to implement clone function as returning this object rather
     120              :    * than base */
     121        43699 :   LiteralExpr *clone_expr_without_block_impl () const final override
     122              :   {
     123        43699 :     return clone_literal_expr_impl ();
     124              :   }
     125              : 
     126              :   /* not virtual as currently no subclasses of LiteralExpr, but could be in
     127              :    * future */
     128        43699 :   /*virtual*/ LiteralExpr *clone_literal_expr_impl () const
     129              :   {
     130        43699 :     return new LiteralExpr (*this);
     131              :   }
     132              : };
     133              : 
     134              : // Literal expression attribute body (non-macro attribute)
     135        42799 : class AttrInputLiteral : public AttrInput
     136              : {
     137              :   LiteralExpr literal_expr;
     138              : 
     139              : public:
     140        11740 :   AttrInputLiteral (LiteralExpr lit_expr) : literal_expr (std::move (lit_expr))
     141              :   {}
     142              : 
     143           57 :   std::string as_string () const override
     144              :   {
     145          114 :     return " = " + literal_expr.as_string ();
     146              :   }
     147              : 
     148              :   void accept_vis (ASTVisitor &vis) override;
     149              : 
     150              :   /* this can never be a cfg predicate - cfg and cfg_attr require a token-tree
     151              :    * cfg */
     152            0 :   bool check_cfg_predicate (const Session &) const override { return false; }
     153              : 
     154            0 :   bool is_meta_item () const override { return false; }
     155              : 
     156         8389 :   LiteralExpr &get_literal () { return literal_expr; }
     157              : 
     158        25429 :   AttrInputType get_attr_input_type () const final override
     159              :   {
     160        25429 :     return AttrInput::AttrInputType::LITERAL;
     161              :   }
     162              : 
     163              : protected:
     164              :   /* Use covariance to implement clone function as returning this object rather
     165              :    * than base */
     166        42799 :   AttrInputLiteral *clone_attr_input_impl () const override
     167              :   {
     168        42799 :     return new AttrInputLiteral (*this);
     169              :   }
     170              : };
     171              : 
     172              : // Like an AttrInputLiteral, but stores a MacroInvocation
     173              : class AttrInputMacro : public AttrInput
     174              : {
     175              :   std::unique_ptr<MacroInvocation> macro;
     176              : 
     177              : public:
     178            2 :   AttrInputMacro (std::unique_ptr<MacroInvocation> macro)
     179            2 :     : macro (std::move (macro))
     180              :   {}
     181              : 
     182              :   AttrInputMacro (const AttrInputMacro &oth);
     183              : 
     184              :   AttrInputMacro (AttrInputMacro &&oth) : macro (std::move (oth.macro)) {}
     185              : 
     186              :   AttrInputMacro &operator= (const AttrInputMacro &oth);
     187              : 
     188              :   AttrInputMacro &operator= (AttrInputMacro &&oth)
     189              :   {
     190              :     macro = std::move (oth.macro);
     191              :     return *this;
     192              :   }
     193              : 
     194              :   std::string as_string () const override;
     195              : 
     196              :   void accept_vis (ASTVisitor &vis) override;
     197              : 
     198              :   // assuming this can't be a cfg predicate
     199            0 :   bool check_cfg_predicate (const Session &) const override { return false; }
     200              : 
     201              :   // assuming this is like AttrInputLiteral
     202            0 :   bool is_meta_item () const override { return false; }
     203              : 
     204            0 :   std::unique_ptr<MacroInvocation> &get_macro () { return macro; }
     205              : 
     206            2 :   AttrInputType get_attr_input_type () const final override
     207              :   {
     208            2 :     return AttrInput::AttrInputType::MACRO;
     209              :   }
     210              : 
     211              : protected:
     212            7 :   AttrInputMacro *clone_attr_input_impl () const override
     213              :   {
     214            7 :     return new AttrInputMacro (*this);
     215              :   }
     216              : };
     217              : 
     218              : /* literal expr only meta item inner - TODO possibly replace with inheritance of
     219              :  * LiteralExpr itself? */
     220            0 : class MetaItemLitExpr : public MetaItemInner
     221              : {
     222              :   LiteralExpr lit_expr;
     223              : 
     224              : public:
     225           54 :   MetaItemLitExpr (LiteralExpr lit_expr) : lit_expr (std::move (lit_expr)) {}
     226              : 
     227            8 :   std::string as_string () const override { return lit_expr.as_string (); }
     228              : 
     229            1 :   location_t get_locus () const override { return lit_expr.get_locus (); }
     230              : 
     231              :   LiteralExpr get_literal () const { return lit_expr; }
     232              : 
     233            0 :   LiteralExpr &get_literal () { return lit_expr; }
     234              : 
     235              :   void accept_vis (ASTVisitor &vis) override;
     236              : 
     237              :   bool check_cfg_predicate (const Session &session) const override;
     238              : 
     239            0 :   MetaItemInner::Kind get_kind () override
     240              :   {
     241            0 :     return MetaItemInner::Kind::LitExpr;
     242              :   }
     243              : 
     244              : protected:
     245              :   // Use covariance to implement clone function as returning this type
     246            0 :   MetaItemLitExpr *clone_meta_item_inner_impl () const override
     247              :   {
     248            0 :     return new MetaItemLitExpr (*this);
     249              :   }
     250              : };
     251              : 
     252              : // more generic meta item "path = expr" form
     253              : class MetaItemPathExpr : public MetaItem
     254              : {
     255              :   SimplePath path;
     256              :   std::unique_ptr<Expr> expr;
     257              : 
     258              : public:
     259            4 :   MetaItemPathExpr (SimplePath path, std::unique_ptr<Expr> expr)
     260            4 :     : path (std::move (path)), expr (std::move (expr))
     261              :   {}
     262              : 
     263            0 :   MetaItemPathExpr (const MetaItemPathExpr &other)
     264            0 :     : MetaItem (other), path (other.path), expr (other.expr->clone_expr ())
     265            0 :   {}
     266              : 
     267              :   MetaItemPathExpr (MetaItemPathExpr &&) = default;
     268              : 
     269              :   MetaItemPathExpr &operator= (MetaItemPathExpr &&) = default;
     270              : 
     271              :   MetaItemPathExpr operator= (const MetaItemPathExpr &other)
     272              :   {
     273              :     MetaItem::operator= (other);
     274              :     path = other.path;
     275              :     expr = other.expr->clone_expr ();
     276              :     return *this;
     277              :   }
     278              : 
     279              :   SimplePath get_path () const { return path; }
     280              : 
     281            0 :   SimplePath &get_path () { return path; }
     282              : 
     283            5 :   Expr &get_expr () { return *expr; }
     284              : 
     285            0 :   std::unique_ptr<Expr> &get_expr_ptr () { return expr; }
     286              : 
     287            1 :   std::string as_string () const override
     288              :   {
     289            1 :     return path.as_string () + " = " + expr->as_string ();
     290              :   }
     291              : 
     292            1 :   MetaItem::ItemKind get_item_kind () const override
     293              :   {
     294            1 :     return MetaItem::ItemKind::PathExpr;
     295              :   }
     296              : 
     297            1 :   location_t get_locus () const override { return path.get_locus (); }
     298              : 
     299              :   void accept_vis (ASTVisitor &vis) override;
     300              : 
     301              :   bool check_cfg_predicate (const Session &session) const override;
     302              :   /* TODO: return true if "ident" is defined and value of it is "lit", return
     303              :    * false otherwise */
     304              : 
     305              :   Attribute to_attribute () const override;
     306              : 
     307              : protected:
     308              :   // Use covariance to implement clone function as returning this type
     309            0 :   MetaItemPathExpr *clone_meta_item_inner_impl () const override
     310              :   {
     311            0 :     return new MetaItemPathExpr (*this);
     312              :   }
     313              : };
     314              : 
     315              : /* Represents an expression using unary or binary operators as AST node. Can be
     316              :  * overloaded. */
     317              : class OperatorExpr : public ExprWithoutBlock
     318              : {
     319              :   // TODO: create binary and unary operator subclasses?
     320              : private:
     321              :   location_t locus;
     322              : 
     323              : protected:
     324              :   /* Variables must be protected to allow derived classes to use them as first
     325              :    * class citizens */
     326              :   std::vector<Attribute> outer_attrs;
     327              :   std::unique_ptr<Expr> main_or_left_expr;
     328              : 
     329              :   // Constructor (only for initialisation of expr purposes)
     330        22514 :   OperatorExpr (std::unique_ptr<Expr> main_or_left_expr,
     331              :                 std::vector<Attribute> outer_attribs, location_t locus)
     332        45028 :     : locus (locus), outer_attrs (std::move (outer_attribs)),
     333        22514 :       main_or_left_expr (std::move (main_or_left_expr))
     334        22514 :   {}
     335              : 
     336              :   // Copy constructor (only for initialisation of expr purposes)
     337        41332 :   OperatorExpr (OperatorExpr const &other)
     338        41332 :     : locus (other.locus), outer_attrs (other.outer_attrs)
     339              :   {
     340              :     // guard to prevent null dereference (only required if error state)
     341        41332 :     if (other.main_or_left_expr != nullptr)
     342        41332 :       main_or_left_expr = other.main_or_left_expr->clone_expr ();
     343        41332 :   }
     344              : 
     345              :   // Overload assignment operator to deep copy expr
     346              :   OperatorExpr &operator= (OperatorExpr const &other)
     347              :   {
     348              :     ExprWithoutBlock::operator= (other);
     349              :     locus = other.locus;
     350              :     outer_attrs = other.outer_attrs;
     351              : 
     352              :     // guard to prevent null dereference (only required if error state)
     353              :     if (other.main_or_left_expr != nullptr)
     354              :       main_or_left_expr = other.main_or_left_expr->clone_expr ();
     355              :     else
     356              :       main_or_left_expr = nullptr;
     357              : 
     358              :     return *this;
     359              :   }
     360              : 
     361              :   // move constructors
     362              :   OperatorExpr (OperatorExpr &&other) = default;
     363              :   OperatorExpr &operator= (OperatorExpr &&other) = default;
     364              : 
     365              : public:
     366        40247 :   location_t get_locus () const override final { return locus; }
     367              : 
     368              :   // Invalid if expr is null, so base stripping on that.
     369           14 :   void mark_for_strip () override { main_or_left_expr = nullptr; }
     370       655811 :   bool is_marked_for_strip () const override
     371              :   {
     372       655811 :     return main_or_left_expr == nullptr;
     373              :   }
     374              : 
     375              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
     376      2070981 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
     377              : 
     378            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
     379              :   {
     380            0 :     outer_attrs = std::move (new_attrs);
     381            0 :   }
     382              : 
     383            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Operator; }
     384              : };
     385              : 
     386              : /* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be
     387              :  * overloaded. */
     388              : class BorrowExpr : public OperatorExpr
     389              : {
     390              :   Mutability mutability;
     391              :   bool raw_borrow;
     392              :   bool double_borrow;
     393              : 
     394              : public:
     395              :   std::string as_string () const override;
     396              : 
     397         1951 :   BorrowExpr (std::unique_ptr<Expr> borrow_lvalue, Mutability mutability,
     398              :               bool raw_borrow, bool is_double_borrow,
     399              :               std::vector<Attribute> outer_attribs, location_t locus)
     400         1951 :     : OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs),
     401              :                     locus),
     402         1951 :       mutability (mutability), raw_borrow (raw_borrow),
     403         1951 :       double_borrow (is_double_borrow)
     404         1951 :   {}
     405              : 
     406              :   void accept_vis (ASTVisitor &vis) override;
     407              : 
     408              :   // TODO: is this better? Or is a "vis_block" better?
     409        53660 :   Expr &get_borrowed_expr ()
     410              :   {
     411        53660 :     rust_assert (main_or_left_expr != nullptr);
     412        53660 :     return *main_or_left_expr;
     413              :   }
     414              : 
     415         6739 :   std::unique_ptr<Expr> &get_borrowed_expr_ptr ()
     416              :   {
     417         6739 :     rust_assert (main_or_left_expr != nullptr);
     418         6739 :     return main_or_left_expr;
     419              :   }
     420              : 
     421          237 :   bool has_borrow_expr () const { return main_or_left_expr != nullptr; }
     422              : 
     423          474 :   bool get_is_mut () const { return mutability == Mutability::Mut; }
     424              : 
     425         1959 :   Mutability get_mutability () const { return mutability; }
     426              : 
     427         2173 :   bool get_is_double_borrow () const { return double_borrow; }
     428         4362 :   bool is_raw_borrow () const { return raw_borrow; }
     429              : 
     430         1942 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Borrow; }
     431              : 
     432              : protected:
     433              :   /* Use covariance to implement clone function as returning this object rather
     434              :    * than base */
     435         2154 :   BorrowExpr *clone_expr_without_block_impl () const override
     436              :   {
     437         2154 :     return new BorrowExpr (*this);
     438              :   }
     439              : };
     440              : 
     441              : // Unary prefix * deference operator
     442        14550 : class DereferenceExpr : public OperatorExpr
     443              : {
     444              : public:
     445              :   std::string as_string () const override;
     446              : 
     447              :   // Constructor calls OperatorExpr's protected constructor
     448         3965 :   DereferenceExpr (std::unique_ptr<Expr> deref_lvalue,
     449              :                    std::vector<Attribute> outer_attribs, location_t locus)
     450         3965 :     : OperatorExpr (std::move (deref_lvalue), std::move (outer_attribs), locus)
     451         3965 :   {}
     452              : 
     453              :   void accept_vis (ASTVisitor &vis) override;
     454              : 
     455              :   // TODO: is this better? Or is a "vis_block" better?
     456        64868 :   Expr &get_dereferenced_expr ()
     457              :   {
     458        64868 :     rust_assert (main_or_left_expr != nullptr);
     459        64868 :     return *main_or_left_expr;
     460              :   }
     461              : 
     462        13913 :   std::unique_ptr<Expr> &get_dereferenced_expr_ptr ()
     463              :   {
     464        13913 :     rust_assert (main_or_left_expr != nullptr);
     465        13913 :     return main_or_left_expr;
     466              :   }
     467              : 
     468         3963 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Dereference; }
     469              : 
     470              : protected:
     471              :   /* Use covariance to implement clone function as returning this object rather
     472              :    * than base */
     473         7275 :   DereferenceExpr *clone_expr_without_block_impl () const override
     474              :   {
     475         7275 :     return new DereferenceExpr (*this);
     476              :   }
     477              : };
     478              : 
     479              : // Unary postfix ? error propogation operator. Cannot be overloaded.
     480            4 : class ErrorPropagationExpr : public OperatorExpr
     481              : {
     482              : public:
     483              :   std::string as_string () const override;
     484              : 
     485              :   // Constructor calls OperatorExpr's protected constructor
     486            1 :   ErrorPropagationExpr (std::unique_ptr<Expr> potential_error_value,
     487              :                         std::vector<Attribute> outer_attribs, location_t locus)
     488            1 :     : OperatorExpr (std::move (potential_error_value),
     489            1 :                     std::move (outer_attribs), locus)
     490            1 :   {}
     491              : 
     492              :   void accept_vis (ASTVisitor &vis) override;
     493              : 
     494              :   // TODO: is this better? Or is a "vis_block" better?
     495           13 :   Expr &get_propagating_expr ()
     496              :   {
     497           13 :     rust_assert (main_or_left_expr != nullptr);
     498           13 :     return *main_or_left_expr;
     499              :   }
     500              : 
     501            2 :   std::unique_ptr<Expr> &get_propagating_expr_ptr ()
     502              :   {
     503            2 :     rust_assert (main_or_left_expr != nullptr);
     504            2 :     return main_or_left_expr;
     505              :   }
     506              : 
     507            2 :   Expr::Kind get_expr_kind () const override
     508              :   {
     509            2 :     return Expr::Kind::ErrorPropagation;
     510              :   }
     511              : 
     512              : protected:
     513              :   /* Use covariance to implement clone function as returning this object rather
     514              :    * than base */
     515            1 :   ErrorPropagationExpr *clone_expr_without_block_impl () const override
     516              :   {
     517            1 :     return new ErrorPropagationExpr (*this);
     518              :   }
     519              : };
     520              : 
     521              : // Unary prefix - or ! negation or NOT operators.
     522         1070 : class NegationExpr : public OperatorExpr
     523              : {
     524              : public:
     525              :   using ExprType = NegationOperator;
     526              : 
     527              : private:
     528              :   /* Note: overload negation via std::ops::Neg and not via std::ops::Not
     529              :    * Negation only works for signed integer and floating-point types, NOT only
     530              :    * works for boolean and integer types (via bitwise NOT) */
     531              :   ExprType expr_type;
     532              : 
     533              : public:
     534              :   std::string as_string () const override;
     535              : 
     536          705 :   ExprType get_expr_type () const { return expr_type; }
     537              : 
     538              :   // Constructor calls OperatorExpr's protected constructor
     539          535 :   NegationExpr (std::unique_ptr<Expr> negated_value, ExprType expr_kind,
     540              :                 std::vector<Attribute> outer_attribs, location_t locus)
     541          535 :     : OperatorExpr (std::move (negated_value), std::move (outer_attribs),
     542              :                     locus),
     543          535 :       expr_type (expr_kind)
     544          535 :   {}
     545              : 
     546              :   void accept_vis (ASTVisitor &vis) override;
     547              : 
     548              :   // TODO: is this better? Or is a "vis_block" better?
     549        10658 :   Expr &get_negated_expr ()
     550              :   {
     551        10658 :     rust_assert (main_or_left_expr != nullptr);
     552        10658 :     return *main_or_left_expr;
     553              :   }
     554              : 
     555         1710 :   std::unique_ptr<Expr> &get_negated_expr_ptr ()
     556              :   {
     557         1710 :     rust_assert (main_or_left_expr != nullptr);
     558         1710 :     return main_or_left_expr;
     559              :   }
     560              : 
     561          534 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Negation; }
     562              : 
     563              : protected:
     564              :   /* Use covariance to implement clone function as returning this object rather
     565              :    * than base */
     566          535 :   NegationExpr *clone_expr_without_block_impl () const override
     567              :   {
     568          535 :     return new NegationExpr (*this);
     569              :   }
     570              : };
     571              : 
     572              : // Infix binary operators. +, -, *, /, %, &, |, ^, <<, >>
     573              : class ArithmeticOrLogicalExpr : public OperatorExpr
     574              : {
     575              : public:
     576              :   using ExprType = ArithmeticOrLogicalOperator;
     577              : 
     578              : private:
     579              :   // Note: overloading trait specified in comments
     580              :   ExprType expr_type;
     581              : 
     582              :   std::unique_ptr<Expr> right_expr;
     583              : 
     584              : public:
     585              :   std::string as_string () const override;
     586              : 
     587         3708 :   ExprType get_expr_type () const { return expr_type; }
     588              : 
     589              :   // Constructor calls OperatorExpr's protected constructor
     590         4568 :   ArithmeticOrLogicalExpr (std::unique_ptr<Expr> left_value,
     591              :                            std::unique_ptr<Expr> right_value,
     592              :                            ExprType expr_kind, location_t locus)
     593        13704 :     : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
     594         4568 :       expr_type (expr_kind), right_expr (std::move (right_value))
     595         4568 :   {}
     596              :   // outer attributes not allowed
     597              : 
     598              :   // Copy constructor - probably required due to unique pointer
     599        11892 :   ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other)
     600        23784 :     : OperatorExpr (other), expr_type (other.expr_type),
     601        11892 :       right_expr (other.right_expr->clone_expr ())
     602        11892 :   {}
     603              : 
     604              :   // Overload assignment operator
     605              :   ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr const &other)
     606              :   {
     607              :     OperatorExpr::operator= (other);
     608              :     // main_or_left_expr = other.main_or_left_expr->clone_expr();
     609              :     right_expr = other.right_expr->clone_expr ();
     610              :     expr_type = other.expr_type;
     611              : 
     612              :     return *this;
     613              :   }
     614              : 
     615              :   // move constructors
     616              :   ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr &&other) = default;
     617              :   ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr &&other)
     618              :     = default;
     619              : 
     620              :   void accept_vis (ASTVisitor &vis) override;
     621              : 
     622              :   // TODO: is this better? Or is a "vis_block" better?
     623      2169090 :   Expr &get_left_expr ()
     624              :   {
     625      2169090 :     rust_assert (main_or_left_expr != nullptr);
     626      2169090 :     return *main_or_left_expr;
     627              :   }
     628              : 
     629       535056 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     630              :   {
     631       535056 :     rust_assert (main_or_left_expr != nullptr);
     632       535056 :     return main_or_left_expr;
     633              :   }
     634              : 
     635              :   // TODO: is this better? Or is a "vis_block" better?
     636      2169090 :   Expr &get_right_expr ()
     637              :   {
     638      2169090 :     rust_assert (right_expr != nullptr);
     639      2169090 :     return *right_expr;
     640              :   }
     641              : 
     642       535056 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     643              :   {
     644       535056 :     rust_assert (right_expr != nullptr);
     645       535056 :     return right_expr;
     646              :   }
     647              : 
     648              :   void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); }
     649              :   void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); }
     650              : 
     651         3384 :   Expr::Kind get_expr_kind () const override
     652              :   {
     653         3384 :     return Expr::Kind::ArithmeticOrLogical;
     654              :   }
     655              : 
     656              : protected:
     657              :   /* Use covariance to implement clone function as returning this object rather
     658              :    * than base */
     659        11892 :   ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override
     660              :   {
     661        11892 :     return new ArithmeticOrLogicalExpr (*this);
     662              :   }
     663              : };
     664              : 
     665              : // Infix binary comparison operators. ==, !=, <, <=, >, >=
     666              : class ComparisonExpr : public OperatorExpr
     667              : {
     668              : public:
     669              :   using ExprType = ComparisonOperator;
     670              : 
     671              : private:
     672              :   // Note: overloading trait specified in comments
     673              :   ExprType expr_type;
     674              : 
     675              :   std::unique_ptr<Expr> right_expr;
     676              : 
     677              : public:
     678              :   std::string as_string () const override;
     679              : 
     680         2905 :   ExprType get_expr_type () const { return expr_type; }
     681              : 
     682              :   // Constructor requires pointers for polymorphism
     683         2731 :   ComparisonExpr (std::unique_ptr<Expr> left_value,
     684              :                   std::unique_ptr<Expr> right_value, ExprType comparison_kind,
     685              :                   location_t locus)
     686         8193 :     : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
     687         2731 :       expr_type (comparison_kind), right_expr (std::move (right_value))
     688         2731 :   {}
     689              :   // outer attributes not allowed
     690              : 
     691              :   // Copy constructor also calls OperatorExpr's protected constructor
     692         2716 :   ComparisonExpr (ComparisonExpr const &other)
     693         5432 :     : OperatorExpr (other), expr_type (other.expr_type),
     694         2716 :       right_expr (other.right_expr->clone_expr ())
     695         2716 :   {}
     696              : 
     697              :   // Overload assignment operator to deep copy
     698              :   ComparisonExpr &operator= (ComparisonExpr const &other)
     699              :   {
     700              :     OperatorExpr::operator= (other);
     701              :     // main_or_left_expr = other.main_or_left_expr->clone_expr();
     702              :     right_expr = other.right_expr->clone_expr ();
     703              :     expr_type = other.expr_type;
     704              :     // outer_attrs = other.outer_attrs;
     705              : 
     706              :     return *this;
     707              :   }
     708              : 
     709              :   // move constructors
     710              :   ComparisonExpr (ComparisonExpr &&other) = default;
     711              :   ComparisonExpr &operator= (ComparisonExpr &&other) = default;
     712              : 
     713              :   void accept_vis (ASTVisitor &vis) override;
     714              : 
     715              :   // TODO: is this better? Or is a "vis_block" better?
     716        56338 :   Expr &get_left_expr ()
     717              :   {
     718        56338 :     rust_assert (main_or_left_expr != nullptr);
     719        56338 :     return *main_or_left_expr;
     720              :   }
     721              : 
     722         9617 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     723              :   {
     724         9617 :     rust_assert (main_or_left_expr != nullptr);
     725         9617 :     return main_or_left_expr;
     726              :   }
     727              : 
     728              :   // TODO: is this better? Or is a "vis_block" better?
     729        56338 :   Expr &get_right_expr ()
     730              :   {
     731        56338 :     rust_assert (right_expr != nullptr);
     732        56338 :     return *right_expr;
     733              :   }
     734              : 
     735         9617 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     736              :   {
     737         9617 :     rust_assert (right_expr != nullptr);
     738         9617 :     return right_expr;
     739              :   }
     740              : 
     741              :   ExprType get_kind () { return expr_type; }
     742              : 
     743         2719 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Comparison; }
     744              : 
     745              :   /* TODO: implement via a function call to std::cmp::PartialEq::eq(&op1, &op2)
     746              :    * maybe? */
     747              : protected:
     748              :   /* Use covariance to implement clone function as returning this object rather
     749              :    * than base */
     750         2716 :   ComparisonExpr *clone_expr_without_block_impl () const override
     751              :   {
     752         2716 :     return new ComparisonExpr (*this);
     753              :   }
     754              : };
     755              : 
     756              : // Infix binary lazy boolean logical operators && and ||.
     757              : class LazyBooleanExpr : public OperatorExpr
     758              : {
     759              : public:
     760              :   using ExprType = LazyBooleanOperator;
     761              : 
     762              : private:
     763              :   ExprType expr_type;
     764              : 
     765              :   std::unique_ptr<Expr> right_expr;
     766              : 
     767              : public:
     768              :   // Constructor calls OperatorExpr's protected constructor
     769          385 :   LazyBooleanExpr (std::unique_ptr<Expr> left_bool_expr,
     770              :                    std::unique_ptr<Expr> right_bool_expr, ExprType expr_kind,
     771              :                    location_t locus)
     772         1155 :     : OperatorExpr (std::move (left_bool_expr), std::vector<Attribute> (),
     773              :                     locus),
     774          385 :       expr_type (expr_kind), right_expr (std::move (right_bool_expr))
     775          385 :   {}
     776              :   // outer attributes not allowed
     777              : 
     778              :   // Copy constructor also calls OperatorExpr's protected constructor
     779          316 :   LazyBooleanExpr (LazyBooleanExpr const &other)
     780          632 :     : OperatorExpr (other), expr_type (other.expr_type),
     781          316 :       right_expr (other.right_expr->clone_expr ())
     782          316 :   {}
     783              : 
     784              :   // Overload assignment operator to deep copy
     785              :   LazyBooleanExpr &operator= (LazyBooleanExpr const &other)
     786              :   {
     787              :     OperatorExpr::operator= (other);
     788              :     // main_or_left_expr = other.main_or_left_expr->clone_expr();
     789              :     right_expr = other.right_expr->clone_expr ();
     790              :     expr_type = other.expr_type;
     791              : 
     792              :     return *this;
     793              :   }
     794              : 
     795              :   // move constructors
     796              :   LazyBooleanExpr (LazyBooleanExpr &&other) = default;
     797              :   LazyBooleanExpr &operator= (LazyBooleanExpr &&other) = default;
     798              : 
     799              :   std::string as_string () const override;
     800              : 
     801          420 :   ExprType get_expr_type () const { return expr_type; }
     802              : 
     803              :   void accept_vis (ASTVisitor &vis) override;
     804              : 
     805              :   // TODO: is this better? Or is a "vis_block" better?
     806         7060 :   Expr &get_left_expr ()
     807              :   {
     808         7060 :     rust_assert (main_or_left_expr != nullptr);
     809         7060 :     return *main_or_left_expr;
     810              :   }
     811              : 
     812         1231 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     813              :   {
     814         1231 :     rust_assert (main_or_left_expr != nullptr);
     815         1231 :     return main_or_left_expr;
     816              :   }
     817              : 
     818              :   // TODO: is this better? Or is a "vis_block" better?
     819         7060 :   Expr &get_right_expr ()
     820              :   {
     821         7060 :     rust_assert (right_expr != nullptr);
     822         7060 :     return *right_expr;
     823              :   }
     824              : 
     825         1231 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     826              :   {
     827         1231 :     rust_assert (right_expr != nullptr);
     828         1231 :     return right_expr;
     829              :   }
     830              : 
     831              :   ExprType get_kind () { return expr_type; }
     832              : 
     833          385 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::LazyBoolean; }
     834              : 
     835              : protected:
     836              :   /* Use covariance to implement clone function as returning this object rather
     837              :    * than base */
     838          316 :   LazyBooleanExpr *clone_expr_without_block_impl () const override
     839              :   {
     840          316 :     return new LazyBooleanExpr (*this);
     841              :   }
     842              : };
     843              : 
     844              : // Binary infix "as" cast expression.
     845              : class TypeCastExpr : public OperatorExpr
     846              : {
     847              :   std::unique_ptr<TypeNoBounds> type_to_convert_to;
     848              : 
     849              :   // Note: only certain type casts allowed, outlined in reference
     850              : public:
     851              :   std::string as_string () const override;
     852              : 
     853              :   // Constructor requires calling protected constructor of OperatorExpr
     854         5160 :   TypeCastExpr (std::unique_ptr<Expr> expr_to_cast,
     855              :                 std::unique_ptr<TypeNoBounds> type_to_cast_to, location_t locus)
     856        15480 :     : OperatorExpr (std::move (expr_to_cast), std::vector<Attribute> (), locus),
     857         5160 :       type_to_convert_to (std::move (type_to_cast_to))
     858         5160 :   {}
     859              :   // outer attributes not allowed
     860              : 
     861              :   // Copy constructor also requires calling protected constructor
     862         8074 :   TypeCastExpr (TypeCastExpr const &other)
     863         8074 :     : OperatorExpr (other),
     864         8074 :       type_to_convert_to (other.type_to_convert_to->clone_type_no_bounds ())
     865         8074 :   {}
     866              : 
     867              :   // Overload assignment operator to deep copy
     868              :   TypeCastExpr &operator= (TypeCastExpr const &other)
     869              :   {
     870              :     OperatorExpr::operator= (other);
     871              :     // main_or_left_expr = other.main_or_left_expr->clone_expr();
     872              :     type_to_convert_to = other.type_to_convert_to->clone_type_no_bounds ();
     873              : 
     874              :     return *this;
     875              :   }
     876              : 
     877              :   // move constructors
     878              :   TypeCastExpr (TypeCastExpr &&other) = default;
     879              :   TypeCastExpr &operator= (TypeCastExpr &&other) = default;
     880              : 
     881              :   void accept_vis (ASTVisitor &vis) override;
     882              : 
     883              :   // TODO: is this better? Or is a "vis_block" better?
     884       158989 :   Expr &get_casted_expr ()
     885              :   {
     886       158989 :     rust_assert (main_or_left_expr != nullptr);
     887       158989 :     return *main_or_left_expr;
     888              :   }
     889              : 
     890        16883 :   std::unique_ptr<Expr> &get_casted_expr_ptr ()
     891              :   {
     892        16883 :     rust_assert (main_or_left_expr != nullptr);
     893        16883 :     return main_or_left_expr;
     894              :   }
     895              : 
     896              :   // TODO: is this better? Or is a "vis_block" better?
     897       158989 :   TypeNoBounds &get_type_to_cast_to ()
     898              :   {
     899       158989 :     rust_assert (type_to_convert_to != nullptr);
     900       158989 :     return *type_to_convert_to;
     901              :   }
     902              : 
     903        16883 :   std::unique_ptr<TypeNoBounds> &get_type_to_cast_to_ptr ()
     904              :   {
     905        16883 :     rust_assert (type_to_convert_to != nullptr);
     906        16883 :     return type_to_convert_to;
     907              :   }
     908              : 
     909         5114 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::TypeCast; }
     910              : 
     911              : protected:
     912              :   /* Use covariance to implement clone function as returning this object rather
     913              :    * than base */
     914         8074 :   TypeCastExpr *clone_expr_without_block_impl () const override
     915              :   {
     916         8074 :     return new TypeCastExpr (*this);
     917              :   }
     918              : };
     919              : 
     920              : // Binary assignment expression.
     921              : class AssignmentExpr : public OperatorExpr
     922              : {
     923              :   std::unique_ptr<Expr> right_expr;
     924              : 
     925              : public:
     926              :   std::string as_string () const override;
     927              : 
     928              :   // Call OperatorExpr constructor to initialise left_expr
     929         2536 :   AssignmentExpr (std::unique_ptr<Expr> value_to_assign_to,
     930              :                   std::unique_ptr<Expr> value_to_assign,
     931              :                   std::vector<Attribute> outer_attribs, location_t locus)
     932         2536 :     : OperatorExpr (std::move (value_to_assign_to), std::move (outer_attribs),
     933              :                     locus),
     934         2536 :       right_expr (std::move (value_to_assign))
     935         2536 :   {}
     936              :   // outer attributes not allowed
     937              : 
     938              :   // Call OperatorExpr constructor in copy constructor, as well as clone
     939         6150 :   AssignmentExpr (AssignmentExpr const &other)
     940         6150 :     : OperatorExpr (other), right_expr (other.right_expr->clone_expr ())
     941         6150 :   {}
     942              : 
     943              :   // Overload assignment operator to clone unique_ptr right_expr
     944              :   AssignmentExpr &operator= (AssignmentExpr const &other)
     945              :   {
     946              :     OperatorExpr::operator= (other);
     947              :     // main_or_left_expr = other.main_or_left_expr->clone_expr();
     948              :     right_expr = other.right_expr->clone_expr ();
     949              :     // outer_attrs = other.outer_attrs;
     950              : 
     951              :     return *this;
     952              :   }
     953              : 
     954              :   // move constructors
     955              :   AssignmentExpr (AssignmentExpr &&other) = default;
     956              :   AssignmentExpr &operator= (AssignmentExpr &&other) = default;
     957              : 
     958              :   void accept_vis (ASTVisitor &vis) override;
     959              : 
     960          221 :   void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); }
     961          221 :   void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); }
     962              : 
     963              :   // TODO: is this better? Or is a "vis_block" better?
     964        55706 :   Expr &get_left_expr ()
     965              :   {
     966        55706 :     rust_assert (main_or_left_expr != nullptr);
     967        55706 :     return *main_or_left_expr;
     968              :   }
     969              : 
     970         7028 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     971              :   {
     972         7028 :     rust_assert (main_or_left_expr != nullptr);
     973         7028 :     return main_or_left_expr;
     974              :   }
     975              : 
     976         7028 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     977              :   {
     978         7028 :     rust_assert (right_expr != nullptr);
     979         7028 :     return right_expr;
     980              :   }
     981              : 
     982              :   // TODO: is this better? Or is a "vis_block" better?
     983        55706 :   Expr &get_right_expr ()
     984              :   {
     985        55706 :     rust_assert (right_expr != nullptr);
     986        55706 :     return *right_expr;
     987              :   }
     988              : 
     989         2497 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Assignment; }
     990              : 
     991              : protected:
     992              :   /* Use covariance to implement clone function as returning this object rather
     993              :    * than base */
     994         6150 :   AssignmentExpr *clone_expr_without_block_impl () const override
     995              :   {
     996         6150 :     return new AssignmentExpr (*this);
     997              :   }
     998              : };
     999              : 
    1000              : /* Binary infix compound assignment (arithmetic or logic then assignment)
    1001              :  * expressions. */
    1002              : class CompoundAssignmentExpr : public OperatorExpr
    1003              : {
    1004              : public:
    1005              :   using ExprType = CompoundAssignmentOperator;
    1006              : 
    1007              : private:
    1008              :   // Note: overloading trait specified in comments
    1009              :   ExprType expr_type;
    1010              :   std::unique_ptr<Expr> right_expr;
    1011              : 
    1012              : public:
    1013              :   std::string as_string () const override;
    1014              : 
    1015          681 :   ExprType get_expr_type () const { return expr_type; }
    1016              : 
    1017              :   // Use pointers in constructor to enable polymorphism
    1018          682 :   CompoundAssignmentExpr (std::unique_ptr<Expr> value_to_assign_to,
    1019              :                           std::unique_ptr<Expr> value_to_assign,
    1020              :                           ExprType expr_kind, location_t locus)
    1021         2046 :     : OperatorExpr (std::move (value_to_assign_to), std::vector<Attribute> (),
    1022              :                     locus),
    1023          682 :       expr_type (expr_kind), right_expr (std::move (value_to_assign))
    1024          682 :   {}
    1025              :   // outer attributes not allowed
    1026              : 
    1027              :   // Have clone in copy constructor
    1028         2218 :   CompoundAssignmentExpr (CompoundAssignmentExpr const &other)
    1029         4436 :     : OperatorExpr (other), expr_type (other.expr_type),
    1030         2218 :       right_expr (other.right_expr->clone_expr ())
    1031         2218 :   {}
    1032              : 
    1033              :   // Overload assignment operator to clone
    1034              :   CompoundAssignmentExpr &operator= (CompoundAssignmentExpr const &other)
    1035              :   {
    1036              :     OperatorExpr::operator= (other);
    1037              :     // main_or_left_expr = other.main_or_left_expr->clone_expr();
    1038              :     right_expr = other.right_expr->clone_expr ();
    1039              :     expr_type = other.expr_type;
    1040              :     // outer_attrs = other.outer_attrs;
    1041              : 
    1042              :     return *this;
    1043              :   }
    1044              : 
    1045              :   // move constructors
    1046              :   CompoundAssignmentExpr (CompoundAssignmentExpr &&other) = default;
    1047              :   CompoundAssignmentExpr &operator= (CompoundAssignmentExpr &&other) = default;
    1048              : 
    1049              :   void accept_vis (ASTVisitor &vis) override;
    1050              : 
    1051              :   // TODO: is this better? Or is a "vis_block" better?
    1052        19229 :   Expr &get_left_expr ()
    1053              :   {
    1054        19229 :     rust_assert (main_or_left_expr != nullptr);
    1055        19229 :     return *main_or_left_expr;
    1056              :   }
    1057              : 
    1058         1957 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
    1059              :   {
    1060         1957 :     rust_assert (main_or_left_expr != nullptr);
    1061         1957 :     return main_or_left_expr;
    1062              :   }
    1063              : 
    1064              :   // TODO: is this better? Or is a "vis_block" better?
    1065        19229 :   Expr &get_right_expr ()
    1066              :   {
    1067        19229 :     rust_assert (right_expr != nullptr);
    1068        19229 :     return *right_expr;
    1069              :   }
    1070              : 
    1071         1957 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
    1072              :   {
    1073         1957 :     rust_assert (right_expr != nullptr);
    1074         1957 :     return right_expr;
    1075              :   }
    1076              : 
    1077          682 :   Expr::Kind get_expr_kind () const override
    1078              :   {
    1079          682 :     return Expr::Kind::CompoundAssignment;
    1080              :   }
    1081              : 
    1082              : protected:
    1083              :   /* Use covariance to implement clone function as returning this object rather
    1084              :    * than base */
    1085         2218 :   CompoundAssignmentExpr *clone_expr_without_block_impl () const override
    1086              :   {
    1087         2218 :     return new CompoundAssignmentExpr (*this);
    1088              :   }
    1089              : };
    1090              : 
    1091              : // Expression in parentheses (i.e. like literally just any 3 + (2 * 6))
    1092              : class GroupedExpr : public ExprWithoutBlock
    1093              : {
    1094              :   std::vector<Attribute> outer_attrs;
    1095              :   std::vector<Attribute> inner_attrs;
    1096              :   std::unique_ptr<Expr> expr_in_parens;
    1097              :   location_t locus;
    1098              : 
    1099              : public:
    1100              :   std::string as_string () const override;
    1101              : 
    1102              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    1103         7955 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1104              : 
    1105              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1106        10114 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    1107              : 
    1108            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    1109              :   {
    1110            0 :     outer_attrs = std::move (new_attrs);
    1111            0 :   }
    1112              : 
    1113          313 :   GroupedExpr (std::unique_ptr<Expr> parenthesised_expr,
    1114              :                std::vector<Attribute> inner_attribs,
    1115              :                std::vector<Attribute> outer_attribs, location_t locus)
    1116          626 :     : outer_attrs (std::move (outer_attribs)),
    1117          313 :       inner_attrs (std::move (inner_attribs)),
    1118          313 :       expr_in_parens (std::move (parenthesised_expr)), locus (locus)
    1119          313 :   {}
    1120              : 
    1121              :   // Copy constructor includes clone for expr_in_parens
    1122          484 :   GroupedExpr (GroupedExpr const &other)
    1123          484 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1124          484 :       inner_attrs (other.inner_attrs), locus (other.locus)
    1125              :   {
    1126              :     // guard to prevent null dereference (only required if error state)
    1127          484 :     if (other.expr_in_parens != nullptr)
    1128          484 :       expr_in_parens = other.expr_in_parens->clone_expr ();
    1129          484 :   }
    1130              : 
    1131              :   // Overloaded assignment operator to clone expr_in_parens
    1132              :   GroupedExpr &operator= (GroupedExpr const &other)
    1133              :   {
    1134              :     ExprWithoutBlock::operator= (other);
    1135              :     inner_attrs = other.inner_attrs;
    1136              :     locus = other.locus;
    1137              :     outer_attrs = other.outer_attrs;
    1138              : 
    1139              :     // guard to prevent null dereference (only required if error state)
    1140              :     if (other.expr_in_parens != nullptr)
    1141              :       expr_in_parens = other.expr_in_parens->clone_expr ();
    1142              :     else
    1143              :       expr_in_parens = nullptr;
    1144              : 
    1145              :     return *this;
    1146              :   }
    1147              : 
    1148              :   // move constructors
    1149              :   GroupedExpr (GroupedExpr &&other) = default;
    1150              :   GroupedExpr &operator= (GroupedExpr &&other) = default;
    1151              : 
    1152          940 :   location_t get_locus () const override final { return locus; }
    1153              : 
    1154              :   void accept_vis (ASTVisitor &vis) override;
    1155              : 
    1156              :   // Invalid if inner expr is null, so base stripping on that.
    1157            0 :   void mark_for_strip () override { expr_in_parens = nullptr; }
    1158         2143 :   bool is_marked_for_strip () const override
    1159              :   {
    1160         2143 :     return expr_in_parens == nullptr;
    1161              :   }
    1162              : 
    1163              :   // TODO: is this better? Or is a "vis_block" better?
    1164         7702 :   Expr &get_expr_in_parens ()
    1165              :   {
    1166         7702 :     rust_assert (expr_in_parens != nullptr);
    1167         7702 :     return *expr_in_parens;
    1168              :   }
    1169              : 
    1170         1064 :   std::unique_ptr<Expr> &get_expr_in_parens_ptr ()
    1171              :   {
    1172         1064 :     rust_assert (expr_in_parens != nullptr);
    1173         1064 :     return expr_in_parens;
    1174              :   }
    1175              : 
    1176          313 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Grouped; }
    1177              : 
    1178              : protected:
    1179              :   /* Use covariance to implement clone function as returning this object rather
    1180              :    * than base */
    1181          484 :   GroupedExpr *clone_expr_without_block_impl () const override
    1182              :   {
    1183          484 :     return new GroupedExpr (*this);
    1184              :   }
    1185              : };
    1186              : 
    1187              : // Base array initialisation internal element representation thing (abstract)
    1188              : // aka ArrayElements
    1189              : class ArrayElems
    1190              : {
    1191              : public:
    1192              :   virtual ~ArrayElems () {}
    1193              : 
    1194              :   // Unique pointer custom clone ArrayElems function
    1195          609 :   std::unique_ptr<ArrayElems> clone_array_elems () const
    1196              :   {
    1197          609 :     return std::unique_ptr<ArrayElems> (clone_array_elems_impl ());
    1198              :   }
    1199              : 
    1200              :   virtual std::string as_string () const = 0;
    1201              : 
    1202              :   virtual void accept_vis (ASTVisitor &vis) = 0;
    1203              : 
    1204          427 :   NodeId get_node_id () const { return node_id; }
    1205              : 
    1206              : protected:
    1207         1040 :   ArrayElems () : node_id (Analysis::Mappings::get ().get_next_node_id ()) {}
    1208              : 
    1209              :   // pure virtual clone implementation
    1210              :   virtual ArrayElems *clone_array_elems_impl () const = 0;
    1211              : 
    1212              :   NodeId node_id;
    1213              : };
    1214              : 
    1215              : // Value array elements
    1216              : class ArrayElemsValues : public ArrayElems
    1217              : {
    1218              :   std::vector<std::unique_ptr<Expr>> values;
    1219              :   location_t locus;
    1220              : 
    1221              : public:
    1222          308 :   ArrayElemsValues (std::vector<std::unique_ptr<Expr>> elems, location_t locus)
    1223          308 :     : ArrayElems (), values (std::move (elems)), locus (locus)
    1224          308 :   {}
    1225              : 
    1226              :   // copy constructor with vector clone
    1227          487 :   ArrayElemsValues (ArrayElemsValues const &other)
    1228          487 :   {
    1229          487 :     values.reserve (other.values.size ());
    1230        13795 :     for (const auto &e : other.values)
    1231        13308 :       values.push_back (e->clone_expr ());
    1232          487 :   }
    1233              : 
    1234              :   // overloaded assignment operator with vector clone
    1235              :   ArrayElemsValues &operator= (ArrayElemsValues const &other)
    1236              :   {
    1237              :     values.reserve (other.values.size ());
    1238              :     for (const auto &e : other.values)
    1239              :       values.push_back (e->clone_expr ());
    1240              : 
    1241              :     return *this;
    1242              :   }
    1243              : 
    1244              :   // move constructors
    1245              :   ArrayElemsValues (ArrayElemsValues &&other) = default;
    1246              :   ArrayElemsValues &operator= (ArrayElemsValues &&other) = default;
    1247              : 
    1248              :   std::string as_string () const override;
    1249              : 
    1250              :   location_t get_locus () const { return locus; }
    1251              : 
    1252              :   void accept_vis (ASTVisitor &vis) override;
    1253              : 
    1254              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    1255              :   const std::vector<std::unique_ptr<Expr>> &get_values () const
    1256              :   {
    1257              :     return values;
    1258              :   }
    1259         5047 :   std::vector<std::unique_ptr<Expr>> &get_values () { return values; }
    1260              : 
    1261              :   size_t get_num_values () const { return values.size (); }
    1262              : 
    1263              : protected:
    1264          487 :   ArrayElemsValues *clone_array_elems_impl () const override
    1265              :   {
    1266          487 :     return new ArrayElemsValues (*this);
    1267              :   }
    1268              : };
    1269              : 
    1270              : // Copied array element and number of copies
    1271              : class ArrayElemsCopied : public ArrayElems
    1272              : {
    1273              :   std::unique_ptr<Expr> elem_to_copy;
    1274              : 
    1275              :   // TODO: This should be replaced by a ConstExpr
    1276              :   std::unique_ptr<Expr> num_copies;
    1277              :   location_t locus;
    1278              : 
    1279              : public:
    1280              :   // Constructor requires pointers for polymorphism
    1281          123 :   ArrayElemsCopied (std::unique_ptr<Expr> copied_elem,
    1282              :                     std::unique_ptr<Expr> copy_amount, location_t locus)
    1283          246 :     : ArrayElems (), elem_to_copy (std::move (copied_elem)),
    1284          123 :       num_copies (std::move (copy_amount)), locus (locus)
    1285              :   {}
    1286              : 
    1287              :   // Copy constructor required due to unique_ptr - uses custom clone
    1288          122 :   ArrayElemsCopied (ArrayElemsCopied const &other)
    1289          366 :     : elem_to_copy (other.elem_to_copy->clone_expr ()),
    1290          122 :       num_copies (other.num_copies->clone_expr ())
    1291          122 :   {}
    1292              : 
    1293              :   // Overloaded assignment operator for deep copying
    1294              :   ArrayElemsCopied &operator= (ArrayElemsCopied const &other)
    1295              :   {
    1296              :     elem_to_copy = other.elem_to_copy->clone_expr ();
    1297              :     num_copies = other.num_copies->clone_expr ();
    1298              : 
    1299              :     return *this;
    1300              :   }
    1301              : 
    1302              :   // move constructors
    1303              :   ArrayElemsCopied (ArrayElemsCopied &&other) = default;
    1304              :   ArrayElemsCopied &operator= (ArrayElemsCopied &&other) = default;
    1305              : 
    1306              :   std::string as_string () const override;
    1307              : 
    1308              :   location_t get_locus () const { return locus; }
    1309              : 
    1310              :   void accept_vis (ASTVisitor &vis) override;
    1311              : 
    1312              :   // TODO: is this better? Or is a "vis_block" better?
    1313         2164 :   Expr &get_elem_to_copy ()
    1314              :   {
    1315         2164 :     rust_assert (elem_to_copy != nullptr);
    1316         2164 :     return *elem_to_copy;
    1317              :   }
    1318              : 
    1319          366 :   std::unique_ptr<Expr> &get_elem_to_copy_ptr ()
    1320              :   {
    1321          366 :     rust_assert (elem_to_copy != nullptr);
    1322          366 :     return elem_to_copy;
    1323              :   }
    1324              : 
    1325              :   // TODO: is this better? Or is a "vis_block" better?
    1326         2164 :   Expr &get_num_copies ()
    1327              :   {
    1328         2164 :     rust_assert (num_copies != nullptr);
    1329         2164 :     return *num_copies;
    1330              :   }
    1331              : 
    1332          366 :   std::unique_ptr<Expr> &get_num_copies_ptr ()
    1333              :   {
    1334          366 :     rust_assert (num_copies != nullptr);
    1335          366 :     return num_copies;
    1336              :   }
    1337              : 
    1338              : protected:
    1339          122 :   ArrayElemsCopied *clone_array_elems_impl () const override
    1340              :   {
    1341          122 :     return new ArrayElemsCopied (*this);
    1342              :   }
    1343              : };
    1344              : 
    1345              : // Array definition-ish expression
    1346              : class ArrayExpr : public ExprWithoutBlock
    1347              : {
    1348              :   std::vector<Attribute> outer_attrs;
    1349              :   std::vector<Attribute> inner_attrs;
    1350              :   std::unique_ptr<ArrayElems> internal_elements;
    1351              :   location_t locus;
    1352              : 
    1353              :   // TODO: find another way to store this to save memory?
    1354              :   bool marked_for_strip = false;
    1355              : 
    1356              : public:
    1357              :   std::string as_string () const override;
    1358              : 
    1359              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    1360         8713 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1361              : 
    1362              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1363        10361 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    1364              : 
    1365            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    1366              :   {
    1367            0 :     outer_attrs = std::move (new_attrs);
    1368            0 :   }
    1369              : 
    1370              :   // Constructor requires ArrayElems pointer
    1371          431 :   ArrayExpr (std::unique_ptr<ArrayElems> array_elems,
    1372              :              std::vector<Attribute> inner_attribs,
    1373              :              std::vector<Attribute> outer_attribs, location_t locus)
    1374          862 :     : outer_attrs (std::move (outer_attribs)),
    1375          431 :       inner_attrs (std::move (inner_attribs)),
    1376          431 :       internal_elements (std::move (array_elems)), locus (locus)
    1377              :   {
    1378          431 :     rust_assert (internal_elements != nullptr);
    1379          431 :   }
    1380              : 
    1381              :   // Copy constructor requires cloning ArrayElems for polymorphism to hold
    1382          609 :   ArrayExpr (ArrayExpr const &other)
    1383          609 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1384          609 :       inner_attrs (other.inner_attrs), locus (other.locus),
    1385         1218 :       marked_for_strip (other.marked_for_strip)
    1386              :   {
    1387          609 :     internal_elements = other.internal_elements->clone_array_elems ();
    1388          609 :     rust_assert (internal_elements != nullptr);
    1389          609 :   }
    1390              : 
    1391              :   // Overload assignment operator to clone internal_elements
    1392              :   ArrayExpr &operator= (ArrayExpr const &other)
    1393              :   {
    1394              :     ExprWithoutBlock::operator= (other);
    1395              :     inner_attrs = other.inner_attrs;
    1396              :     locus = other.locus;
    1397              :     marked_for_strip = other.marked_for_strip;
    1398              :     outer_attrs = other.outer_attrs;
    1399              : 
    1400              :     internal_elements = other.internal_elements->clone_array_elems ();
    1401              : 
    1402              :     rust_assert (internal_elements != nullptr);
    1403              :     return *this;
    1404              :   }
    1405              : 
    1406              :   // move constructors
    1407              :   ArrayExpr (ArrayExpr &&other) = default;
    1408              :   ArrayExpr &operator= (ArrayExpr &&other) = default;
    1409              : 
    1410          875 :   location_t get_locus () const override final { return locus; }
    1411              : 
    1412              :   void accept_vis (ASTVisitor &vis) override;
    1413              : 
    1414              :   // Can't think of any invalid invariants, so store boolean.
    1415            0 :   void mark_for_strip () override { marked_for_strip = true; }
    1416         1610 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    1417              : 
    1418              :   // TODO: is this better? Or is a "vis_block" better?
    1419         7111 :   std::unique_ptr<ArrayElems> &get_array_elems ()
    1420              :   {
    1421         7111 :     rust_assert (internal_elements != nullptr);
    1422         7111 :     return internal_elements;
    1423              :   }
    1424              : 
    1425          427 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Array; }
    1426              : 
    1427              : protected:
    1428              :   /* Use covariance to implement clone function as returning this object rather
    1429              :    * than base */
    1430          609 :   ArrayExpr *clone_expr_without_block_impl () const override
    1431              :   {
    1432          609 :     return new ArrayExpr (*this);
    1433              :   }
    1434              : };
    1435              : 
    1436              : // Aka IndexExpr (also applies to slices)
    1437              : /* Apparently a[b] is equivalent to *std::ops::Index::index(&a, b) or
    1438              :  * *std::ops::Index::index_mut(&mut a, b) */
    1439              : /* Also apparently deref operations on a will be repeatedly applied to find an
    1440              :  * implementation */
    1441              : class ArrayIndexExpr : public ExprWithoutBlock
    1442              : {
    1443              :   std::vector<Attribute> outer_attrs;
    1444              :   std::unique_ptr<Expr> array_expr;
    1445              :   std::unique_ptr<Expr> index_expr;
    1446              :   location_t locus;
    1447              : 
    1448              : public:
    1449              :   std::string as_string () const override;
    1450              : 
    1451          254 :   ArrayIndexExpr (std::unique_ptr<Expr> array_expr,
    1452              :                   std::unique_ptr<Expr> array_index_expr,
    1453              :                   std::vector<Attribute> outer_attribs, location_t locus)
    1454          508 :     : outer_attrs (std::move (outer_attribs)),
    1455          254 :       array_expr (std::move (array_expr)),
    1456          254 :       index_expr (std::move (array_index_expr)), locus (locus)
    1457          254 :   {}
    1458              : 
    1459              :   // Copy constructor requires special cloning due to unique_ptr
    1460          274 :   ArrayIndexExpr (ArrayIndexExpr const &other)
    1461          274 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1462          274 :       locus (other.locus)
    1463              :   {
    1464              :     // guard to prevent null dereference (only required if error state)
    1465          274 :     if (other.array_expr != nullptr)
    1466          274 :       array_expr = other.array_expr->clone_expr ();
    1467          274 :     if (other.index_expr != nullptr)
    1468          274 :       index_expr = other.index_expr->clone_expr ();
    1469          274 :   }
    1470              : 
    1471              :   // Overload assignment operator to clone unique_ptrs
    1472              :   ArrayIndexExpr &operator= (ArrayIndexExpr const &other)
    1473              :   {
    1474              :     ExprWithoutBlock::operator= (other);
    1475              :     outer_attrs = other.outer_attrs;
    1476              :     locus = other.locus;
    1477              : 
    1478              :     // guard to prevent null dereference (only required if error state)
    1479              :     if (other.array_expr != nullptr)
    1480              :       array_expr = other.array_expr->clone_expr ();
    1481              :     else
    1482              :       array_expr = nullptr;
    1483              :     if (other.index_expr != nullptr)
    1484              :       index_expr = other.index_expr->clone_expr ();
    1485              :     else
    1486              :       index_expr = nullptr;
    1487              : 
    1488              :     return *this;
    1489              :   }
    1490              : 
    1491              :   // move constructors
    1492              :   ArrayIndexExpr (ArrayIndexExpr &&other) = default;
    1493              :   ArrayIndexExpr &operator= (ArrayIndexExpr &&other) = default;
    1494              : 
    1495          550 :   location_t get_locus () const override final { return locus; }
    1496              : 
    1497              :   void accept_vis (ASTVisitor &vis) override;
    1498              : 
    1499              :   // Invalid if either expr is null, so base stripping on that.
    1500            0 :   void mark_for_strip () override
    1501              :   {
    1502            0 :     array_expr = nullptr;
    1503            0 :     index_expr = nullptr;
    1504            0 :   }
    1505         1220 :   bool is_marked_for_strip () const override
    1506              :   {
    1507         1220 :     return array_expr == nullptr && index_expr == nullptr;
    1508              :   }
    1509              : 
    1510              :   // TODO: is this better? Or is a "vis_block" better?
    1511         4928 :   Expr &get_array_expr ()
    1512              :   {
    1513         4928 :     rust_assert (array_expr != nullptr);
    1514         4928 :     return *array_expr;
    1515              :   }
    1516              : 
    1517          765 :   std::unique_ptr<Expr> &get_array_expr_ptr ()
    1518              :   {
    1519          765 :     rust_assert (array_expr != nullptr);
    1520          765 :     return array_expr;
    1521              :   }
    1522              : 
    1523              :   // TODO: is this better? Or is a "vis_block" better?
    1524         4928 :   Expr &get_index_expr ()
    1525              :   {
    1526         4928 :     rust_assert (index_expr != nullptr);
    1527         4928 :     return *index_expr;
    1528              :   }
    1529              : 
    1530          765 :   std::unique_ptr<Expr> &get_index_expr_ptr ()
    1531              :   {
    1532          765 :     rust_assert (index_expr != nullptr);
    1533          765 :     return index_expr;
    1534              :   }
    1535              : 
    1536              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1537         6922 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    1538              : 
    1539            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    1540              :   {
    1541            0 :     outer_attrs = std::move (new_attrs);
    1542            0 :   }
    1543              : 
    1544          250 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::ArrayIndex; }
    1545              : 
    1546              : protected:
    1547              :   /* Use covariance to implement clone function as returning this object rather
    1548              :    * than base */
    1549          274 :   ArrayIndexExpr *clone_expr_without_block_impl () const override
    1550              :   {
    1551          274 :     return new ArrayIndexExpr (*this);
    1552              :   }
    1553              : };
    1554              : 
    1555              : // AST representation of a tuple
    1556              : class TupleExpr : public ExprWithoutBlock
    1557              : {
    1558              :   std::vector<Attribute> outer_attrs;
    1559              :   std::vector<Attribute> inner_attrs;
    1560              :   std::vector<std::unique_ptr<Expr>> tuple_elems;
    1561              :   location_t locus;
    1562              : 
    1563              :   // TODO: find another way to store this to save memory?
    1564              :   bool marked_for_strip = false;
    1565              : 
    1566              : public:
    1567              :   std::string as_string () const override;
    1568              : 
    1569              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    1570         9045 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1571              : 
    1572              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1573        12198 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    1574              : 
    1575            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    1576              :   {
    1577            0 :     outer_attrs = std::move (new_attrs);
    1578            0 :   }
    1579              : 
    1580          583 :   TupleExpr (std::vector<std::unique_ptr<Expr>> tuple_elements,
    1581              :              std::vector<Attribute> inner_attribs,
    1582              :              std::vector<Attribute> outer_attribs, location_t locus)
    1583         1166 :     : outer_attrs (std::move (outer_attribs)),
    1584          583 :       inner_attrs (std::move (inner_attribs)),
    1585          583 :       tuple_elems (std::move (tuple_elements)), locus (locus)
    1586          583 :   {}
    1587              : 
    1588              :   // copy constructor with vector clone
    1589          992 :   TupleExpr (TupleExpr const &other)
    1590          992 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1591          992 :       inner_attrs (other.inner_attrs), locus (other.locus),
    1592         1984 :       marked_for_strip (other.marked_for_strip)
    1593              :   {
    1594          992 :     tuple_elems.reserve (other.tuple_elems.size ());
    1595         2787 :     for (const auto &e : other.tuple_elems)
    1596         1795 :       tuple_elems.push_back (e->clone_expr ());
    1597          992 :   }
    1598              : 
    1599              :   // overloaded assignment operator to vector clone
    1600              :   TupleExpr &operator= (TupleExpr const &other)
    1601              :   {
    1602              :     ExprWithoutBlock::operator= (other);
    1603              :     outer_attrs = other.outer_attrs;
    1604              :     inner_attrs = other.inner_attrs;
    1605              :     locus = other.locus;
    1606              :     marked_for_strip = other.marked_for_strip;
    1607              : 
    1608              :     tuple_elems.reserve (other.tuple_elems.size ());
    1609              :     for (const auto &e : other.tuple_elems)
    1610              :       tuple_elems.push_back (e->clone_expr ());
    1611              : 
    1612              :     return *this;
    1613              :   }
    1614              : 
    1615              :   // move constructors
    1616              :   TupleExpr (TupleExpr &&other) = default;
    1617              :   TupleExpr &operator= (TupleExpr &&other) = default;
    1618              : 
    1619              :   /* Note: syntactically, can disambiguate single-element tuple from parens with
    1620              :    * comma, i.e. (0,) rather than (0) */
    1621              : 
    1622         1165 :   location_t get_locus () const override final { return locus; }
    1623              : 
    1624              :   void accept_vis (ASTVisitor &vis) override;
    1625              : 
    1626              :   // Can't think of any invalid invariants, so store boolean.
    1627            0 :   void mark_for_strip () override { marked_for_strip = true; }
    1628         2410 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    1629              : 
    1630              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    1631              :   const std::vector<std::unique_ptr<Expr>> &get_tuple_elems () const
    1632              :   {
    1633              :     return tuple_elems;
    1634              :   }
    1635        10376 :   std::vector<std::unique_ptr<Expr>> &get_tuple_elems () { return tuple_elems; }
    1636              : 
    1637              :   bool is_unit () const { return tuple_elems.size () == 0; }
    1638              : 
    1639          565 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Tuple; }
    1640              : 
    1641              : protected:
    1642              :   /* Use covariance to implement clone function as returning this object rather
    1643              :    * than base */
    1644          992 :   TupleExpr *clone_expr_without_block_impl () const override
    1645              :   {
    1646          992 :     return new TupleExpr (*this);
    1647              :   }
    1648              : };
    1649              : 
    1650              : // aka TupleIndexingExpr
    1651              : // AST representation of a tuple indexing expression
    1652              : class TupleIndexExpr : public ExprWithoutBlock
    1653              : {
    1654              :   std::vector<Attribute> outer_attrs;
    1655              :   std::unique_ptr<Expr> tuple_expr;
    1656              :   // TupleIndex is a decimal int literal with no underscores or suffix
    1657              :   TupleIndex tuple_index;
    1658              : 
    1659              :   location_t locus;
    1660              :   bool to_strip;
    1661              : 
    1662              :   // i.e. pair.0
    1663              : 
    1664              : public:
    1665              :   std::string as_string () const override;
    1666              : 
    1667          960 :   TupleIndex get_tuple_index () const { return tuple_index; }
    1668              : 
    1669          936 :   TupleIndexExpr (std::unique_ptr<Expr> tuple_expr, TupleIndex index,
    1670              :                   std::vector<Attribute> outer_attribs, location_t locus)
    1671         1872 :     : outer_attrs (std::move (outer_attribs)),
    1672          936 :       tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus),
    1673          936 :       to_strip (false)
    1674          936 :   {}
    1675              : 
    1676              :   // Copy constructor requires a clone for tuple_expr
    1677          907 :   TupleIndexExpr (TupleIndexExpr const &other)
    1678          907 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1679          907 :       tuple_index (other.tuple_index), locus (other.locus),
    1680          907 :       to_strip (other.to_strip)
    1681              :   {
    1682              :     // guard to prevent null dereference (only required if error state)
    1683          907 :     if (other.tuple_expr != nullptr)
    1684          907 :       tuple_expr = other.tuple_expr->clone_expr ();
    1685          907 :   }
    1686              : 
    1687              :   // Overload assignment operator in order to clone
    1688              :   TupleIndexExpr &operator= (TupleIndexExpr const &other)
    1689              :   {
    1690              :     ExprWithoutBlock::operator= (other);
    1691              :     tuple_index = other.tuple_index;
    1692              :     locus = other.locus;
    1693              :     outer_attrs = other.outer_attrs;
    1694              :     to_strip = other.to_strip;
    1695              : 
    1696              :     // guard to prevent null dereference (only required if error state)
    1697              :     if (other.tuple_expr != nullptr)
    1698              :       tuple_expr = other.tuple_expr->clone_expr ();
    1699              :     else
    1700              :       tuple_expr = nullptr;
    1701              : 
    1702              :     return *this;
    1703              :   }
    1704              : 
    1705              :   // move constructors
    1706              :   TupleIndexExpr (TupleIndexExpr &&other) = default;
    1707              :   TupleIndexExpr &operator= (TupleIndexExpr &&other) = default;
    1708              : 
    1709         2000 :   location_t get_locus () const override final { return locus; }
    1710              : 
    1711              :   void accept_vis (ASTVisitor &vis) override;
    1712              : 
    1713              :   // Invalid if tuple expr is null, so base stripping on that.
    1714            0 :   void mark_for_strip () override { to_strip = true; }
    1715         4728 :   bool is_marked_for_strip () const override { return to_strip; }
    1716              : 
    1717              :   // TODO: is this better? Or is a "vis_block" better?
    1718        20371 :   Expr &get_tuple_expr ()
    1719              :   {
    1720        20371 :     rust_assert (tuple_expr != nullptr);
    1721        20371 :     return *tuple_expr;
    1722              :   }
    1723              : 
    1724         2801 :   std::unique_ptr<Expr> &get_tuple_expr_ptr ()
    1725              :   {
    1726         2801 :     rust_assert (tuple_expr != nullptr);
    1727         2801 :     return tuple_expr;
    1728              :   }
    1729              : 
    1730              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1731        29116 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    1732              : 
    1733            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    1734              :   {
    1735            0 :     outer_attrs = std::move (new_attrs);
    1736            0 :   }
    1737              : 
    1738          902 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::TupleIndex; }
    1739              : 
    1740              : protected:
    1741              :   /* Use covariance to implement clone function as returning this object rather
    1742              :    * than base */
    1743          907 :   TupleIndexExpr *clone_expr_without_block_impl () const override
    1744              :   {
    1745          907 :     return new TupleIndexExpr (*this);
    1746              :   }
    1747              : };
    1748              : 
    1749              : // Base struct/tuple/union value creator AST node (abstract)
    1750              : class StructExpr : public ExprWithoutBlock
    1751              : {
    1752              :   std::vector<Attribute> outer_attrs;
    1753              :   PathInExpression struct_name;
    1754              : 
    1755              : protected:
    1756              :   // Protected constructor to allow initialising struct_name
    1757         1407 :   StructExpr (PathInExpression struct_path,
    1758              :               std::vector<Attribute> outer_attribs)
    1759         2814 :     : outer_attrs (std::move (outer_attribs)),
    1760         1407 :       struct_name (std::move (struct_path))
    1761         1407 :   {}
    1762              : 
    1763              : public:
    1764            0 :   const PathInExpression &get_struct_name () const { return struct_name; }
    1765        30750 :   PathInExpression &get_struct_name () { return struct_name; }
    1766              : 
    1767              :   std::string as_string () const override;
    1768              : 
    1769              :   // Invalid if path is empty, so base stripping on that.
    1770            0 :   void mark_for_strip () override
    1771              :   {
    1772            0 :     struct_name = PathInExpression::create_error ();
    1773            0 :   }
    1774         6010 :   bool is_marked_for_strip () const override { return struct_name.is_error (); }
    1775              : 
    1776              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1777        37099 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    1778              : 
    1779            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    1780              :   {
    1781            0 :     outer_attrs = std::move (new_attrs);
    1782            0 :   }
    1783              : 
    1784         1407 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Struct; }
    1785              : };
    1786              : 
    1787              : // Actual AST node of the struct creator (with no fields). Not abstract!
    1788              : class StructExprStruct : public StructExpr
    1789              : {
    1790              :   std::vector<Attribute> inner_attrs;
    1791              : 
    1792              :   location_t locus;
    1793              : 
    1794              : public:
    1795              :   std::string as_string () const override;
    1796              : 
    1797              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    1798        30620 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1799              : 
    1800              :   // Constructor has to call protected constructor of base class
    1801         1407 :   StructExprStruct (PathInExpression struct_path,
    1802              :                     std::vector<Attribute> inner_attribs,
    1803              :                     std::vector<Attribute> outer_attribs, location_t locus)
    1804         1407 :     : StructExpr (std::move (struct_path), std::move (outer_attribs)),
    1805         1407 :       inner_attrs (std::move (inner_attribs)), locus (locus)
    1806         1407 :   {}
    1807              : 
    1808         3188 :   location_t get_locus () const override final { return locus; }
    1809              : 
    1810              :   void accept_vis (ASTVisitor &vis) override;
    1811              : 
    1812              : protected:
    1813              :   /* Use covariance to implement clone function as returning this object rather
    1814              :    * than base */
    1815           78 :   StructExprStruct *clone_expr_without_block_impl () const override
    1816              :   {
    1817           78 :     return new StructExprStruct (*this);
    1818              :   }
    1819              : };
    1820              : 
    1821              : /* AST node representing expression used to fill a struct's fields from another
    1822              :  * struct */
    1823              : struct StructBase
    1824              : {
    1825              : private:
    1826              :   std::unique_ptr<Expr> base_struct;
    1827              :   location_t locus;
    1828              : 
    1829              : public:
    1830         1389 :   StructBase (std::unique_ptr<Expr> base_struct_ptr, location_t locus)
    1831         1389 :     : base_struct (std::move (base_struct_ptr)), locus (locus)
    1832              :   {}
    1833              : 
    1834              :   // Copy constructor requires clone
    1835         1330 :   StructBase (StructBase const &other)
    1836         1330 :   {
    1837              :     /* HACK: gets around base_struct pointer being null (e.g. if no struct base
    1838              :      * exists) */
    1839         1330 :     if (other.base_struct != nullptr)
    1840           63 :       base_struct = other.base_struct->clone_expr ();
    1841         1330 :   }
    1842              : 
    1843              :   // Destructor
    1844         2682 :   ~StructBase () = default;
    1845              : 
    1846              :   // Overload assignment operator to clone base_struct
    1847              :   StructBase &operator= (StructBase const &other)
    1848              :   {
    1849              :     // prevent null pointer dereference
    1850              :     if (other.base_struct != nullptr)
    1851              :       base_struct = other.base_struct->clone_expr ();
    1852              :     else
    1853              :       base_struct = nullptr;
    1854              : 
    1855              :     return *this;
    1856              :   }
    1857              : 
    1858              :   // move constructors
    1859         1326 :   StructBase (StructBase &&other) = default;
    1860           63 :   StructBase &operator= (StructBase &&other) = default;
    1861              : 
    1862              :   // Returns a null expr-ed StructBase - error state
    1863         1326 :   static StructBase error () { return StructBase (nullptr, UNDEF_LOCATION); }
    1864              : 
    1865              :   // Returns whether StructBase is in error state
    1866        29524 :   bool is_invalid () const { return base_struct == nullptr; }
    1867              : 
    1868              :   std::string as_string () const;
    1869              : 
    1870              :   location_t get_locus () const { return locus; }
    1871              : 
    1872              :   // TODO: is this better? Or is a "vis_block" better?
    1873         1155 :   Expr &get_base_struct ()
    1874              :   {
    1875         1155 :     rust_assert (base_struct != nullptr);
    1876         1155 :     return *base_struct;
    1877              :   }
    1878              : 
    1879          189 :   std::unique_ptr<Expr> &get_base_struct_ptr ()
    1880              :   {
    1881          189 :     rust_assert (base_struct != nullptr);
    1882          189 :     return base_struct;
    1883              :   }
    1884              : };
    1885              : 
    1886              : /* Base AST node for a single struct expression field (in struct instance
    1887              :  * creation) - abstract */
    1888              : class StructExprField
    1889              : {
    1890              : public:
    1891              :   virtual ~StructExprField () {}
    1892              : 
    1893              :   // Unique pointer custom clone function
    1894         2343 :   std::unique_ptr<StructExprField> clone_struct_expr_field () const
    1895              :   {
    1896         2343 :     return std::unique_ptr<StructExprField> (clone_struct_expr_field_impl ());
    1897              :   }
    1898              : 
    1899              :   virtual std::string as_string () const = 0;
    1900              : 
    1901              :   virtual void accept_vis (ASTVisitor &vis) = 0;
    1902              : 
    1903              :   virtual location_t get_locus () const = 0;
    1904              : 
    1905         2500 :   NodeId get_node_id () const { return node_id; }
    1906              : 
    1907         2125 :   const std::vector<AST::Attribute> &get_outer_attrs () const
    1908              :   {
    1909         2125 :     return outer_attrs;
    1910              :   }
    1911              : 
    1912        10585 :   std::vector<AST::Attribute> &get_outer_attrs () { return outer_attrs; }
    1913              : 
    1914              : protected:
    1915              :   // pure virtual clone implementation
    1916              :   virtual StructExprField *clone_struct_expr_field_impl () const = 0;
    1917              : 
    1918              :   StructExprField () : node_id (Analysis::Mappings::get ().get_next_node_id ())
    1919              :   {}
    1920              : 
    1921         4412 :   StructExprField (AST::AttrVec outer_attrs)
    1922         4412 :     : outer_attrs (std::move (outer_attrs)),
    1923         4412 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
    1924         4412 :   {}
    1925              : 
    1926              :   AST::AttrVec outer_attrs;
    1927              :   NodeId node_id;
    1928              : };
    1929              : 
    1930              : // Identifier-only variant of StructExprField AST node
    1931              : class StructExprFieldIdentifier : public StructExprField
    1932              : {
    1933              :   Identifier field_name;
    1934              :   location_t locus;
    1935              : 
    1936              : public:
    1937          216 :   StructExprFieldIdentifier (Identifier field_identifier,
    1938              :                              AST::AttrVec outer_attrs, location_t locus)
    1939          216 :     : StructExprField (std::move (outer_attrs)),
    1940          216 :       field_name (std::move (field_identifier)), locus (locus)
    1941          216 :   {}
    1942              : 
    1943            0 :   std::string as_string () const override { return field_name.as_string (); }
    1944              : 
    1945          433 :   location_t get_locus () const override final { return locus; }
    1946              : 
    1947              :   void accept_vis (ASTVisitor &vis) override;
    1948              : 
    1949          433 :   Identifier get_field_name () const { return field_name; }
    1950              : 
    1951              : protected:
    1952              :   /* Use covariance to implement clone function as returning this object rather
    1953              :    * than base */
    1954          218 :   StructExprFieldIdentifier *clone_struct_expr_field_impl () const override
    1955              :   {
    1956          218 :     return new StructExprFieldIdentifier (*this);
    1957              :   }
    1958              : };
    1959              : 
    1960              : /* Base AST node for a single struct expression field with an assigned value -
    1961              :  * abstract */
    1962              : class StructExprFieldWithVal : public StructExprField
    1963              : {
    1964              :   std::unique_ptr<Expr> value;
    1965              : 
    1966              : protected:
    1967         2071 :   StructExprFieldWithVal (std::unique_ptr<Expr> field_value,
    1968              :                           AST::AttrVec outer_attrs)
    1969         2071 :     : StructExprField (std::move (outer_attrs)), value (std::move (field_value))
    1970         2071 :   {}
    1971              : 
    1972              :   // Copy constructor requires clone
    1973         2125 :   StructExprFieldWithVal (StructExprFieldWithVal const &other)
    1974         2125 :     : StructExprField (other.get_outer_attrs ()),
    1975         2125 :       value (other.value->clone_expr ())
    1976         2125 :   {}
    1977              : 
    1978              :   // Overload assignment operator to clone unique_ptr
    1979              :   StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other)
    1980              :   {
    1981              :     value = other.value->clone_expr ();
    1982              :     outer_attrs = other.get_outer_attrs ();
    1983              : 
    1984              :     return *this;
    1985              :   }
    1986              : 
    1987              :   // move constructors
    1988              :   StructExprFieldWithVal (StructExprFieldWithVal &&other) = default;
    1989              :   StructExprFieldWithVal &operator= (StructExprFieldWithVal &&other) = default;
    1990              : 
    1991              : public:
    1992              :   std::string as_string () const override;
    1993              : 
    1994              :   // TODO: is this better? Or is a "vis_block" better?
    1995        41025 :   Expr &get_value ()
    1996              :   {
    1997        41025 :     rust_assert (value != nullptr);
    1998        41025 :     return *value;
    1999              :   }
    2000              : 
    2001         6667 :   std::unique_ptr<Expr> &get_value_ptr ()
    2002              :   {
    2003         6667 :     rust_assert (value != nullptr);
    2004         6667 :     return value;
    2005              :   }
    2006              : };
    2007              : 
    2008              : // Identifier and value variant of StructExprField AST node
    2009              : class StructExprFieldIdentifierValue : public StructExprFieldWithVal
    2010              : {
    2011              :   Identifier field_name;
    2012              :   location_t locus;
    2013              : 
    2014              : public:
    2015         1992 :   StructExprFieldIdentifierValue (Identifier field_identifier,
    2016              :                                   std::unique_ptr<Expr> field_value,
    2017              :                                   AST::AttrVec outer_attrs, location_t locus)
    2018         1992 :     : StructExprFieldWithVal (std::move (field_value), std::move (outer_attrs)),
    2019         1992 :       field_name (std::move (field_identifier)), locus (locus)
    2020         1992 :   {}
    2021              : 
    2022           35 :   StructExprFieldIdentifierValue (Identifier field_identifier,
    2023              :                                   std::unique_ptr<Expr> field_value,
    2024              :                                   location_t locus)
    2025           35 :     : StructExprFieldWithVal (std::move (field_value), {}),
    2026           35 :       field_name (std::move (field_identifier)), locus (locus)
    2027           35 :   {}
    2028              : 
    2029              :   std::string as_string () const override;
    2030              : 
    2031              :   void accept_vis (ASTVisitor &vis) override;
    2032              : 
    2033         4694 :   std::string get_field_name () const { return field_name.as_string (); }
    2034              : 
    2035         4371 :   location_t get_locus () const override final { return locus; }
    2036              : 
    2037              : protected:
    2038              :   /* Use covariance to implement clone function as returning this object rather
    2039              :    * than base */
    2040         2081 :   StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override
    2041              :   {
    2042         2081 :     return new StructExprFieldIdentifierValue (*this);
    2043              :   }
    2044              : };
    2045              : 
    2046              : // Tuple index and value variant of StructExprField AST node
    2047           88 : class StructExprFieldIndexValue : public StructExprFieldWithVal
    2048              : {
    2049              :   TupleIndex index;
    2050              :   location_t locus;
    2051              : 
    2052              : public:
    2053           44 :   StructExprFieldIndexValue (TupleIndex tuple_index,
    2054              :                              std::unique_ptr<Expr> field_value,
    2055              :                              AST::AttrVec outer_attrs, location_t locus)
    2056           44 :     : StructExprFieldWithVal (std::move (field_value), std::move (outer_attrs)),
    2057           44 :       index (tuple_index), locus (locus)
    2058           44 :   {}
    2059              : 
    2060              :   std::string as_string () const override;
    2061              : 
    2062              :   void accept_vis (ASTVisitor &vis) override;
    2063              : 
    2064           44 :   TupleIndex get_index () const { return index; }
    2065              : 
    2066           88 :   location_t get_locus () const override final { return locus; }
    2067              : 
    2068              : protected:
    2069              :   /* Use covariance to implement clone function as returning this object rather
    2070              :    * than base */
    2071           44 :   StructExprFieldIndexValue *clone_struct_expr_field_impl () const override
    2072              :   {
    2073           44 :     return new StructExprFieldIndexValue (*this);
    2074              :   }
    2075              : };
    2076              : 
    2077              : // AST node of a struct creator with fields
    2078              : class StructExprStructFields : public StructExprStruct
    2079              : {
    2080              :   // std::vector<StructExprField> fields;
    2081              :   std::vector<std::unique_ptr<StructExprField>> fields;
    2082              : 
    2083              :   // bool has_struct_base;
    2084              :   StructBase struct_base;
    2085              : 
    2086              : public:
    2087              :   std::string as_string () const override;
    2088              : 
    2089        29524 :   bool has_struct_base () const { return !struct_base.is_invalid (); }
    2090              : 
    2091              :   // Constructor for StructExprStructFields when no struct base is used
    2092         1326 :   StructExprStructFields (
    2093              :     PathInExpression struct_path,
    2094              :     std::vector<std::unique_ptr<StructExprField>> expr_fields, location_t locus,
    2095              :     StructBase base_struct = StructBase::error (),
    2096              :     std::vector<Attribute> inner_attribs = std::vector<Attribute> (),
    2097              :     std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
    2098         1326 :     : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
    2099              :                         std::move (outer_attribs), locus),
    2100         1326 :       fields (std::move (expr_fields)), struct_base (std::move (base_struct))
    2101         1326 :   {}
    2102              : 
    2103              :   // copy constructor with vector clone
    2104         1330 :   StructExprStructFields (StructExprStructFields const &other)
    2105         1330 :     : StructExprStruct (other), struct_base (other.struct_base)
    2106              :   {
    2107         1330 :     fields.reserve (other.fields.size ());
    2108         3673 :     for (const auto &e : other.fields)
    2109         2343 :       fields.push_back (e->clone_struct_expr_field ());
    2110         1330 :   }
    2111              : 
    2112              :   // overloaded assignment operator with vector clone
    2113              :   StructExprStructFields &operator= (StructExprStructFields const &other)
    2114              :   {
    2115              :     StructExprStruct::operator= (other);
    2116              :     struct_base = other.struct_base;
    2117              : 
    2118              :     fields.reserve (other.fields.size ());
    2119              :     for (const auto &e : other.fields)
    2120              :       fields.push_back (e->clone_struct_expr_field ());
    2121              : 
    2122              :     return *this;
    2123              :   }
    2124              : 
    2125              :   // move constructors
    2126              :   StructExprStructFields (StructExprStructFields &&other) = default;
    2127              :   StructExprStructFields &operator= (StructExprStructFields &&other) = default;
    2128              : 
    2129              :   void accept_vis (ASTVisitor &vis) override;
    2130              : 
    2131              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    2132         5855 :   std::vector<std::unique_ptr<StructExprField>> &get_fields ()
    2133              :   {
    2134        29524 :     return fields;
    2135              :   }
    2136              :   const std::vector<std::unique_ptr<StructExprField>> &get_fields () const
    2137              :   {
    2138              :     return fields;
    2139              :   }
    2140              : 
    2141         1344 :   StructBase &get_struct_base () { return struct_base; }
    2142              :   const StructBase &get_struct_base () const { return struct_base; }
    2143              : 
    2144              : protected:
    2145              :   /* Use covariance to implement clone function as returning this object rather
    2146              :    * than base */
    2147         1330 :   StructExprStructFields *clone_expr_without_block_impl () const override
    2148              :   {
    2149         1330 :     return new StructExprStructFields (*this);
    2150              :   }
    2151              : };
    2152              : 
    2153              : // AST node of the functional update struct creator
    2154              : /* TODO: remove and replace with StructExprStructFields, except with empty
    2155              :  * vector of fields? */
    2156              : class StructExprStructBase : public StructExprStruct
    2157              : {
    2158              :   StructBase struct_base;
    2159              : 
    2160              : public:
    2161              :   std::string as_string () const override;
    2162              : 
    2163              :   StructExprStructBase (PathInExpression struct_path, StructBase base_struct,
    2164              :                         std::vector<Attribute> inner_attribs,
    2165              :                         std::vector<Attribute> outer_attribs, location_t locus)
    2166              :     : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
    2167              :                         std::move (outer_attribs), locus),
    2168              :       struct_base (std::move (base_struct))
    2169              :   {}
    2170              : 
    2171              :   void accept_vis (ASTVisitor &vis) override;
    2172              : 
    2173            0 :   StructBase &get_struct_base () { return struct_base; }
    2174              :   const StructBase &get_struct_base () const { return struct_base; }
    2175              : 
    2176              : protected:
    2177              :   /* Use covariance to implement clone function as returning this object rather
    2178              :    * than base */
    2179            0 :   StructExprStructBase *clone_expr_without_block_impl () const override
    2180              :   {
    2181            0 :     return new StructExprStructBase (*this);
    2182              :   }
    2183              : };
    2184              : 
    2185              : // Forward decl for Function - used in CallExpr
    2186              : class Function;
    2187              : 
    2188              : // Function call expression AST node
    2189              : class CallExpr : public ExprWithoutBlock
    2190              : {
    2191              :   std::vector<Attribute> outer_attrs;
    2192              :   std::unique_ptr<Expr> function;
    2193              :   std::vector<std::unique_ptr<Expr>> params;
    2194              :   location_t locus;
    2195              : 
    2196              : public:
    2197              :   Function *fndeclRef;
    2198              : 
    2199              :   std::string as_string () const override;
    2200              : 
    2201        11224 :   CallExpr (std::unique_ptr<Expr> function_expr,
    2202              :             std::vector<std::unique_ptr<Expr>> function_params,
    2203              :             std::vector<Attribute> outer_attribs, location_t locus)
    2204        22448 :     : outer_attrs (std::move (outer_attribs)),
    2205        11224 :       function (std::move (function_expr)),
    2206        11224 :       params (std::move (function_params)), locus (locus)
    2207        11224 :   {}
    2208              : 
    2209              :   // copy constructor requires clone
    2210        19244 :   CallExpr (CallExpr const &other)
    2211        19244 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2212        19244 :       locus (other.locus)
    2213              :   {
    2214              :     // guard to prevent null dereference (only required if error state)
    2215        19244 :     if (other.function != nullptr)
    2216        19244 :       function = other.function->clone_expr ();
    2217              : 
    2218        19244 :     params.reserve (other.params.size ());
    2219        41072 :     for (const auto &e : other.params)
    2220        21828 :       params.push_back (e->clone_expr ());
    2221        19244 :   }
    2222              : 
    2223              :   // Overload assignment operator to clone
    2224              :   CallExpr &operator= (CallExpr const &other)
    2225              :   {
    2226              :     ExprWithoutBlock::operator= (other);
    2227              :     locus = other.locus;
    2228              :     outer_attrs = other.outer_attrs;
    2229              : 
    2230              :     // guard to prevent null dereference (only required if error state)
    2231              :     if (other.function != nullptr)
    2232              :       function = other.function->clone_expr ();
    2233              :     else
    2234              :       function = nullptr;
    2235              : 
    2236              :     params.reserve (other.params.size ());
    2237              :     for (const auto &e : other.params)
    2238              :       params.push_back (e->clone_expr ());
    2239              : 
    2240              :     return *this;
    2241              :   }
    2242              : 
    2243              :   // move constructors
    2244              :   CallExpr (CallExpr &&other) = default;
    2245              :   CallExpr &operator= (CallExpr &&other) = default;
    2246              : 
    2247              :   // Returns whether function call has parameters.
    2248            0 :   bool has_params () const { return !params.empty (); }
    2249              : 
    2250        22433 :   location_t get_locus () const override final { return locus; }
    2251              : 
    2252              :   void accept_vis (ASTVisitor &vis) override;
    2253              : 
    2254              :   // Invalid if function expr is null, so base stripping on that.
    2255            0 :   void mark_for_strip () override { function = nullptr; }
    2256        62361 :   bool is_marked_for_strip () const override { return function == nullptr; }
    2257              : 
    2258              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    2259              :   const std::vector<std::unique_ptr<Expr>> &get_params () const
    2260              :   {
    2261              :     return params;
    2262              :   }
    2263       292864 :   std::vector<std::unique_ptr<Expr>> &get_params () { return params; }
    2264              : 
    2265              :   // TODO: is this better? Or is a "vis_block" better?
    2266       281746 :   Expr &get_function_expr ()
    2267              :   {
    2268       281746 :     rust_assert (function != nullptr);
    2269       281746 :     return *function;
    2270              :   }
    2271              : 
    2272        11118 :   std::unique_ptr<Expr> &get_function_expr_ptr () { return function; }
    2273              : 
    2274              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2275       341420 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    2276              : 
    2277            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    2278              :   {
    2279            0 :     outer_attrs = std::move (new_attrs);
    2280            0 :   }
    2281              : 
    2282        11125 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Call; }
    2283              : 
    2284              : protected:
    2285              :   /* Use covariance to implement clone function as returning this object rather
    2286              :    * than base */
    2287        19244 :   CallExpr *clone_expr_without_block_impl () const override
    2288              :   {
    2289        19244 :     return new CallExpr (*this);
    2290              :   }
    2291              : };
    2292              : 
    2293              : // Method call expression AST node
    2294              : class MethodCallExpr : public ExprWithoutBlock
    2295              : {
    2296              :   std::vector<Attribute> outer_attrs;
    2297              :   std::unique_ptr<Expr> receiver;
    2298              :   PathExprSegment method_name;
    2299              :   std::vector<std::unique_ptr<Expr>> params;
    2300              :   location_t locus;
    2301              : 
    2302              : public:
    2303              :   std::string as_string () const override;
    2304              : 
    2305         3020 :   MethodCallExpr (std::unique_ptr<Expr> call_receiver,
    2306              :                   PathExprSegment method_path,
    2307              :                   std::vector<std::unique_ptr<Expr>> method_params,
    2308              :                   std::vector<Attribute> outer_attribs, location_t locus)
    2309         6040 :     : outer_attrs (std::move (outer_attribs)),
    2310         3020 :       receiver (std::move (call_receiver)),
    2311         3020 :       method_name (std::move (method_path)), params (std::move (method_params)),
    2312         3020 :       locus (locus)
    2313         3020 :   {}
    2314              : 
    2315              :   // copy constructor required due to cloning
    2316         7478 :   MethodCallExpr (MethodCallExpr const &other)
    2317         7478 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2318         7478 :       method_name (other.method_name), locus (other.locus)
    2319              :   {
    2320              :     // guard to prevent null dereference (only required if error state)
    2321         7478 :     if (other.receiver != nullptr)
    2322         7478 :       receiver = other.receiver->clone_expr ();
    2323              : 
    2324         7478 :     params.reserve (other.params.size ());
    2325        13594 :     for (const auto &e : other.params)
    2326         6116 :       params.push_back (e->clone_expr ());
    2327         7478 :   }
    2328              : 
    2329              :   // Overload assignment operator to clone receiver object
    2330              :   MethodCallExpr &operator= (MethodCallExpr const &other)
    2331              :   {
    2332              :     ExprWithoutBlock::operator= (other);
    2333              :     method_name = other.method_name;
    2334              :     locus = other.locus;
    2335              :     outer_attrs = other.outer_attrs;
    2336              : 
    2337              :     // guard to prevent null dereference (only required if error state)
    2338              :     if (other.receiver != nullptr)
    2339              :       receiver = other.receiver->clone_expr ();
    2340              :     else
    2341              :       receiver = nullptr;
    2342              : 
    2343              :     params.reserve (other.params.size ());
    2344              :     for (const auto &e : other.params)
    2345              :       params.push_back (e->clone_expr ());
    2346              : 
    2347              :     return *this;
    2348              :   }
    2349              : 
    2350              :   // move constructors
    2351              :   MethodCallExpr (MethodCallExpr &&other) = default;
    2352              :   MethodCallExpr &operator= (MethodCallExpr &&other) = default;
    2353              : 
    2354         6360 :   location_t get_locus () const override final { return locus; }
    2355              : 
    2356              :   void accept_vis (ASTVisitor &vis) override;
    2357              : 
    2358              :   // Invalid if receiver expr is null, so base stripping on that.
    2359            0 :   void mark_for_strip () override { receiver = nullptr; }
    2360        19705 :   bool is_marked_for_strip () const override { return receiver == nullptr; }
    2361              : 
    2362              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    2363              :   const std::vector<std::unique_ptr<Expr>> &get_params () const
    2364              :   {
    2365              :     return params;
    2366              :   }
    2367        79810 :   std::vector<std::unique_ptr<Expr>> &get_params () { return params; }
    2368              : 
    2369              :   // TODO: is this better? Or is a "vis_block" better?
    2370        70663 :   Expr &get_receiver_expr ()
    2371              :   {
    2372        70663 :     rust_assert (receiver != nullptr);
    2373        70663 :     return *receiver;
    2374              :   }
    2375              : 
    2376         9147 :   std::unique_ptr<Expr> &get_receiver_expr_ptr ()
    2377              :   {
    2378         9147 :     rust_assert (receiver != nullptr);
    2379         9147 :     return receiver;
    2380              :   }
    2381              : 
    2382              :   const PathExprSegment &get_method_name () const { return method_name; }
    2383        79810 :   PathExprSegment &get_method_name () { return method_name; }
    2384              : 
    2385              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2386       100350 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    2387              : 
    2388            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    2389              :   {
    2390            0 :     outer_attrs = std::move (new_attrs);
    2391            0 :   }
    2392              : 
    2393         2976 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::MethodCall; }
    2394              : 
    2395              : protected:
    2396              :   /* Use covariance to implement clone function as returning this object rather
    2397              :    * than base */
    2398         7478 :   MethodCallExpr *clone_expr_without_block_impl () const override
    2399              :   {
    2400         7478 :     return new MethodCallExpr (*this);
    2401              :   }
    2402              : };
    2403              : 
    2404              : // aka FieldExpression
    2405              : // Struct or union field access expression AST node
    2406              : class FieldAccessExpr : public ExprWithoutBlock
    2407              : {
    2408              :   std::vector<Attribute> outer_attrs;
    2409              :   std::unique_ptr<Expr> receiver;
    2410              :   Identifier field;
    2411              :   location_t locus;
    2412              : 
    2413              : public:
    2414              :   std::string as_string () const override;
    2415              : 
    2416         5290 :   FieldAccessExpr (std::unique_ptr<Expr> field_access_receiver,
    2417              :                    Identifier field_name, std::vector<Attribute> outer_attribs,
    2418              :                    location_t locus)
    2419        10580 :     : outer_attrs (std::move (outer_attribs)),
    2420         5290 :       receiver (std::move (field_access_receiver)),
    2421         5290 :       field (std::move (field_name)), locus (locus)
    2422         5290 :   {}
    2423              : 
    2424              :   // Copy constructor required due to unique_ptr cloning
    2425        16294 :   FieldAccessExpr (FieldAccessExpr const &other)
    2426        16294 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2427        16294 :       field (other.field), locus (other.locus)
    2428              :   {
    2429              :     // guard to prevent null dereference (only required if error state)
    2430        16294 :     if (other.receiver != nullptr)
    2431        16294 :       receiver = other.receiver->clone_expr ();
    2432        16294 :   }
    2433              : 
    2434              :   // Overload assignment operator to clone unique_ptr
    2435              :   FieldAccessExpr &operator= (FieldAccessExpr const &other)
    2436              :   {
    2437              :     ExprWithoutBlock::operator= (other);
    2438              :     field = other.field;
    2439              :     locus = other.locus;
    2440              :     outer_attrs = other.outer_attrs;
    2441              : 
    2442              :     // guard to prevent null dereference (only required if error state)
    2443              :     if (other.receiver != nullptr)
    2444              :       receiver = other.receiver->clone_expr ();
    2445              :     else
    2446              :       receiver = nullptr;
    2447              : 
    2448              :     return *this;
    2449              :   }
    2450              : 
    2451              :   // move constructors
    2452              :   FieldAccessExpr (FieldAccessExpr &&other) = default;
    2453              :   FieldAccessExpr &operator= (FieldAccessExpr &&other) = default;
    2454              : 
    2455        12667 :   location_t get_locus () const override final { return locus; }
    2456              : 
    2457              :   void accept_vis (ASTVisitor &vis) override;
    2458              : 
    2459              :   // Invalid if receiver expr is null, so base stripping on that.
    2460            0 :   void mark_for_strip () override { receiver = nullptr; }
    2461        36727 :   bool is_marked_for_strip () const override { return receiver == nullptr; }
    2462              : 
    2463              :   // TODO: is this better? Or is a "vis_block" better?
    2464       125270 :   Expr &get_receiver_expr ()
    2465              :   {
    2466       125270 :     rust_assert (receiver != nullptr);
    2467       125270 :     return *receiver;
    2468              :   }
    2469              : 
    2470        13613 :   std::unique_ptr<Expr> &get_receiver_expr_ptr ()
    2471              :   {
    2472        13613 :     rust_assert (receiver != nullptr);
    2473        13613 :     return receiver;
    2474              :   }
    2475              : 
    2476         5060 :   Identifier get_field_name () const { return field; }
    2477              : 
    2478              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2479       178979 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    2480              : 
    2481            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    2482              :   {
    2483            0 :     outer_attrs = std::move (new_attrs);
    2484            0 :   }
    2485              : 
    2486         4937 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::FieldAccess; }
    2487              : 
    2488              : protected:
    2489              :   /* Use covariance to implement clone function as returning this object rather
    2490              :    * than base */
    2491        16294 :   FieldAccessExpr *clone_expr_without_block_impl () const override
    2492              :   {
    2493        16294 :     return new FieldAccessExpr (*this);
    2494              :   }
    2495              : };
    2496              : 
    2497              : // Closure parameter data structure
    2498              : struct ClosureParam
    2499              : {
    2500              : private:
    2501              :   std::vector<Attribute> outer_attrs;
    2502              :   std::unique_ptr<Pattern> pattern;
    2503              :   std::unique_ptr<Type> type;
    2504              :   location_t locus;
    2505              : 
    2506              : public:
    2507              :   // Returns whether the type of the parameter has been given.
    2508         1400 :   bool has_type_given () const { return type != nullptr; }
    2509              : 
    2510              :   bool has_outer_attrs () const { return !outer_attrs.empty (); }
    2511              : 
    2512              :   // Constructor for closure parameter
    2513           72 :   ClosureParam (std::unique_ptr<Pattern> param_pattern, location_t locus,
    2514              :                 std::unique_ptr<Type> param_type = nullptr,
    2515              :                 std::vector<Attribute> outer_attrs = {})
    2516            0 :     : outer_attrs (std::move (outer_attrs)),
    2517            0 :       pattern (std::move (param_pattern)), type (std::move (param_type)),
    2518           72 :       locus (locus)
    2519              :   {}
    2520              : 
    2521              :   // Copy constructor required due to cloning as a result of unique_ptrs
    2522           63 :   ClosureParam (ClosureParam const &other) : outer_attrs (other.outer_attrs)
    2523              :   {
    2524              :     // guard to protect from null pointer dereference
    2525           63 :     if (other.pattern != nullptr)
    2526           63 :       pattern = other.pattern->clone_pattern ();
    2527           63 :     if (other.type != nullptr)
    2528           58 :       type = other.type->clone_type ();
    2529           63 :   }
    2530              : 
    2531          168 :   ~ClosureParam () = default;
    2532              : 
    2533              :   // Assignment operator must be overloaded to clone as well
    2534              :   ClosureParam &operator= (ClosureParam const &other)
    2535              :   {
    2536              :     outer_attrs = other.outer_attrs;
    2537              : 
    2538              :     // guard to protect from null pointer dereference
    2539              :     if (other.pattern != nullptr)
    2540              :       pattern = other.pattern->clone_pattern ();
    2541              :     else
    2542              :       pattern = nullptr;
    2543              :     if (other.type != nullptr)
    2544              :       type = other.type->clone_type ();
    2545              :     else
    2546              :       type = nullptr;
    2547              : 
    2548              :     return *this;
    2549              :   }
    2550              : 
    2551              :   // move constructors
    2552           83 :   ClosureParam (ClosureParam &&other) = default;
    2553            0 :   ClosureParam &operator= (ClosureParam &&other) = default;
    2554              : 
    2555              :   // Returns whether closure parameter is in an error state.
    2556           72 :   bool is_error () const { return pattern == nullptr; }
    2557              : 
    2558              :   // Creates an error state closure parameter.
    2559            0 :   static ClosureParam create_error ()
    2560              :   {
    2561            0 :     return ClosureParam (nullptr, UNDEF_LOCATION);
    2562              :   }
    2563              : 
    2564              :   std::string as_string () const;
    2565              : 
    2566              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2567         1213 :   std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
    2568              : 
    2569         1150 :   Pattern &get_pattern ()
    2570              :   {
    2571         1150 :     rust_assert (pattern != nullptr);
    2572         1150 :     return *pattern;
    2573              :   }
    2574              : 
    2575          189 :   std::unique_ptr<Pattern> &get_pattern_ptr ()
    2576              :   {
    2577          189 :     rust_assert (pattern != nullptr);
    2578          189 :     return pattern;
    2579              :   }
    2580              : 
    2581         1066 :   Type &get_type ()
    2582              :   {
    2583         1066 :     rust_assert (has_type_given ());
    2584         1066 :     return *type;
    2585              :   }
    2586              : 
    2587          174 :   std::unique_ptr<Type> &get_type_ptr ()
    2588              :   {
    2589          174 :     rust_assert (has_type_given ());
    2590          174 :     return type;
    2591              :   }
    2592              : 
    2593           83 :   location_t get_locus () const { return locus; }
    2594              : };
    2595              : 
    2596              : // Base closure definition expression AST node - abstract
    2597              : class ClosureExpr : public ExprWithoutBlock
    2598              : {
    2599              :   std::vector<Attribute> outer_attrs;
    2600              :   bool has_move;
    2601              :   std::vector<ClosureParam> params; // may be empty
    2602              :   location_t locus;
    2603              : 
    2604              : protected:
    2605           73 :   ClosureExpr (std::vector<ClosureParam> closure_params, bool has_move,
    2606              :                std::vector<Attribute> outer_attribs, location_t locus)
    2607          146 :     : outer_attrs (std::move (outer_attribs)), has_move (has_move),
    2608           73 :       params (std::move (closure_params)), locus (locus)
    2609           73 :   {}
    2610              : 
    2611              : public:
    2612              :   std::string as_string () const override;
    2613              : 
    2614          133 :   location_t get_locus () const override final { return locus; }
    2615              : 
    2616              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    2617              :   const std::vector<ClosureParam> &get_params () const { return params; }
    2618         1400 :   std::vector<ClosureParam> &get_params () { return params; }
    2619              : 
    2620              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2621         1489 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    2622              : 
    2623            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    2624              :   {
    2625            0 :     outer_attrs = std::move (new_attrs);
    2626            0 :   }
    2627              : 
    2628           95 :   bool get_has_move () const { return has_move; }
    2629              : 
    2630           67 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Closure; }
    2631              : 
    2632              :   virtual Expr &get_definition_expr () = 0;
    2633              :   virtual std::unique_ptr<Expr> &get_definition_expr_ptr () = 0;
    2634              : };
    2635              : 
    2636              : // Represents a non-type-specified closure expression AST node
    2637              : class ClosureExprInner : public ClosureExpr
    2638              : {
    2639              :   std::unique_ptr<Expr> closure_inner;
    2640              : 
    2641              : public:
    2642              :   std::string as_string () const override;
    2643              : 
    2644              :   // Constructor for a ClosureExprInner
    2645           42 :   ClosureExprInner (std::unique_ptr<Expr> closure_inner_expr,
    2646              :                     std::vector<ClosureParam> closure_params, location_t locus,
    2647              :                     bool is_move = false,
    2648              :                     std::vector<Attribute> outer_attribs
    2649              :                     = std::vector<Attribute> ())
    2650           42 :     : ClosureExpr (std::move (closure_params), is_move,
    2651              :                    std::move (outer_attribs), locus),
    2652           42 :       closure_inner (std::move (closure_inner_expr))
    2653           42 :   {}
    2654              : 
    2655              :   // Copy constructor must be defined to allow copying via cloning of unique_ptr
    2656           40 :   ClosureExprInner (ClosureExprInner const &other) : ClosureExpr (other)
    2657              :   {
    2658              :     // guard to prevent null dereference (only required if error state)
    2659           40 :     if (other.closure_inner != nullptr)
    2660           40 :       closure_inner = other.closure_inner->clone_expr ();
    2661           40 :   }
    2662              : 
    2663              :   // Overload assignment operator to clone closure_inner
    2664              :   ClosureExprInner &operator= (ClosureExprInner const &other)
    2665              :   {
    2666              :     ClosureExpr::operator= (other);
    2667              :     // params = other.params;
    2668              :     // has_move = other.has_move;
    2669              :     // outer_attrs = other.outer_attrs;
    2670              : 
    2671              :     // guard to prevent null dereference (only required if error state)
    2672              :     if (other.closure_inner != nullptr)
    2673              :       closure_inner = other.closure_inner->clone_expr ();
    2674              :     else
    2675              :       closure_inner = nullptr;
    2676              : 
    2677              :     return *this;
    2678              :   }
    2679              : 
    2680              :   // move constructors
    2681              :   ClosureExprInner (ClosureExprInner &&other) = default;
    2682              :   ClosureExprInner &operator= (ClosureExprInner &&other) = default;
    2683              : 
    2684              :   void accept_vis (ASTVisitor &vis) override;
    2685              : 
    2686              :   // Invalid if inner expr is null, so base stripping on that.
    2687            0 :   void mark_for_strip () override { closure_inner = nullptr; }
    2688          352 :   bool is_marked_for_strip () const override
    2689              :   {
    2690          352 :     return closure_inner == nullptr;
    2691              :   }
    2692              : 
    2693          658 :   Expr &get_definition_expr () override
    2694              :   {
    2695          658 :     rust_assert (closure_inner != nullptr);
    2696          658 :     return *closure_inner;
    2697              :   }
    2698              : 
    2699          111 :   std::unique_ptr<Expr> &get_definition_expr_ptr () override
    2700              :   {
    2701          111 :     rust_assert (closure_inner != nullptr);
    2702          111 :     return closure_inner;
    2703              :   }
    2704              : 
    2705              : protected:
    2706              :   /* Use covariance to implement clone function as returning this object rather
    2707              :    * than base */
    2708           40 :   ClosureExprInner *clone_expr_without_block_impl () const override
    2709              :   {
    2710           40 :     return new ClosureExprInner (*this);
    2711              :   }
    2712              : };
    2713              : 
    2714              : // A block AST node
    2715              : class BlockExpr : public ExprWithBlock
    2716              : {
    2717              :   std::vector<Attribute> outer_attrs;
    2718              :   std::vector<Attribute> inner_attrs;
    2719              :   std::vector<std::unique_ptr<Stmt>> statements;
    2720              :   std::unique_ptr<Expr> expr;
    2721              :   tl::optional<LoopLabel> label;
    2722              :   location_t start_locus;
    2723              :   location_t end_locus;
    2724              :   bool marked_for_strip = false;
    2725              : 
    2726              : public:
    2727              :   std::string as_string () const override;
    2728              : 
    2729              :   // Returns whether the block contains statements.
    2730            1 :   bool has_statements () const { return !statements.empty (); }
    2731              : 
    2732              :   // Returns whether the block contains a final expression.
    2733       628045 :   bool has_tail_expr () const { return expr != nullptr; }
    2734              : 
    2735        22477 :   BlockExpr (std::vector<std::unique_ptr<Stmt>> block_statements,
    2736              :              std::unique_ptr<Expr> block_expr,
    2737              :              std::vector<Attribute> inner_attribs,
    2738              :              std::vector<Attribute> outer_attribs,
    2739              :              tl::optional<LoopLabel> label, location_t start_locus,
    2740              :              location_t end_locus)
    2741        44954 :     : outer_attrs (std::move (outer_attribs)),
    2742        22477 :       inner_attrs (std::move (inner_attribs)),
    2743        22477 :       statements (std::move (block_statements)), expr (std::move (block_expr)),
    2744        22477 :       label (std::move (label)), start_locus (start_locus),
    2745        22477 :       end_locus (end_locus)
    2746        22477 :   {}
    2747              : 
    2748              :   // Copy constructor with clone
    2749        38043 :   BlockExpr (BlockExpr const &other)
    2750        38043 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    2751        38043 :       inner_attrs (other.inner_attrs), label (other.label),
    2752        38043 :       start_locus (other.start_locus), end_locus (other.end_locus),
    2753        76086 :       marked_for_strip (other.marked_for_strip)
    2754              :   {
    2755              :     // guard to protect from null pointer dereference
    2756        38043 :     if (other.expr != nullptr)
    2757        31305 :       expr = other.expr->clone_expr ();
    2758              : 
    2759        38043 :     statements.reserve (other.statements.size ());
    2760        67142 :     for (const auto &e : other.statements)
    2761        29099 :       statements.push_back (e->clone_stmt ());
    2762        38043 :   }
    2763              : 
    2764              :   // Overloaded assignment operator to clone pointer
    2765              :   BlockExpr &operator= (BlockExpr const &other)
    2766              :   {
    2767              :     ExprWithBlock::operator= (other);
    2768              :     inner_attrs = other.inner_attrs;
    2769              :     start_locus = other.start_locus;
    2770              :     end_locus = other.end_locus;
    2771              :     marked_for_strip = other.marked_for_strip;
    2772              :     outer_attrs = other.outer_attrs;
    2773              : 
    2774              :     // guard to protect from null pointer dereference
    2775              :     if (other.expr != nullptr)
    2776              :       expr = other.expr->clone_expr ();
    2777              :     else
    2778              :       expr = nullptr;
    2779              : 
    2780              :     statements.reserve (other.statements.size ());
    2781              :     for (const auto &e : other.statements)
    2782              :       statements.push_back (e->clone_stmt ());
    2783              : 
    2784              :     return *this;
    2785              :   }
    2786              : 
    2787              :   // move constructors
    2788              :   BlockExpr (BlockExpr &&other) = default;
    2789              :   BlockExpr &operator= (BlockExpr &&other) = default;
    2790              : 
    2791              :   // Unique pointer custom clone function
    2792        32753 :   std::unique_ptr<BlockExpr> clone_block_expr () const
    2793              :   {
    2794        32753 :     return std::unique_ptr<BlockExpr> (clone_block_expr_impl ());
    2795              :   }
    2796              : 
    2797         4735 :   location_t get_locus () const override final { return start_locus; }
    2798              : 
    2799        21916 :   location_t get_start_locus () const { return start_locus; }
    2800        21916 :   location_t get_end_locus () const { return end_locus; }
    2801              : 
    2802              :   void accept_vis (ASTVisitor &vis) override;
    2803              : 
    2804              :   // Can be completely empty, so have to have a separate flag.
    2805           54 :   void mark_for_strip () override { marked_for_strip = true; }
    2806        95909 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    2807              : 
    2808              :   size_t num_statements () const { return statements.size (); }
    2809              : 
    2810              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    2811              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    2812       487984 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    2813              : 
    2814              :   const std::vector<std::unique_ptr<Stmt>> &get_statements () const
    2815              :   {
    2816              :     return statements;
    2817              :   }
    2818       517613 :   std::vector<std::unique_ptr<Stmt>> &get_statements () { return statements; }
    2819              : 
    2820              :   // TODO: is this better? Or is a "vis_block" better?
    2821       325859 :   Expr &get_tail_expr ()
    2822              :   {
    2823       325859 :     rust_assert (has_tail_expr ());
    2824       325859 :     return *expr;
    2825              :   }
    2826              : 
    2827        55927 :   std::unique_ptr<Expr> &get_tail_expr_ptr ()
    2828              :   {
    2829        55927 :     rust_assert (has_tail_expr ());
    2830        55927 :     return expr;
    2831              :   }
    2832              : 
    2833        39875 :   std::unique_ptr<Expr> take_tail_expr ()
    2834              :   {
    2835        39875 :     rust_assert (has_tail_expr ());
    2836        39875 :     return std::move (expr);
    2837              :   }
    2838              : 
    2839        39875 :   void set_tail_expr (std::unique_ptr<Expr> expr)
    2840              :   {
    2841        39875 :     this->expr = std::move (expr);
    2842              :   }
    2843              : 
    2844              :   // Removes the tail expression from the block.
    2845           57 :   void strip_tail_expr () { expr = nullptr; }
    2846              :   // Normalizes a trailing statement without a semicolon to a tail expression.
    2847              :   void normalize_tail_expr ();
    2848              : 
    2849              :   void try_convert_last_stmt ();
    2850              : 
    2851              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2852       545003 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    2853              : 
    2854         1288 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    2855              :   {
    2856         1288 :     outer_attrs = std::move (new_attrs);
    2857         1288 :   }
    2858              : 
    2859       361979 :   bool has_label () { return label.has_value (); }
    2860            0 :   LoopLabel &get_label () { return label.value (); }
    2861              : 
    2862         1532 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Block; }
    2863              : 
    2864              : protected:
    2865              :   /* Use covariance to implement clone function as returning this object rather
    2866              :    * than base */
    2867         5290 :   BlockExpr *clone_expr_with_block_impl () const final override
    2868              :   {
    2869         5290 :     return clone_block_expr_impl ();
    2870              :   }
    2871              : 
    2872              :   /* This is the base method as not an abstract class - not virtual but could be
    2873              :    * in future if required. */
    2874        38043 :   /*virtual*/ BlockExpr *clone_block_expr_impl () const
    2875              :   {
    2876        38043 :     return new BlockExpr (*this);
    2877              :   }
    2878              : };
    2879              : 
    2880         3999 : class AnonConst : public ExprWithBlock
    2881              : {
    2882              : public:
    2883              :   enum class Kind
    2884              :   {
    2885              :     Explicit,
    2886              :     DeferredInference,
    2887              :   };
    2888              : 
    2889          671 :   AnonConst (std::unique_ptr<Expr> &&expr, location_t locus = UNKNOWN_LOCATION)
    2890         1342 :     : ExprWithBlock (), locus (locus), kind (Kind::Explicit),
    2891          671 :       expr (std::move (expr))
    2892              :   {
    2893          671 :     rust_assert (this->expr.value ());
    2894          671 :   }
    2895              : 
    2896           11 :   AnonConst (location_t locus = UNKNOWN_LOCATION)
    2897           22 :     : ExprWithBlock (), locus (locus), kind (Kind::DeferredInference),
    2898           11 :       expr (tl::nullopt)
    2899              :   {}
    2900              : 
    2901         4043 :   AnonConst (const AnonConst &other)
    2902         4043 :   {
    2903         4043 :     node_id = other.node_id;
    2904         4043 :     locus = other.locus;
    2905         4043 :     kind = other.kind;
    2906              : 
    2907         4043 :     if (other.expr)
    2908         3964 :       expr = other.expr.value ()->clone_expr ();
    2909         4043 :   }
    2910              : 
    2911              :   AnonConst operator= (const AnonConst &other)
    2912              :   {
    2913              :     node_id = other.node_id;
    2914              :     locus = other.locus;
    2915              :     kind = other.kind;
    2916              : 
    2917              :     if (other.expr)
    2918              :       expr = other.expr.value ()->clone_expr ();
    2919              : 
    2920              :     return *this;
    2921              :   }
    2922              : 
    2923              :   std::string as_string () const override;
    2924              : 
    2925            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::ConstExpr; }
    2926              : 
    2927         1362 :   location_t get_locus () const override { return locus; }
    2928              : 
    2929        12138 :   Expr &get_inner_expr ()
    2930              :   {
    2931        12138 :     rust_assert (expr.has_value ());
    2932        12138 :     return *expr.value ();
    2933              :   }
    2934              : 
    2935         2252 :   std::unique_ptr<Expr> &get_inner_expr_ptr ()
    2936              :   {
    2937         2252 :     rust_assert (expr.has_value ());
    2938         2252 :     return expr.value ();
    2939              :   }
    2940              : 
    2941          678 :   NodeId get_node_id () const override { return node_id; }
    2942              : 
    2943              :   /* FIXME: AnonConst are always "internal" and should not have outer attributes
    2944              :    * - is that true? Or should we instead call
    2945              :    * expr->get_outer_attrs()/expr->set_outer_attrs() */
    2946              : 
    2947            0 :   std::vector<Attribute> &get_outer_attrs () override
    2948              :   {
    2949        19205 :     static auto attrs = std::vector<Attribute> ();
    2950            0 :     return attrs;
    2951              :   }
    2952              : 
    2953            0 :   void set_outer_attrs (std::vector<Attribute>) override {}
    2954              : 
    2955              :   /* FIXME: Likewise for mark_for_strip() ? */
    2956            0 :   void mark_for_strip () override {}
    2957         4876 :   bool is_marked_for_strip () const override { return false; }
    2958              : 
    2959              :   void accept_vis (ASTVisitor &vis) override;
    2960              : 
    2961        14590 :   bool is_deferred () const { return kind == Kind::DeferredInference; }
    2962              : 
    2963              : private:
    2964              :   location_t locus;
    2965              :   Kind kind;
    2966              :   tl::optional<std::unique_ptr<Expr>> expr;
    2967              : 
    2968            0 :   AnonConst *clone_expr_with_block_impl () const override
    2969              :   {
    2970            0 :     return new AnonConst (*this);
    2971              :   }
    2972              : };
    2973              : 
    2974              : class ConstBlock : public ExprWithBlock
    2975              : {
    2976              : public:
    2977           15 :   ConstBlock (AnonConst &&expr, location_t locus = UNKNOWN_LOCATION,
    2978              :               std::vector<Attribute> &&outer_attrs = {})
    2979           30 :     : ExprWithBlock (), expr (std::move (expr)),
    2980           15 :       outer_attrs (std::move (outer_attrs)), locus (locus)
    2981           15 :   {}
    2982              : 
    2983           43 :   ConstBlock (const ConstBlock &other)
    2984           43 :     : ExprWithBlock (other), expr (other.expr), outer_attrs (other.outer_attrs),
    2985           43 :       locus (other.locus)
    2986           43 :   {}
    2987              : 
    2988              :   ConstBlock operator= (const ConstBlock &other)
    2989              :   {
    2990              :     expr = other.expr;
    2991              :     node_id = other.node_id;
    2992              :     outer_attrs = other.outer_attrs;
    2993              :     locus = other.locus;
    2994              : 
    2995              :     return *this;
    2996              :   }
    2997              : 
    2998              :   std::string as_string () const override;
    2999              : 
    3000           15 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::ConstBlock; }
    3001              : 
    3002          211 :   AnonConst &get_const_expr () { return expr; }
    3003              : 
    3004              :   void accept_vis (ASTVisitor &vis) override;
    3005              : 
    3006           15 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3007              : 
    3008            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3009              :   {
    3010            0 :     outer_attrs = std::move (new_attrs);
    3011            0 :   }
    3012              : 
    3013           30 :   location_t get_locus () const override { return locus; }
    3014              : 
    3015           30 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3016            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3017              : 
    3018              : private:
    3019              :   AnonConst expr;
    3020              : 
    3021              :   std::vector<Attribute> outer_attrs;
    3022              :   location_t locus;
    3023              :   bool marked_for_strip = false;
    3024              : 
    3025           43 :   ConstBlock *clone_expr_with_block_impl () const override
    3026              :   {
    3027           43 :     return new ConstBlock (*this);
    3028              :   }
    3029              : };
    3030              : 
    3031              : // Represents a type-specified closure expression AST node
    3032              : class ClosureExprInnerTyped : public ClosureExpr
    3033              : {
    3034              :   // TODO: spec says typenobounds
    3035              :   std::unique_ptr<Type> return_type;
    3036              :   std::unique_ptr<Expr> expr; // only used because may be polymorphic in future
    3037              : 
    3038              : public:
    3039              :   std::string as_string () const override;
    3040              : 
    3041              :   // Constructor potentially with a move
    3042           31 :   ClosureExprInnerTyped (std::unique_ptr<Type> closure_return_type,
    3043              :                          std::unique_ptr<BlockExpr> closure_expr,
    3044              :                          std::vector<ClosureParam> closure_params,
    3045              :                          location_t locus, bool is_move = false,
    3046              :                          std::vector<Attribute> outer_attribs
    3047              :                          = std::vector<Attribute> ())
    3048           31 :     : ClosureExpr (std::move (closure_params), is_move,
    3049              :                    std::move (outer_attribs), locus),
    3050           31 :       return_type (std::move (closure_return_type)),
    3051           31 :       expr (std::move (closure_expr))
    3052           31 :   {}
    3053              : 
    3054              :   // Copy constructor requires cloning
    3055           30 :   ClosureExprInnerTyped (ClosureExprInnerTyped const &other)
    3056           30 :     : ClosureExpr (other)
    3057              :   {
    3058              :     // guard to prevent null dereference (only required if error state)
    3059           30 :     if (other.expr != nullptr)
    3060           30 :       expr = other.expr->clone_expr ();
    3061           30 :     if (other.return_type != nullptr)
    3062           30 :       return_type = other.return_type->clone_type ();
    3063           30 :   }
    3064              : 
    3065              :   // Overload assignment operator to clone unique_ptrs
    3066              :   ClosureExprInnerTyped &operator= (ClosureExprInnerTyped const &other)
    3067              :   {
    3068              :     ClosureExpr::operator= (other);
    3069              :     // params = other.params;
    3070              :     // has_move = other.has_move;
    3071              :     // outer_attrs = other.outer_attrs;
    3072              : 
    3073              :     // guard to prevent null dereference (only required if error state)
    3074              :     if (other.expr != nullptr)
    3075              :       expr = other.expr->clone_expr ();
    3076              :     else
    3077              :       expr = nullptr;
    3078              :     if (other.return_type != nullptr)
    3079              :       return_type = other.return_type->clone_type ();
    3080              :     else
    3081              :       return_type = nullptr;
    3082              : 
    3083              :     return *this;
    3084              :   }
    3085              : 
    3086              :   // move constructors
    3087              :   ClosureExprInnerTyped (ClosureExprInnerTyped &&other) = default;
    3088              :   ClosureExprInnerTyped &operator= (ClosureExprInnerTyped &&other) = default;
    3089              : 
    3090              :   void accept_vis (ASTVisitor &vis) override;
    3091              : 
    3092              :   /* Invalid if inner expr is null, so base stripping on that. Technically,
    3093              :    * type should also not be null. */
    3094            0 :   void mark_for_strip () override { expr = nullptr; }
    3095          300 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    3096              : 
    3097              :   // TODO: is this better? Or is a "vis_block" better?
    3098          601 :   Expr &get_definition_expr () override
    3099              :   {
    3100          601 :     rust_assert (expr != nullptr);
    3101          601 :     return *expr;
    3102              :   }
    3103              : 
    3104           30 :   std::unique_ptr<Expr> &get_definition_expr_ptr () override
    3105              :   {
    3106           30 :     rust_assert (expr != nullptr);
    3107              : 
    3108           30 :     return expr;
    3109              :   }
    3110              : 
    3111              :   // TODO: is this better? Or is a "vis_block" better?
    3112          511 :   Type &get_return_type ()
    3113              :   {
    3114          511 :     rust_assert (return_type != nullptr);
    3115          511 :     return *return_type;
    3116              :   }
    3117              : 
    3118           90 :   std::unique_ptr<Type> &get_return_type_ptr ()
    3119              :   {
    3120           90 :     rust_assert (return_type != nullptr);
    3121           90 :     return return_type;
    3122              :   }
    3123              : 
    3124              : protected:
    3125              :   /* Use covariance to implement clone function as returning this object rather
    3126              :    * than base */
    3127           30 :   ClosureExprInnerTyped *clone_expr_without_block_impl () const override
    3128              :   {
    3129           30 :     return new ClosureExprInnerTyped (*this);
    3130              :   }
    3131              : };
    3132              : 
    3133              : // AST node representing continue expression within loops
    3134              : class ContinueExpr : public ExprWithoutBlock
    3135              : {
    3136              :   std::vector<Attribute> outer_attrs;
    3137              :   tl::optional<Lifetime> label;
    3138              :   location_t locus;
    3139              : 
    3140              :   // TODO: find another way to store this to save memory?
    3141              :   bool marked_for_strip = false;
    3142              : 
    3143              : public:
    3144              :   std::string as_string () const override;
    3145              : 
    3146              :   // Returns true if the continue expr has a label.
    3147          205 :   bool has_label () const { return label.has_value (); }
    3148              : 
    3149              :   // Constructor for a ContinueExpr with a label.
    3150           17 :   ContinueExpr (tl::optional<Lifetime> label,
    3151              :                 std::vector<Attribute> outer_attribs, location_t locus)
    3152           51 :     : outer_attrs (std::move (outer_attribs)), label (std::move (label)),
    3153           17 :       locus (locus)
    3154           17 :   {}
    3155              : 
    3156           34 :   location_t get_locus () const override final { return locus; }
    3157              : 
    3158              :   void accept_vis (ASTVisitor &vis) override;
    3159              : 
    3160              :   // Can't think of any invalid invariants, so store boolean.
    3161            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3162          154 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3163              : 
    3164              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3165          500 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3166              : 
    3167            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3168              :   {
    3169            0 :     outer_attrs = std::move (new_attrs);
    3170            0 :   }
    3171              : 
    3172           36 :   Lifetime &get_label_unchecked () { return label.value (); }
    3173            0 :   const Lifetime &get_label_unchecked () const { return label.value (); }
    3174              : 
    3175              :   tl::optional<Lifetime> &get_label () { return label; }
    3176              :   const tl::optional<Lifetime> &get_label () const { return label; }
    3177              : 
    3178           17 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Continue; }
    3179              : 
    3180              : protected:
    3181              :   /* Use covariance to implement clone function as returning this object rather
    3182              :    * than base */
    3183           17 :   ContinueExpr *clone_expr_without_block_impl () const override
    3184              :   {
    3185           17 :     return new ContinueExpr (*this);
    3186              :   }
    3187              : };
    3188              : // TODO: merge "break" and "continue"? Or even merge in "return"?
    3189              : 
    3190              : // AST node representing break expression within loops
    3191              : class BreakExpr : public ExprWithoutBlock
    3192              : {
    3193              :   std::vector<Attribute> outer_attrs;
    3194              :   tl::optional<LoopLabel> label;
    3195              :   tl::optional<std::unique_ptr<Expr>> break_expr;
    3196              :   location_t locus;
    3197              : 
    3198              :   // TODO: find another way to store this to save memory?
    3199              :   bool marked_for_strip = false;
    3200              : 
    3201              : public:
    3202              :   std::string as_string () const override;
    3203              : 
    3204              :   // Returns whether the break expression has a label or not.
    3205         1916 :   bool has_label () const { return label.has_value (); }
    3206              : 
    3207              :   /* Returns whether the break expression has an expression used in the break or
    3208              :    * not. */
    3209         2641 :   bool has_break_expr () const { return break_expr.has_value (); }
    3210              : 
    3211              :   // Constructor for a break expression
    3212           97 :   BreakExpr (tl::optional<LoopLabel> break_label,
    3213              :              tl::optional<std::unique_ptr<Expr>> expr_in_break,
    3214              :              std::vector<Attribute> outer_attribs, location_t locus)
    3215          291 :     : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)),
    3216           97 :       break_expr (std::move (expr_in_break)), locus (locus)
    3217              :   {
    3218           97 :     if (this->has_break_expr ())
    3219           22 :       rust_assert (this->break_expr != nullptr);
    3220           97 :   }
    3221              : 
    3222              :   // Copy constructor defined to use clone for unique pointer
    3223          111 :   BreakExpr (BreakExpr const &other)
    3224          111 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3225          111 :       label (other.label), locus (other.locus),
    3226          222 :       marked_for_strip (other.marked_for_strip)
    3227              :   {
    3228          111 :     if (other.has_break_expr ())
    3229           22 :       break_expr = other.get_break_expr_unchecked ().clone_expr ();
    3230          111 :   }
    3231              : 
    3232              :   // Overload assignment operator to clone unique pointer
    3233              :   BreakExpr &operator= (BreakExpr const &other)
    3234              :   {
    3235              :     ExprWithoutBlock::operator= (other);
    3236              :     label = other.label;
    3237              :     locus = other.locus;
    3238              :     marked_for_strip = other.marked_for_strip;
    3239              :     outer_attrs = other.outer_attrs;
    3240              : 
    3241              :     // guard to protect from null pointer dereference
    3242              :     if (other.has_break_expr ())
    3243              :       break_expr = other.get_break_expr_unchecked ().clone_expr ();
    3244              :     else
    3245              :       break_expr = tl::nullopt;
    3246              : 
    3247              :     return *this;
    3248              :   }
    3249              : 
    3250              :   // move constructors
    3251              :   BreakExpr (BreakExpr &&other) = default;
    3252              :   BreakExpr &operator= (BreakExpr &&other) = default;
    3253              : 
    3254          200 :   location_t get_locus () const override final { return locus; }
    3255              : 
    3256              :   void accept_vis (ASTVisitor &vis) override;
    3257              : 
    3258              :   // Can't think of any invalid invariants, so store boolean.
    3259            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3260          628 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3261              : 
    3262              :   // TODO: is this better? Or is a "vis_block" better?
    3263          747 :   Expr &get_break_expr_unchecked ()
    3264              :   {
    3265          747 :     rust_assert (has_break_expr ());
    3266          747 :     return *break_expr.value ();
    3267              :   }
    3268              : 
    3269           22 :   const Expr &get_break_expr_unchecked () const
    3270              :   {
    3271           22 :     rust_assert (has_break_expr ());
    3272           22 :     return *break_expr.value ();
    3273              :   }
    3274              : 
    3275           66 :   std::unique_ptr<Expr> &get_break_expr_ptr_unchecked ()
    3276              :   {
    3277           66 :     rust_assert (has_break_expr ());
    3278           66 :     return break_expr.value ();
    3279              :   }
    3280              : 
    3281              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3282         3059 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3283              : 
    3284            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3285              :   {
    3286            0 :     outer_attrs = std::move (new_attrs);
    3287            0 :   }
    3288              : 
    3289          418 :   LoopLabel &get_label_unchecked () { return label.value (); }
    3290            0 :   const LoopLabel &get_label_unchecked () const { return label.value (); }
    3291              : 
    3292              :   tl::optional<LoopLabel> &get_label () { return label; }
    3293              :   const tl::optional<LoopLabel> &get_label () const { return label; }
    3294              : 
    3295           95 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Break; }
    3296              : 
    3297              : protected:
    3298              :   /* Use covariance to implement clone function as returning this object rather
    3299              :    * than base */
    3300          111 :   BreakExpr *clone_expr_without_block_impl () const override
    3301              :   {
    3302          111 :     return new BreakExpr (*this);
    3303              :   }
    3304              : };
    3305              : 
    3306              : // Base range expression AST node object - abstract
    3307           88 : class RangeExpr : public ExprWithoutBlock
    3308              : {
    3309              :   location_t locus;
    3310              : 
    3311              :   // Some visitors still check for attributes on RangeExprs, and they will need
    3312              :   // to be supported in the future - so keep that for now
    3313              :   std::vector<Attribute> empty_attributes = {};
    3314              : 
    3315              : protected:
    3316              :   // outer attributes not allowed before range expressions
    3317           93 :   RangeExpr (location_t locus) : locus (locus) {}
    3318              : 
    3319              : public:
    3320          198 :   location_t get_locus () const override final { return locus; }
    3321              : 
    3322            0 :   std::vector<Attribute> &get_outer_attrs () override final
    3323              :   {
    3324            0 :     return empty_attributes;
    3325              :   }
    3326              : 
    3327              :   // should never be called - error if called
    3328            0 :   void set_outer_attrs (std::vector<Attribute> /* new_attrs */) override {}
    3329              : 
    3330           87 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Range; }
    3331              : };
    3332              : 
    3333              : // Range from (inclusive) and to (exclusive) expression AST node object
    3334              : // aka RangeExpr; constructs a std::ops::Range object
    3335              : class RangeFromToExpr : public RangeExpr
    3336              : {
    3337              :   std::unique_ptr<Expr> from;
    3338              :   std::unique_ptr<Expr> to;
    3339              : 
    3340              : public:
    3341              :   std::string as_string () const override;
    3342              : 
    3343           69 :   RangeFromToExpr (std::unique_ptr<Expr> range_from,
    3344              :                    std::unique_ptr<Expr> range_to, location_t locus)
    3345          138 :     : RangeExpr (locus), from (std::move (range_from)),
    3346           69 :       to (std::move (range_to))
    3347              :   {}
    3348              : 
    3349              :   // Copy constructor with cloning
    3350           67 :   RangeFromToExpr (RangeFromToExpr const &other) : RangeExpr (other)
    3351              :   {
    3352              :     // guard to prevent null dereference (only required if error state)
    3353           67 :     if (other.from != nullptr)
    3354           67 :       from = other.from->clone_expr ();
    3355           67 :     if (other.to != nullptr)
    3356           67 :       to = other.to->clone_expr ();
    3357           67 :   }
    3358              : 
    3359              :   // Overload assignment operator to clone unique pointers
    3360              :   RangeFromToExpr &operator= (RangeFromToExpr const &other)
    3361              :   {
    3362              :     RangeExpr::operator= (other);
    3363              : 
    3364              :     // guard to prevent null dereference (only required if error state)
    3365              :     if (other.from != nullptr)
    3366              :       from = other.from->clone_expr ();
    3367              :     else
    3368              :       from = nullptr;
    3369              :     if (other.to != nullptr)
    3370              :       to = other.to->clone_expr ();
    3371              :     else
    3372              :       to = nullptr;
    3373              : 
    3374              :     return *this;
    3375              :   }
    3376              : 
    3377              :   // move constructors
    3378              :   RangeFromToExpr (RangeFromToExpr &&other) = default;
    3379              :   RangeFromToExpr &operator= (RangeFromToExpr &&other) = default;
    3380              : 
    3381              :   void accept_vis (ASTVisitor &vis) override;
    3382              : 
    3383              :   // Invalid if either expr is null, so base stripping on that.
    3384            0 :   void mark_for_strip () override
    3385              :   {
    3386            0 :     from = nullptr;
    3387            0 :     to = nullptr;
    3388            0 :   }
    3389          356 :   bool is_marked_for_strip () const override
    3390              :   {
    3391          356 :     return from == nullptr && to == nullptr;
    3392              :   }
    3393              : 
    3394              :   // TODO: is this better? Or is a "vis_block" better?
    3395         1457 :   Expr &get_from_expr ()
    3396              :   {
    3397         1457 :     rust_assert (from != nullptr);
    3398         1457 :     return *from;
    3399              :   }
    3400              : 
    3401              :   // TODO: is this better? Or is a "vis_block" better?
    3402         1457 :   Expr &get_to_expr ()
    3403              :   {
    3404         1457 :     rust_assert (to != nullptr);
    3405         1457 :     return *to;
    3406              :   }
    3407              : 
    3408          246 :   std::unique_ptr<Expr> &get_from_expr_ptr ()
    3409              :   {
    3410          246 :     rust_assert (from != nullptr);
    3411          246 :     return from;
    3412              :   }
    3413              : 
    3414          246 :   std::unique_ptr<Expr> &get_to_expr_ptr ()
    3415              :   {
    3416          246 :     rust_assert (to != nullptr);
    3417          246 :     return to;
    3418              :   }
    3419              : 
    3420              : protected:
    3421              :   /* Use covariance to implement clone function as returning this object rather
    3422              :    * than base */
    3423           67 :   RangeFromToExpr *clone_expr_without_block_impl () const override
    3424              :   {
    3425           67 :     return new RangeFromToExpr (*this);
    3426              :   }
    3427              : };
    3428              : 
    3429              : // Range from (inclusive) expression AST node object
    3430              : // constructs a std::ops::RangeFrom object
    3431              : class RangeFromExpr : public RangeExpr
    3432              : {
    3433              :   std::unique_ptr<Expr> from;
    3434              : 
    3435              : public:
    3436              :   std::string as_string () const override;
    3437              : 
    3438            9 :   RangeFromExpr (std::unique_ptr<Expr> range_from, location_t locus)
    3439            9 :     : RangeExpr (locus), from (std::move (range_from))
    3440              :   {}
    3441              : 
    3442              :   // Copy constructor with clone
    3443            7 :   RangeFromExpr (RangeFromExpr const &other) : RangeExpr (other)
    3444              :   {
    3445              :     // guard to prevent null dereference (only required if error state)
    3446            7 :     if (other.from != nullptr)
    3447            7 :       from = other.from->clone_expr ();
    3448            7 :   }
    3449              : 
    3450              :   // Overload assignment operator to clone unique_ptr
    3451              :   RangeFromExpr &operator= (RangeFromExpr const &other)
    3452              :   {
    3453              :     RangeExpr::operator= (other);
    3454              : 
    3455              :     // guard to prevent null dereference (only required if error state)
    3456              :     if (other.from != nullptr)
    3457              :       from = other.from->clone_expr ();
    3458              :     else
    3459              :       from = nullptr;
    3460              : 
    3461              :     return *this;
    3462              :   }
    3463              : 
    3464              :   // move constructors
    3465              :   RangeFromExpr (RangeFromExpr &&other) = default;
    3466              :   RangeFromExpr &operator= (RangeFromExpr &&other) = default;
    3467              : 
    3468              :   void accept_vis (ASTVisitor &vis) override;
    3469              : 
    3470              :   // Invalid if expr is null, so base stripping on that.
    3471            0 :   void mark_for_strip () override { from = nullptr; }
    3472           28 :   bool is_marked_for_strip () const override { return from == nullptr; }
    3473              : 
    3474              :   // TODO: is this better? Or is a "vis_block" better?
    3475          126 :   Expr &get_from_expr ()
    3476              :   {
    3477          126 :     rust_assert (from != nullptr);
    3478          126 :     return *from;
    3479              :   }
    3480              : 
    3481           21 :   std::unique_ptr<Expr> &get_from_expr_ptr ()
    3482              :   {
    3483           21 :     rust_assert (from != nullptr);
    3484           21 :     return from;
    3485              :   }
    3486              : 
    3487              : protected:
    3488              :   /* Use covariance to implement clone function as returning this object rather
    3489              :    * than base */
    3490            7 :   RangeFromExpr *clone_expr_without_block_impl () const override
    3491              :   {
    3492            7 :     return new RangeFromExpr (*this);
    3493              :   }
    3494              : };
    3495              : 
    3496              : // Range to (exclusive) expression AST node object
    3497              : // constructs a std::ops::RangeTo object
    3498              : class RangeToExpr : public RangeExpr
    3499              : {
    3500              :   std::unique_ptr<Expr> to;
    3501              : 
    3502              : public:
    3503              :   std::string as_string () const override;
    3504              : 
    3505              :   // outer attributes not allowed
    3506            8 :   RangeToExpr (std::unique_ptr<Expr> range_to, location_t locus)
    3507            8 :     : RangeExpr (locus), to (std::move (range_to))
    3508              :   {}
    3509              : 
    3510              :   // Copy constructor with clone
    3511            7 :   RangeToExpr (RangeToExpr const &other) : RangeExpr (other)
    3512              :   {
    3513              :     // guard to prevent null dereference (only required if error state)
    3514            7 :     if (other.to != nullptr)
    3515            7 :       to = other.to->clone_expr ();
    3516            7 :   }
    3517              : 
    3518              :   // Overload assignment operator to clone unique_ptr
    3519              :   RangeToExpr &operator= (RangeToExpr const &other)
    3520              :   {
    3521              :     RangeExpr::operator= (other);
    3522              : 
    3523              :     // guard to prevent null dereference (only required if error state)
    3524              :     if (other.to != nullptr)
    3525              :       to = other.to->clone_expr ();
    3526              :     else
    3527              :       to = nullptr;
    3528              : 
    3529              :     return *this;
    3530              :   }
    3531              : 
    3532              :   // move constructors
    3533              :   RangeToExpr (RangeToExpr &&other) = default;
    3534              :   RangeToExpr &operator= (RangeToExpr &&other) = default;
    3535              : 
    3536              :   void accept_vis (ASTVisitor &vis) override;
    3537              : 
    3538              :   // Invalid if expr is null, so base stripping on that.
    3539            0 :   void mark_for_strip () override { to = nullptr; }
    3540           28 :   bool is_marked_for_strip () const override { return to == nullptr; }
    3541              : 
    3542              :   // TODO: is this better? Or is a "vis_block" better?
    3543          126 :   Expr &get_to_expr ()
    3544              :   {
    3545          126 :     rust_assert (to != nullptr);
    3546          126 :     return *to;
    3547              :   }
    3548              : 
    3549           21 :   std::unique_ptr<Expr> &get_to_expr_ptr ()
    3550              :   {
    3551           21 :     rust_assert (to != nullptr);
    3552           21 :     return to;
    3553              :   }
    3554              : 
    3555              : protected:
    3556              :   /* Use covariance to implement clone function as returning this object rather
    3557              :    * than base */
    3558            7 :   RangeToExpr *clone_expr_without_block_impl () const override
    3559              :   {
    3560            7 :     return new RangeToExpr (*this);
    3561              :   }
    3562              : };
    3563              : 
    3564              : // Full range expression AST node object
    3565              : // constructs a std::ops::RangeFull object
    3566              : class RangeFullExpr : public RangeExpr
    3567              : {
    3568              :   // TODO: find another way to store this to save memory?
    3569              :   bool marked_for_strip = false;
    3570              : 
    3571              : public:
    3572              :   std::string as_string () const override;
    3573              : 
    3574            1 :   RangeFullExpr (location_t locus) : RangeExpr (locus) {}
    3575              :   // outer attributes not allowed
    3576              : 
    3577              :   void accept_vis (ASTVisitor &vis) override;
    3578              : 
    3579              :   // Can't think of any invalid invariants, so store boolean.
    3580            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3581            0 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3582              : 
    3583              : protected:
    3584              :   /* Use covariance to implement clone function as returning this object rather
    3585              :    * than base */
    3586            0 :   RangeFullExpr *clone_expr_without_block_impl () const override
    3587              :   {
    3588            0 :     return new RangeFullExpr (*this);
    3589              :   }
    3590              : };
    3591              : 
    3592              : // Range from (inclusive) and to (inclusive) expression AST node object
    3593              : // aka RangeInclusiveExpr; constructs a std::ops::RangeInclusive object
    3594              : class RangeFromToInclExpr : public RangeExpr
    3595              : {
    3596              :   std::unique_ptr<Expr> from;
    3597              :   std::unique_ptr<Expr> to;
    3598              : 
    3599              : public:
    3600              :   std::string as_string () const override;
    3601              : 
    3602            7 :   RangeFromToInclExpr (std::unique_ptr<Expr> range_from,
    3603              :                        std::unique_ptr<Expr> range_to, location_t locus)
    3604           14 :     : RangeExpr (locus), from (std::move (range_from)),
    3605            7 :       to (std::move (range_to))
    3606              :   {}
    3607              :   // outer attributes not allowed
    3608              : 
    3609              :   // Copy constructor with clone
    3610            7 :   RangeFromToInclExpr (RangeFromToInclExpr const &other) : RangeExpr (other)
    3611              :   {
    3612              :     // guard to prevent null dereference (only required if error state)
    3613            7 :     if (other.from != nullptr)
    3614            7 :       from = other.from->clone_expr ();
    3615            7 :     if (other.to != nullptr)
    3616            7 :       to = other.to->clone_expr ();
    3617            7 :   }
    3618              : 
    3619              :   // Overload assignment operator to use clone
    3620              :   RangeFromToInclExpr &operator= (RangeFromToInclExpr const &other)
    3621              :   {
    3622              :     RangeExpr::operator= (other);
    3623              : 
    3624              :     // guard to prevent null dereference (only required if error state)
    3625              :     if (other.from != nullptr)
    3626              :       from = other.from->clone_expr ();
    3627              :     else
    3628              :       from = nullptr;
    3629              :     if (other.to != nullptr)
    3630              :       to = other.to->clone_expr ();
    3631              :     else
    3632              :       to = nullptr;
    3633              : 
    3634              :     return *this;
    3635              :   }
    3636              : 
    3637              :   // move constructors
    3638              :   RangeFromToInclExpr (RangeFromToInclExpr &&other) = default;
    3639              :   RangeFromToInclExpr &operator= (RangeFromToInclExpr &&other) = default;
    3640              : 
    3641              :   void accept_vis (ASTVisitor &vis) override;
    3642              : 
    3643              :   // Invalid if either expr is null, so base stripping on that.
    3644            0 :   void mark_for_strip () override
    3645              :   {
    3646            0 :     from = nullptr;
    3647            0 :     to = nullptr;
    3648            0 :   }
    3649           28 :   bool is_marked_for_strip () const override
    3650              :   {
    3651           28 :     return from == nullptr && to == nullptr;
    3652              :   }
    3653              : 
    3654              :   // TODO: is this better? Or is a "vis_block" better?
    3655          126 :   Expr &get_from_expr ()
    3656              :   {
    3657          126 :     rust_assert (from != nullptr);
    3658          126 :     return *from;
    3659              :   }
    3660              : 
    3661              :   // TODO: is this better? Or is a "vis_block" better?
    3662          126 :   Expr &get_to_expr ()
    3663              :   {
    3664          126 :     rust_assert (to != nullptr);
    3665          126 :     return *to;
    3666              :   }
    3667              : 
    3668           21 :   std::unique_ptr<Expr> &get_from_expr_ptr ()
    3669              :   {
    3670           21 :     rust_assert (from != nullptr);
    3671           21 :     return from;
    3672              :   }
    3673              : 
    3674           21 :   std::unique_ptr<Expr> &get_to_expr_ptr ()
    3675              :   {
    3676           21 :     rust_assert (to != nullptr);
    3677           21 :     return to;
    3678              :   }
    3679              : 
    3680              : protected:
    3681              :   /* Use covariance to implement clone function as returning this object rather
    3682              :    * than base */
    3683            7 :   RangeFromToInclExpr *clone_expr_without_block_impl () const override
    3684              :   {
    3685            7 :     return new RangeFromToInclExpr (*this);
    3686              :   }
    3687              : };
    3688              : 
    3689              : // Range to (inclusive) expression AST node object
    3690              : // aka RangeToInclusiveExpr; constructs a std::ops::RangeToInclusive object
    3691              : class RangeToInclExpr : public RangeExpr
    3692              : {
    3693              :   std::unique_ptr<Expr> to;
    3694              : 
    3695              : public:
    3696              :   std::string as_string () const override;
    3697              : 
    3698            0 :   RangeToInclExpr (std::unique_ptr<Expr> range_to, location_t locus)
    3699            0 :     : RangeExpr (locus), to (std::move (range_to))
    3700              :   {}
    3701              :   // outer attributes not allowed
    3702              : 
    3703              :   // Copy constructor with clone
    3704            0 :   RangeToInclExpr (RangeToInclExpr const &other) : RangeExpr (other)
    3705              :   {
    3706              :     // guard to prevent null dereference (only required if error state)
    3707            0 :     if (other.to != nullptr)
    3708            0 :       to = other.to->clone_expr ();
    3709            0 :   }
    3710              : 
    3711              :   // Overload assignment operator to clone pointer
    3712              :   RangeToInclExpr &operator= (RangeToInclExpr const &other)
    3713              :   {
    3714              :     RangeExpr::operator= (other);
    3715              : 
    3716              :     // guard to prevent null dereference (only required if error state)
    3717              :     if (other.to != nullptr)
    3718              :       to = other.to->clone_expr ();
    3719              :     else
    3720              :       to = nullptr;
    3721              : 
    3722              :     return *this;
    3723              :   }
    3724              : 
    3725              :   // move constructors
    3726              :   RangeToInclExpr (RangeToInclExpr &&other) = default;
    3727              :   RangeToInclExpr &operator= (RangeToInclExpr &&other) = default;
    3728              : 
    3729              :   void accept_vis (ASTVisitor &vis) override;
    3730              : 
    3731              :   // Invalid if expr is null, so base stripping on that.
    3732            0 :   void mark_for_strip () override { to = nullptr; }
    3733            0 :   bool is_marked_for_strip () const override { return to == nullptr; }
    3734              : 
    3735              :   // TODO: is this better? Or is a "vis_block" better?
    3736            0 :   Expr &get_to_expr ()
    3737              :   {
    3738            0 :     rust_assert (to != nullptr);
    3739            0 :     return *to;
    3740              :   }
    3741              : 
    3742            0 :   std::unique_ptr<Expr> &get_to_expr_ptr ()
    3743              :   {
    3744            0 :     rust_assert (to != nullptr);
    3745            0 :     return to;
    3746              :   }
    3747              : 
    3748              : protected:
    3749              :   /* Use covariance to implement clone function as returning this object rather
    3750              :    * than base */
    3751            0 :   RangeToInclExpr *clone_expr_without_block_impl () const override
    3752              :   {
    3753            0 :     return new RangeToInclExpr (*this);
    3754              :   }
    3755              : };
    3756              : 
    3757              : class BoxExpr : public ExprWithoutBlock
    3758              : {
    3759              :   std::unique_ptr<Expr> expr;
    3760              :   std::vector<Attribute> outer_attrs;
    3761              :   location_t locus;
    3762              : 
    3763              : public:
    3764            2 :   BoxExpr (std::unique_ptr<Expr> expr, std::vector<Attribute> outer_attrs,
    3765              :            location_t locus)
    3766            2 :     : expr (std::move (expr)), outer_attrs (outer_attrs), locus (locus)
    3767            2 :   {}
    3768              : 
    3769              :   // Copy constructor with clone
    3770            1 :   BoxExpr (BoxExpr const &other)
    3771            1 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3772            1 :       locus (other.locus)
    3773              :   {
    3774              :     // guard to protect from null pointer dereference
    3775            1 :     if (other.expr != nullptr)
    3776            1 :       expr = other.expr->clone_expr ();
    3777            1 :   }
    3778              : 
    3779              :   BoxExpr &operator= (BoxExpr const &other)
    3780              :   {
    3781              :     ExprWithoutBlock::operator= (other);
    3782              :     locus = other.locus;
    3783              :     outer_attrs = other.outer_attrs;
    3784              : 
    3785              :     // guard to protect from null pointer dereference
    3786              :     if (other.expr != nullptr)
    3787              :       expr = other.expr->clone_expr ();
    3788              :     else
    3789              :       expr = nullptr;
    3790              : 
    3791              :     return *this;
    3792              :   }
    3793              : 
    3794              :   // move constructors
    3795              :   BoxExpr (BoxExpr &&other) = default;
    3796              :   BoxExpr &operator= (BoxExpr &&other) = default;
    3797              : 
    3798            1 :   location_t get_locus () const override final { return locus; }
    3799              : 
    3800              :   void accept_vis (ASTVisitor &vis) override;
    3801              : 
    3802            0 :   void mark_for_strip () override { expr = nullptr; }
    3803            4 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    3804              : 
    3805              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3806           16 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3807              : 
    3808            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3809              :   {
    3810            0 :     outer_attrs = std::move (new_attrs);
    3811            0 :   }
    3812              : 
    3813              :   std::string as_string () const override;
    3814              : 
    3815           13 :   Expr &get_boxed_expr ()
    3816              :   {
    3817           13 :     rust_assert (expr != nullptr);
    3818           13 :     return *expr;
    3819              :   }
    3820              : 
    3821            3 :   std::unique_ptr<Expr> &get_boxed_expr_ptr ()
    3822              :   {
    3823            3 :     rust_assert (expr != nullptr);
    3824            3 :     return expr;
    3825              :   }
    3826              : 
    3827            1 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Box; }
    3828              : 
    3829              : protected:
    3830              :   /* Use covariance to implement clone function as returning this object rather
    3831              :    * than base */
    3832            1 :   BoxExpr *clone_expr_without_block_impl () const override
    3833              :   {
    3834            1 :     return new BoxExpr (*this);
    3835              :   }
    3836              : };
    3837              : 
    3838              : // Return expression AST node representation
    3839              : class ReturnExpr : public ExprWithoutBlock
    3840              : {
    3841              :   std::vector<Attribute> outer_attrs;
    3842              :   tl::optional<std::unique_ptr<Expr>> return_expr;
    3843              :   location_t locus;
    3844              : 
    3845              :   // TODO: find another way to store this to save memory?
    3846              :   bool marked_for_strip = false;
    3847              : 
    3848              : public:
    3849              :   std::string as_string () const override;
    3850              : 
    3851              :   /* Returns whether the object has an expression returned (i.e. not void return
    3852              :    * type). */
    3853        13264 :   bool has_returned_expr () const { return return_expr.has_value (); }
    3854              : 
    3855              :   // Constructor for ReturnExpr.
    3856          547 :   ReturnExpr (tl::optional<std::unique_ptr<Expr>> returned_expr,
    3857              :               std::vector<Attribute> outer_attribs, location_t locus)
    3858         1094 :     : outer_attrs (std::move (outer_attribs)),
    3859          547 :       return_expr (std::move (returned_expr)), locus (locus)
    3860          547 :   {}
    3861              : 
    3862              :   // Copy constructor with clone
    3863          606 :   ReturnExpr (ReturnExpr const &other)
    3864          606 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3865          606 :       locus (other.locus), marked_for_strip (other.marked_for_strip)
    3866              :   {
    3867              :     // guard to protect from null pointer dereference
    3868          606 :     if (other.return_expr)
    3869          566 :       return_expr = other.return_expr.value ()->clone_expr ();
    3870          606 :   }
    3871              : 
    3872              :   // Overloaded assignment operator to clone return_expr pointer
    3873              :   ReturnExpr &operator= (ReturnExpr const &other)
    3874              :   {
    3875              :     ExprWithoutBlock::operator= (other);
    3876              :     locus = other.locus;
    3877              :     marked_for_strip = other.marked_for_strip;
    3878              :     outer_attrs = other.outer_attrs;
    3879              : 
    3880              :     // guard to protect from null pointer dereference
    3881              :     if (other.return_expr)
    3882              :       return_expr = other.return_expr.value ()->clone_expr ();
    3883              :     else
    3884              :       return_expr = tl::nullopt;
    3885              : 
    3886              :     return *this;
    3887              :   }
    3888              : 
    3889              :   // move constructors
    3890              :   ReturnExpr (ReturnExpr &&other) = default;
    3891              :   ReturnExpr &operator= (ReturnExpr &&other) = default;
    3892              : 
    3893         1072 :   location_t get_locus () const override final { return locus; }
    3894              : 
    3895              :   void accept_vis (ASTVisitor &vis) override;
    3896              : 
    3897              :   // Can't think of any invalid invariants, so store boolean.
    3898            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3899         3108 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3900              : 
    3901              :   // TODO: is this better? Or is a "vis_block" better?
    3902        10586 :   Expr &get_returned_expr ()
    3903              :   {
    3904        10586 :     rust_assert (return_expr);
    3905        10586 :     return *return_expr.value ();
    3906              :   }
    3907              : 
    3908            0 :   const Expr &get_returned_expr () const
    3909              :   {
    3910            0 :     rust_assert (return_expr);
    3911            0 :     return *return_expr.value ();
    3912              :   }
    3913              : 
    3914         1523 :   std::unique_ptr<Expr> &get_returned_expr_ptr ()
    3915              :   {
    3916         1523 :     rust_assert (return_expr);
    3917         1523 :     return return_expr.value ();
    3918              :   }
    3919              : 
    3920              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3921        15872 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3922              : 
    3923            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3924              :   {
    3925            0 :     outer_attrs = std::move (new_attrs);
    3926            0 :   }
    3927              : 
    3928          542 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Return; }
    3929              : 
    3930              : protected:
    3931              :   /* Use covariance to implement clone function as returning this object rather
    3932              :    * than base */
    3933          606 :   ReturnExpr *clone_expr_without_block_impl () const override
    3934              :   {
    3935          606 :     return new ReturnExpr (*this);
    3936              :   }
    3937              : };
    3938              : 
    3939              : // Try expression AST node representation
    3940              : class TryExpr : public ExprWithBlock
    3941              : {
    3942              :   std::vector<Attribute> outer_attrs;
    3943              :   std::unique_ptr<BlockExpr> block_expr;
    3944              :   location_t locus;
    3945              : 
    3946              :   // TODO: find another way to store this to save memory?
    3947              :   bool marked_for_strip = false;
    3948              : 
    3949              : public:
    3950              :   std::string as_string () const override;
    3951              : 
    3952              :   // Constructor for ReturnExpr.
    3953            1 :   TryExpr (std::unique_ptr<BlockExpr> block_expr,
    3954              :            std::vector<Attribute> outer_attribs, location_t locus)
    3955            2 :     : outer_attrs (std::move (outer_attribs)),
    3956            1 :       block_expr (std::move (block_expr)), locus (locus)
    3957              :   {
    3958            1 :     rust_assert (this->block_expr);
    3959            1 :   }
    3960              : 
    3961              :   // Copy constructor with clone
    3962            2 :   TryExpr (TryExpr const &other)
    3963            2 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    3964            2 :       block_expr (other.block_expr->clone_block_expr ()), locus (other.locus),
    3965            2 :       marked_for_strip (other.marked_for_strip)
    3966            2 :   {}
    3967              : 
    3968              :   // Overloaded assignment operator to clone return_expr pointer
    3969              :   TryExpr &operator= (TryExpr const &other)
    3970              :   {
    3971              :     ExprWithBlock::operator= (other);
    3972              :     locus = other.locus;
    3973              :     marked_for_strip = other.marked_for_strip;
    3974              :     outer_attrs = other.outer_attrs;
    3975              : 
    3976              :     block_expr = other.block_expr->clone_block_expr ();
    3977              : 
    3978              :     return *this;
    3979              :   }
    3980              : 
    3981              :   // move constructors
    3982              :   TryExpr (TryExpr &&other) = default;
    3983              :   TryExpr &operator= (TryExpr &&other) = default;
    3984              : 
    3985            1 :   location_t get_locus () const override final { return locus; }
    3986              : 
    3987              :   void accept_vis (ASTVisitor &vis) override;
    3988              : 
    3989              :   // Can't think of any invalid invariants, so store boolean.
    3990            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3991            4 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3992              : 
    3993              :   // TODO: is this better? Or is a "vis_block" better?
    3994            9 :   BlockExpr &get_block_expr () { return *block_expr; }
    3995            2 :   std::unique_ptr<BlockExpr> &get_block_expr_ptr () { return block_expr; }
    3996              : 
    3997              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3998           10 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3999              : 
    4000            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4001              :   {
    4002            0 :     outer_attrs = std::move (new_attrs);
    4003            0 :   }
    4004              : 
    4005            2 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Try; }
    4006              : 
    4007              : protected:
    4008              :   /* Use covariance to implement clone function as returning this object rather
    4009              :    * than base */
    4010            1 :   TryExpr *clone_expr_with_block_impl () const override
    4011              :   {
    4012            1 :     return new TryExpr (*this);
    4013              :   }
    4014              : };
    4015              : 
    4016              : // Forward decl - defined in rust-macro.h
    4017              : class MacroInvocation;
    4018              : 
    4019              : // An unsafe block AST node
    4020              : class UnsafeBlockExpr : public ExprWithBlock
    4021              : {
    4022              :   std::vector<Attribute> outer_attrs;
    4023              :   // Or just have it extend BlockExpr
    4024              :   std::unique_ptr<BlockExpr> expr;
    4025              :   location_t locus;
    4026              : 
    4027              : public:
    4028              :   std::string as_string () const override;
    4029              : 
    4030         3544 :   UnsafeBlockExpr (std::unique_ptr<BlockExpr> block_expr,
    4031              :                    std::vector<Attribute> outer_attribs, location_t locus)
    4032         7088 :     : outer_attrs (std::move (outer_attribs)), expr (std::move (block_expr)),
    4033         3544 :       locus (locus)
    4034         3544 :   {}
    4035              : 
    4036              :   // Copy constructor with clone
    4037         6251 :   UnsafeBlockExpr (UnsafeBlockExpr const &other)
    4038         6251 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4039         6251 :       locus (other.locus)
    4040              :   {
    4041              :     // guard to prevent null dereference (only required if error state)
    4042         6251 :     if (other.expr != nullptr)
    4043         6251 :       expr = other.expr->clone_block_expr ();
    4044         6251 :   }
    4045              : 
    4046              :   // Overloaded assignment operator to clone
    4047              :   UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other)
    4048              :   {
    4049              :     ExprWithBlock::operator= (other);
    4050              :     locus = other.locus;
    4051              :     outer_attrs = other.outer_attrs;
    4052              : 
    4053              :     // guard to prevent null dereference (only required if error state)
    4054              :     if (other.expr != nullptr)
    4055              :       expr = other.expr->clone_block_expr ();
    4056              :     else
    4057              :       expr = nullptr;
    4058              : 
    4059              :     return *this;
    4060              :   }
    4061              : 
    4062              :   // move constructors
    4063              :   UnsafeBlockExpr (UnsafeBlockExpr &&other) = default;
    4064              :   UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default;
    4065              : 
    4066         7438 :   location_t get_locus () const override final { return locus; }
    4067              : 
    4068              :   void accept_vis (ASTVisitor &vis) override;
    4069              : 
    4070              :   // Invalid if block is null, so base stripping on that.
    4071            7 :   void mark_for_strip () override { expr = nullptr; }
    4072        16480 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    4073              : 
    4074              :   // TODO: is this better? Or is a "vis_block" better?
    4075        71612 :   BlockExpr &get_block_expr ()
    4076              :   {
    4077        71612 :     rust_assert (expr != nullptr);
    4078        71612 :     return *expr;
    4079              :   }
    4080              : 
    4081        11553 :   std::unique_ptr<BlockExpr> &get_block_expr_ptr ()
    4082              :   {
    4083        11553 :     rust_assert (expr != nullptr);
    4084        11553 :     return expr;
    4085              :   }
    4086              : 
    4087              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4088       104959 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4089              : 
    4090         3151 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4091              :   {
    4092         3151 :     outer_attrs = std::move (new_attrs);
    4093         3151 :   }
    4094              : 
    4095         3517 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::UnsafeBlock; }
    4096              : 
    4097              : protected:
    4098              :   /* Use covariance to implement clone function as returning this object rather
    4099              :    * than base */
    4100         6251 :   UnsafeBlockExpr *clone_expr_with_block_impl () const override
    4101              :   {
    4102         6251 :     return new UnsafeBlockExpr (*this);
    4103              :   }
    4104              : };
    4105              : 
    4106              : // Base loop expression AST node - aka LoopExpr
    4107              : class BaseLoopExpr : public ExprWithBlock
    4108              : {
    4109              : protected:
    4110              :   // protected to allow subclasses better use of them
    4111              :   std::vector<Attribute> outer_attrs;
    4112              :   tl::optional<LoopLabel> loop_label;
    4113              :   std::unique_ptr<BlockExpr> loop_block;
    4114              : 
    4115              : private:
    4116              :   location_t locus;
    4117              : 
    4118              : protected:
    4119              :   // Constructor for BaseLoopExpr
    4120          232 :   BaseLoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4121              :                 tl::optional<LoopLabel> loop_label = tl::nullopt,
    4122              :                 std::vector<Attribute> outer_attribs
    4123              :                 = std::vector<Attribute> ())
    4124          464 :     : outer_attrs (std::move (outer_attribs)),
    4125          232 :       loop_label (std::move (loop_label)), loop_block (std::move (loop_block)),
    4126          232 :       locus (locus)
    4127          232 :   {}
    4128              : 
    4129              :   // Copy constructor for BaseLoopExpr with clone
    4130          232 :   BaseLoopExpr (BaseLoopExpr const &other)
    4131          232 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4132          232 :       loop_label (other.loop_label), locus (other.locus)
    4133              :   {
    4134              :     // guard to prevent null dereference (only required if error state)
    4135          232 :     if (other.loop_block != nullptr)
    4136          232 :       loop_block = other.loop_block->clone_block_expr ();
    4137          232 :   }
    4138              : 
    4139              :   // Overloaded assignment operator to clone
    4140              :   BaseLoopExpr &operator= (BaseLoopExpr const &other)
    4141              :   {
    4142              :     ExprWithBlock::operator= (other);
    4143              :     loop_label = other.loop_label;
    4144              :     locus = other.locus;
    4145              :     outer_attrs = other.outer_attrs;
    4146              : 
    4147              :     // guard to prevent null dereference (only required if error state)
    4148              :     if (other.loop_block != nullptr)
    4149              :       loop_block = other.loop_block->clone_block_expr ();
    4150              :     else
    4151              :       loop_block = nullptr;
    4152              : 
    4153              :     return *this;
    4154              :   }
    4155              : 
    4156              :   // move constructors
    4157              :   BaseLoopExpr (BaseLoopExpr &&other) = default;
    4158              :   BaseLoopExpr &operator= (BaseLoopExpr &&other) = default;
    4159              : 
    4160              : public:
    4161         4007 :   bool has_loop_label () const { return loop_label.has_value (); }
    4162              : 
    4163          608 :   LoopLabel &get_loop_label () { return loop_label.value (); }
    4164            0 :   const LoopLabel &get_loop_label () const { return loop_label.value (); }
    4165              : 
    4166          455 :   location_t get_locus () const override final { return locus; }
    4167              : 
    4168              :   // Invalid if loop block is null, so base stripping on that.
    4169            0 :   void mark_for_strip () override { loop_block = nullptr; }
    4170          924 :   bool is_marked_for_strip () const override { return loop_block == nullptr; }
    4171              : 
    4172              :   // TODO: is this better? Or is a "vis_block" better?
    4173         4210 :   BlockExpr &get_loop_block ()
    4174              :   {
    4175         4210 :     rust_assert (loop_block != nullptr);
    4176         4210 :     return *loop_block;
    4177              :   }
    4178              : 
    4179          739 :   std::unique_ptr<BlockExpr> &get_loop_block_ptr ()
    4180              :   {
    4181          739 :     rust_assert (loop_block != nullptr);
    4182          739 :     return loop_block;
    4183              :   }
    4184              : 
    4185              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4186         6016 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4187              : 
    4188          178 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4189              :   {
    4190          178 :     outer_attrs = std::move (new_attrs);
    4191          178 :   }
    4192              : 
    4193          243 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Loop; }
    4194              : 
    4195              :   enum class Kind
    4196              :   {
    4197              :     Loop,
    4198              :     While,
    4199              :     WhileLet,
    4200              :     For
    4201              :   };
    4202              : 
    4203              :   virtual Kind get_loop_kind () const = 0;
    4204              : };
    4205              : 
    4206              : // 'Loop' expression (i.e. the infinite loop) AST node
    4207          258 : class LoopExpr : public BaseLoopExpr
    4208              : {
    4209              : public:
    4210              :   std::string as_string () const override;
    4211              : 
    4212              :   // Constructor for LoopExpr
    4213          132 :   LoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4214           36 :             tl::optional<LoopLabel> loop_label = tl::nullopt,
    4215              :             std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
    4216          132 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4217          170 :                     std::move (outer_attribs))
    4218          132 :   {}
    4219              : 
    4220              :   void accept_vis (ASTVisitor &vis) override;
    4221              : 
    4222          129 :   BaseLoopExpr::Kind get_loop_kind () const override
    4223              :   {
    4224          129 :     return BaseLoopExpr::Kind::Loop;
    4225              :   }
    4226              : 
    4227              : protected:
    4228              :   /* Use covariance to implement clone function as returning this object rather
    4229              :    * than base */
    4230          129 :   LoopExpr *clone_expr_with_block_impl () const override
    4231              :   {
    4232          129 :     return new LoopExpr (*this);
    4233              :   }
    4234              : };
    4235              : 
    4236              : // While loop expression AST node (predicate loop)
    4237              : class WhileLoopExpr : public BaseLoopExpr
    4238              : {
    4239              :   std::unique_ptr<Expr> condition;
    4240              : 
    4241              : public:
    4242              :   std::string as_string () const override;
    4243              : 
    4244              :   // Constructor for while loop with loop label
    4245           78 :   WhileLoopExpr (std::unique_ptr<Expr> loop_condition,
    4246              :                  std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4247              :                  tl::optional<LoopLabel> loop_label = tl::nullopt,
    4248              :                  std::vector<Attribute> outer_attribs
    4249              :                  = std::vector<Attribute> ())
    4250           78 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4251              :                     std::move (outer_attribs)),
    4252           80 :       condition (std::move (loop_condition))
    4253           78 :   {}
    4254              : 
    4255              :   // Copy constructor with clone
    4256           85 :   WhileLoopExpr (WhileLoopExpr const &other)
    4257           85 :     : BaseLoopExpr (other), condition (other.condition->clone_expr ())
    4258           85 :   {}
    4259              : 
    4260              :   // Overloaded assignment operator to clone
    4261              :   WhileLoopExpr &operator= (WhileLoopExpr const &other)
    4262              :   {
    4263              :     BaseLoopExpr::operator= (other);
    4264              :     condition = other.condition->clone_expr ();
    4265              :     // loop_block = other.loop_block->clone_block_expr();
    4266              :     // loop_label = other.loop_label;
    4267              :     // outer_attrs = other.outer_attrs;
    4268              : 
    4269              :     return *this;
    4270              :   }
    4271              : 
    4272              :   // move constructors
    4273              :   WhileLoopExpr (WhileLoopExpr &&other) = default;
    4274              :   WhileLoopExpr &operator= (WhileLoopExpr &&other) = default;
    4275              : 
    4276              :   void accept_vis (ASTVisitor &vis) override;
    4277              : 
    4278              :   // TODO: is this better? Or is a "vis_block" better?
    4279         1854 :   Expr &get_predicate_expr ()
    4280              :   {
    4281         1854 :     rust_assert (condition != nullptr);
    4282         1854 :     return *condition;
    4283              :   }
    4284              : 
    4285          298 :   std::unique_ptr<Expr> &get_predicate_expr_ptr ()
    4286              :   {
    4287          298 :     rust_assert (condition != nullptr);
    4288          298 :     return condition;
    4289              :   }
    4290              : 
    4291           78 :   BaseLoopExpr::Kind get_loop_kind () const override
    4292              :   {
    4293           78 :     return BaseLoopExpr::Kind::While;
    4294              :   }
    4295              : 
    4296              : protected:
    4297              :   /* Use covariance to implement clone function as returning this object rather
    4298              :    * than base */
    4299           85 :   WhileLoopExpr *clone_expr_with_block_impl () const override
    4300              :   {
    4301           85 :     return new WhileLoopExpr (*this);
    4302              :   }
    4303              : };
    4304              : 
    4305              : // While let loop expression AST node (predicate pattern loop)
    4306              : class WhileLetLoopExpr : public BaseLoopExpr
    4307              : {
    4308              :   // MatchArmPatterns patterns;
    4309              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4310              :   std::unique_ptr<Expr> scrutinee;
    4311              : 
    4312              : public:
    4313              :   std::string as_string () const override;
    4314              : 
    4315              :   // Constructor with a loop label
    4316            3 :   WhileLetLoopExpr (std::unique_ptr<Pattern> match_arm_pattern,
    4317              :                     std::unique_ptr<Expr> scrutinee,
    4318              :                     std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4319              :                     tl::optional<LoopLabel> loop_label = tl::nullopt,
    4320              :                     std::vector<Attribute> outer_attribs
    4321              :                     = std::vector<Attribute> ())
    4322            3 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4323              :                     std::move (outer_attribs)),
    4324            3 :       match_arm_pattern (std::move (match_arm_pattern)),
    4325            3 :       scrutinee (std::move (scrutinee))
    4326            3 :   {}
    4327              : 
    4328              :   // Copy constructor with clone
    4329            2 :   WhileLetLoopExpr (WhileLetLoopExpr const &other)
    4330            2 :     : BaseLoopExpr (other),
    4331            2 :       /*match_arm_patterns(other.match_arm_patterns),*/ scrutinee (
    4332            2 :         other.scrutinee->clone_expr ())
    4333              :   {
    4334            2 :     match_arm_pattern = other.get_pattern ()->clone_pattern ();
    4335            2 :   }
    4336              : 
    4337              :   // Overloaded assignment operator to clone pointers
    4338              :   WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other)
    4339              :   {
    4340              :     BaseLoopExpr::operator= (other);
    4341              :     scrutinee = other.scrutinee->clone_expr ();
    4342              :     match_arm_pattern = other.get_pattern ()->clone_pattern ();
    4343              :     return *this;
    4344              :   }
    4345              : 
    4346              :   // move constructors
    4347              :   WhileLetLoopExpr (WhileLetLoopExpr &&other) = default;
    4348              :   WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default;
    4349              : 
    4350              :   void accept_vis (ASTVisitor &vis) override;
    4351              : 
    4352              :   // TODO: is this better? Or is a "vis_block" better?
    4353           18 :   Expr &get_scrutinee_expr ()
    4354              :   {
    4355           18 :     rust_assert (scrutinee != nullptr);
    4356           18 :     return *scrutinee;
    4357              :   }
    4358              : 
    4359            4 :   std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
    4360              :   {
    4361            4 :     rust_assert (scrutinee != nullptr);
    4362            4 :     return scrutinee;
    4363              :   }
    4364              : 
    4365              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4366            2 :   const std::unique_ptr<Pattern> &get_pattern () const
    4367              :   {
    4368            2 :     return match_arm_pattern;
    4369              :   }
    4370           22 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4371              : 
    4372            4 :   BaseLoopExpr::Kind get_loop_kind () const override
    4373              :   {
    4374            4 :     return BaseLoopExpr::Kind::WhileLet;
    4375              :   }
    4376              : 
    4377              : protected:
    4378              :   /* Use covariance to implement clone function as returning this object rather
    4379              :    * than base */
    4380            2 :   WhileLetLoopExpr *clone_expr_with_block_impl () const override
    4381              :   {
    4382            2 :     return new WhileLetLoopExpr (*this);
    4383              :   }
    4384              : };
    4385              : 
    4386              : // For loop expression AST node (iterator loop)
    4387              : class ForLoopExpr : public BaseLoopExpr
    4388              : {
    4389              :   std::unique_ptr<Pattern> pattern;
    4390              :   std::unique_ptr<Expr> iterator_expr;
    4391              : 
    4392              : public:
    4393              :   std::string as_string () const override;
    4394              : 
    4395              :   // Constructor with loop label
    4396           19 :   ForLoopExpr (std::unique_ptr<Pattern> loop_pattern,
    4397              :                std::unique_ptr<Expr> iterator_expr,
    4398              :                std::unique_ptr<BlockExpr> loop_body, location_t locus,
    4399              :                tl::optional<LoopLabel> loop_label = tl::nullopt,
    4400              :                std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
    4401           19 :     : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label),
    4402              :                     std::move (outer_attribs)),
    4403           19 :       pattern (std::move (loop_pattern)),
    4404           19 :       iterator_expr (std::move (iterator_expr))
    4405           19 :   {}
    4406              : 
    4407              :   // Copy constructor with clone
    4408           16 :   ForLoopExpr (ForLoopExpr const &other)
    4409           48 :     : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()),
    4410           16 :       iterator_expr (other.iterator_expr->clone_expr ())
    4411           16 :   {}
    4412              : 
    4413              :   // Overloaded assignment operator to clone
    4414              :   ForLoopExpr &operator= (ForLoopExpr const &other)
    4415              :   {
    4416              :     BaseLoopExpr::operator= (other);
    4417              :     pattern = other.pattern->clone_pattern ();
    4418              :     iterator_expr = other.iterator_expr->clone_expr ();
    4419              :     /*loop_block = other.loop_block->clone_block_expr();
    4420              :     loop_label = other.loop_label;
    4421              :     outer_attrs = other.outer_attrs;*/
    4422              : 
    4423              :     return *this;
    4424              :   }
    4425              : 
    4426              :   // move constructors
    4427              :   ForLoopExpr (ForLoopExpr &&other) = default;
    4428              :   ForLoopExpr &operator= (ForLoopExpr &&other) = default;
    4429              : 
    4430              :   void accept_vis (ASTVisitor &vis) override;
    4431              : 
    4432              :   // TODO: is this better? Or is a "vis_block" better?
    4433          384 :   Expr &get_iterator_expr ()
    4434              :   {
    4435          384 :     rust_assert (iterator_expr != nullptr);
    4436          384 :     return *iterator_expr;
    4437              :   }
    4438              : 
    4439           64 :   std::unique_ptr<Expr> &get_iterator_expr_ptr ()
    4440              :   {
    4441           64 :     rust_assert (iterator_expr != nullptr);
    4442           64 :     return iterator_expr;
    4443              :   }
    4444              : 
    4445              :   // TODO: is this better? Or is a "vis_block" better?
    4446          384 :   Pattern &get_pattern ()
    4447              :   {
    4448          384 :     rust_assert (pattern != nullptr);
    4449          384 :     return *pattern;
    4450              :   }
    4451              : 
    4452           64 :   std::unique_ptr<Pattern> &get_pattern_ptr ()
    4453              :   {
    4454           64 :     rust_assert (pattern != nullptr);
    4455           64 :     return pattern;
    4456              :   }
    4457              : 
    4458           32 :   BaseLoopExpr::Kind get_loop_kind () const override
    4459              :   {
    4460           32 :     return BaseLoopExpr::Kind::For;
    4461              :   }
    4462              : 
    4463              : protected:
    4464              :   /* Use covariance to implement clone function as returning this object rather
    4465              :    * than base */
    4466           16 :   ForLoopExpr *clone_expr_with_block_impl () const override
    4467              :   {
    4468           16 :     return new ForLoopExpr (*this);
    4469              :   }
    4470              : };
    4471              : 
    4472              : // forward decl for IfExpr
    4473              : class IfLetExpr;
    4474              : 
    4475              : // Base if expression with no "else" or "if let" AST node
    4476              : class IfExpr : public ExprWithBlock
    4477              : {
    4478              :   std::vector<Attribute> outer_attrs;
    4479              :   std::unique_ptr<Expr> condition;
    4480              :   std::unique_ptr<BlockExpr> if_block;
    4481              :   location_t locus;
    4482              : 
    4483              : public:
    4484              :   std::string as_string () const override;
    4485              : 
    4486         1693 :   IfExpr (std::unique_ptr<Expr> condition, std::unique_ptr<BlockExpr> if_block,
    4487              :           std::vector<Attribute> outer_attrs, location_t locus)
    4488         3386 :     : outer_attrs (std::move (outer_attrs)), condition (std::move (condition)),
    4489         1693 :       if_block (std::move (if_block)), locus (locus)
    4490         1693 :   {}
    4491              :   // outer attributes are never allowed on IfExprs
    4492              : 
    4493              :   // Copy constructor with clone
    4494         2209 :   IfExpr (IfExpr const &other)
    4495         2209 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4496         2209 :       locus (other.locus)
    4497              :   {
    4498              :     // guard to prevent null dereference (only required if error state)
    4499         2209 :     if (other.condition != nullptr)
    4500         2209 :       condition = other.condition->clone_expr ();
    4501         2209 :     if (other.if_block != nullptr)
    4502         2209 :       if_block = other.if_block->clone_block_expr ();
    4503         2209 :   }
    4504              : 
    4505              :   // Overloaded assignment operator to clone expressions
    4506              :   IfExpr &operator= (IfExpr const &other)
    4507              :   {
    4508              :     ExprWithBlock::operator= (other);
    4509              :     outer_attrs = other.outer_attrs;
    4510              :     locus = other.locus;
    4511              : 
    4512              :     // guard to prevent null dereference (only required if error state)
    4513              :     if (other.condition != nullptr)
    4514              :       condition = other.condition->clone_expr ();
    4515              :     else
    4516              :       condition = nullptr;
    4517              :     if (other.if_block != nullptr)
    4518              :       if_block = other.if_block->clone_block_expr ();
    4519              :     else
    4520              :       if_block = nullptr;
    4521              : 
    4522              :     return *this;
    4523              :   }
    4524              : 
    4525              :   // move constructors
    4526              :   IfExpr (IfExpr &&other) = default;
    4527              :   IfExpr &operator= (IfExpr &&other) = default;
    4528              : 
    4529              :   // Unique pointer custom clone function
    4530              :   std::unique_ptr<IfExpr> clone_if_expr () const
    4531              :   {
    4532              :     return std::unique_ptr<IfExpr> (clone_if_expr_impl ());
    4533              :   }
    4534              : 
    4535              :   /* Note that multiple "else if"s are handled via nested ASTs rather than a
    4536              :    * vector of else ifs - i.e. not like a switch statement. TODO - is this a
    4537              :    * better approach? or does it not parse correctly and have downsides? */
    4538              : 
    4539         3268 :   location_t get_locus () const override final { return locus; }
    4540              : 
    4541              :   void accept_vis (ASTVisitor &vis) override;
    4542              : 
    4543              :   void vis_if_condition (ASTVisitor &vis) { condition->accept_vis (vis); }
    4544              :   void vis_if_block (ASTVisitor &vis) { if_block->accept_vis (vis); }
    4545              : 
    4546              :   // TODO: is this better? Or is a "vis_block" better?
    4547        40910 :   Expr &get_condition_expr ()
    4548              :   {
    4549        40910 :     rust_assert (condition != nullptr);
    4550        40910 :     return *condition;
    4551              :   }
    4552              : 
    4553         5906 :   std::unique_ptr<Expr> &get_condition_expr_ptr ()
    4554              :   {
    4555         5906 :     rust_assert (condition != nullptr);
    4556         5906 :     return condition;
    4557              :   }
    4558              : 
    4559              :   // TODO: is this better? Or is a "vis_block" better?
    4560        46816 :   BlockExpr &get_if_block ()
    4561              :   {
    4562        46816 :     rust_assert (if_block != nullptr);
    4563        46816 :     return *if_block;
    4564              :   }
    4565              : 
    4566              :   // Invalid if if block or condition is null, so base stripping on that.
    4567            0 :   void mark_for_strip () override
    4568              :   {
    4569            0 :     if_block = nullptr;
    4570            0 :     condition = nullptr;
    4571            0 :   }
    4572         8103 :   bool is_marked_for_strip () const override
    4573              :   {
    4574         8103 :     return if_block == nullptr && condition == nullptr;
    4575              :   }
    4576              : 
    4577         1335 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4578              :   {
    4579         1335 :     outer_attrs = std::move (new_attrs);
    4580         1335 :   }
    4581              : 
    4582              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4583              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4584        56832 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4585              : 
    4586         1364 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::If; }
    4587              : 
    4588              : protected:
    4589              :   // Base clone function but still concrete as concrete base class
    4590          532 :   virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); }
    4591              : 
    4592              :   /* Use covariance to implement clone function as returning this object rather
    4593              :    * than base */
    4594         2209 :   IfExpr *clone_expr_with_block_impl () const final override
    4595              :   {
    4596         2209 :     return clone_if_expr_impl ();
    4597              :   }
    4598              : };
    4599              : 
    4600              : // If expression with an ending "else" expression AST node (trailing)
    4601              : class IfExprConseqElse : public IfExpr
    4602              : {
    4603              :   std::unique_ptr<ExprWithBlock> else_block;
    4604              : 
    4605              : public:
    4606              :   std::string as_string () const override;
    4607              : 
    4608         1213 :   IfExprConseqElse (std::unique_ptr<Expr> condition,
    4609              :                     std::unique_ptr<BlockExpr> if_block,
    4610              :                     std::unique_ptr<ExprWithBlock> else_block,
    4611              :                     std::vector<Attribute> outer_attrs, location_t locus)
    4612         1213 :     : IfExpr (std::move (condition), std::move (if_block),
    4613              :               std::move (outer_attrs), locus),
    4614         1213 :       else_block (std::move (else_block))
    4615         1213 :   {}
    4616              :   // again, outer attributes not allowed
    4617              : 
    4618              :   // Copy constructor with clone
    4619         1677 :   IfExprConseqElse (IfExprConseqElse const &other)
    4620         1677 :     : IfExpr (other), else_block (other.else_block->clone_expr_with_block ())
    4621         1677 :   {}
    4622              : 
    4623              :   // Overloaded assignment operator with cloning
    4624              :   IfExprConseqElse &operator= (IfExprConseqElse const &other)
    4625              :   {
    4626              :     IfExpr::operator= (other);
    4627              :     // condition = other.condition->clone_expr();
    4628              :     // if_block = other.if_block->clone_block_expr();
    4629              :     else_block = other.else_block->clone_expr_with_block ();
    4630              : 
    4631              :     return *this;
    4632              :   }
    4633              : 
    4634              :   // move constructors
    4635              :   IfExprConseqElse (IfExprConseqElse &&other) = default;
    4636              :   IfExprConseqElse &operator= (IfExprConseqElse &&other) = default;
    4637              : 
    4638              :   void accept_vis (ASTVisitor &vis) override;
    4639              : 
    4640              :   void vis_else_block (ASTVisitor &vis) { else_block->accept_vis (vis); }
    4641              : 
    4642              :   // TODO: is this better? Or is a "vis_block" better?
    4643        29837 :   ExprWithBlock &get_else_block ()
    4644              :   {
    4645        29837 :     rust_assert (else_block != nullptr);
    4646        29837 :     return *else_block;
    4647              :   }
    4648              : 
    4649              : protected:
    4650              :   /* Use covariance to implement clone function as returning this object rather
    4651              :    * than base */
    4652         1677 :   IfExprConseqElse *clone_if_expr_impl () const override
    4653              :   {
    4654         1677 :     return new IfExprConseqElse (*this);
    4655              :   }
    4656              : };
    4657              : 
    4658              : // Basic "if let" expression AST node with no else
    4659              : class IfLetExpr : public ExprWithBlock
    4660              : {
    4661              :   std::vector<Attribute> outer_attrs;
    4662              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4663              :   std::unique_ptr<Expr> value;
    4664              :   std::unique_ptr<BlockExpr> if_block;
    4665              :   location_t locus;
    4666              : 
    4667              : public:
    4668              :   std::string as_string () const override;
    4669              : 
    4670           31 :   IfLetExpr (std::unique_ptr<Pattern> match_arm_pattern,
    4671              :              std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
    4672              :              std::vector<Attribute> outer_attrs, location_t locus)
    4673           62 :     : outer_attrs (std::move (outer_attrs)),
    4674           31 :       match_arm_pattern (std::move (match_arm_pattern)),
    4675           31 :       value (std::move (value)), if_block (std::move (if_block)), locus (locus)
    4676           31 :   {}
    4677              : 
    4678              :   // copy constructor with clone
    4679           30 :   IfLetExpr (IfLetExpr const &other)
    4680           30 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4681           30 :       locus (other.locus)
    4682              :   {
    4683              :     // guard to prevent null dereference (only required if error state)
    4684           30 :     if (other.value != nullptr)
    4685           30 :       value = other.value->clone_expr ();
    4686           30 :     if (other.if_block != nullptr)
    4687           30 :       if_block = other.if_block->clone_block_expr ();
    4688           30 :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4689           30 :   }
    4690              : 
    4691              :   // overload assignment operator to clone
    4692              :   IfLetExpr &operator= (IfLetExpr const &other)
    4693              :   {
    4694              :     ExprWithBlock::operator= (other);
    4695              :     outer_attrs = other.outer_attrs;
    4696              :     locus = other.locus;
    4697              : 
    4698              :     // guard to prevent null dereference (only required if error state)
    4699              :     if (other.value != nullptr)
    4700              :       value = other.value->clone_expr ();
    4701              :     else
    4702              :       value = nullptr;
    4703              :     if (other.if_block != nullptr)
    4704              :       if_block = other.if_block->clone_block_expr ();
    4705              :     else
    4706              :       if_block = nullptr;
    4707              : 
    4708              :     if (other.if_block != nullptr)
    4709              :       if_block = other.if_block->clone_block_expr ();
    4710              : 
    4711              :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4712              :     return *this;
    4713              :   }
    4714              : 
    4715              :   // move constructors
    4716              :   IfLetExpr (IfLetExpr &&other) = default;
    4717              :   IfLetExpr &operator= (IfLetExpr &&other) = default;
    4718              : 
    4719              :   // Unique pointer custom clone function
    4720              :   std::unique_ptr<IfLetExpr> clone_if_let_expr () const
    4721              :   {
    4722              :     return std::unique_ptr<IfLetExpr> (clone_if_let_expr_impl ());
    4723              :   }
    4724              : 
    4725          137 :   location_t get_locus () const override final { return locus; }
    4726              : 
    4727              :   void accept_vis (ASTVisitor &vis) override;
    4728              : 
    4729              :   // Invalid if block or value is null, so base stripping on that.
    4730            0 :   void mark_for_strip () override
    4731              :   {
    4732            0 :     if_block = nullptr;
    4733            0 :     value = nullptr;
    4734            0 :   }
    4735          102 :   bool is_marked_for_strip () const override
    4736              :   {
    4737          102 :     return if_block == nullptr && value == nullptr;
    4738              :   }
    4739              : 
    4740              :   // TODO: is this better? Or is a "vis_block" better?
    4741          536 :   Expr &get_value_expr ()
    4742              :   {
    4743          536 :     rust_assert (value != nullptr);
    4744          536 :     return *value;
    4745              :   }
    4746              : 
    4747           90 :   std::unique_ptr<Expr> &get_value_expr_ptr ()
    4748              :   {
    4749           90 :     rust_assert (value != nullptr);
    4750           90 :     return value;
    4751              :   }
    4752              : 
    4753              :   // TODO: is this better? Or is a "vis_block" better?
    4754          626 :   BlockExpr &get_if_block ()
    4755              :   {
    4756          626 :     rust_assert (if_block != nullptr);
    4757          626 :     return *if_block;
    4758              :   }
    4759              : 
    4760              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4761              :   const std::unique_ptr<Pattern> &get_pattern () const
    4762              :   {
    4763              :     return match_arm_pattern;
    4764              :   }
    4765          566 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4766              : 
    4767           29 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4768              :   {
    4769           29 :     outer_attrs = std::move (new_attrs);
    4770           29 :   }
    4771              : 
    4772              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4773              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4774          684 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4775              : 
    4776           29 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::IfLet; }
    4777              : 
    4778              : protected:
    4779              :   /* Use covariance to implement clone function as returning this object rather
    4780              :    * than base (or rather this or any derived object) */
    4781           30 :   IfLetExpr *clone_expr_with_block_impl () const final override
    4782              :   {
    4783           30 :     return clone_if_let_expr_impl ();
    4784              :   }
    4785              : 
    4786              :   // Base clone function but still concrete as concrete base class
    4787           18 :   virtual IfLetExpr *clone_if_let_expr_impl () const
    4788              :   {
    4789           18 :     return new IfLetExpr (*this);
    4790              :   }
    4791              : };
    4792              : 
    4793              : /* AST node representing "if let" expression with an "else" expression at the
    4794              :  * end */
    4795              : class IfLetExprConseqElse : public IfLetExpr
    4796              : {
    4797              :   std::unique_ptr<ExprWithBlock> else_block;
    4798              : 
    4799              : public:
    4800              :   std::string as_string () const override;
    4801              : 
    4802           12 :   IfLetExprConseqElse (std::unique_ptr<Pattern> match_arm_pattern,
    4803              :                        std::unique_ptr<Expr> value,
    4804              :                        std::unique_ptr<BlockExpr> if_block,
    4805              :                        std::unique_ptr<ExprWithBlock> else_block,
    4806              :                        std::vector<Attribute> outer_attrs, location_t locus)
    4807           12 :     : IfLetExpr (std::move (match_arm_pattern), std::move (value),
    4808              :                  std::move (if_block), std::move (outer_attrs), locus),
    4809           12 :       else_block (std::move (else_block))
    4810           12 :   {}
    4811              :   // outer attributes not allowed
    4812              : 
    4813              :   // copy constructor with clone
    4814           12 :   IfLetExprConseqElse (IfLetExprConseqElse const &other)
    4815           12 :     : IfLetExpr (other), else_block (other.else_block->clone_expr_with_block ())
    4816           12 :   {}
    4817              : 
    4818              :   // overload assignment operator to clone
    4819              :   IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other)
    4820              :   {
    4821              :     IfLetExpr::operator= (other);
    4822              :     // match_arm_patterns = other.match_arm_patterns;
    4823              :     // value = other.value->clone_expr();
    4824              :     // if_block = other.if_block->clone_block_expr();
    4825              :     else_block = other.else_block->clone_expr_with_block ();
    4826              :     // outer_attrs = other.outer_attrs;
    4827              : 
    4828              :     return *this;
    4829              :   }
    4830              : 
    4831              :   // move constructors
    4832              :   IfLetExprConseqElse (IfLetExprConseqElse &&other) = default;
    4833              :   IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default;
    4834              : 
    4835              :   void accept_vis (ASTVisitor &vis) override;
    4836              : 
    4837              :   // TODO: is this better? Or is a "vis_block" better?
    4838          218 :   ExprWithBlock &get_else_block ()
    4839              :   {
    4840          218 :     rust_assert (else_block != nullptr);
    4841          218 :     return *else_block;
    4842              :   }
    4843              : 
    4844              : protected:
    4845              :   /* Use covariance to implement clone function as returning this object rather
    4846              :    * than base */
    4847           12 :   IfLetExprConseqElse *clone_if_let_expr_impl () const override
    4848              :   {
    4849           12 :     return new IfLetExprConseqElse (*this);
    4850              :   }
    4851              : };
    4852              : 
    4853              : // Match arm expression
    4854              : struct MatchArm
    4855              : {
    4856              : private:
    4857              :   std::vector<Attribute> outer_attrs;
    4858              :   // MatchArmPatterns patterns;
    4859              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4860              : 
    4861              :   // bool has_match_arm_guard;
    4862              :   // inlined from MatchArmGuard
    4863              :   std::unique_ptr<Expr> guard_expr;
    4864              : 
    4865              :   location_t locus;
    4866              : 
    4867              : public:
    4868              :   // Returns whether the MatchArm has a match arm guard expression
    4869        54973 :   bool has_match_arm_guard () const { return guard_expr != nullptr; }
    4870              : 
    4871              :   // Constructor for match arm with a guard expression
    4872         2503 :   MatchArm (std::unique_ptr<Pattern> match_arm_pattern, location_t locus,
    4873              :             std::unique_ptr<Expr> guard_expr = nullptr,
    4874              :             std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
    4875         2503 :     : outer_attrs (std::move (outer_attrs)),
    4876          367 :       match_arm_pattern (std::move (match_arm_pattern)),
    4877         2503 :       guard_expr (std::move (guard_expr)), locus (locus)
    4878              :   {}
    4879              : 
    4880              :   // Copy constructor with clone
    4881         2754 :   MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs)
    4882              :   {
    4883              :     // guard to protect from null pointer dereference
    4884         2754 :     if (other.guard_expr != nullptr)
    4885            1 :       guard_expr = other.guard_expr->clone_expr ();
    4886              : 
    4887         2754 :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4888              : 
    4889         2754 :     locus = other.locus;
    4890         2754 :   }
    4891              : 
    4892        11810 :   ~MatchArm () = default;
    4893              : 
    4894              :   // Overload assignment operator to clone
    4895              :   MatchArm &operator= (MatchArm const &other)
    4896              :   {
    4897              :     outer_attrs = other.outer_attrs;
    4898              : 
    4899              :     if (other.guard_expr != nullptr)
    4900              :       guard_expr = other.guard_expr->clone_expr ();
    4901              :     else
    4902              :       guard_expr = nullptr;
    4903              : 
    4904              :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4905              : 
    4906              :     return *this;
    4907              :   }
    4908              : 
    4909              :   // move constructors
    4910         2588 :   MatchArm (MatchArm &&other) = default;
    4911            0 :   MatchArm &operator= (MatchArm &&other) = default;
    4912              : 
    4913              :   // Returns whether match arm is in an error state.
    4914        62063 :   bool is_error () const { return match_arm_pattern == nullptr; }
    4915              : 
    4916              :   // Creates a match arm in an error state.
    4917            0 :   static MatchArm create_error ()
    4918              :   {
    4919            0 :     location_t locus = UNDEF_LOCATION;
    4920            0 :     return MatchArm (nullptr, locus);
    4921              :   }
    4922              : 
    4923              :   std::string as_string () const;
    4924              : 
    4925              :   // TODO: is this better? Or is a "vis_block" better?
    4926           15 :   Expr &get_guard_expr ()
    4927              :   {
    4928           15 :     rust_assert (has_match_arm_guard ());
    4929           15 :     return *guard_expr;
    4930              :   }
    4931              : 
    4932            3 :   std::unique_ptr<Expr> &get_guard_expr_ptr ()
    4933              :   {
    4934            3 :     rust_assert (has_match_arm_guard ());
    4935            3 :     return guard_expr;
    4936              :   }
    4937              : 
    4938              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4939              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4940        54973 :   std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
    4941              : 
    4942              :   const std::unique_ptr<Pattern> &get_pattern () const
    4943              :   {
    4944              :     return match_arm_pattern;
    4945              :   }
    4946        54973 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4947              : 
    4948              :   location_t get_locus () const { return locus; }
    4949              : };
    4950              : 
    4951              : /* A "match case" - a correlated match arm and resulting expression. Not
    4952              :  * abstract. */
    4953              : struct MatchCase
    4954              : {
    4955              : private:
    4956              :   MatchArm arm;
    4957              :   std::unique_ptr<Expr> expr;
    4958              :   NodeId node_id;
    4959              : 
    4960              :   /* TODO: does whether trailing comma exists need to be stored? currently
    4961              :    * assuming it is only syntactical and has no effect on meaning. */
    4962              : 
    4963              : public:
    4964         2502 :   MatchCase (MatchArm arm, std::unique_ptr<Expr> expr)
    4965         2502 :     : arm (std::move (arm)), expr (std::move (expr)),
    4966         2502 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
    4967         2502 :   {}
    4968              : 
    4969         2754 :   MatchCase (const MatchCase &other)
    4970         2754 :     : arm (other.arm), expr (other.expr->clone_expr ()), node_id (other.node_id)
    4971         2754 :   {}
    4972              : 
    4973              :   MatchCase &operator= (const MatchCase &other)
    4974              :   {
    4975              :     arm = other.arm;
    4976              :     expr = other.expr->clone_expr ();
    4977              :     node_id = other.node_id;
    4978              : 
    4979              :     return *this;
    4980              :   }
    4981              : 
    4982         3753 :   MatchCase (MatchCase &&other) = default;
    4983            0 :   MatchCase &operator= (MatchCase &&other) = default;
    4984              : 
    4985         6614 :   ~MatchCase () = default;
    4986              : 
    4987              :   std::string as_string () const;
    4988              : 
    4989              :   // TODO: is this better? Or is a "vis_block" better?
    4990        46589 :   Expr &get_expr ()
    4991              :   {
    4992        46589 :     rust_assert (expr != nullptr);
    4993        46589 :     return *expr;
    4994              :   }
    4995              : 
    4996         8384 :   std::unique_ptr<Expr> &get_expr_ptr ()
    4997              :   {
    4998         8384 :     rust_assert (expr != nullptr);
    4999         8384 :     return expr;
    5000              :   }
    5001              : 
    5002              :   // TODO: is this better? Or is a "vis_block" better?
    5003        59927 :   MatchArm &get_arm ()
    5004              :   {
    5005        59927 :     rust_assert (!arm.is_error ());
    5006        59927 :     return arm;
    5007              :   }
    5008              : 
    5009              :   NodeId get_node_id () const { return node_id; }
    5010              : };
    5011              : 
    5012              : // Match expression AST node
    5013              : class MatchExpr : public ExprWithBlock
    5014              : {
    5015              :   std::vector<Attribute> outer_attrs;
    5016              :   std::unique_ptr<Expr> branch_value;
    5017              :   std::vector<Attribute> inner_attrs;
    5018              :   std::vector<MatchCase> match_arms;
    5019              :   location_t locus;
    5020              : 
    5021              : public:
    5022              :   std::string as_string () const override;
    5023              : 
    5024              :   // Returns whether the match expression has any match arms.
    5025              :   bool has_match_arms () const { return !match_arms.empty (); }
    5026              : 
    5027         1079 :   MatchExpr (std::unique_ptr<Expr> branch_value,
    5028              :              std::vector<MatchCase> match_arms,
    5029              :              std::vector<Attribute> inner_attrs,
    5030              :              std::vector<Attribute> outer_attrs, location_t locus)
    5031         2158 :     : outer_attrs (std::move (outer_attrs)),
    5032         1079 :       branch_value (std::move (branch_value)),
    5033         1079 :       inner_attrs (std::move (inner_attrs)),
    5034         1079 :       match_arms (std::move (match_arms)), locus (locus)
    5035         1079 :   {}
    5036              : 
    5037              :   // Copy constructor requires clone due to unique_ptr
    5038         1101 :   MatchExpr (MatchExpr const &other)
    5039         1101 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    5040         1101 :       inner_attrs (other.inner_attrs), match_arms (other.match_arms),
    5041         1101 :       locus (other.locus)
    5042              :   {
    5043              :     // guard to prevent null dereference (only required if error state)
    5044         1101 :     if (other.branch_value != nullptr)
    5045         1101 :       branch_value = other.branch_value->clone_expr ();
    5046         1101 :   }
    5047              : 
    5048              :   // Overloaded assignment operator to clone due to unique_ptr
    5049              :   MatchExpr &operator= (MatchExpr const &other)
    5050              :   {
    5051              :     ExprWithBlock::operator= (other);
    5052              :     inner_attrs = other.inner_attrs;
    5053              :     match_arms = other.match_arms;
    5054              :     outer_attrs = other.outer_attrs;
    5055              :     locus = other.locus;
    5056              : 
    5057              :     // guard to prevent null dereference (only required if error state)
    5058              :     if (other.branch_value != nullptr)
    5059              :       branch_value = other.branch_value->clone_expr ();
    5060              :     else
    5061              :       branch_value = nullptr;
    5062              : 
    5063              :     return *this;
    5064              :   }
    5065              : 
    5066              :   // move constructors
    5067              :   MatchExpr (MatchExpr &&other) = default;
    5068              :   MatchExpr &operator= (MatchExpr &&other) = default;
    5069              : 
    5070         4683 :   location_t get_locus () const override final { return locus; }
    5071              : 
    5072              :   void accept_vis (ASTVisitor &vis) override;
    5073              : 
    5074              :   // Invalid if branch value is null, so base stripping on that.
    5075            0 :   void mark_for_strip () override { branch_value = nullptr; }
    5076        11357 :   bool is_marked_for_strip () const override { return branch_value == nullptr; }
    5077              : 
    5078              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    5079              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    5080        23746 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    5081              : 
    5082              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5083        29768 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5084              : 
    5085          841 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5086              :   {
    5087          841 :     outer_attrs = std::move (new_attrs);
    5088          841 :   }
    5089              : 
    5090              :   // TODO: is this better? Or is a "vis_block" better?
    5091        20137 :   Expr &get_scrutinee_expr ()
    5092              :   {
    5093        20137 :     rust_assert (branch_value != nullptr);
    5094        20137 :     return *branch_value;
    5095              :   }
    5096              : 
    5097         3609 :   std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
    5098              :   {
    5099         3609 :     rust_assert (branch_value != nullptr);
    5100         3609 :     return branch_value;
    5101              :   }
    5102              : 
    5103              :   const std::vector<MatchCase> &get_match_cases () const { return match_arms; }
    5104        23746 :   std::vector<MatchCase> &get_match_cases () { return match_arms; }
    5105              : 
    5106         1071 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Match; }
    5107              : 
    5108              : protected:
    5109              :   /* Use covariance to implement clone function as returning this object rather
    5110              :    * than base */
    5111         1101 :   MatchExpr *clone_expr_with_block_impl () const override
    5112              :   {
    5113         1101 :     return new MatchExpr (*this);
    5114              :   }
    5115              : };
    5116              : 
    5117              : // Await expression AST node (pseudo-member variable access)
    5118              : class AwaitExpr : public ExprWithoutBlock
    5119              : {
    5120              :   std::vector<Attribute> outer_attrs;
    5121              :   std::unique_ptr<Expr> awaited_expr;
    5122              :   location_t locus;
    5123              : 
    5124              : public:
    5125              :   // TODO: ensure outer attributes are actually allowed
    5126            0 :   AwaitExpr (std::unique_ptr<Expr> awaited_expr,
    5127              :              std::vector<Attribute> outer_attrs, location_t locus)
    5128            0 :     : outer_attrs (std::move (outer_attrs)),
    5129            0 :       awaited_expr (std::move (awaited_expr)), locus (locus)
    5130            0 :   {}
    5131              : 
    5132              :   // copy constructor with clone
    5133            0 :   AwaitExpr (AwaitExpr const &other)
    5134            0 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    5135            0 :       locus (other.locus)
    5136              :   {
    5137              :     // guard to prevent null dereference (only required if error state)
    5138            0 :     if (other.awaited_expr != nullptr)
    5139            0 :       awaited_expr = other.awaited_expr->clone_expr ();
    5140            0 :   }
    5141              : 
    5142              :   // overloaded assignment operator with clone
    5143              :   AwaitExpr &operator= (AwaitExpr const &other)
    5144              :   {
    5145              :     ExprWithoutBlock::operator= (other);
    5146              :     outer_attrs = other.outer_attrs;
    5147              :     locus = other.locus;
    5148              : 
    5149              :     // guard to prevent null dereference (only required if error state)
    5150              :     if (other.awaited_expr != nullptr)
    5151              :       awaited_expr = other.awaited_expr->clone_expr ();
    5152              :     else
    5153              :       awaited_expr = nullptr;
    5154              : 
    5155              :     return *this;
    5156              :   }
    5157              : 
    5158              :   // move constructors
    5159              :   AwaitExpr (AwaitExpr &&other) = default;
    5160              :   AwaitExpr &operator= (AwaitExpr &&other) = default;
    5161              : 
    5162              :   std::string as_string () const override;
    5163              : 
    5164            0 :   location_t get_locus () const override final { return locus; }
    5165              : 
    5166              :   void accept_vis (ASTVisitor &vis) override;
    5167              : 
    5168              :   // Invalid if awaited expr is null, so base stripping on that.
    5169            0 :   void mark_for_strip () override { awaited_expr = nullptr; }
    5170            0 :   bool is_marked_for_strip () const override { return awaited_expr == nullptr; }
    5171              : 
    5172              :   // TODO: is this better? Or is a "vis_block" better?
    5173            0 :   std::unique_ptr<Expr> &get_awaited_expr ()
    5174              :   {
    5175            0 :     rust_assert (awaited_expr != nullptr);
    5176            0 :     return awaited_expr;
    5177              :   }
    5178              : 
    5179              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5180            0 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5181              : 
    5182            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5183              :   {
    5184            0 :     outer_attrs = std::move (new_attrs);
    5185            0 :   }
    5186              : 
    5187            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Await; }
    5188              : 
    5189              : protected:
    5190              :   /* Use covariance to implement clone function as returning this object rather
    5191              :    * than base */
    5192            0 :   AwaitExpr *clone_expr_without_block_impl () const override
    5193              :   {
    5194            0 :     return new AwaitExpr (*this);
    5195              :   }
    5196              : };
    5197              : 
    5198              : // Async block expression AST node (block expr that evaluates to a future)
    5199              : class AsyncBlockExpr : public ExprWithBlock
    5200              : {
    5201              :   // TODO: should this extend BlockExpr rather than be a composite of it?
    5202              :   std::vector<Attribute> outer_attrs;
    5203              :   bool has_move;
    5204              :   std::unique_ptr<BlockExpr> block_expr;
    5205              :   location_t locus;
    5206              : 
    5207              : public:
    5208            0 :   AsyncBlockExpr (std::unique_ptr<BlockExpr> block_expr, bool has_move,
    5209              :                   std::vector<Attribute> outer_attrs, location_t locus)
    5210            0 :     : outer_attrs (std::move (outer_attrs)), has_move (has_move),
    5211            0 :       block_expr (std::move (block_expr)), locus (locus)
    5212            0 :   {}
    5213              : 
    5214              :   // copy constructor with clone
    5215            0 :   AsyncBlockExpr (AsyncBlockExpr const &other)
    5216            0 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    5217            0 :       has_move (other.has_move), locus (other.locus)
    5218              :   {
    5219              :     // guard to prevent null dereference (only required if error state)
    5220            0 :     if (other.block_expr != nullptr)
    5221            0 :       block_expr = other.block_expr->clone_block_expr ();
    5222            0 :   }
    5223              : 
    5224              :   // overloaded assignment operator to clone
    5225              :   AsyncBlockExpr &operator= (AsyncBlockExpr const &other)
    5226              :   {
    5227              :     ExprWithBlock::operator= (other);
    5228              :     outer_attrs = other.outer_attrs;
    5229              :     has_move = other.has_move;
    5230              :     locus = other.locus;
    5231              : 
    5232              :     // guard to prevent null dereference (only required if error state)
    5233              :     if (other.block_expr != nullptr)
    5234              :       block_expr = other.block_expr->clone_block_expr ();
    5235              :     else
    5236              :       block_expr = nullptr;
    5237              : 
    5238              :     return *this;
    5239              :   }
    5240              : 
    5241              :   // move constructors
    5242              :   AsyncBlockExpr (AsyncBlockExpr &&other) = default;
    5243              :   AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default;
    5244              : 
    5245              :   std::string as_string () const override;
    5246              : 
    5247            0 :   bool get_has_move () { return has_move; }
    5248            0 :   location_t get_locus () const override final { return locus; }
    5249              : 
    5250              :   void accept_vis (ASTVisitor &vis) override;
    5251              : 
    5252              :   // Invalid if block is null, so base stripping on that.
    5253            0 :   void mark_for_strip () override { block_expr = nullptr; }
    5254            0 :   bool is_marked_for_strip () const override { return block_expr == nullptr; }
    5255              : 
    5256              :   // TODO: is this better? Or is a "vis_block" better?
    5257            0 :   std::unique_ptr<BlockExpr> &get_block_expr ()
    5258              :   {
    5259            0 :     rust_assert (block_expr != nullptr);
    5260            0 :     return block_expr;
    5261              :   }
    5262              : 
    5263              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5264            0 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5265              : 
    5266            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5267              :   {
    5268            0 :     outer_attrs = std::move (new_attrs);
    5269            0 :   }
    5270              : 
    5271            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::AsyncBlock; }
    5272              : 
    5273              : protected:
    5274              :   /* Use covariance to implement clone function as returning this object rather
    5275              :    * than base */
    5276            0 :   AsyncBlockExpr *clone_expr_with_block_impl () const override
    5277              :   {
    5278            0 :     return new AsyncBlockExpr (*this);
    5279              :   }
    5280              : };
    5281              : 
    5282              : // Inline-assembly specific options
    5283              : enum class InlineAsmOption
    5284              : {
    5285              :   PURE = 1 << 0,
    5286              :   NOMEM = 1 << 1,
    5287              :   READONLY = 1 << 2,
    5288              :   PRESERVES_FLAGS = 1 << 3,
    5289              :   NORETURN = 1 << 4,
    5290              :   NOSTACK = 1 << 5,
    5291              :   ATT_SYNTAX = 1 << 6,
    5292              :   RAW = 1 << 7,
    5293              :   MAY_UNWIND = 1 << 8,
    5294              : };
    5295              : 
    5296              : struct InlineAsmRegOrRegClass
    5297              : {
    5298              :   enum Type
    5299              :   {
    5300              :     Reg,
    5301              :     RegClass,
    5302              :   };
    5303              : 
    5304              :   struct Reg
    5305              :   {
    5306              :     std::string Symbol;
    5307              :   };
    5308              : 
    5309              :   struct RegClass
    5310              :   {
    5311              :     std::string Symbol;
    5312              :   };
    5313              : 
    5314              :   Type type;
    5315              :   struct Reg reg;
    5316              :   struct RegClass reg_class;
    5317              : 
    5318              :   Identifier name;
    5319              :   location_t locus;
    5320              : };
    5321              : 
    5322           18 : struct LlvmOperand
    5323              : {
    5324              :   std::string constraint;
    5325              :   std::unique_ptr<Expr> expr;
    5326              : 
    5327            2 :   LlvmOperand (std::string constraint, std::unique_ptr<Expr> &&expr)
    5328            4 :     : constraint (constraint), expr (std::move (expr))
    5329              :   {}
    5330              : 
    5331           18 :   LlvmOperand (const LlvmOperand &other)
    5332           36 :     : constraint (other.constraint), expr (other.expr->clone_expr ())
    5333           18 :   {}
    5334              :   LlvmOperand &operator= (const LlvmOperand &other)
    5335              :   {
    5336              :     constraint = other.constraint;
    5337              :     expr = other.expr->clone_expr ();
    5338              : 
    5339              :     return *this;
    5340              :   }
    5341              : };
    5342              : 
    5343          618 : class InlineAsmOperand
    5344              : {
    5345              : public:
    5346              :   enum class RegisterType
    5347              :   {
    5348              :     In,
    5349              :     Out,
    5350              :     InOut,
    5351              :     SplitInOut,
    5352              :     Const,
    5353              :     Sym,
    5354              :     Label,
    5355              :   };
    5356              : 
    5357            0 :   class Register
    5358              :   {
    5359              :   public:
    5360          734 :     Register () {}
    5361            0 :     virtual ~Register () = default;
    5362              : 
    5363          615 :     std::unique_ptr<Register> clone () const
    5364              :     {
    5365          615 :       return std::unique_ptr<Register> (clone_impl ());
    5366              :     }
    5367              : 
    5368              :   protected:
    5369              :     virtual Register *clone_impl () const = 0;
    5370              :   };
    5371              : 
    5372              :   class In : public Register
    5373              :   {
    5374              :   public:
    5375              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5376              :     std::unique_ptr<Expr> expr;
    5377              : 
    5378           12 :     In (tl::optional<struct InlineAsmRegOrRegClass> &reg,
    5379              :         std::unique_ptr<Expr> expr)
    5380           12 :       : reg (reg), expr (std::move (expr))
    5381              :     {
    5382           12 :       rust_assert (this->expr != nullptr);
    5383           12 :     }
    5384              : 
    5385          241 :     In (const In &other)
    5386          241 :     {
    5387          241 :       reg = other.reg;
    5388              : 
    5389          241 :       expr = other.expr->clone_expr ();
    5390          241 :     }
    5391              : 
    5392              :     In operator= (const In &other)
    5393              :     {
    5394              :       reg = other.reg;
    5395              :       expr = other.expr->clone_expr ();
    5396              : 
    5397              :       return *this;
    5398              :     }
    5399              : 
    5400              :   private:
    5401          219 :     In *clone_impl () const { return new In (*this); }
    5402              :   };
    5403              : 
    5404              :   class Out : public Register
    5405              :   {
    5406              :   public:
    5407              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5408              :     bool late;
    5409              :     std::unique_ptr<Expr> expr; // can be null
    5410              : 
    5411           17 :     Out (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5412              :          std::unique_ptr<Expr> expr)
    5413           17 :       : reg (reg), late (late), expr (std::move (expr))
    5414              :     {
    5415           17 :       rust_assert (this->expr != nullptr);
    5416           17 :     }
    5417              : 
    5418          388 :     Out (const Out &other)
    5419          388 :     {
    5420          388 :       reg = other.reg;
    5421          388 :       late = other.late;
    5422          388 :       expr = other.expr->clone_expr ();
    5423          388 :     }
    5424              : 
    5425              :     Out operator= (const Out &other)
    5426              :     {
    5427              :       reg = other.reg;
    5428              :       late = other.late;
    5429              :       expr = other.expr->clone_expr ();
    5430              :       return *this;
    5431              :     }
    5432              : 
    5433              :   private:
    5434          354 :     Out *clone_impl () const { return new Out (*this); }
    5435              :   };
    5436              : 
    5437              :   class InOut : public Register
    5438              :   {
    5439              :   public:
    5440              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5441              :     bool late;
    5442              :     std::unique_ptr<Expr> expr; // this can't be null
    5443              : 
    5444            1 :     InOut (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5445              :            std::unique_ptr<Expr> expr)
    5446            1 :       : reg (reg), late (late), expr (std::move (expr))
    5447              :     {
    5448            1 :       rust_assert (this->expr != nullptr);
    5449            1 :     }
    5450              : 
    5451            1 :     InOut (const InOut &other)
    5452            1 :     {
    5453            1 :       reg = other.reg;
    5454            1 :       late = other.late;
    5455            1 :       expr = other.expr->clone_expr ();
    5456            1 :     }
    5457              : 
    5458              :     InOut operator= (const InOut &other)
    5459              :     {
    5460              :       reg = other.reg;
    5461              :       late = other.late;
    5462              :       expr = other.expr->clone_expr ();
    5463              : 
    5464              :       return *this;
    5465              :     }
    5466              : 
    5467              :   private:
    5468            0 :     InOut *clone_impl () const { return new InOut (*this); }
    5469              :   };
    5470              : 
    5471              :   class SplitInOut : public Register
    5472              :   {
    5473              :   public:
    5474              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5475              :     bool late;
    5476              :     std::unique_ptr<Expr> in_expr;
    5477              :     std::unique_ptr<Expr> out_expr; // could be null
    5478              : 
    5479            2 :     SplitInOut (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5480              :                 std::unique_ptr<Expr> in_expr, std::unique_ptr<Expr> out_expr)
    5481            2 :       : reg (reg), late (late), in_expr (std::move (in_expr)),
    5482            2 :         out_expr (std::move (out_expr))
    5483              :     {
    5484            2 :       rust_assert (this->in_expr != nullptr);
    5485            2 :       rust_assert (this->out_expr != nullptr);
    5486            2 :     }
    5487              : 
    5488           72 :     SplitInOut (const SplitInOut &other)
    5489           72 :     {
    5490           72 :       reg = other.reg;
    5491           72 :       late = other.late;
    5492           72 :       in_expr = other.in_expr->clone_expr ();
    5493           72 :       out_expr = other.out_expr->clone_expr ();
    5494           72 :     }
    5495              : 
    5496              :     SplitInOut operator= (const SplitInOut &other)
    5497              :     {
    5498              :       reg = other.reg;
    5499              :       late = other.late;
    5500              :       in_expr = other.in_expr->clone_expr ();
    5501              :       out_expr = other.out_expr->clone_expr ();
    5502              : 
    5503              :       return *this;
    5504              :     }
    5505              : 
    5506              :   private:
    5507           42 :     SplitInOut *clone_impl () const { return new SplitInOut (*this); }
    5508              :   };
    5509              : 
    5510            0 :   class Const : public Register
    5511              :   {
    5512              :   public:
    5513              :     AnonConst anon_const;
    5514              : 
    5515              :   private:
    5516            0 :     Const *clone_impl () const { return new Const (*this); }
    5517              :   };
    5518              : 
    5519            0 :   class Sym : public Register
    5520              :   {
    5521              :   public:
    5522              :     std::unique_ptr<Expr> expr;
    5523              : 
    5524              :     Sym (std::unique_ptr<Expr> expr) : expr (std::move (expr))
    5525              :     {
    5526              :       rust_assert (this->expr != nullptr);
    5527              :     }
    5528            0 :     Sym (const Sym &other)
    5529            0 :     {
    5530            0 :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5531            0 :     }
    5532              : 
    5533              :     Sym operator= (const Sym &other)
    5534              :     {
    5535              :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5536              :       return *this;
    5537              :     }
    5538              : 
    5539              :   private:
    5540            0 :     Sym *clone_impl () const { return new Sym (*this); }
    5541              :   };
    5542              : 
    5543              :   class Label : public Register
    5544              :   {
    5545              :   public:
    5546              :     std::string label_name;
    5547              :     std::unique_ptr<Expr> expr;
    5548              : 
    5549              :     Label (tl::optional<std::string> label_name, std::unique_ptr<Expr> expr)
    5550              :       : expr (std::move (expr))
    5551              :     {
    5552              :       rust_assert (this->expr != nullptr);
    5553              :       if (label_name.has_value ())
    5554              :         this->label_name = label_name.value ();
    5555              :     }
    5556            0 :     Label (const Label &other)
    5557            0 :     {
    5558            0 :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5559            0 :     }
    5560              : 
    5561              :     Label operator= (const Label &other)
    5562              :     {
    5563              :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5564              :       return *this;
    5565              :     }
    5566              : 
    5567              :   private:
    5568            0 :     Label *clone_impl () const { return new Label (*this); }
    5569              :   };
    5570              : 
    5571          615 :   InlineAsmOperand (const InlineAsmOperand &other)
    5572          615 :     : register_type (other.register_type), locus (other.locus),
    5573          615 :       reg (other.reg->clone ())
    5574          615 :   {}
    5575              : 
    5576           12 :   InlineAsmOperand (const In &reg, location_t locus)
    5577           12 :     : register_type (RegisterType::In), locus (locus), reg (new In (reg))
    5578           12 :   {}
    5579           17 :   InlineAsmOperand (const Out &reg, location_t locus)
    5580           17 :     : register_type (RegisterType::Out), locus (locus), reg (new Out (reg))
    5581           17 :   {}
    5582            1 :   InlineAsmOperand (const InOut &reg, location_t locus)
    5583            1 :     : register_type (RegisterType::InOut), locus (locus), reg (new InOut (reg))
    5584            1 :   {}
    5585            2 :   InlineAsmOperand (const SplitInOut &reg, location_t locus)
    5586            2 :     : register_type (RegisterType::SplitInOut), locus (locus),
    5587            2 :       reg (new SplitInOut (reg))
    5588            2 :   {}
    5589              :   InlineAsmOperand (const Const &reg, location_t locus)
    5590              :     : register_type (RegisterType::Const), locus (locus), reg (new Const (reg))
    5591              :   {}
    5592              :   InlineAsmOperand (const Sym &reg, location_t locus)
    5593              :     : register_type (RegisterType::Sym), locus (locus), reg (new Sym (reg))
    5594              :   {}
    5595              :   InlineAsmOperand (const Label &reg, location_t locus)
    5596              :     : register_type (RegisterType::Label), locus (locus), reg (new Label (reg))
    5597              :   {}
    5598              : 
    5599              :   location_t get_locus () const { return locus; }
    5600          402 :   RegisterType get_register_type () const { return register_type; }
    5601              : 
    5602              :   // Potentially fail immediately if you don't use get_register_type() to
    5603              :   // inspect the RegisterType first before calling the following functions Check
    5604              :   // first
    5605          130 :   In &get_in ()
    5606              :   {
    5607          130 :     rust_assert (register_type == RegisterType::In);
    5608          130 :     return static_cast<In &> (*reg);
    5609              :   }
    5610           10 :   const In &get_in () const
    5611              :   {
    5612           10 :     rust_assert (register_type == RegisterType::In);
    5613           10 :     return static_cast<const In &> (*reg);
    5614              :   }
    5615              : 
    5616          217 :   Out &get_out ()
    5617              :   {
    5618          217 :     rust_assert (register_type == RegisterType::Out);
    5619          217 :     return static_cast<Out &> (*reg);
    5620              :   }
    5621           17 :   const Out &get_out () const
    5622              :   {
    5623           17 :     rust_assert (register_type == RegisterType::Out);
    5624           17 :     return static_cast<const Out &> (*reg);
    5625              :   }
    5626              : 
    5627            0 :   InOut &get_in_out ()
    5628              :   {
    5629            0 :     rust_assert (register_type == RegisterType::InOut);
    5630            0 :     return static_cast<InOut &> (*reg);
    5631              :   }
    5632            0 :   const InOut &get_in_out () const
    5633              :   {
    5634            0 :     rust_assert (register_type == RegisterType::InOut);
    5635            0 :     return static_cast<const InOut &> (*reg);
    5636              :   }
    5637              : 
    5638           26 :   SplitInOut &get_split_in_out ()
    5639              :   {
    5640           26 :     rust_assert (register_type == RegisterType::SplitInOut);
    5641           26 :     return static_cast<SplitInOut &> (*reg);
    5642              :   }
    5643            2 :   const SplitInOut &get_split_in_out () const
    5644              :   {
    5645            2 :     rust_assert (register_type == RegisterType::SplitInOut);
    5646            2 :     return static_cast<const SplitInOut &> (*reg);
    5647              :   }
    5648              : 
    5649            0 :   Const &get_const ()
    5650              :   {
    5651            0 :     rust_assert (register_type == RegisterType::Const);
    5652            0 :     return static_cast<Const &> (*reg);
    5653              :   }
    5654            0 :   const Const &get_const () const
    5655              :   {
    5656            0 :     rust_assert (register_type == RegisterType::Const);
    5657            0 :     return static_cast<Const &> (*reg);
    5658              :   }
    5659              : 
    5660            0 :   Sym &get_sym ()
    5661              :   {
    5662            0 :     rust_assert (register_type == RegisterType::Sym);
    5663            0 :     return static_cast<Sym &> (*reg);
    5664              :   }
    5665            0 :   const Sym &get_sym () const
    5666              :   {
    5667            0 :     rust_assert (register_type == RegisterType::Sym);
    5668            0 :     return static_cast<const Sym &> (*reg);
    5669              :   }
    5670              : 
    5671            0 :   Label &get_label ()
    5672              :   {
    5673            0 :     rust_assert (register_type == RegisterType::Label);
    5674            0 :     return static_cast<Label &> (*reg);
    5675              :   }
    5676            0 :   const Label &get_label () const
    5677              :   {
    5678            0 :     rust_assert (register_type == RegisterType::Label);
    5679            0 :     return static_cast<const Label &> (*reg);
    5680              :   }
    5681              : 
    5682              : private:
    5683              :   RegisterType register_type;
    5684              : 
    5685              :   location_t locus;
    5686              :   std::unique_ptr<Register> reg;
    5687              : };
    5688              : 
    5689              : struct InlineAsmPlaceHolder
    5690              : {
    5691              :   size_t operand_idx;
    5692              :   char modifier; // can be null
    5693              :   location_t locus;
    5694              : };
    5695              : 
    5696            0 : struct InlineAsmTemplatePiece
    5697              : {
    5698              :   bool is_placeholder;
    5699              :   std::string string;
    5700              :   InlineAsmPlaceHolder placeholder;
    5701              : };
    5702              : 
    5703           20 : struct TupleClobber
    5704              : {
    5705            4 :   TupleClobber (std::string symbol, location_t loc) : symbol (symbol), loc (loc)
    5706              :   {}
    5707              :   // as gccrs still doesn't contain a symbol class I have put them as strings
    5708              :   std::string symbol;
    5709              :   location_t loc;
    5710              : };
    5711              : 
    5712          322 : struct TupleTemplateStr
    5713              : {
    5714              :   // as gccrs still doesn't contain a symbol class I have put them as strings
    5715              :   location_t loc;
    5716              :   std::string symbol;
    5717              : 
    5718            2 :   location_t get_locus () { return loc; }
    5719           40 :   TupleTemplateStr (location_t loc, const std::string &symbol)
    5720           80 :     : loc (loc), symbol (symbol)
    5721              :   {}
    5722              : };
    5723              : 
    5724              : // Inline Assembly Node
    5725              : class InlineAsm : public ExprWithoutBlock
    5726              : {
    5727              : public:
    5728              :   enum class Option
    5729              :   {
    5730              :     PURE = 1 << 0,
    5731              :     NOMEM = 1 << 1,
    5732              :     READONLY = 1 << 2,
    5733              :     PRESERVES_FLAGS = 1 << 3,
    5734              :     NORETURN = 1 << 4,
    5735              :     NOSTACK = 1 << 5,
    5736              :     ATT_SYNTAX = 1 << 6,
    5737              :     RAW = 1 << 7,
    5738              :     MAY_UNWIND = 1 << 8,
    5739              :   };
    5740              : 
    5741              : private:
    5742              :   location_t locus;
    5743              :   // TODO: Not sure how outer_attrs plays with InlineAsm, I put it here in order
    5744              :   // to override, very hacky.
    5745              :   std::vector<Attribute> outer_attrs;
    5746              : 
    5747              : public:
    5748              :   // https://github.com/rust-lang/rust/blob/55cac26a9ef17da1c9c77c0816e88e178b7cc5dd/compiler/rustc_builtin_macros/src/asm.rs#L56C1-L64C7
    5749              :   //   let mut args = AsmArgs {
    5750              :   //     templates: vec![first_template],
    5751              :   //     operands: vec![],
    5752              :   //     named_args: Default::default(),
    5753              :   //     reg_args: Default::default(),
    5754              :   //     clobber_abis: Vec::new(),
    5755              :   //     options: ast::InlineAsmOptions::empty(),
    5756              :   //     options_spans: vec![],
    5757              :   // };
    5758              :   std::vector<InlineAsmTemplatePiece> template_;
    5759              :   std::vector<TupleTemplateStr> template_strs;
    5760              :   std::vector<InlineAsmOperand> operands;
    5761              :   std::map<std::string, int> named_args;
    5762              :   std::set<int> reg_args;
    5763              :   std::vector<TupleClobber> clobber_abi;
    5764              :   std::set<InlineAsm::Option> options;
    5765              : 
    5766              :   std::vector<location_t> line_spans;
    5767              : 
    5768              :   bool is_global_asm;
    5769              : 
    5770           39 :   InlineAsm (location_t locus, bool is_global_asm)
    5771           39 :     : locus (locus), is_global_asm (is_global_asm)
    5772           39 :   {}
    5773              : 
    5774              :   void accept_vis (ASTVisitor &vis) override;
    5775            0 :   std::string as_string () const override { return "InlineAsm AST Node"; }
    5776              : 
    5777           54 :   location_t get_locus () const override { return locus; }
    5778              : 
    5779            0 :   void mark_for_strip () override {}
    5780              : 
    5781           92 :   bool is_marked_for_strip () const override { return false; }
    5782              : 
    5783          343 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5784              : 
    5785            0 :   void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
    5786              : 
    5787           27 :   std::vector<InlineAsmTemplatePiece> get_template_ () { return template_; }
    5788              : 
    5789           56 :   std::vector<TupleTemplateStr> get_template_strs () { return template_strs; }
    5790              : 
    5791          370 :   std::vector<InlineAsmOperand> get_operands () { return operands; }
    5792              : 
    5793           27 :   std::vector<TupleClobber> get_clobber_abi () { return clobber_abi; }
    5794              : 
    5795           27 :   std::set<InlineAsm::Option> get_options () { return options; }
    5796              : 
    5797          203 :   InlineAsm *clone_expr_without_block_impl () const override
    5798              :   {
    5799          203 :     return new InlineAsm (*this);
    5800              :   }
    5801              : 
    5802           27 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::InlineAsm; }
    5803              : 
    5804            2 :   static std::string option_to_string (Option option)
    5805              :   {
    5806            2 :     switch (option)
    5807              :       {
    5808            0 :       case Option::PURE:
    5809            0 :         return "pure";
    5810            1 :       case Option::NOMEM:
    5811            1 :         return "nomem";
    5812            0 :       case Option::READONLY:
    5813            0 :         return "readonly";
    5814            0 :       case Option::PRESERVES_FLAGS:
    5815            0 :         return "preserves_flags";
    5816            1 :       case Option::NORETURN:
    5817            1 :         return "noreturn";
    5818            0 :       case Option::NOSTACK:
    5819            0 :         return "nostack";
    5820            0 :       case Option::ATT_SYNTAX:
    5821            0 :         return "att_syntax";
    5822            0 :       case Option::RAW:
    5823            0 :         return "raw";
    5824            0 :       case Option::MAY_UNWIND:
    5825            0 :         return "may_unwind";
    5826            0 :       default:
    5827            0 :         rust_unreachable ();
    5828              :       }
    5829              :   }
    5830              : };
    5831              : 
    5832              : class LlvmInlineAsm : public ExprWithoutBlock
    5833              : {
    5834              :   // llvm_asm!("" :         : "r"(&mut dummy) : "memory" : "volatile");
    5835              :   //           Asm, Outputs, Inputs,            Clobbers, Options,
    5836              : 
    5837              : public:
    5838              :   enum class Dialect
    5839              :   {
    5840              :     Att,
    5841              :     Intel,
    5842              :   };
    5843              : 
    5844              : private:
    5845              :   location_t locus;
    5846              :   std::vector<Attribute> outer_attrs;
    5847              :   std::vector<LlvmOperand> inputs;
    5848              :   std::vector<LlvmOperand> outputs;
    5849              :   std::vector<TupleTemplateStr> templates;
    5850              :   std::vector<TupleClobber> clobbers;
    5851              :   bool volatility;
    5852              :   bool align_stack;
    5853              :   Dialect dialect;
    5854              : 
    5855              : public:
    5856            2 :   LlvmInlineAsm (location_t locus) : locus (locus) {}
    5857              : 
    5858            2 :   Dialect get_dialect () { return dialect; }
    5859              : 
    5860           28 :   location_t get_locus () const override { return locus; }
    5861              : 
    5862            0 :   void mark_for_strip () override {}
    5863              : 
    5864            8 :   bool is_marked_for_strip () const override { return false; }
    5865              : 
    5866            2 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5867              : 
    5868              :   void accept_vis (ASTVisitor &vis) override;
    5869              : 
    5870            0 :   std::string as_string () const override { return "InlineAsm AST Node"; }
    5871              : 
    5872            0 :   void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
    5873              : 
    5874           14 :   LlvmInlineAsm *clone_expr_without_block_impl () const override
    5875              :   {
    5876           14 :     return new LlvmInlineAsm (*this);
    5877              :   }
    5878              : 
    5879            6 :   std::vector<TupleTemplateStr> &get_templates () { return templates; }
    5880              :   const std::vector<TupleTemplateStr> &get_templates () const
    5881              :   {
    5882              :     return templates;
    5883              :   }
    5884              : 
    5885            2 :   Expr::Kind get_expr_kind () const override
    5886              :   {
    5887            2 :     return Expr::Kind::LlvmInlineAsm;
    5888              :   }
    5889              : 
    5890            0 :   void set_align_stack (bool align_stack) { this->align_stack = align_stack; }
    5891            2 :   bool is_stack_aligned () { return align_stack; }
    5892              : 
    5893            2 :   void set_volatile (bool volatility) { this->volatility = volatility; }
    5894            2 :   bool is_volatile () { return volatility; }
    5895              : 
    5896            0 :   void set_dialect (Dialect dialect) { this->dialect = dialect; }
    5897              : 
    5898              :   void set_inputs (std::vector<LlvmOperand> operands) { inputs = operands; }
    5899              :   void set_outputs (std::vector<LlvmOperand> operands) { outputs = operands; }
    5900              : 
    5901           32 :   std::vector<LlvmOperand> &get_inputs () { return inputs; }
    5902              :   const std::vector<LlvmOperand> &get_inputs () const { return inputs; }
    5903           32 :   std::vector<LlvmOperand> &get_outputs () { return outputs; }
    5904              :   const std::vector<LlvmOperand> &get_outputs () const { return outputs; }
    5905              : 
    5906            6 :   std::vector<TupleClobber> &get_clobbers () { return clobbers; }
    5907              :   const std::vector<TupleClobber> &get_clobbers () const { return clobbers; }
    5908              : };
    5909              : 
    5910              : } // namespace AST
    5911              : } // namespace Rust
    5912              : 
    5913              : #endif
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.