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