LCOV - code coverage report
Current view: top level - gcc/rust/ast - rust-expr.h (source / functions) Coverage Total Hit
Test: gcc.info Lines: 83.2 % 1693 1408
Test Date: 2026-04-20 14:57:17 Functions: 79.4 % 572 454
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #ifndef RUST_AST_EXPR_H
       2              : #define RUST_AST_EXPR_H
       3              : 
       4              : #include "optional.h"
       5              : #include "rust-ast.h"
       6              : #include "rust-common.h"
       7              : #include "rust-path.h"
       8              : #include "rust-macro.h"
       9              : #include "rust-operators.h"
      10              : 
      11              : namespace Rust {
      12              : namespace AST {
      13              : /* TODO: if GCC moves to C++17 or allows boost, replace some boolean
      14              :  * "has_whatever" pairs with
      15              :  * optional types (std::optional or boost::optional)? */
      16              : 
      17              : // Loop label expression AST node used with break and continue expressions
      18              : // TODO: inline?
      19          549 : class LoopLabel /*: public Visitable*/
      20              : {
      21              :   Lifetime label; // or type LIFETIME_OR_LABEL
      22              :   location_t locus;
      23              : 
      24              :   NodeId node_id;
      25              : 
      26              : public:
      27              :   std::string as_string () const;
      28              : 
      29           63 :   LoopLabel (Lifetime loop_label, location_t locus = UNDEF_LOCATION)
      30           63 :     : label (std::move (loop_label)), locus (locus),
      31           63 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
      32           63 :   {}
      33              : 
      34              :   // Returns whether the LoopLabel is in an error state.
      35           81 :   location_t get_locus () const { return locus; }
      36              : 
      37         1278 :   Lifetime &get_lifetime () { return label; }
      38              : 
      39           36 :   NodeId get_node_id () const { return node_id; }
      40              : };
      41              : 
      42              : // AST node for an expression with an accompanying block - abstract
      43        68928 : 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        18547 :   ExprWithBlock *clone_expr_impl () const final override
      51              :   {
      52        18547 :     return clone_expr_with_block_impl ();
      53              :   }
      54              : 
      55        19614 :   bool is_expr_without_block () const final override { return false; };
      56              : 
      57              : public:
      58              :   // Unique pointer custom clone function
      59         2750 :   std::unique_ptr<ExprWithBlock> clone_expr_with_block () const
      60              :   {
      61         2750 :     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         6998 :   std::string as_string () const override { return literal.as_string (); }
      74              : 
      75         6953 :   Literal::LitType get_lit_type () const { return literal.get_lit_type (); }
      76              : 
      77        39468 :   LiteralExpr (std::string value_as_string, Literal::LitType type,
      78              :                PrimitiveCoreType type_hint, std::vector<Attribute> outer_attrs,
      79              :                location_t locus)
      80        78936 :     : outer_attrs (std::move (outer_attrs)),
      81        39468 :       literal (std::move (value_as_string), type, type_hint), locus (locus)
      82        39468 :   {}
      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        46547 :   location_t get_locus () const override final { return locus; }
      97              : 
      98          239 :   bool is_literal () const override final { return true; }
      99              : 
     100        24115 :   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       620715 :   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      3482549 :   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        20442 :   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        50829 :   LiteralExpr *clone_expr_without_block_impl () const final override
     122              :   {
     123        50829 :     return clone_literal_expr_impl ();
     124              :   }
     125              : 
     126              :   /* not virtual as currently no subclasses of LiteralExpr, but could be in
     127              :    * future */
     128        50829 :   /*virtual*/ LiteralExpr *clone_literal_expr_impl () const
     129              :   {
     130        50829 :     return new LiteralExpr (*this);
     131              :   }
     132              : };
     133              : 
     134              : // Literal expression attribute body (non-macro attribute)
     135        71670 : class AttrInputLiteral : public AttrInput
     136              : {
     137              :   LiteralExpr literal_expr;
     138              : 
     139              : public:
     140        11901 :   AttrInputLiteral (LiteralExpr lit_expr) : literal_expr (std::move (lit_expr))
     141              :   {}
     142              : 
     143           58 :   std::string as_string () const override
     144              :   {
     145          116 :     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       252082 :   LiteralExpr &get_literal () { return literal_expr; }
     157              : 
     158        39344 :   AttrInputType get_attr_input_type () const final override
     159              :   {
     160        39344 :     return AttrInput::AttrInputType::LITERAL;
     161              :   }
     162              : 
     163              : protected:
     164              :   /* Use covariance to implement clone function as returning this object rather
     165              :    * than base */
     166        71670 :   AttrInputLiteral *clone_attr_input_impl () const override
     167              :   {
     168        71670 :     return new AttrInputLiteral (*this);
     169              :   }
     170              : };
     171              : 
     172              : class AttrInputExpr : public AttrInput
     173              : {
     174              :   std::unique_ptr<Expr> expr;
     175              : 
     176              : public:
     177            5 :   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            7 :   AttrInputType get_attr_input_type () const final override
     184              :   {
     185            7 :     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           60 :   Expr &get_expr () { return *expr; }
     206              : 
     207            4 :   std::unique_ptr<Expr> &get_expr_ptr () { return expr; }
     208              : 
     209           19 :   AttrInputExpr *clone_attr_input_impl () const override
     210              :   {
     211           19 :     return new AttrInputExpr (*this);
     212              :   }
     213              : };
     214              : 
     215              : /* literal expr only meta item inner - TODO possibly replace with inheritance of
     216              :  * LiteralExpr itself? */
     217            0 : class MetaItemLitExpr : public MetaItemInner
     218              : {
     219              :   LiteralExpr lit_expr;
     220              : 
     221              : public:
     222           28 :   MetaItemLitExpr (LiteralExpr lit_expr) : lit_expr (std::move (lit_expr)) {}
     223              : 
     224            8 :   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            0 :   MetaItemLitExpr *clone_meta_item_inner_impl () const override
     244              :   {
     245            0 :     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            2 :   MetaItemPathExpr (SimplePath path, std::unique_ptr<Expr> expr)
     257            2 :     : 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        23436 :   OperatorExpr (std::unique_ptr<Expr> main_or_left_expr,
     328              :                 std::vector<Attribute> outer_attribs, location_t locus)
     329        46872 :     : locus (locus), outer_attrs (std::move (outer_attribs)),
     330        23436 :       main_or_left_expr (std::move (main_or_left_expr))
     331        23436 :   {}
     332              : 
     333              :   // Copy constructor (only for initialisation of expr purposes)
     334        55261 :   OperatorExpr (OperatorExpr const &other)
     335        55261 :     : locus (other.locus), outer_attrs (other.outer_attrs)
     336              :   {
     337              :     // guard to prevent null dereference (only required if error state)
     338        55261 :     if (other.main_or_left_expr != nullptr)
     339        55261 :       main_or_left_expr = other.main_or_left_expr->clone_expr ();
     340        55261 :   }
     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        41346 :   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       660489 :   bool is_marked_for_strip () const override
     368              :   {
     369       660489 :     return main_or_left_expr == nullptr;
     370              :   }
     371              : 
     372              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
     373      3262425 :   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         1951 :   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         1951 :     : OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs),
     398              :                     locus),
     399         1951 :       mutability (mutability), raw_borrow (raw_borrow),
     400         1951 :       double_borrow (is_double_borrow)
     401         1951 :   {}
     402              : 
     403              :   void accept_vis (ASTVisitor &vis) override;
     404              : 
     405              :   // TODO: is this better? Or is a "vis_block" better?
     406        57020 :   Expr &get_borrowed_expr ()
     407              :   {
     408        57020 :     rust_assert (main_or_left_expr != nullptr);
     409        57020 :     return *main_or_left_expr;
     410              :   }
     411              : 
     412        11086 :   std::unique_ptr<Expr> &get_borrowed_expr_ptr ()
     413              :   {
     414        11086 :     rust_assert (main_or_left_expr != nullptr);
     415        11086 :     return main_or_left_expr;
     416              :   }
     417              : 
     418          237 :   bool has_borrow_expr () const { return main_or_left_expr != nullptr; }
     419              : 
     420          474 :   bool get_is_mut () const { return mutability == Mutability::Mut; }
     421              : 
     422         1959 :   Mutability get_mutability () const { return mutability; }
     423              : 
     424         2173 :   bool get_is_double_borrow () const { return double_borrow; }
     425         4362 :   bool is_raw_borrow () const { return raw_borrow; }
     426              : 
     427         1942 :   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         3193 :   BorrowExpr *clone_expr_without_block_impl () const override
     433              :   {
     434         3193 :     return new BorrowExpr (*this);
     435              :   }
     436              : };
     437              : 
     438              : // Unary prefix * deference operator
     439        23932 : class DereferenceExpr : public OperatorExpr
     440              : {
     441              : public:
     442              :   std::string as_string () const override;
     443              : 
     444              :   // Constructor calls OperatorExpr's protected constructor
     445         3965 :   DereferenceExpr (std::unique_ptr<Expr> deref_lvalue,
     446              :                    std::vector<Attribute> outer_attribs, location_t locus)
     447         3965 :     : OperatorExpr (std::move (deref_lvalue), std::move (outer_attribs), locus)
     448         3965 :   {}
     449              : 
     450              :   void accept_vis (ASTVisitor &vis) override;
     451              : 
     452              :   // TODO: is this better? Or is a "vis_block" better?
     453        71692 :   Expr &get_dereferenced_expr ()
     454              :   {
     455        71692 :     rust_assert (main_or_left_expr != nullptr);
     456        71692 :     return *main_or_left_expr;
     457              :   }
     458              : 
     459        23861 :   std::unique_ptr<Expr> &get_dereferenced_expr_ptr ()
     460              :   {
     461        23861 :     rust_assert (main_or_left_expr != nullptr);
     462        23861 :     return main_or_left_expr;
     463              :   }
     464              : 
     465         3963 :   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        11966 :   DereferenceExpr *clone_expr_without_block_impl () const override
     471              :   {
     472        11966 :     return new DereferenceExpr (*this);
     473              :   }
     474              : };
     475              : 
     476              : // Unary postfix ? error propogation operator. Cannot be overloaded.
     477            4 : class ErrorPropagationExpr : public OperatorExpr
     478              : {
     479              : public:
     480              :   std::string as_string () const override;
     481              : 
     482              :   // Constructor calls OperatorExpr's protected constructor
     483            1 :   ErrorPropagationExpr (std::unique_ptr<Expr> potential_error_value,
     484              :                         std::vector<Attribute> outer_attribs, location_t locus)
     485            1 :     : OperatorExpr (std::move (potential_error_value),
     486            1 :                     std::move (outer_attribs), locus)
     487            1 :   {}
     488              : 
     489              :   void accept_vis (ASTVisitor &vis) override;
     490              : 
     491              :   // TODO: is this better? Or is a "vis_block" better?
     492           14 :   Expr &get_propagating_expr ()
     493              :   {
     494           14 :     rust_assert (main_or_left_expr != nullptr);
     495           14 :     return *main_or_left_expr;
     496              :   }
     497              : 
     498            4 :   std::unique_ptr<Expr> &get_propagating_expr_ptr ()
     499              :   {
     500            4 :     rust_assert (main_or_left_expr != nullptr);
     501            4 :     return main_or_left_expr;
     502              :   }
     503              : 
     504            2 :   Expr::Kind get_expr_kind () const override
     505              :   {
     506            2 :     return Expr::Kind::ErrorPropagation;
     507              :   }
     508              : 
     509              : protected:
     510              :   /* Use covariance to implement clone function as returning this object rather
     511              :    * than base */
     512            1 :   ErrorPropagationExpr *clone_expr_without_block_impl () const override
     513              :   {
     514            1 :     return new ErrorPropagationExpr (*this);
     515              :   }
     516              : };
     517              : 
     518              : // Unary prefix - or ! negation or NOT operators.
     519         2514 : 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          860 :   ExprType get_expr_type () const { return expr_type; }
     534              : 
     535              :   // Constructor calls OperatorExpr's protected constructor
     536          690 :   NegationExpr (std::unique_ptr<Expr> negated_value, ExprType expr_kind,
     537              :                 std::vector<Attribute> outer_attribs, location_t locus)
     538          690 :     : OperatorExpr (std::move (negated_value), std::move (outer_attribs),
     539              :                     locus),
     540          690 :       expr_type (expr_kind)
     541          690 :   {}
     542              : 
     543              :   void accept_vis (ASTVisitor &vis) override;
     544              : 
     545              :   // TODO: is this better? Or is a "vis_block" better?
     546        16532 :   Expr &get_negated_expr ()
     547              :   {
     548        16532 :     rust_assert (main_or_left_expr != nullptr);
     549        16532 :     return *main_or_left_expr;
     550              :   }
     551              : 
     552         3661 :   std::unique_ptr<Expr> &get_negated_expr_ptr ()
     553              :   {
     554         3661 :     rust_assert (main_or_left_expr != nullptr);
     555         3661 :     return main_or_left_expr;
     556              :   }
     557              : 
     558          689 :   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         1257 :   NegationExpr *clone_expr_without_block_impl () const override
     564              :   {
     565         1257 :     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         3712 :   ExprType get_expr_type () const { return expr_type; }
     585              : 
     586              :   // Constructor calls OperatorExpr's protected constructor
     587         4572 :   ArithmeticOrLogicalExpr (std::unique_ptr<Expr> left_value,
     588              :                            std::unique_ptr<Expr> right_value,
     589              :                            ExprType expr_kind, location_t locus)
     590        13716 :     : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
     591         4572 :       expr_type (expr_kind), right_expr (std::move (right_value))
     592         4572 :   {}
     593              :   // outer attributes not allowed
     594              : 
     595              :   // Copy constructor - probably required due to unique pointer
     596        12958 :   ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other)
     597        25916 :     : OperatorExpr (other), expr_type (other.expr_type),
     598        12958 :       right_expr (other.right_expr->clone_expr ())
     599        12958 :   {}
     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      2176073 :   Expr &get_left_expr ()
     621              :   {
     622      2176073 :     rust_assert (main_or_left_expr != nullptr);
     623      2176073 :     return *main_or_left_expr;
     624              :   }
     625              : 
     626      1066751 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     627              :   {
     628      1066751 :     rust_assert (main_or_left_expr != nullptr);
     629      1066751 :     return main_or_left_expr;
     630              :   }
     631              : 
     632              :   // TODO: is this better? Or is a "vis_block" better?
     633      2176073 :   Expr &get_right_expr ()
     634              :   {
     635      2176073 :     rust_assert (right_expr != nullptr);
     636      2176073 :     return *right_expr;
     637              :   }
     638              : 
     639      1066751 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     640              :   {
     641      1066751 :     rust_assert (right_expr != nullptr);
     642      1066751 :     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         3388 :   Expr::Kind get_expr_kind () const override
     649              :   {
     650         3388 :     return Expr::Kind::ArithmeticOrLogical;
     651              :   }
     652              : 
     653              : protected:
     654              :   /* Use covariance to implement clone function as returning this object rather
     655              :    * than base */
     656        12958 :   ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override
     657              :   {
     658        12958 :     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         3660 :   ExprType get_expr_type () const { return expr_type; }
     678              : 
     679              :   // Constructor requires pointers for polymorphism
     680         3486 :   ComparisonExpr (std::unique_ptr<Expr> left_value,
     681              :                   std::unique_ptr<Expr> right_value, ExprType comparison_kind,
     682              :                   location_t locus)
     683        10458 :     : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
     684         3486 :       expr_type (comparison_kind), right_expr (std::move (right_value))
     685         3486 :   {}
     686              :   // outer attributes not allowed
     687              : 
     688              :   // Copy constructor also calls OperatorExpr's protected constructor
     689         5398 :   ComparisonExpr (ComparisonExpr const &other)
     690        10796 :     : OperatorExpr (other), expr_type (other.expr_type),
     691         5398 :       right_expr (other.right_expr->clone_expr ())
     692         5398 :   {}
     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        78331 :   Expr &get_left_expr ()
     714              :   {
     715        78331 :     rust_assert (main_or_left_expr != nullptr);
     716        78331 :     return *main_or_left_expr;
     717              :   }
     718              : 
     719        20108 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     720              :   {
     721        20108 :     rust_assert (main_or_left_expr != nullptr);
     722        20108 :     return main_or_left_expr;
     723              :   }
     724              : 
     725              :   // TODO: is this better? Or is a "vis_block" better?
     726        78331 :   Expr &get_right_expr ()
     727              :   {
     728        78331 :     rust_assert (right_expr != nullptr);
     729        78331 :     return *right_expr;
     730              :   }
     731              : 
     732        20108 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     733              :   {
     734        20108 :     rust_assert (right_expr != nullptr);
     735        20108 :     return right_expr;
     736              :   }
     737              : 
     738              :   ExprType get_kind () { return expr_type; }
     739              : 
     740         3475 :   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         5398 :   ComparisonExpr *clone_expr_without_block_impl () const override
     748              :   {
     749         5398 :     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          385 :   LazyBooleanExpr (std::unique_ptr<Expr> left_bool_expr,
     767              :                    std::unique_ptr<Expr> right_bool_expr, ExprType expr_kind,
     768              :                    location_t locus)
     769         1155 :     : OperatorExpr (std::move (left_bool_expr), std::vector<Attribute> (),
     770              :                     locus),
     771          385 :       expr_type (expr_kind), right_expr (std::move (right_bool_expr))
     772          385 :   {}
     773              :   // outer attributes not allowed
     774              : 
     775              :   // Copy constructor also calls OperatorExpr's protected constructor
     776          421 :   LazyBooleanExpr (LazyBooleanExpr const &other)
     777          842 :     : OperatorExpr (other), expr_type (other.expr_type),
     778          421 :       right_expr (other.right_expr->clone_expr ())
     779          421 :   {}
     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          420 :   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         7761 :   Expr &get_left_expr ()
     804              :   {
     805         7761 :     rust_assert (main_or_left_expr != nullptr);
     806         7761 :     return *main_or_left_expr;
     807              :   }
     808              : 
     809         2008 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     810              :   {
     811         2008 :     rust_assert (main_or_left_expr != nullptr);
     812         2008 :     return main_or_left_expr;
     813              :   }
     814              : 
     815              :   // TODO: is this better? Or is a "vis_block" better?
     816         7761 :   Expr &get_right_expr ()
     817              :   {
     818         7761 :     rust_assert (right_expr != nullptr);
     819         7761 :     return *right_expr;
     820              :   }
     821              : 
     822         2008 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     823              :   {
     824         2008 :     rust_assert (right_expr != nullptr);
     825         2008 :     return right_expr;
     826              :   }
     827              : 
     828              :   ExprType get_kind () { return expr_type; }
     829              : 
     830          385 :   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          421 :   LazyBooleanExpr *clone_expr_without_block_impl () const override
     836              :   {
     837          421 :     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         5163 :   TypeCastExpr (std::unique_ptr<Expr> expr_to_cast,
     852              :                 std::unique_ptr<TypeNoBounds> type_to_cast_to, location_t locus)
     853        15489 :     : OperatorExpr (std::move (expr_to_cast), std::vector<Attribute> (), locus),
     854         5163 :       type_to_convert_to (std::move (type_to_cast_to))
     855         5163 :   {}
     856              :   // outer attributes not allowed
     857              : 
     858              :   // Copy constructor also requires calling protected constructor
     859        10452 :   TypeCastExpr (TypeCastExpr const &other)
     860        10452 :     : OperatorExpr (other),
     861        10452 :       type_to_convert_to (other.type_to_convert_to->clone_type_no_bounds ())
     862        10452 :   {}
     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       168460 :   Expr &get_casted_expr ()
     882              :   {
     883       168460 :     rust_assert (main_or_left_expr != nullptr);
     884       168460 :     return *main_or_left_expr;
     885              :   }
     886              : 
     887        28667 :   std::unique_ptr<Expr> &get_casted_expr_ptr ()
     888              :   {
     889        28667 :     rust_assert (main_or_left_expr != nullptr);
     890        28667 :     return main_or_left_expr;
     891              :   }
     892              : 
     893              :   // TODO: is this better? Or is a "vis_block" better?
     894       168460 :   TypeNoBounds &get_type_to_cast_to ()
     895              :   {
     896       168460 :     rust_assert (type_to_convert_to != nullptr);
     897       168460 :     return *type_to_convert_to;
     898              :   }
     899              : 
     900        28667 :   std::unique_ptr<TypeNoBounds> &get_type_to_cast_to_ptr ()
     901              :   {
     902        28667 :     rust_assert (type_to_convert_to != nullptr);
     903        28667 :     return type_to_convert_to;
     904              :   }
     905              : 
     906         5117 :   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        10452 :   TypeCastExpr *clone_expr_without_block_impl () const override
     912              :   {
     913        10452 :     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         2541 :   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         2541 :     : OperatorExpr (std::move (value_to_assign_to), std::move (outer_attribs),
     930              :                     locus),
     931         2541 :       right_expr (std::move (value_to_assign))
     932         2541 :   {}
     933              :   // outer attributes not allowed
     934              : 
     935              :   // Call OperatorExpr constructor in copy constructor, as well as clone
     936         6997 :   AssignmentExpr (AssignmentExpr const &other)
     937         6997 :     : OperatorExpr (other), right_expr (other.right_expr->clone_expr ())
     938         6997 :   {}
     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          228 :   void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); }
     958          228 :   void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); }
     959              : 
     960              :   // TODO: is this better? Or is a "vis_block" better?
     961        60146 :   Expr &get_left_expr ()
     962              :   {
     963        60146 :     rust_assert (main_or_left_expr != nullptr);
     964        60146 :     return *main_or_left_expr;
     965              :   }
     966              : 
     967        11598 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     968              :   {
     969        11598 :     rust_assert (main_or_left_expr != nullptr);
     970        11598 :     return main_or_left_expr;
     971              :   }
     972              : 
     973        11598 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     974              :   {
     975        11598 :     rust_assert (right_expr != nullptr);
     976        11598 :     return right_expr;
     977              :   }
     978              : 
     979              :   // TODO: is this better? Or is a "vis_block" better?
     980        60146 :   Expr &get_right_expr ()
     981              :   {
     982        60146 :     rust_assert (right_expr != nullptr);
     983        60146 :     return *right_expr;
     984              :   }
     985              : 
     986         2504 :   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         6997 :   AssignmentExpr *clone_expr_without_block_impl () const override
     992              :   {
     993         6997 :     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          681 :   ExprType get_expr_type () const { return expr_type; }
    1013              : 
    1014              :   // Use pointers in constructor to enable polymorphism
    1015          682 :   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         2046 :     : OperatorExpr (std::move (value_to_assign_to), std::vector<Attribute> (),
    1019              :                     locus),
    1020          682 :       expr_type (expr_kind), right_expr (std::move (value_to_assign))
    1021          682 :   {}
    1022              :   // outer attributes not allowed
    1023              : 
    1024              :   // Have clone in copy constructor
    1025         2617 :   CompoundAssignmentExpr (CompoundAssignmentExpr const &other)
    1026         5234 :     : OperatorExpr (other), expr_type (other.expr_type),
    1027         2617 :       right_expr (other.right_expr->clone_expr ())
    1028         2617 :   {}
    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        20363 :   Expr &get_left_expr ()
    1050              :   {
    1051        20363 :     rust_assert (main_or_left_expr != nullptr);
    1052        20363 :     return *main_or_left_expr;
    1053              :   }
    1054              : 
    1055         3232 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
    1056              :   {
    1057         3232 :     rust_assert (main_or_left_expr != nullptr);
    1058         3232 :     return main_or_left_expr;
    1059              :   }
    1060              : 
    1061              :   // TODO: is this better? Or is a "vis_block" better?
    1062        20363 :   Expr &get_right_expr ()
    1063              :   {
    1064        20363 :     rust_assert (right_expr != nullptr);
    1065        20363 :     return *right_expr;
    1066              :   }
    1067              : 
    1068         3232 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
    1069              :   {
    1070         3232 :     rust_assert (right_expr != nullptr);
    1071         3232 :     return right_expr;
    1072              :   }
    1073              : 
    1074          682 :   Expr::Kind get_expr_kind () const override
    1075              :   {
    1076          682 :     return Expr::Kind::CompoundAssignment;
    1077              :   }
    1078              : 
    1079              : protected:
    1080              :   /* Use covariance to implement clone function as returning this object rather
    1081              :    * than base */
    1082         2617 :   CompoundAssignmentExpr *clone_expr_without_block_impl () const override
    1083              :   {
    1084         2617 :     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        10127 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1101              : 
    1102              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1103        12298 :   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          316 :   GroupedExpr (std::unique_ptr<Expr> parenthesised_expr,
    1111              :                std::vector<Attribute> inner_attribs,
    1112              :                std::vector<Attribute> outer_attribs, location_t locus)
    1113          632 :     : outer_attrs (std::move (outer_attribs)),
    1114          316 :       inner_attrs (std::move (inner_attribs)),
    1115          316 :       expr_in_parens (std::move (parenthesised_expr)), locus (locus)
    1116          316 :   {}
    1117              : 
    1118              :   // Copy constructor includes clone for expr_in_parens
    1119          553 :   GroupedExpr (GroupedExpr const &other)
    1120          553 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1121          553 :       inner_attrs (other.inner_attrs), locus (other.locus)
    1122              :   {
    1123              :     // guard to prevent null dereference (only required if error state)
    1124          553 :     if (other.expr_in_parens != nullptr)
    1125          553 :       expr_in_parens = other.expr_in_parens->clone_expr ();
    1126          553 :   }
    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          949 :   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         2155 :   bool is_marked_for_strip () const override
    1156              :   {
    1157         2155 :     return expr_in_parens == nullptr;
    1158              :   }
    1159              : 
    1160              :   // TODO: is this better? Or is a "vis_block" better?
    1161         8353 :   Expr &get_expr_in_parens ()
    1162              :   {
    1163         8353 :     rust_assert (expr_in_parens != nullptr);
    1164         8353 :     return *expr_in_parens;
    1165              :   }
    1166              : 
    1167         1832 :   std::unique_ptr<Expr> &get_expr_in_parens_ptr ()
    1168              :   {
    1169         1832 :     rust_assert (expr_in_parens != nullptr);
    1170         1832 :     return expr_in_parens;
    1171              :   }
    1172              : 
    1173          316 :   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          553 :   GroupedExpr *clone_expr_without_block_impl () const override
    1179              :   {
    1180          553 :     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          678 :   std::unique_ptr<ArrayElems> clone_array_elems () const
    1193              :   {
    1194          678 :     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          431 :   NodeId get_node_id () const { return node_id; }
    1202              : 
    1203              : protected:
    1204         1113 :   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          312 :   ArrayElemsValues (std::vector<std::unique_ptr<Expr>> elems, location_t locus)
    1220          312 :     : ArrayElems (), values (std::move (elems)), locus (locus)
    1221          312 :   {}
    1222              : 
    1223              :   // copy constructor with vector clone
    1224          523 :   ArrayElemsValues (ArrayElemsValues const &other)
    1225          523 :   {
    1226          523 :     values.reserve (other.values.size ());
    1227        14091 :     for (const auto &e : other.values)
    1228        13568 :       values.push_back (e->clone_expr ());
    1229          523 :   }
    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         6304 :   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          523 :   ArrayElemsValues *clone_array_elems_impl () const override
    1262              :   {
    1263          523 :     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          123 :   ArrayElemsCopied (std::unique_ptr<Expr> copied_elem,
    1279              :                     std::unique_ptr<Expr> copy_amount, location_t locus)
    1280          246 :     : ArrayElems (), elem_to_copy (std::move (copied_elem)),
    1281          123 :       num_copies (std::move (copy_amount)), locus (locus)
    1282              :   {}
    1283              : 
    1284              :   // Copy constructor required due to unique_ptr - uses custom clone
    1285          155 :   ArrayElemsCopied (ArrayElemsCopied const &other)
    1286          465 :     : elem_to_copy (other.elem_to_copy->clone_expr ()),
    1287          155 :       num_copies (other.num_copies->clone_expr ())
    1288          155 :   {}
    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         2408 :   Expr &get_elem_to_copy ()
    1311              :   {
    1312         2408 :     rust_assert (elem_to_copy != nullptr);
    1313         2408 :     return *elem_to_copy;
    1314              :   }
    1315              : 
    1316          610 :   std::unique_ptr<Expr> &get_elem_to_copy_ptr ()
    1317              :   {
    1318          610 :     rust_assert (elem_to_copy != nullptr);
    1319          610 :     return elem_to_copy;
    1320              :   }
    1321              : 
    1322              :   // TODO: is this better? Or is a "vis_block" better?
    1323         2408 :   Expr &get_num_copies ()
    1324              :   {
    1325         2408 :     rust_assert (num_copies != nullptr);
    1326         2408 :     return *num_copies;
    1327              :   }
    1328              : 
    1329          610 :   std::unique_ptr<Expr> &get_num_copies_ptr ()
    1330              :   {
    1331          610 :     rust_assert (num_copies != nullptr);
    1332          610 :     return num_copies;
    1333              :   }
    1334              : 
    1335              : protected:
    1336          155 :   ArrayElemsCopied *clone_array_elems_impl () const override
    1337              :   {
    1338          155 :     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        10474 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1358              : 
    1359              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1360        12138 :   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          435 :   ArrayExpr (std::unique_ptr<ArrayElems> array_elems,
    1369              :              std::vector<Attribute> inner_attribs,
    1370              :              std::vector<Attribute> outer_attribs, location_t locus)
    1371          870 :     : outer_attrs (std::move (outer_attribs)),
    1372          435 :       inner_attrs (std::move (inner_attribs)),
    1373          435 :       internal_elements (std::move (array_elems)), locus (locus)
    1374              :   {
    1375          435 :     rust_assert (internal_elements != nullptr);
    1376          435 :   }
    1377              : 
    1378              :   // Copy constructor requires cloning ArrayElems for polymorphism to hold
    1379          678 :   ArrayExpr (ArrayExpr const &other)
    1380          678 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1381          678 :       inner_attrs (other.inner_attrs), locus (other.locus),
    1382         1356 :       marked_for_strip (other.marked_for_strip)
    1383              :   {
    1384          678 :     internal_elements = other.internal_elements->clone_array_elems ();
    1385          678 :     rust_assert (internal_elements != nullptr);
    1386          678 :   }
    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          883 :   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         1626 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    1414              : 
    1415              :   // TODO: is this better? Or is a "vis_block" better?
    1416         8856 :   std::unique_ptr<ArrayElems> &get_array_elems ()
    1417              :   {
    1418         8856 :     rust_assert (internal_elements != nullptr);
    1419         8856 :     return internal_elements;
    1420              :   }
    1421              : 
    1422          431 :   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          678 :   ArrayExpr *clone_expr_without_block_impl () const override
    1428              :   {
    1429          678 :     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          302 :   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          604 :     : outer_attrs (std::move (outer_attribs)),
    1452          302 :       array_expr (std::move (array_expr)),
    1453          302 :       index_expr (std::move (array_index_expr)), locus (locus)
    1454          302 :   {}
    1455              : 
    1456              :   // Copy constructor requires special cloning due to unique_ptr
    1457          334 :   ArrayIndexExpr (ArrayIndexExpr const &other)
    1458          334 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1459          334 :       locus (other.locus)
    1460              :   {
    1461              :     // guard to prevent null dereference (only required if error state)
    1462          334 :     if (other.array_expr != nullptr)
    1463          334 :       array_expr = other.array_expr->clone_expr ();
    1464          334 :     if (other.index_expr != nullptr)
    1465          334 :       index_expr = other.index_expr->clone_expr ();
    1466          334 :   }
    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          694 :   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            0 :   void mark_for_strip () override
    1498              :   {
    1499            0 :     array_expr = nullptr;
    1500            0 :     index_expr = nullptr;
    1501            0 :   }
    1502         1404 :   bool is_marked_for_strip () const override
    1503              :   {
    1504         1404 :     return array_expr == nullptr && index_expr == nullptr;
    1505              :   }
    1506              : 
    1507              :   // TODO: is this better? Or is a "vis_block" better?
    1508         6366 :   Expr &get_array_expr ()
    1509              :   {
    1510         6366 :     rust_assert (array_expr != nullptr);
    1511         6366 :     return *array_expr;
    1512              :   }
    1513              : 
    1514         1520 :   std::unique_ptr<Expr> &get_array_expr_ptr ()
    1515              :   {
    1516         1520 :     rust_assert (array_expr != nullptr);
    1517         1520 :     return array_expr;
    1518              :   }
    1519              : 
    1520              :   // TODO: is this better? Or is a "vis_block" better?
    1521         6366 :   Expr &get_index_expr ()
    1522              :   {
    1523         6366 :     rust_assert (index_expr != nullptr);
    1524         6366 :     return *index_expr;
    1525              :   }
    1526              : 
    1527         1520 :   std::unique_ptr<Expr> &get_index_expr_ptr ()
    1528              :   {
    1529         1520 :     rust_assert (index_expr != nullptr);
    1530         1520 :     return index_expr;
    1531              :   }
    1532              : 
    1533              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1534         9299 :   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          298 :   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          334 :   ArrayIndexExpr *clone_expr_without_block_impl () const override
    1547              :   {
    1548          334 :     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        12548 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1568              : 
    1569              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1570        15706 :   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          584 :   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         1168 :     : outer_attrs (std::move (outer_attribs)),
    1581          584 :       inner_attrs (std::move (inner_attribs)),
    1582          584 :       tuple_elems (std::move (tuple_elements)), locus (locus)
    1583          584 :   {}
    1584              : 
    1585              :   // copy constructor with vector clone
    1586         1210 :   TupleExpr (TupleExpr const &other)
    1587         1210 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1588         1210 :       inner_attrs (other.inner_attrs), locus (other.locus),
    1589         2420 :       marked_for_strip (other.marked_for_strip)
    1590              :   {
    1591         1210 :     tuple_elems.reserve (other.tuple_elems.size ());
    1592         3428 :     for (const auto &e : other.tuple_elems)
    1593         2218 :       tuple_elems.push_back (e->clone_expr ());
    1594         1210 :   }
    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         1170 :   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         2414 :   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        12598 :   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          567 :   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         1210 :   TupleExpr *clone_expr_without_block_impl () const override
    1642              :   {
    1643         1210 :     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          960 :   TupleIndex get_tuple_index () const { return tuple_index; }
    1665              : 
    1666          936 :   TupleIndexExpr (std::unique_ptr<Expr> tuple_expr, TupleIndex index,
    1667              :                   std::vector<Attribute> outer_attribs, location_t locus)
    1668         1872 :     : outer_attrs (std::move (outer_attribs)),
    1669          936 :       tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus),
    1670          936 :       to_strip (false)
    1671          936 :   {}
    1672              : 
    1673              :   // Copy constructor requires a clone for tuple_expr
    1674         1425 :   TupleIndexExpr (TupleIndexExpr const &other)
    1675         1425 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1676         1425 :       tuple_index (other.tuple_index), locus (other.locus),
    1677         1425 :       to_strip (other.to_strip)
    1678              :   {
    1679              :     // guard to prevent null dereference (only required if error state)
    1680         1425 :     if (other.tuple_expr != nullptr)
    1681         1425 :       tuple_expr = other.tuple_expr->clone_expr ();
    1682         1425 :   }
    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         2000 :   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         4728 :   bool is_marked_for_strip () const override { return to_strip; }
    1713              : 
    1714              :   // TODO: is this better? Or is a "vis_block" better?
    1715        22149 :   Expr &get_tuple_expr ()
    1716              :   {
    1717        22149 :     rust_assert (tuple_expr != nullptr);
    1718        22149 :     return *tuple_expr;
    1719              :   }
    1720              : 
    1721         4667 :   std::unique_ptr<Expr> &get_tuple_expr_ptr ()
    1722              :   {
    1723         4667 :     rust_assert (tuple_expr != nullptr);
    1724         4667 :     return tuple_expr;
    1725              :   }
    1726              : 
    1727              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1728        32760 :   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          902 :   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         1425 :   TupleIndexExpr *clone_expr_without_block_impl () const override
    1741              :   {
    1742         1425 :     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         1416 :   StructExpr (PathInExpression struct_path,
    1755              :               std::vector<Attribute> outer_attribs)
    1756         2832 :     : outer_attrs (std::move (outer_attribs)),
    1757         1416 :       struct_name (std::move (struct_path))
    1758         1416 :   {}
    1759              : 
    1760              : public:
    1761            0 :   const PathInExpression &get_struct_name () const { return struct_name; }
    1762        36771 :   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         6042 :   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        43153 :   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         1415 :   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        36641 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1796              : 
    1797              :   // Constructor has to call protected constructor of base class
    1798         1416 :   StructExprStruct (PathInExpression struct_path,
    1799              :                     std::vector<Attribute> inner_attribs,
    1800              :                     std::vector<Attribute> outer_attribs, location_t locus)
    1801         1416 :     : StructExpr (std::move (struct_path), std::move (outer_attribs)),
    1802         1416 :       inner_attrs (std::move (inner_attribs)), locus (locus)
    1803         1416 :   {}
    1804              : 
    1805         3204 :   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           89 :   StructExprStruct *clone_expr_without_block_impl () const override
    1813              :   {
    1814           89 :     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         1397 :   StructBase (std::unique_ptr<Expr> base_struct_ptr, location_t locus)
    1828         1397 :     : base_struct (std::move (base_struct_ptr)), locus (locus)
    1829              :   {}
    1830              : 
    1831              :   // Copy constructor requires clone
    1832         1661 :   StructBase (StructBase const &other)
    1833         1661 :   {
    1834              :     /* HACK: gets around base_struct pointer being null (e.g. if no struct base
    1835              :      * exists) */
    1836         1661 :     if (other.base_struct != nullptr)
    1837           63 :       base_struct = other.base_struct->clone_expr ();
    1838         1661 :   }
    1839              : 
    1840              :   // Destructor
    1841         2698 :   ~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         1334 :   StructBase (StructBase &&other) = default;
    1857           63 :   StructBase &operator= (StructBase &&other) = default;
    1858              : 
    1859              :   // Returns a null expr-ed StructBase - error state
    1860         1334 :   static StructBase error () { return StructBase (nullptr, UNDEF_LOCATION); }
    1861              : 
    1862              :   // Returns whether StructBase is in error state
    1863        35219 :   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         1281 :   Expr &get_base_struct ()
    1871              :   {
    1872         1281 :     rust_assert (base_struct != nullptr);
    1873         1281 :     return *base_struct;
    1874              :   }
    1875              : 
    1876          315 :   std::unique_ptr<Expr> &get_base_struct_ptr ()
    1877              :   {
    1878          315 :     rust_assert (base_struct != nullptr);
    1879          315 :     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         2931 :   std::unique_ptr<StructExprField> clone_struct_expr_field () const
    1892              :   {
    1893         2931 :     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         2508 :   NodeId get_node_id () const { return node_id; }
    1903              : 
    1904         2623 :   const std::vector<AST::Attribute> &get_outer_attrs () const
    1905              :   {
    1906         2623 :     return outer_attrs;
    1907              :   }
    1908              : 
    1909        10617 :   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         4918 :   StructExprField (AST::AttrVec outer_attrs)
    1919         4918 :     : outer_attrs (std::move (outer_attrs)),
    1920         4918 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
    1921         4918 :   {}
    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          216 :   StructExprFieldIdentifier (Identifier field_identifier,
    1935              :                              AST::AttrVec outer_attrs, location_t locus)
    1936          216 :     : StructExprField (std::move (outer_attrs)),
    1937          216 :       field_name (std::move (field_identifier)), locus (locus)
    1938          216 :   {}
    1939              : 
    1940            0 :   std::string as_string () const override { return field_name.as_string (); }
    1941              : 
    1942          433 :   location_t get_locus () const override final { return locus; }
    1943              : 
    1944              :   void accept_vis (ASTVisitor &vis) override;
    1945              : 
    1946          433 :   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          308 :   StructExprFieldIdentifier *clone_struct_expr_field_impl () const override
    1952              :   {
    1953          308 :     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         2079 :   StructExprFieldWithVal (std::unique_ptr<Expr> field_value,
    1965              :                           AST::AttrVec outer_attrs)
    1966         2079 :     : StructExprField (std::move (outer_attrs)), value (std::move (field_value))
    1967         2079 :   {}
    1968              : 
    1969              :   // Copy constructor requires clone
    1970         2623 :   StructExprFieldWithVal (StructExprFieldWithVal const &other)
    1971         2623 :     : StructExprField (other.get_outer_attrs ()),
    1972         2623 :       value (other.value->clone_expr ())
    1973         2623 :   {}
    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        45280 :   Expr &get_value ()
    1993              :   {
    1994        45280 :     rust_assert (value != nullptr);
    1995        45280 :     return *value;
    1996              :   }
    1997              : 
    1998        11258 :   std::unique_ptr<Expr> &get_value_ptr ()
    1999              :   {
    2000        11258 :     rust_assert (value != nullptr);
    2001        11258 :     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         2000 :   StructExprFieldIdentifierValue (Identifier field_identifier,
    2013              :                                   std::unique_ptr<Expr> field_value,
    2014              :                                   AST::AttrVec outer_attrs, location_t locus)
    2015         2000 :     : StructExprFieldWithVal (std::move (field_value), std::move (outer_attrs)),
    2016         2000 :       field_name (std::move (field_identifier)), locus (locus)
    2017         2000 :   {}
    2018              : 
    2019           35 :   StructExprFieldIdentifierValue (Identifier field_identifier,
    2020              :                                   std::unique_ptr<Expr> field_value,
    2021              :                                   location_t locus)
    2022           35 :     : StructExprFieldWithVal (std::move (field_value), {}),
    2023           35 :       field_name (std::move (field_identifier)), locus (locus)
    2024           35 :   {}
    2025              : 
    2026              :   std::string as_string () const override;
    2027              : 
    2028              :   void accept_vis (ASTVisitor &vis) override;
    2029              : 
    2030         4710 :   std::string get_field_name () const { return field_name.as_string (); }
    2031              : 
    2032         4387 :   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         2579 :   StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override
    2038              :   {
    2039         2579 :     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           44 :     : 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           44 :   TupleIndex get_index () const { return index; }
    2062              : 
    2063           88 :   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        35219 :   bool has_struct_base () const { return !struct_base.is_invalid (); }
    2087              : 
    2088              :   // Constructor for StructExprStructFields when no struct base is used
    2089         1334 :   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         1334 :     : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
    2096              :                         std::move (outer_attribs), locus),
    2097         1334 :       fields (std::move (expr_fields)), struct_base (std::move (base_struct))
    2098         1334 :   {}
    2099              : 
    2100              :   // copy constructor with vector clone
    2101         1661 :   StructExprStructFields (StructExprStructFields const &other)
    2102         1661 :     : StructExprStruct (other), struct_base (other.struct_base)
    2103              :   {
    2104         1661 :     fields.reserve (other.fields.size ());
    2105         4592 :     for (const auto &e : other.fields)
    2106         2931 :       fields.push_back (e->clone_struct_expr_field ());
    2107         1661 :   }
    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         5887 :   std::vector<std::unique_ptr<StructExprField>> &get_fields ()
    2130              :   {
    2131        35219 :     return fields;
    2132              :   }
    2133              :   const std::vector<std::unique_ptr<StructExprField>> &get_fields () const
    2134              :   {
    2135              :     return fields;
    2136              :   }
    2137              : 
    2138         1596 :   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         1661 :   StructExprStructFields *clone_expr_without_block_impl () const override
    2145              :   {
    2146         1661 :     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        12699 :   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        25398 :     : outer_attrs (std::move (outer_attribs)),
    2202        12699 :       function (std::move (function_expr)),
    2203        12699 :       params (std::move (function_params)), locus (locus)
    2204        12699 :   {}
    2205              : 
    2206              :   // copy constructor requires clone
    2207        25384 :   CallExpr (CallExpr const &other)
    2208        25384 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2209        25384 :       locus (other.locus)
    2210              :   {
    2211              :     // guard to prevent null dereference (only required if error state)
    2212        25384 :     if (other.function != nullptr)
    2213        25384 :       function = other.function->clone_expr ();
    2214              : 
    2215        25384 :     params.reserve (other.params.size ());
    2216        54352 :     for (const auto &e : other.params)
    2217        28968 :       params.push_back (e->clone_expr ());
    2218        25384 :   }
    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        26087 :   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            0 :   void mark_for_strip () override { function = nullptr; }
    2253        73765 :   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       386367 :   std::vector<std::unique_ptr<Expr>> &get_params () { return params; }
    2261              : 
    2262              :   // TODO: is this better? Or is a "vis_block" better?
    2263       315613 :   Expr &get_function_expr ()
    2264              :   {
    2265       315613 :     rust_assert (function != nullptr);
    2266       315613 :     return *function;
    2267              :   }
    2268              : 
    2269        70754 :   std::unique_ptr<Expr> &get_function_expr_ptr () { return function; }
    2270              : 
    2271              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2272       472655 :   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        12600 :   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        25384 :   CallExpr *clone_expr_without_block_impl () const override
    2285              :   {
    2286        25384 :     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         3020 :   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         6040 :     : outer_attrs (std::move (outer_attribs)),
    2307         3020 :       receiver (std::move (call_receiver)),
    2308         3020 :       method_name (std::move (method_path)), params (std::move (method_params)),
    2309         3020 :       locus (locus)
    2310         3020 :   {}
    2311              : 
    2312              :   // copy constructor required due to cloning
    2313        10343 :   MethodCallExpr (MethodCallExpr const &other)
    2314        10343 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2315        10343 :       method_name (other.method_name), locus (other.locus)
    2316              :   {
    2317              :     // guard to prevent null dereference (only required if error state)
    2318        10343 :     if (other.receiver != nullptr)
    2319        10343 :       receiver = other.receiver->clone_expr ();
    2320              : 
    2321        10343 :     params.reserve (other.params.size ());
    2322        19045 :     for (const auto &e : other.params)
    2323         8702 :       params.push_back (e->clone_expr ());
    2324        10343 :   }
    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         6360 :   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        19705 :   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        91026 :   std::vector<std::unique_ptr<Expr>> &get_params () { return params; }
    2365              : 
    2366              :   // TODO: is this better? Or is a "vis_block" better?
    2367        75708 :   Expr &get_receiver_expr ()
    2368              :   {
    2369        75708 :     rust_assert (receiver != nullptr);
    2370        75708 :     return *receiver;
    2371              :   }
    2372              : 
    2373        15318 :   std::unique_ptr<Expr> &get_receiver_expr_ptr ()
    2374              :   {
    2375        15318 :     rust_assert (receiver != nullptr);
    2376        15318 :     return receiver;
    2377              :   }
    2378              : 
    2379              :   const PathExprSegment &get_method_name () const { return method_name; }
    2380        91026 :   PathExprSegment &get_method_name () { return method_name; }
    2381              : 
    2382              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2383       111566 :   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         2976 :   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        10343 :   MethodCallExpr *clone_expr_without_block_impl () const override
    2396              :   {
    2397        10343 :     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         5342 :   FieldAccessExpr (std::unique_ptr<Expr> field_access_receiver,
    2414              :                    Identifier field_name, std::vector<Attribute> outer_attribs,
    2415              :                    location_t locus)
    2416        10684 :     : outer_attrs (std::move (outer_attribs)),
    2417         5342 :       receiver (std::move (field_access_receiver)),
    2418         5342 :       field (std::move (field_name)), locus (locus)
    2419         5342 :   {}
    2420              : 
    2421              :   // Copy constructor required due to unique_ptr cloning
    2422        20344 :   FieldAccessExpr (FieldAccessExpr const &other)
    2423        20344 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2424        20344 :       field (other.field), locus (other.locus)
    2425              :   {
    2426              :     // guard to prevent null dereference (only required if error state)
    2427        20344 :     if (other.receiver != nullptr)
    2428        20344 :       receiver = other.receiver->clone_expr ();
    2429        20344 :   }
    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        12819 :   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        36927 :   bool is_marked_for_strip () const override { return receiver == nullptr; }
    2459              : 
    2460              :   // TODO: is this better? Or is a "vis_block" better?
    2461       133533 :   Expr &get_receiver_expr ()
    2462              :   {
    2463       133533 :     rust_assert (receiver != nullptr);
    2464       133533 :     return *receiver;
    2465              :   }
    2466              : 
    2467        22122 :   std::unique_ptr<Expr> &get_receiver_expr_ptr ()
    2468              :   {
    2469        22122 :     rust_assert (receiver != nullptr);
    2470        22122 :     return receiver;
    2471              :   }
    2472              : 
    2473         5112 :   Identifier get_field_name () const { return field; }
    2474              : 
    2475              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2476       195967 :   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         4989 :   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        20344 :   FieldAccessExpr *clone_expr_without_block_impl () const override
    2489              :   {
    2490        20344 :     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         1652 :   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           72 :   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            0 :     : outer_attrs (std::move (outer_attrs)),
    2514            0 :       pattern (std::move (param_pattern)), type (std::move (param_type)),
    2515           72 :       locus (locus)
    2516              :   {}
    2517              : 
    2518              :   // Copy constructor required due to cloning as a result of unique_ptrs
    2519           63 :   ClosureParam (ClosureParam const &other) : outer_attrs (other.outer_attrs)
    2520              :   {
    2521              :     // guard to protect from null pointer dereference
    2522           63 :     if (other.pattern != nullptr)
    2523           63 :       pattern = other.pattern->clone_pattern ();
    2524           63 :     if (other.type != nullptr)
    2525           58 :       type = other.type->clone_type ();
    2526           63 :   }
    2527              : 
    2528          168 :   ~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           83 :   ClosureParam (ClosureParam &&other) = default;
    2550            0 :   ClosureParam &operator= (ClosureParam &&other) = default;
    2551              : 
    2552              :   // Returns whether closure parameter is in an error state.
    2553           72 :   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         1465 :   std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
    2565              : 
    2566         1276 :   Pattern &get_pattern ()
    2567              :   {
    2568         1276 :     rust_assert (pattern != nullptr);
    2569         1276 :     return *pattern;
    2570              :   }
    2571              : 
    2572          315 :   std::unique_ptr<Pattern> &get_pattern_ptr ()
    2573              :   {
    2574          315 :     rust_assert (pattern != nullptr);
    2575          315 :     return pattern;
    2576              :   }
    2577              : 
    2578         1182 :   Type &get_type ()
    2579              :   {
    2580         1182 :     rust_assert (has_type_given ());
    2581         1182 :     return *type;
    2582              :   }
    2583              : 
    2584          290 :   std::unique_ptr<Type> &get_type_ptr ()
    2585              :   {
    2586          290 :     rust_assert (has_type_given ());
    2587          290 :     return type;
    2588              :   }
    2589              : 
    2590           83 :   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           73 :   ClosureExpr (std::vector<ClosureParam> closure_params, bool has_move,
    2603              :                std::vector<Attribute> outer_attribs, location_t locus)
    2604          146 :     : outer_attrs (std::move (outer_attribs)), has_move (has_move),
    2605           73 :       params (std::move (closure_params)), locus (locus)
    2606           73 :   {}
    2607              : 
    2608              : public:
    2609              :   std::string as_string () const override;
    2610              : 
    2611          133 :   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         1668 :   std::vector<ClosureParam> &get_params () { return params; }
    2616              : 
    2617              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2618         1757 :   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           95 :   bool get_has_move () const { return has_move; }
    2626              : 
    2627           67 :   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           42 :   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           42 :     : ClosureExpr (std::move (closure_params), is_move,
    2648              :                    std::move (outer_attribs), locus),
    2649           42 :       closure_inner (std::move (closure_inner_expr))
    2650           42 :   {}
    2651              : 
    2652              :   // Copy constructor must be defined to allow copying via cloning of unique_ptr
    2653           42 :   ClosureExprInner (ClosureExprInner const &other) : ClosureExpr (other)
    2654              :   {
    2655              :     // guard to prevent null dereference (only required if error state)
    2656           42 :     if (other.closure_inner != nullptr)
    2657           42 :       closure_inner = other.closure_inner->clone_expr ();
    2658           42 :   }
    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          352 :   bool is_marked_for_strip () const override
    2686              :   {
    2687          352 :     return closure_inner == nullptr;
    2688              :   }
    2689              : 
    2690          732 :   Expr &get_definition_expr () override
    2691              :   {
    2692          732 :     rust_assert (closure_inner != nullptr);
    2693          732 :     return *closure_inner;
    2694              :   }
    2695              : 
    2696          185 :   std::unique_ptr<Expr> &get_definition_expr_ptr () override
    2697              :   {
    2698          185 :     rust_assert (closure_inner != nullptr);
    2699          185 :     return closure_inner;
    2700              :   }
    2701              : 
    2702              : protected:
    2703              :   /* Use covariance to implement clone function as returning this object rather
    2704              :    * than base */
    2705           42 :   ClosureExprInner *clone_expr_without_block_impl () const override
    2706              :   {
    2707           42 :     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            1 :   bool has_statements () const { return !statements.empty (); }
    2728              : 
    2729              :   // Returns whether the block contains a final expression.
    2730       751891 :   bool has_tail_expr () const { return expr != nullptr; }
    2731              : 
    2732        23502 :   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        47004 :     : outer_attrs (std::move (outer_attribs)),
    2739        23502 :       inner_attrs (std::move (inner_attribs)),
    2740        23502 :       statements (std::move (block_statements)), expr (std::move (block_expr)),
    2741        23502 :       label (std::move (label)), start_locus (start_locus),
    2742        23502 :       end_locus (end_locus)
    2743        23502 :   {}
    2744              : 
    2745              :   // Copy constructor with clone
    2746        53688 :   BlockExpr (BlockExpr const &other)
    2747        53688 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    2748        53688 :       inner_attrs (other.inner_attrs), label (other.label),
    2749        53688 :       start_locus (other.start_locus), end_locus (other.end_locus),
    2750       107376 :       marked_for_strip (other.marked_for_strip)
    2751              :   {
    2752              :     // guard to protect from null pointer dereference
    2753        53688 :     if (other.expr != nullptr)
    2754        44884 :       expr = other.expr->clone_expr ();
    2755              : 
    2756        53688 :     statements.reserve (other.statements.size ());
    2757        89490 :     for (const auto &e : other.statements)
    2758        35802 :       statements.push_back (e->clone_stmt ());
    2759        53688 :   }
    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        47109 :   std::unique_ptr<BlockExpr> clone_block_expr () const
    2790              :   {
    2791        47109 :     return std::unique_ptr<BlockExpr> (clone_block_expr_impl ());
    2792              :   }
    2793              : 
    2794         4747 :   location_t get_locus () const override final { return start_locus; }
    2795              : 
    2796        22922 :   location_t get_start_locus () const { return start_locus; }
    2797        22922 :   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           54 :   void mark_for_strip () override { marked_for_strip = true; }
    2803       100449 :   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       605790 :   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       638175 :   std::vector<std::unique_ptr<Stmt>> &get_statements () { return statements; }
    2816              : 
    2817              :   // TODO: is this better? Or is a "vis_block" better?
    2818       357955 :   Expr &get_tail_expr ()
    2819              :   {
    2820       357955 :     rust_assert (has_tail_expr ());
    2821       357955 :     return *expr;
    2822              :   }
    2823              : 
    2824        96711 :   std::unique_ptr<Expr> &get_tail_expr_ptr ()
    2825              :   {
    2826        96711 :     rust_assert (has_tail_expr ());
    2827        96711 :     return expr;
    2828              :   }
    2829              : 
    2830        40346 :   std::unique_ptr<Expr> take_tail_expr ()
    2831              :   {
    2832        40346 :     rust_assert (has_tail_expr ());
    2833        40346 :     return std::move (expr);
    2834              :   }
    2835              : 
    2836        40346 :   void set_tail_expr (std::unique_ptr<Expr> expr)
    2837              :   {
    2838        40346 :     this->expr = std::move (expr);
    2839              :   }
    2840              : 
    2841              :   // Removes the tail expression from the block.
    2842           57 :   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       666971 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    2850              : 
    2851         1292 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    2852              :   {
    2853         1292 :     outer_attrs = std::move (new_attrs);
    2854         1292 :   }
    2855              : 
    2856       474866 :   bool has_label () { return label.has_value (); }
    2857            0 :   LoopLabel &get_label () { return label.value (); }
    2858              : 
    2859         1543 :   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         6579 :   BlockExpr *clone_expr_with_block_impl () const final override
    2865              :   {
    2866         6579 :     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        53688 :   /*virtual*/ BlockExpr *clone_block_expr_impl () const
    2872              :   {
    2873        53688 :     return new BlockExpr (*this);
    2874              :   }
    2875              : };
    2876              : 
    2877         4041 : class AnonConst : public ExprWithBlock
    2878              : {
    2879              : public:
    2880              :   enum class Kind
    2881              :   {
    2882              :     Explicit,
    2883              :     DeferredInference,
    2884              :   };
    2885              : 
    2886          678 :   AnonConst (std::unique_ptr<Expr> &&expr, location_t locus = UNKNOWN_LOCATION)
    2887         1356 :     : ExprWithBlock (), locus (locus), kind (Kind::Explicit),
    2888          678 :       expr (std::move (expr))
    2889              :   {
    2890          678 :     rust_assert (this->expr.value ());
    2891          678 :   }
    2892              : 
    2893           11 :   AnonConst (location_t locus = UNKNOWN_LOCATION)
    2894           22 :     : ExprWithBlock (), locus (locus), kind (Kind::DeferredInference),
    2895           11 :       expr (tl::nullopt)
    2896              :   {}
    2897              : 
    2898         4511 :   AnonConst (const AnonConst &other)
    2899         4511 :   {
    2900         4511 :     node_id = other.node_id;
    2901         4511 :     locus = other.locus;
    2902         4511 :     kind = other.kind;
    2903              : 
    2904         4511 :     if (other.expr)
    2905         4432 :       expr = other.expr.value ()->clone_expr ();
    2906         4511 :   }
    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         1376 :   location_t get_locus () const override { return locus; }
    2925              : 
    2926        13273 :   Expr &get_inner_expr ()
    2927              :   {
    2928        13273 :     rust_assert (expr.has_value ());
    2929        13273 :     return *expr.value ();
    2930              :   }
    2931              : 
    2932         3873 :   std::unique_ptr<Expr> &get_inner_expr_ptr ()
    2933              :   {
    2934         3873 :     rust_assert (expr.has_value ());
    2935         3873 :     return expr.value ();
    2936              :   }
    2937              : 
    2938          685 :   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        15435 :     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         4906 :   bool is_marked_for_strip () const override { return false; }
    2955              : 
    2956              :   void accept_vis (ASTVisitor &vis) override;
    2957              : 
    2958        17376 :   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           30 :     : 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          257 :   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           30 :   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           31 :   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           31 :     : ClosureExpr (std::move (closure_params), is_move,
    3046              :                    std::move (outer_attribs), locus),
    3047           31 :       return_type (std::move (closure_return_type)),
    3048           31 :       expr (std::move (closure_expr))
    3049           31 :   {}
    3050              : 
    3051              :   // Copy constructor requires cloning
    3052           30 :   ClosureExprInnerTyped (ClosureExprInnerTyped const &other)
    3053           30 :     : ClosureExpr (other)
    3054              :   {
    3055              :     // guard to prevent null dereference (only required if error state)
    3056           30 :     if (other.expr != nullptr)
    3057           30 :       expr = other.expr->clone_expr ();
    3058           30 :     if (other.return_type != nullptr)
    3059           30 :       return_type = other.return_type->clone_type ();
    3060           30 :   }
    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          300 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    3093              : 
    3094              :   // TODO: is this better? Or is a "vis_block" better?
    3095          661 :   Expr &get_definition_expr () override
    3096              :   {
    3097          661 :     rust_assert (expr != nullptr);
    3098          661 :     return *expr;
    3099              :   }
    3100              : 
    3101           90 :   std::unique_ptr<Expr> &get_definition_expr_ptr () override
    3102              :   {
    3103           90 :     rust_assert (expr != nullptr);
    3104              : 
    3105           90 :     return expr;
    3106              :   }
    3107              : 
    3108              :   // TODO: is this better? Or is a "vis_block" better?
    3109          571 :   Type &get_return_type ()
    3110              :   {
    3111          571 :     rust_assert (return_type != nullptr);
    3112          571 :     return *return_type;
    3113              :   }
    3114              : 
    3115          150 :   std::unique_ptr<Type> &get_return_type_ptr ()
    3116              :   {
    3117          150 :     rust_assert (return_type != nullptr);
    3118          150 :     return return_type;
    3119              :   }
    3120              : 
    3121              : protected:
    3122              :   /* Use covariance to implement clone function as returning this object rather
    3123              :    * than base */
    3124           30 :   ClosureExprInnerTyped *clone_expr_without_block_impl () const override
    3125              :   {
    3126           30 :     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          307 :   bool has_label () const { return label.has_value (); }
    3145              : 
    3146              :   // Constructor for a ContinueExpr with a label.
    3147           17 :   ContinueExpr (tl::optional<Lifetime> label,
    3148              :                 std::vector<Attribute> outer_attribs, location_t locus)
    3149           51 :     : outer_attrs (std::move (outer_attribs)), label (std::move (label)),
    3150           17 :       locus (locus)
    3151           17 :   {}
    3152              : 
    3153           34 :   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          154 :   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          602 :   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           54 :   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           17 :   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           17 :   ContinueExpr *clone_expr_without_block_impl () const override
    3181              :   {
    3182           17 :     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         2258 :   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         2983 :   bool has_break_expr () const { return break_expr.has_value (); }
    3207              : 
    3208              :   // Constructor for a break expression
    3209           97 :   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          291 :     : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)),
    3213           97 :       break_expr (std::move (expr_in_break)), locus (locus)
    3214              :   {
    3215           97 :     if (this->has_break_expr ())
    3216           22 :       rust_assert (this->break_expr != nullptr);
    3217           97 :   }
    3218              : 
    3219              :   // Copy constructor defined to use clone for unique pointer
    3220          111 :   BreakExpr (BreakExpr const &other)
    3221          111 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3222          111 :       label (other.label), locus (other.locus),
    3223          222 :       marked_for_strip (other.marked_for_strip)
    3224              :   {
    3225          111 :     if (other.has_break_expr ())
    3226           22 :       break_expr = other.get_break_expr_unchecked ().clone_expr ();
    3227          111 :   }
    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          200 :   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          628 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3258              : 
    3259              :   // TODO: is this better? Or is a "vis_block" better?
    3260          791 :   Expr &get_break_expr_unchecked ()
    3261              :   {
    3262          791 :     rust_assert (has_break_expr ());
    3263          791 :     return *break_expr.value ();
    3264              :   }
    3265              : 
    3266           22 :   const Expr &get_break_expr_unchecked () const
    3267              :   {
    3268           22 :     rust_assert (has_break_expr ());
    3269           22 :     return *break_expr.value ();
    3270              :   }
    3271              : 
    3272          110 :   std::unique_ptr<Expr> &get_break_expr_ptr_unchecked ()
    3273              :   {
    3274          110 :     rust_assert (has_break_expr ());
    3275          110 :     return break_expr.value ();
    3276              :   }
    3277              : 
    3278              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3279         3401 :   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          510 :   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           95 :   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          111 :   BreakExpr *clone_expr_without_block_impl () const override
    3298              :   {
    3299          111 :     return new BreakExpr (*this);
    3300              :   }
    3301              : };
    3302              : 
    3303              : // Base range expression AST node object - abstract
    3304           88 : 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           93 :   RangeExpr (location_t locus) : locus (locus) {}
    3315              : 
    3316              : public:
    3317          198 :   location_t get_locus () const override final { return locus; }
    3318              : 
    3319            0 :   std::vector<Attribute> &get_outer_attrs () override final
    3320              :   {
    3321            0 :     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           87 :   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           69 :   RangeFromToExpr (std::unique_ptr<Expr> range_from,
    3341              :                    std::unique_ptr<Expr> range_to, location_t locus)
    3342          138 :     : RangeExpr (locus), from (std::move (range_from)),
    3343           69 :       to (std::move (range_to))
    3344              :   {}
    3345              : 
    3346              :   // Copy constructor with cloning
    3347           67 :   RangeFromToExpr (RangeFromToExpr const &other) : RangeExpr (other)
    3348              :   {
    3349              :     // guard to prevent null dereference (only required if error state)
    3350           67 :     if (other.from != nullptr)
    3351           67 :       from = other.from->clone_expr ();
    3352           67 :     if (other.to != nullptr)
    3353           67 :       to = other.to->clone_expr ();
    3354           67 :   }
    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          356 :   bool is_marked_for_strip () const override
    3387              :   {
    3388          356 :     return from == nullptr && to == nullptr;
    3389              :   }
    3390              : 
    3391              :   // TODO: is this better? Or is a "vis_block" better?
    3392         1589 :   Expr &get_from_expr ()
    3393              :   {
    3394         1589 :     rust_assert (from != nullptr);
    3395         1589 :     return *from;
    3396              :   }
    3397              : 
    3398              :   // TODO: is this better? Or is a "vis_block" better?
    3399         1589 :   Expr &get_to_expr ()
    3400              :   {
    3401         1589 :     rust_assert (to != nullptr);
    3402         1589 :     return *to;
    3403              :   }
    3404              : 
    3405          426 :   std::unique_ptr<Expr> &get_from_expr_ptr ()
    3406              :   {
    3407          426 :     rust_assert (from != nullptr);
    3408          426 :     return from;
    3409              :   }
    3410              : 
    3411          426 :   std::unique_ptr<Expr> &get_to_expr_ptr ()
    3412              :   {
    3413          426 :     rust_assert (to != nullptr);
    3414          426 :     return to;
    3415              :   }
    3416              : 
    3417              : protected:
    3418              :   /* Use covariance to implement clone function as returning this object rather
    3419              :    * than base */
    3420           67 :   RangeFromToExpr *clone_expr_without_block_impl () const override
    3421              :   {
    3422           67 :     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            9 :   RangeFromExpr (std::unique_ptr<Expr> range_from, location_t locus)
    3436            9 :     : RangeExpr (locus), from (std::move (range_from))
    3437              :   {}
    3438              : 
    3439              :   // Copy constructor with clone
    3440            7 :   RangeFromExpr (RangeFromExpr const &other) : RangeExpr (other)
    3441              :   {
    3442              :     // guard to prevent null dereference (only required if error state)
    3443            7 :     if (other.from != nullptr)
    3444            7 :       from = other.from->clone_expr ();
    3445            7 :   }
    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           28 :   bool is_marked_for_strip () const override { return from == nullptr; }
    3470              : 
    3471              :   // TODO: is this better? Or is a "vis_block" better?
    3472          140 :   Expr &get_from_expr ()
    3473              :   {
    3474          140 :     rust_assert (from != nullptr);
    3475          140 :     return *from;
    3476              :   }
    3477              : 
    3478           35 :   std::unique_ptr<Expr> &get_from_expr_ptr ()
    3479              :   {
    3480           35 :     rust_assert (from != nullptr);
    3481           35 :     return from;
    3482              :   }
    3483              : 
    3484              : protected:
    3485              :   /* Use covariance to implement clone function as returning this object rather
    3486              :    * than base */
    3487            7 :   RangeFromExpr *clone_expr_without_block_impl () const override
    3488              :   {
    3489            7 :     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            8 :   RangeToExpr (std::unique_ptr<Expr> range_to, location_t locus)
    3504            8 :     : RangeExpr (locus), to (std::move (range_to))
    3505              :   {}
    3506              : 
    3507              :   // Copy constructor with clone
    3508            7 :   RangeToExpr (RangeToExpr const &other) : RangeExpr (other)
    3509              :   {
    3510              :     // guard to prevent null dereference (only required if error state)
    3511            7 :     if (other.to != nullptr)
    3512            7 :       to = other.to->clone_expr ();
    3513            7 :   }
    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           28 :   bool is_marked_for_strip () const override { return to == nullptr; }
    3538              : 
    3539              :   // TODO: is this better? Or is a "vis_block" better?
    3540          140 :   Expr &get_to_expr ()
    3541              :   {
    3542          140 :     rust_assert (to != nullptr);
    3543          140 :     return *to;
    3544              :   }
    3545              : 
    3546           35 :   std::unique_ptr<Expr> &get_to_expr_ptr ()
    3547              :   {
    3548           35 :     rust_assert (to != nullptr);
    3549           35 :     return to;
    3550              :   }
    3551              : 
    3552              : protected:
    3553              :   /* Use covariance to implement clone function as returning this object rather
    3554              :    * than base */
    3555            7 :   RangeToExpr *clone_expr_without_block_impl () const override
    3556              :   {
    3557            7 :     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            1 :   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            0 :   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            0 :   RangeFullExpr *clone_expr_without_block_impl () const override
    3584              :   {
    3585            0 :     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            7 :   RangeFromToInclExpr (std::unique_ptr<Expr> range_from,
    3600              :                        std::unique_ptr<Expr> range_to, location_t locus)
    3601           14 :     : RangeExpr (locus), from (std::move (range_from)),
    3602            7 :       to (std::move (range_to))
    3603              :   {}
    3604              :   // outer attributes not allowed
    3605              : 
    3606              :   // Copy constructor with clone
    3607            7 :   RangeFromToInclExpr (RangeFromToInclExpr const &other) : RangeExpr (other)
    3608              :   {
    3609              :     // guard to prevent null dereference (only required if error state)
    3610            7 :     if (other.from != nullptr)
    3611            7 :       from = other.from->clone_expr ();
    3612            7 :     if (other.to != nullptr)
    3613            7 :       to = other.to->clone_expr ();
    3614            7 :   }
    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           28 :   bool is_marked_for_strip () const override
    3647              :   {
    3648           28 :     return from == nullptr && to == nullptr;
    3649              :   }
    3650              : 
    3651              :   // TODO: is this better? Or is a "vis_block" better?
    3652          140 :   Expr &get_from_expr ()
    3653              :   {
    3654          140 :     rust_assert (from != nullptr);
    3655          140 :     return *from;
    3656              :   }
    3657              : 
    3658              :   // TODO: is this better? Or is a "vis_block" better?
    3659          140 :   Expr &get_to_expr ()
    3660              :   {
    3661          140 :     rust_assert (to != nullptr);
    3662          140 :     return *to;
    3663              :   }
    3664              : 
    3665           35 :   std::unique_ptr<Expr> &get_from_expr_ptr ()
    3666              :   {
    3667           35 :     rust_assert (from != nullptr);
    3668           35 :     return from;
    3669              :   }
    3670              : 
    3671           35 :   std::unique_ptr<Expr> &get_to_expr_ptr ()
    3672              :   {
    3673           35 :     rust_assert (to != nullptr);
    3674           35 :     return to;
    3675              :   }
    3676              : 
    3677              : protected:
    3678              :   /* Use covariance to implement clone function as returning this object rather
    3679              :    * than base */
    3680            7 :   RangeFromToInclExpr *clone_expr_without_block_impl () const override
    3681              :   {
    3682            7 :     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              : class BoxExpr : public ExprWithoutBlock
    3755              : {
    3756              :   std::unique_ptr<Expr> expr;
    3757              :   std::vector<Attribute> outer_attrs;
    3758              :   location_t locus;
    3759              : 
    3760              : public:
    3761            2 :   BoxExpr (std::unique_ptr<Expr> expr, std::vector<Attribute> outer_attrs,
    3762              :            location_t locus)
    3763            2 :     : expr (std::move (expr)), outer_attrs (outer_attrs), locus (locus)
    3764            2 :   {}
    3765              : 
    3766              :   // Copy constructor with clone
    3767            1 :   BoxExpr (BoxExpr const &other)
    3768            1 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3769            1 :       locus (other.locus)
    3770              :   {
    3771              :     // guard to protect from null pointer dereference
    3772            1 :     if (other.expr != nullptr)
    3773            1 :       expr = other.expr->clone_expr ();
    3774            1 :   }
    3775              : 
    3776              :   BoxExpr &operator= (BoxExpr const &other)
    3777              :   {
    3778              :     ExprWithoutBlock::operator= (other);
    3779              :     locus = other.locus;
    3780              :     outer_attrs = other.outer_attrs;
    3781              : 
    3782              :     // guard to protect from null pointer dereference
    3783              :     if (other.expr != nullptr)
    3784              :       expr = other.expr->clone_expr ();
    3785              :     else
    3786              :       expr = nullptr;
    3787              : 
    3788              :     return *this;
    3789              :   }
    3790              : 
    3791              :   // move constructors
    3792              :   BoxExpr (BoxExpr &&other) = default;
    3793              :   BoxExpr &operator= (BoxExpr &&other) = default;
    3794              : 
    3795            1 :   location_t get_locus () const override final { return locus; }
    3796              : 
    3797              :   void accept_vis (ASTVisitor &vis) override;
    3798              : 
    3799            0 :   void mark_for_strip () override { expr = nullptr; }
    3800            4 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    3801              : 
    3802              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3803           20 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3804              : 
    3805            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3806              :   {
    3807            0 :     outer_attrs = std::move (new_attrs);
    3808            0 :   }
    3809              : 
    3810              :   std::string as_string () const override;
    3811              : 
    3812           15 :   Expr &get_boxed_expr ()
    3813              :   {
    3814           15 :     rust_assert (expr != nullptr);
    3815           15 :     return *expr;
    3816              :   }
    3817              : 
    3818            5 :   std::unique_ptr<Expr> &get_boxed_expr_ptr ()
    3819              :   {
    3820            5 :     rust_assert (expr != nullptr);
    3821            5 :     return expr;
    3822              :   }
    3823              : 
    3824            1 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Box; }
    3825              : 
    3826              : protected:
    3827              :   /* Use covariance to implement clone function as returning this object rather
    3828              :    * than base */
    3829            1 :   BoxExpr *clone_expr_without_block_impl () const override
    3830              :   {
    3831            1 :     return new BoxExpr (*this);
    3832              :   }
    3833              : };
    3834              : 
    3835              : // Return expression AST node representation
    3836              : class ReturnExpr : public ExprWithoutBlock
    3837              : {
    3838              :   std::vector<Attribute> outer_attrs;
    3839              :   tl::optional<std::unique_ptr<Expr>> return_expr;
    3840              :   location_t locus;
    3841              : 
    3842              :   // TODO: find another way to store this to save memory?
    3843              :   bool marked_for_strip = false;
    3844              : 
    3845              : public:
    3846              :   std::string as_string () const override;
    3847              : 
    3848              :   /* Returns whether the object has an expression returned (i.e. not void return
    3849              :    * type). */
    3850        15517 :   bool has_returned_expr () const { return return_expr.has_value (); }
    3851              : 
    3852              :   // Constructor for ReturnExpr.
    3853          550 :   ReturnExpr (tl::optional<std::unique_ptr<Expr>> returned_expr,
    3854              :               std::vector<Attribute> outer_attribs, location_t locus)
    3855         1100 :     : outer_attrs (std::move (outer_attribs)),
    3856          550 :       return_expr (std::move (returned_expr)), locus (locus)
    3857          550 :   {}
    3858              : 
    3859              :   // Copy constructor with clone
    3860          667 :   ReturnExpr (ReturnExpr const &other)
    3861          667 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3862          667 :       locus (other.locus), marked_for_strip (other.marked_for_strip)
    3863              :   {
    3864              :     // guard to protect from null pointer dereference
    3865          667 :     if (other.return_expr)
    3866          606 :       return_expr = other.return_expr.value ()->clone_expr ();
    3867          667 :   }
    3868              : 
    3869              :   // Overloaded assignment operator to clone return_expr pointer
    3870              :   ReturnExpr &operator= (ReturnExpr const &other)
    3871              :   {
    3872              :     ExprWithoutBlock::operator= (other);
    3873              :     locus = other.locus;
    3874              :     marked_for_strip = other.marked_for_strip;
    3875              :     outer_attrs = other.outer_attrs;
    3876              : 
    3877              :     // guard to protect from null pointer dereference
    3878              :     if (other.return_expr)
    3879              :       return_expr = other.return_expr.value ()->clone_expr ();
    3880              :     else
    3881              :       return_expr = tl::nullopt;
    3882              : 
    3883              :     return *this;
    3884              :   }
    3885              : 
    3886              :   // move constructors
    3887              :   ReturnExpr (ReturnExpr &&other) = default;
    3888              :   ReturnExpr &operator= (ReturnExpr &&other) = default;
    3889              : 
    3890         1078 :   location_t get_locus () const override final { return locus; }
    3891              : 
    3892              :   void accept_vis (ASTVisitor &vis) override;
    3893              : 
    3894              :   // Can't think of any invalid invariants, so store boolean.
    3895            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3896         3132 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3897              : 
    3898              :   // TODO: is this better? Or is a "vis_block" better?
    3899        11676 :   Expr &get_returned_expr ()
    3900              :   {
    3901        11676 :     rust_assert (return_expr);
    3902        11676 :     return *return_expr.value ();
    3903              :   }
    3904              : 
    3905            0 :   const Expr &get_returned_expr () const
    3906              :   {
    3907            0 :     rust_assert (return_expr);
    3908            0 :     return *return_expr.value ();
    3909              :   }
    3910              : 
    3911         2551 :   std::unique_ptr<Expr> &get_returned_expr_ptr ()
    3912              :   {
    3913         2551 :     rust_assert (return_expr);
    3914         2551 :     return return_expr.value ();
    3915              :   }
    3916              : 
    3917              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3918        18146 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3919              : 
    3920            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3921              :   {
    3922            0 :     outer_attrs = std::move (new_attrs);
    3923            0 :   }
    3924              : 
    3925          545 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Return; }
    3926              : 
    3927              : protected:
    3928              :   /* Use covariance to implement clone function as returning this object rather
    3929              :    * than base */
    3930          667 :   ReturnExpr *clone_expr_without_block_impl () const override
    3931              :   {
    3932          667 :     return new ReturnExpr (*this);
    3933              :   }
    3934              : };
    3935              : 
    3936              : // Try expression AST node representation
    3937              : class TryExpr : public ExprWithBlock
    3938              : {
    3939              :   std::vector<Attribute> outer_attrs;
    3940              :   std::unique_ptr<BlockExpr> block_expr;
    3941              :   location_t locus;
    3942              : 
    3943              :   // TODO: find another way to store this to save memory?
    3944              :   bool marked_for_strip = false;
    3945              : 
    3946              : public:
    3947              :   std::string as_string () const override;
    3948              : 
    3949              :   // Constructor for ReturnExpr.
    3950            1 :   TryExpr (std::unique_ptr<BlockExpr> block_expr,
    3951              :            std::vector<Attribute> outer_attribs, location_t locus)
    3952            2 :     : outer_attrs (std::move (outer_attribs)),
    3953            1 :       block_expr (std::move (block_expr)), locus (locus)
    3954              :   {
    3955            1 :     rust_assert (this->block_expr);
    3956            1 :   }
    3957              : 
    3958              :   // Copy constructor with clone
    3959            2 :   TryExpr (TryExpr const &other)
    3960            2 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    3961            2 :       block_expr (other.block_expr->clone_block_expr ()), locus (other.locus),
    3962            2 :       marked_for_strip (other.marked_for_strip)
    3963            2 :   {}
    3964              : 
    3965              :   // Overloaded assignment operator to clone return_expr pointer
    3966              :   TryExpr &operator= (TryExpr const &other)
    3967              :   {
    3968              :     ExprWithBlock::operator= (other);
    3969              :     locus = other.locus;
    3970              :     marked_for_strip = other.marked_for_strip;
    3971              :     outer_attrs = other.outer_attrs;
    3972              : 
    3973              :     block_expr = other.block_expr->clone_block_expr ();
    3974              : 
    3975              :     return *this;
    3976              :   }
    3977              : 
    3978              :   // move constructors
    3979              :   TryExpr (TryExpr &&other) = default;
    3980              :   TryExpr &operator= (TryExpr &&other) = default;
    3981              : 
    3982            1 :   location_t get_locus () const override final { return locus; }
    3983              : 
    3984              :   void accept_vis (ASTVisitor &vis) override;
    3985              : 
    3986              :   // Can't think of any invalid invariants, so store boolean.
    3987            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3988            4 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3989              : 
    3990              :   // TODO: is this better? Or is a "vis_block" better?
    3991           10 :   BlockExpr &get_block_expr () { return *block_expr; }
    3992            4 :   std::unique_ptr<BlockExpr> &get_block_expr_ptr () { return block_expr; }
    3993              : 
    3994              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3995           13 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3996              : 
    3997            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3998              :   {
    3999            0 :     outer_attrs = std::move (new_attrs);
    4000            0 :   }
    4001              : 
    4002            2 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Try; }
    4003              : 
    4004              : protected:
    4005              :   /* Use covariance to implement clone function as returning this object rather
    4006              :    * than base */
    4007            1 :   TryExpr *clone_expr_with_block_impl () const override
    4008              :   {
    4009            1 :     return new TryExpr (*this);
    4010              :   }
    4011              : };
    4012              : 
    4013              : // Forward decl - defined in rust-macro.h
    4014              : class MacroInvocation;
    4015              : 
    4016              : // An unsafe block AST node
    4017              : class UnsafeBlockExpr : public ExprWithBlock
    4018              : {
    4019              :   std::vector<Attribute> outer_attrs;
    4020              :   // Or just have it extend BlockExpr
    4021              :   std::unique_ptr<BlockExpr> expr;
    4022              :   location_t locus;
    4023              : 
    4024              : public:
    4025              :   std::string as_string () const override;
    4026              : 
    4027         3620 :   UnsafeBlockExpr (std::unique_ptr<BlockExpr> block_expr,
    4028              :                    std::vector<Attribute> outer_attribs, location_t locus)
    4029         7240 :     : outer_attrs (std::move (outer_attribs)), expr (std::move (block_expr)),
    4030         3620 :       locus (locus)
    4031         3620 :   {}
    4032              : 
    4033              :   // Copy constructor with clone
    4034         8124 :   UnsafeBlockExpr (UnsafeBlockExpr const &other)
    4035         8124 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4036         8124 :       locus (other.locus)
    4037              :   {
    4038              :     // guard to prevent null dereference (only required if error state)
    4039         8124 :     if (other.expr != nullptr)
    4040         8124 :       expr = other.expr->clone_block_expr ();
    4041         8124 :   }
    4042              : 
    4043              :   // Overloaded assignment operator to clone
    4044              :   UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other)
    4045              :   {
    4046              :     ExprWithBlock::operator= (other);
    4047              :     locus = other.locus;
    4048              :     outer_attrs = other.outer_attrs;
    4049              : 
    4050              :     // guard to prevent null dereference (only required if error state)
    4051              :     if (other.expr != nullptr)
    4052              :       expr = other.expr->clone_block_expr ();
    4053              :     else
    4054              :       expr = nullptr;
    4055              : 
    4056              :     return *this;
    4057              :   }
    4058              : 
    4059              :   // move constructors
    4060              :   UnsafeBlockExpr (UnsafeBlockExpr &&other) = default;
    4061              :   UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default;
    4062              : 
    4063         7590 :   location_t get_locus () const override final { return locus; }
    4064              : 
    4065              :   void accept_vis (ASTVisitor &vis) override;
    4066              : 
    4067              :   // Invalid if block is null, so base stripping on that.
    4068            7 :   void mark_for_strip () override { expr = nullptr; }
    4069        16772 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    4070              : 
    4071              :   // TODO: is this better? Or is a "vis_block" better?
    4072        79349 :   BlockExpr &get_block_expr ()
    4073              :   {
    4074        79349 :     rust_assert (expr != nullptr);
    4075        79349 :     return *expr;
    4076              :   }
    4077              : 
    4078        19969 :   std::unique_ptr<BlockExpr> &get_block_expr_ptr ()
    4079              :   {
    4080        19969 :     rust_assert (expr != nullptr);
    4081        19969 :     return expr;
    4082              :   }
    4083              : 
    4084              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4085       121416 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4086              : 
    4087         3227 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4088              :   {
    4089         3227 :     outer_attrs = std::move (new_attrs);
    4090         3227 :   }
    4091              : 
    4092         3593 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::UnsafeBlock; }
    4093              : 
    4094              : protected:
    4095              :   /* Use covariance to implement clone function as returning this object rather
    4096              :    * than base */
    4097         8124 :   UnsafeBlockExpr *clone_expr_with_block_impl () const override
    4098              :   {
    4099         8124 :     return new UnsafeBlockExpr (*this);
    4100              :   }
    4101              : };
    4102              : 
    4103              : // Base loop expression AST node - aka LoopExpr
    4104              : class BaseLoopExpr : public ExprWithBlock
    4105              : {
    4106              : protected:
    4107              :   // protected to allow subclasses better use of them
    4108              :   std::vector<Attribute> outer_attrs;
    4109              :   tl::optional<LoopLabel> loop_label;
    4110              :   std::unique_ptr<BlockExpr> loop_block;
    4111              : 
    4112              : private:
    4113              :   location_t locus;
    4114              : 
    4115              : protected:
    4116              :   // Constructor for BaseLoopExpr
    4117          232 :   BaseLoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4118              :                 tl::optional<LoopLabel> loop_label = tl::nullopt,
    4119              :                 std::vector<Attribute> outer_attribs
    4120              :                 = std::vector<Attribute> ())
    4121          464 :     : outer_attrs (std::move (outer_attribs)),
    4122          232 :       loop_label (std::move (loop_label)), loop_block (std::move (loop_block)),
    4123          232 :       locus (locus)
    4124          232 :   {}
    4125              : 
    4126              :   // Copy constructor for BaseLoopExpr with clone
    4127          267 :   BaseLoopExpr (BaseLoopExpr const &other)
    4128          267 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4129          267 :       loop_label (other.loop_label), locus (other.locus)
    4130              :   {
    4131              :     // guard to prevent null dereference (only required if error state)
    4132          267 :     if (other.loop_block != nullptr)
    4133          267 :       loop_block = other.loop_block->clone_block_expr ();
    4134          267 :   }
    4135              : 
    4136              :   // Overloaded assignment operator to clone
    4137              :   BaseLoopExpr &operator= (BaseLoopExpr const &other)
    4138              :   {
    4139              :     ExprWithBlock::operator= (other);
    4140              :     loop_label = other.loop_label;
    4141              :     locus = other.locus;
    4142              :     outer_attrs = other.outer_attrs;
    4143              : 
    4144              :     // guard to prevent null dereference (only required if error state)
    4145              :     if (other.loop_block != nullptr)
    4146              :       loop_block = other.loop_block->clone_block_expr ();
    4147              :     else
    4148              :       loop_block = nullptr;
    4149              : 
    4150              :     return *this;
    4151              :   }
    4152              : 
    4153              :   // move constructors
    4154              :   BaseLoopExpr (BaseLoopExpr &&other) = default;
    4155              :   BaseLoopExpr &operator= (BaseLoopExpr &&other) = default;
    4156              : 
    4157              : public:
    4158         4955 :   bool has_loop_label () const { return loop_label.has_value (); }
    4159              : 
    4160          768 :   LoopLabel &get_loop_label () { return loop_label.value (); }
    4161            0 :   const LoopLabel &get_loop_label () const { return loop_label.value (); }
    4162              : 
    4163          455 :   location_t get_locus () const override final { return locus; }
    4164              : 
    4165              :   // Invalid if loop block is null, so base stripping on that.
    4166            0 :   void mark_for_strip () override { loop_block = nullptr; }
    4167          924 :   bool is_marked_for_strip () const override { return loop_block == nullptr; }
    4168              : 
    4169              :   // TODO: is this better? Or is a "vis_block" better?
    4170         4628 :   BlockExpr &get_loop_block ()
    4171              :   {
    4172         4628 :     rust_assert (loop_block != nullptr);
    4173         4628 :     return *loop_block;
    4174              :   }
    4175              : 
    4176         1269 :   std::unique_ptr<BlockExpr> &get_loop_block_ptr ()
    4177              :   {
    4178         1269 :     rust_assert (loop_block != nullptr);
    4179         1269 :     return loop_block;
    4180              :   }
    4181              : 
    4182              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4183         6964 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4184              : 
    4185          178 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4186              :   {
    4187          178 :     outer_attrs = std::move (new_attrs);
    4188          178 :   }
    4189              : 
    4190          243 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Loop; }
    4191              : 
    4192              :   enum class Kind
    4193              :   {
    4194              :     Loop,
    4195              :     While,
    4196              :     WhileLet,
    4197              :     For
    4198              :   };
    4199              : 
    4200              :   virtual Kind get_loop_kind () const = 0;
    4201              : };
    4202              : 
    4203              : // 'Loop' expression (i.e. the infinite loop) AST node
    4204          262 : class LoopExpr : public BaseLoopExpr
    4205              : {
    4206              : public:
    4207              :   std::string as_string () const override;
    4208              : 
    4209              :   // Constructor for LoopExpr
    4210          132 :   LoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4211           36 :             tl::optional<LoopLabel> loop_label = tl::nullopt,
    4212              :             std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
    4213          132 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4214          170 :                     std::move (outer_attribs))
    4215          132 :   {}
    4216              : 
    4217              :   void accept_vis (ASTVisitor &vis) override;
    4218              : 
    4219          129 :   BaseLoopExpr::Kind get_loop_kind () const override
    4220              :   {
    4221          129 :     return BaseLoopExpr::Kind::Loop;
    4222              :   }
    4223              : 
    4224              : protected:
    4225              :   /* Use covariance to implement clone function as returning this object rather
    4226              :    * than base */
    4227          131 :   LoopExpr *clone_expr_with_block_impl () const override
    4228              :   {
    4229          131 :     return new LoopExpr (*this);
    4230              :   }
    4231              : };
    4232              : 
    4233              : // While loop expression AST node (predicate loop)
    4234              : class WhileLoopExpr : public BaseLoopExpr
    4235              : {
    4236              :   std::unique_ptr<Expr> condition;
    4237              : 
    4238              : public:
    4239              :   std::string as_string () const override;
    4240              : 
    4241              :   // Constructor for while loop with loop label
    4242           78 :   WhileLoopExpr (std::unique_ptr<Expr> loop_condition,
    4243              :                  std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4244              :                  tl::optional<LoopLabel> loop_label = tl::nullopt,
    4245              :                  std::vector<Attribute> outer_attribs
    4246              :                  = std::vector<Attribute> ())
    4247           78 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4248              :                     std::move (outer_attribs)),
    4249           80 :       condition (std::move (loop_condition))
    4250           78 :   {}
    4251              : 
    4252              :   // Copy constructor with clone
    4253          118 :   WhileLoopExpr (WhileLoopExpr const &other)
    4254          118 :     : BaseLoopExpr (other), condition (other.condition->clone_expr ())
    4255          118 :   {}
    4256              : 
    4257              :   // Overloaded assignment operator to clone
    4258              :   WhileLoopExpr &operator= (WhileLoopExpr const &other)
    4259              :   {
    4260              :     BaseLoopExpr::operator= (other);
    4261              :     condition = other.condition->clone_expr ();
    4262              :     // loop_block = other.loop_block->clone_block_expr();
    4263              :     // loop_label = other.loop_label;
    4264              :     // outer_attrs = other.outer_attrs;
    4265              : 
    4266              :     return *this;
    4267              :   }
    4268              : 
    4269              :   // move constructors
    4270              :   WhileLoopExpr (WhileLoopExpr &&other) = default;
    4271              :   WhileLoopExpr &operator= (WhileLoopExpr &&other) = default;
    4272              : 
    4273              :   void accept_vis (ASTVisitor &vis) override;
    4274              : 
    4275              :   // TODO: is this better? Or is a "vis_block" better?
    4276         2010 :   Expr &get_predicate_expr ()
    4277              :   {
    4278         2010 :     rust_assert (condition != nullptr);
    4279         2010 :     return *condition;
    4280              :   }
    4281              : 
    4282          518 :   std::unique_ptr<Expr> &get_predicate_expr_ptr ()
    4283              :   {
    4284          518 :     rust_assert (condition != nullptr);
    4285          518 :     return condition;
    4286              :   }
    4287              : 
    4288           78 :   BaseLoopExpr::Kind get_loop_kind () const override
    4289              :   {
    4290           78 :     return BaseLoopExpr::Kind::While;
    4291              :   }
    4292              : 
    4293              : protected:
    4294              :   /* Use covariance to implement clone function as returning this object rather
    4295              :    * than base */
    4296          118 :   WhileLoopExpr *clone_expr_with_block_impl () const override
    4297              :   {
    4298          118 :     return new WhileLoopExpr (*this);
    4299              :   }
    4300              : };
    4301              : 
    4302              : // While let loop expression AST node (predicate pattern loop)
    4303              : class WhileLetLoopExpr : public BaseLoopExpr
    4304              : {
    4305              :   // MatchArmPatterns patterns;
    4306              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4307              :   std::unique_ptr<Expr> scrutinee;
    4308              : 
    4309              : public:
    4310              :   std::string as_string () const override;
    4311              : 
    4312              :   // Constructor with a loop label
    4313            3 :   WhileLetLoopExpr (std::unique_ptr<Pattern> match_arm_pattern,
    4314              :                     std::unique_ptr<Expr> scrutinee,
    4315              :                     std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4316              :                     tl::optional<LoopLabel> loop_label = tl::nullopt,
    4317              :                     std::vector<Attribute> outer_attribs
    4318              :                     = std::vector<Attribute> ())
    4319            3 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4320              :                     std::move (outer_attribs)),
    4321            3 :       match_arm_pattern (std::move (match_arm_pattern)),
    4322            3 :       scrutinee (std::move (scrutinee))
    4323            3 :   {}
    4324              : 
    4325              :   // Copy constructor with clone
    4326            2 :   WhileLetLoopExpr (WhileLetLoopExpr const &other)
    4327            2 :     : BaseLoopExpr (other),
    4328            2 :       /*match_arm_patterns(other.match_arm_patterns),*/ scrutinee (
    4329            2 :         other.scrutinee->clone_expr ())
    4330              :   {
    4331            2 :     match_arm_pattern = other.get_pattern ()->clone_pattern ();
    4332            2 :   }
    4333              : 
    4334              :   // Overloaded assignment operator to clone pointers
    4335              :   WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other)
    4336              :   {
    4337              :     BaseLoopExpr::operator= (other);
    4338              :     scrutinee = other.scrutinee->clone_expr ();
    4339              :     match_arm_pattern = other.get_pattern ()->clone_pattern ();
    4340              :     return *this;
    4341              :   }
    4342              : 
    4343              :   // move constructors
    4344              :   WhileLetLoopExpr (WhileLetLoopExpr &&other) = default;
    4345              :   WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default;
    4346              : 
    4347              :   void accept_vis (ASTVisitor &vis) override;
    4348              : 
    4349              :   // TODO: is this better? Or is a "vis_block" better?
    4350           20 :   Expr &get_scrutinee_expr ()
    4351              :   {
    4352           20 :     rust_assert (scrutinee != nullptr);
    4353           20 :     return *scrutinee;
    4354              :   }
    4355              : 
    4356            8 :   std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
    4357              :   {
    4358            8 :     rust_assert (scrutinee != nullptr);
    4359            8 :     return scrutinee;
    4360              :   }
    4361              : 
    4362              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4363            2 :   const std::unique_ptr<Pattern> &get_pattern () const
    4364              :   {
    4365            2 :     return match_arm_pattern;
    4366              :   }
    4367           28 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4368              : 
    4369            4 :   BaseLoopExpr::Kind get_loop_kind () const override
    4370              :   {
    4371            4 :     return BaseLoopExpr::Kind::WhileLet;
    4372              :   }
    4373              : 
    4374              : protected:
    4375              :   /* Use covariance to implement clone function as returning this object rather
    4376              :    * than base */
    4377            2 :   WhileLetLoopExpr *clone_expr_with_block_impl () const override
    4378              :   {
    4379            2 :     return new WhileLetLoopExpr (*this);
    4380              :   }
    4381              : };
    4382              : 
    4383              : // For loop expression AST node (iterator loop)
    4384              : class ForLoopExpr : public BaseLoopExpr
    4385              : {
    4386              :   std::unique_ptr<Pattern> pattern;
    4387              :   std::unique_ptr<Expr> iterator_expr;
    4388              : 
    4389              : public:
    4390              :   std::string as_string () const override;
    4391              : 
    4392              :   // Constructor with loop label
    4393           19 :   ForLoopExpr (std::unique_ptr<Pattern> loop_pattern,
    4394              :                std::unique_ptr<Expr> iterator_expr,
    4395              :                std::unique_ptr<BlockExpr> loop_body, location_t locus,
    4396              :                tl::optional<LoopLabel> loop_label = tl::nullopt,
    4397              :                std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
    4398           19 :     : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label),
    4399              :                     std::move (outer_attribs)),
    4400           19 :       pattern (std::move (loop_pattern)),
    4401           19 :       iterator_expr (std::move (iterator_expr))
    4402           19 :   {}
    4403              : 
    4404              :   // Copy constructor with clone
    4405           16 :   ForLoopExpr (ForLoopExpr const &other)
    4406           48 :     : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()),
    4407           16 :       iterator_expr (other.iterator_expr->clone_expr ())
    4408           16 :   {}
    4409              : 
    4410              :   // Overloaded assignment operator to clone
    4411              :   ForLoopExpr &operator= (ForLoopExpr const &other)
    4412              :   {
    4413              :     BaseLoopExpr::operator= (other);
    4414              :     pattern = other.pattern->clone_pattern ();
    4415              :     iterator_expr = other.iterator_expr->clone_expr ();
    4416              :     /*loop_block = other.loop_block->clone_block_expr();
    4417              :     loop_label = other.loop_label;
    4418              :     outer_attrs = other.outer_attrs;*/
    4419              : 
    4420              :     return *this;
    4421              :   }
    4422              : 
    4423              :   // move constructors
    4424              :   ForLoopExpr (ForLoopExpr &&other) = default;
    4425              :   ForLoopExpr &operator= (ForLoopExpr &&other) = default;
    4426              : 
    4427              :   void accept_vis (ASTVisitor &vis) override;
    4428              : 
    4429              :   // TODO: is this better? Or is a "vis_block" better?
    4430          400 :   Expr &get_iterator_expr ()
    4431              :   {
    4432          400 :     rust_assert (iterator_expr != nullptr);
    4433          400 :     return *iterator_expr;
    4434              :   }
    4435              : 
    4436          128 :   std::unique_ptr<Expr> &get_iterator_expr_ptr ()
    4437              :   {
    4438          128 :     rust_assert (iterator_expr != nullptr);
    4439          128 :     return iterator_expr;
    4440              :   }
    4441              : 
    4442              :   // TODO: is this better? Or is a "vis_block" better?
    4443          400 :   Pattern &get_pattern ()
    4444              :   {
    4445          400 :     rust_assert (pattern != nullptr);
    4446          400 :     return *pattern;
    4447              :   }
    4448              : 
    4449          128 :   std::unique_ptr<Pattern> &get_pattern_ptr ()
    4450              :   {
    4451          128 :     rust_assert (pattern != nullptr);
    4452          128 :     return pattern;
    4453              :   }
    4454              : 
    4455           32 :   BaseLoopExpr::Kind get_loop_kind () const override
    4456              :   {
    4457           32 :     return BaseLoopExpr::Kind::For;
    4458              :   }
    4459              : 
    4460              : protected:
    4461              :   /* Use covariance to implement clone function as returning this object rather
    4462              :    * than base */
    4463           16 :   ForLoopExpr *clone_expr_with_block_impl () const override
    4464              :   {
    4465           16 :     return new ForLoopExpr (*this);
    4466              :   }
    4467              : };
    4468              : 
    4469              : // forward decl for IfExpr
    4470              : class IfLetExpr;
    4471              : 
    4472              : // Base if expression with no "else" or "if let" AST node
    4473              : class IfExpr : public ExprWithBlock
    4474              : {
    4475              :   std::vector<Attribute> outer_attrs;
    4476              :   std::unique_ptr<Expr> condition;
    4477              :   std::unique_ptr<BlockExpr> if_block;
    4478              :   location_t locus;
    4479              : 
    4480              : public:
    4481              :   std::string as_string () const override;
    4482              : 
    4483         2450 :   IfExpr (std::unique_ptr<Expr> condition, std::unique_ptr<BlockExpr> if_block,
    4484              :           std::vector<Attribute> outer_attrs, location_t locus)
    4485         4900 :     : outer_attrs (std::move (outer_attrs)), condition (std::move (condition)),
    4486         2450 :       if_block (std::move (if_block)), locus (locus)
    4487         2450 :   {}
    4488              :   // outer attributes are never allowed on IfExprs
    4489              : 
    4490              :   // Copy constructor with clone
    4491         4063 :   IfExpr (IfExpr const &other)
    4492         4063 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4493         4063 :       locus (other.locus)
    4494              :   {
    4495              :     // guard to prevent null dereference (only required if error state)
    4496         4063 :     if (other.condition != nullptr)
    4497         4063 :       condition = other.condition->clone_expr ();
    4498         4063 :     if (other.if_block != nullptr)
    4499         4063 :       if_block = other.if_block->clone_block_expr ();
    4500         4063 :   }
    4501              : 
    4502              :   // Overloaded assignment operator to clone expressions
    4503              :   IfExpr &operator= (IfExpr const &other)
    4504              :   {
    4505              :     ExprWithBlock::operator= (other);
    4506              :     outer_attrs = other.outer_attrs;
    4507              :     locus = other.locus;
    4508              : 
    4509              :     // guard to prevent null dereference (only required if error state)
    4510              :     if (other.condition != nullptr)
    4511              :       condition = other.condition->clone_expr ();
    4512              :     else
    4513              :       condition = nullptr;
    4514              :     if (other.if_block != nullptr)
    4515              :       if_block = other.if_block->clone_block_expr ();
    4516              :     else
    4517              :       if_block = nullptr;
    4518              : 
    4519              :     return *this;
    4520              :   }
    4521              : 
    4522              :   // move constructors
    4523              :   IfExpr (IfExpr &&other) = default;
    4524              :   IfExpr &operator= (IfExpr &&other) = default;
    4525              : 
    4526              :   // Unique pointer custom clone function
    4527              :   std::unique_ptr<IfExpr> clone_if_expr () const
    4528              :   {
    4529              :     return std::unique_ptr<IfExpr> (clone_if_expr_impl ());
    4530              :   }
    4531              : 
    4532              :   /* Note that multiple "else if"s are handled via nested ASTs rather than a
    4533              :    * vector of else ifs - i.e. not like a switch statement. TODO - is this a
    4534              :    * better approach? or does it not parse correctly and have downsides? */
    4535              : 
    4536         4782 :   location_t get_locus () const override final { return locus; }
    4537              : 
    4538              :   void accept_vis (ASTVisitor &vis) override;
    4539              : 
    4540              :   void vis_if_condition (ASTVisitor &vis) { condition->accept_vis (vis); }
    4541              :   void vis_if_block (ASTVisitor &vis) { if_block->accept_vis (vis); }
    4542              : 
    4543              :   // TODO: is this better? Or is a "vis_block" better?
    4544        60934 :   Expr &get_condition_expr ()
    4545              :   {
    4546        60934 :     rust_assert (condition != nullptr);
    4547        60934 :     return *condition;
    4548              :   }
    4549              : 
    4550        13910 :   std::unique_ptr<Expr> &get_condition_expr_ptr ()
    4551              :   {
    4552        13910 :     rust_assert (condition != nullptr);
    4553        13910 :     return condition;
    4554              :   }
    4555              : 
    4556              :   // TODO: is this better? Or is a "vis_block" better?
    4557        74844 :   BlockExpr &get_if_block ()
    4558              :   {
    4559        74844 :     rust_assert (if_block != nullptr);
    4560        74844 :     return *if_block;
    4561              :   }
    4562              : 
    4563              :   // Invalid if if block or condition is null, so base stripping on that.
    4564            0 :   void mark_for_strip () override
    4565              :   {
    4566            0 :     if_block = nullptr;
    4567            0 :     condition = nullptr;
    4568            0 :   }
    4569        11957 :   bool is_marked_for_strip () const override
    4570              :   {
    4571        11957 :     return if_block == nullptr && condition == nullptr;
    4572              :   }
    4573              : 
    4574         2090 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4575              :   {
    4576         2090 :     outer_attrs = std::move (new_attrs);
    4577         2090 :   }
    4578              : 
    4579              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4580              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4581        86593 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4582              : 
    4583         2121 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::If; }
    4584              : 
    4585              : protected:
    4586              :   // Base clone function but still concrete as concrete base class
    4587         1325 :   virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); }
    4588              : 
    4589              :   /* Use covariance to implement clone function as returning this object rather
    4590              :    * than base */
    4591         4063 :   IfExpr *clone_expr_with_block_impl () const final override
    4592              :   {
    4593         4063 :     return clone_if_expr_impl ();
    4594              :   }
    4595              : };
    4596              : 
    4597              : // If expression with an ending "else" expression AST node (trailing)
    4598              : class IfExprConseqElse : public IfExpr
    4599              : {
    4600              :   std::unique_ptr<ExprWithBlock> else_block;
    4601              : 
    4602              : public:
    4603              :   std::string as_string () const override;
    4604              : 
    4605         1213 :   IfExprConseqElse (std::unique_ptr<Expr> condition,
    4606              :                     std::unique_ptr<BlockExpr> if_block,
    4607              :                     std::unique_ptr<ExprWithBlock> else_block,
    4608              :                     std::vector<Attribute> outer_attrs, location_t locus)
    4609         1213 :     : IfExpr (std::move (condition), std::move (if_block),
    4610              :               std::move (outer_attrs), locus),
    4611         1213 :       else_block (std::move (else_block))
    4612         1213 :   {}
    4613              :   // again, outer attributes not allowed
    4614              : 
    4615              :   // Copy constructor with clone
    4616         2738 :   IfExprConseqElse (IfExprConseqElse const &other)
    4617         2738 :     : IfExpr (other), else_block (other.else_block->clone_expr_with_block ())
    4618         2738 :   {}
    4619              : 
    4620              :   // Overloaded assignment operator with cloning
    4621              :   IfExprConseqElse &operator= (IfExprConseqElse const &other)
    4622              :   {
    4623              :     IfExpr::operator= (other);
    4624              :     // condition = other.condition->clone_expr();
    4625              :     // if_block = other.if_block->clone_block_expr();
    4626              :     else_block = other.else_block->clone_expr_with_block ();
    4627              : 
    4628              :     return *this;
    4629              :   }
    4630              : 
    4631              :   // move constructors
    4632              :   IfExprConseqElse (IfExprConseqElse &&other) = default;
    4633              :   IfExprConseqElse &operator= (IfExprConseqElse &&other) = default;
    4634              : 
    4635              :   void accept_vis (ASTVisitor &vis) override;
    4636              : 
    4637              :   void vis_else_block (ASTVisitor &vis) { else_block->accept_vis (vis); }
    4638              : 
    4639              :   // TODO: is this better? Or is a "vis_block" better?
    4640        35275 :   ExprWithBlock &get_else_block ()
    4641              :   {
    4642        35275 :     rust_assert (else_block != nullptr);
    4643        35275 :     return *else_block;
    4644              :   }
    4645              : 
    4646              : protected:
    4647              :   /* Use covariance to implement clone function as returning this object rather
    4648              :    * than base */
    4649         2738 :   IfExprConseqElse *clone_if_expr_impl () const override
    4650              :   {
    4651         2738 :     return new IfExprConseqElse (*this);
    4652              :   }
    4653              : };
    4654              : 
    4655              : // Basic "if let" expression AST node with no else
    4656              : class IfLetExpr : public ExprWithBlock
    4657              : {
    4658              :   std::vector<Attribute> outer_attrs;
    4659              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4660              :   std::unique_ptr<Expr> value;
    4661              :   std::unique_ptr<BlockExpr> if_block;
    4662              :   location_t locus;
    4663              : 
    4664              : public:
    4665              :   std::string as_string () const override;
    4666              : 
    4667           31 :   IfLetExpr (std::unique_ptr<Pattern> match_arm_pattern,
    4668              :              std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
    4669              :              std::vector<Attribute> outer_attrs, location_t locus)
    4670           62 :     : outer_attrs (std::move (outer_attrs)),
    4671           31 :       match_arm_pattern (std::move (match_arm_pattern)),
    4672           31 :       value (std::move (value)), if_block (std::move (if_block)), locus (locus)
    4673           31 :   {}
    4674              : 
    4675              :   // copy constructor with clone
    4676           30 :   IfLetExpr (IfLetExpr const &other)
    4677           30 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4678           30 :       locus (other.locus)
    4679              :   {
    4680              :     // guard to prevent null dereference (only required if error state)
    4681           30 :     if (other.value != nullptr)
    4682           30 :       value = other.value->clone_expr ();
    4683           30 :     if (other.if_block != nullptr)
    4684           30 :       if_block = other.if_block->clone_block_expr ();
    4685           30 :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4686           30 :   }
    4687              : 
    4688              :   // overload assignment operator to clone
    4689              :   IfLetExpr &operator= (IfLetExpr const &other)
    4690              :   {
    4691              :     ExprWithBlock::operator= (other);
    4692              :     outer_attrs = other.outer_attrs;
    4693              :     locus = other.locus;
    4694              : 
    4695              :     // guard to prevent null dereference (only required if error state)
    4696              :     if (other.value != nullptr)
    4697              :       value = other.value->clone_expr ();
    4698              :     else
    4699              :       value = nullptr;
    4700              :     if (other.if_block != nullptr)
    4701              :       if_block = other.if_block->clone_block_expr ();
    4702              :     else
    4703              :       if_block = nullptr;
    4704              : 
    4705              :     if (other.if_block != nullptr)
    4706              :       if_block = other.if_block->clone_block_expr ();
    4707              : 
    4708              :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4709              :     return *this;
    4710              :   }
    4711              : 
    4712              :   // move constructors
    4713              :   IfLetExpr (IfLetExpr &&other) = default;
    4714              :   IfLetExpr &operator= (IfLetExpr &&other) = default;
    4715              : 
    4716              :   // Unique pointer custom clone function
    4717              :   std::unique_ptr<IfLetExpr> clone_if_let_expr () const
    4718              :   {
    4719              :     return std::unique_ptr<IfLetExpr> (clone_if_let_expr_impl ());
    4720              :   }
    4721              : 
    4722          137 :   location_t get_locus () const override final { return locus; }
    4723              : 
    4724              :   void accept_vis (ASTVisitor &vis) override;
    4725              : 
    4726              :   // Invalid if block or value is null, so base stripping on that.
    4727            0 :   void mark_for_strip () override
    4728              :   {
    4729            0 :     if_block = nullptr;
    4730            0 :     value = nullptr;
    4731            0 :   }
    4732          102 :   bool is_marked_for_strip () const override
    4733              :   {
    4734          102 :     return if_block == nullptr && value == nullptr;
    4735              :   }
    4736              : 
    4737              :   // TODO: is this better? Or is a "vis_block" better?
    4738          596 :   Expr &get_value_expr ()
    4739              :   {
    4740          596 :     rust_assert (value != nullptr);
    4741          596 :     return *value;
    4742              :   }
    4743              : 
    4744          150 :   std::unique_ptr<Expr> &get_value_expr_ptr ()
    4745              :   {
    4746          150 :     rust_assert (value != nullptr);
    4747          150 :     return value;
    4748              :   }
    4749              : 
    4750              :   // TODO: is this better? Or is a "vis_block" better?
    4751          746 :   BlockExpr &get_if_block ()
    4752              :   {
    4753          746 :     rust_assert (if_block != nullptr);
    4754          746 :     return *if_block;
    4755              :   }
    4756              : 
    4757              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4758              :   const std::unique_ptr<Pattern> &get_pattern () const
    4759              :   {
    4760              :     return match_arm_pattern;
    4761              :   }
    4762          686 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4763              : 
    4764           29 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4765              :   {
    4766           29 :     outer_attrs = std::move (new_attrs);
    4767           29 :   }
    4768              : 
    4769              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4770              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4771          804 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4772              : 
    4773           29 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::IfLet; }
    4774              : 
    4775              : protected:
    4776              :   /* Use covariance to implement clone function as returning this object rather
    4777              :    * than base (or rather this or any derived object) */
    4778           30 :   IfLetExpr *clone_expr_with_block_impl () const final override
    4779              :   {
    4780           30 :     return clone_if_let_expr_impl ();
    4781              :   }
    4782              : 
    4783              :   // Base clone function but still concrete as concrete base class
    4784           18 :   virtual IfLetExpr *clone_if_let_expr_impl () const
    4785              :   {
    4786           18 :     return new IfLetExpr (*this);
    4787              :   }
    4788              : };
    4789              : 
    4790              : /* AST node representing "if let" expression with an "else" expression at the
    4791              :  * end */
    4792              : class IfLetExprConseqElse : public IfLetExpr
    4793              : {
    4794              :   std::unique_ptr<ExprWithBlock> else_block;
    4795              : 
    4796              : public:
    4797              :   std::string as_string () const override;
    4798              : 
    4799           12 :   IfLetExprConseqElse (std::unique_ptr<Pattern> match_arm_pattern,
    4800              :                        std::unique_ptr<Expr> value,
    4801              :                        std::unique_ptr<BlockExpr> if_block,
    4802              :                        std::unique_ptr<ExprWithBlock> else_block,
    4803              :                        std::vector<Attribute> outer_attrs, location_t locus)
    4804           12 :     : IfLetExpr (std::move (match_arm_pattern), std::move (value),
    4805              :                  std::move (if_block), std::move (outer_attrs), locus),
    4806           12 :       else_block (std::move (else_block))
    4807           12 :   {}
    4808              :   // outer attributes not allowed
    4809              : 
    4810              :   // copy constructor with clone
    4811           12 :   IfLetExprConseqElse (IfLetExprConseqElse const &other)
    4812           12 :     : IfLetExpr (other), else_block (other.else_block->clone_expr_with_block ())
    4813           12 :   {}
    4814              : 
    4815              :   // overload assignment operator to clone
    4816              :   IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other)
    4817              :   {
    4818              :     IfLetExpr::operator= (other);
    4819              :     // match_arm_patterns = other.match_arm_patterns;
    4820              :     // value = other.value->clone_expr();
    4821              :     // if_block = other.if_block->clone_block_expr();
    4822              :     else_block = other.else_block->clone_expr_with_block ();
    4823              :     // outer_attrs = other.outer_attrs;
    4824              : 
    4825              :     return *this;
    4826              :   }
    4827              : 
    4828              :   // move constructors
    4829              :   IfLetExprConseqElse (IfLetExprConseqElse &&other) = default;
    4830              :   IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default;
    4831              : 
    4832              :   void accept_vis (ASTVisitor &vis) override;
    4833              : 
    4834              :   // TODO: is this better? Or is a "vis_block" better?
    4835          266 :   ExprWithBlock &get_else_block ()
    4836              :   {
    4837          266 :     rust_assert (else_block != nullptr);
    4838          266 :     return *else_block;
    4839              :   }
    4840              : 
    4841              : protected:
    4842              :   /* Use covariance to implement clone function as returning this object rather
    4843              :    * than base */
    4844           12 :   IfLetExprConseqElse *clone_if_let_expr_impl () const override
    4845              :   {
    4846           12 :     return new IfLetExprConseqElse (*this);
    4847              :   }
    4848              : };
    4849              : 
    4850              : // Match arm expression
    4851              : struct MatchArm
    4852              : {
    4853              : private:
    4854              :   std::vector<Attribute> outer_attrs;
    4855              :   // MatchArmPatterns patterns;
    4856              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4857              : 
    4858              :   // bool has_match_arm_guard;
    4859              :   // inlined from MatchArmGuard
    4860              :   std::unique_ptr<Expr> guard_expr;
    4861              : 
    4862              :   location_t locus;
    4863              : 
    4864              : public:
    4865              :   // Returns whether the MatchArm has a match arm guard expression
    4866        65429 :   bool has_match_arm_guard () const { return guard_expr != nullptr; }
    4867              : 
    4868              :   // Constructor for match arm with a guard expression
    4869         2518 :   MatchArm (std::unique_ptr<Pattern> match_arm_pattern, location_t locus,
    4870              :             std::unique_ptr<Expr> guard_expr = nullptr,
    4871              :             std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
    4872         2518 :     : outer_attrs (std::move (outer_attrs)),
    4873          367 :       match_arm_pattern (std::move (match_arm_pattern)),
    4874         2518 :       guard_expr (std::move (guard_expr)), locus (locus)
    4875              :   {}
    4876              : 
    4877              :   // Copy constructor with clone
    4878         4945 :   MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs)
    4879              :   {
    4880              :     // guard to protect from null pointer dereference
    4881         4945 :     if (other.guard_expr != nullptr)
    4882            1 :       guard_expr = other.guard_expr->clone_expr ();
    4883              : 
    4884         4945 :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4885              : 
    4886         4945 :     locus = other.locus;
    4887         4945 :   }
    4888              : 
    4889        14053 :   ~MatchArm () = default;
    4890              : 
    4891              :   // Overload assignment operator to clone
    4892              :   MatchArm &operator= (MatchArm const &other)
    4893              :   {
    4894              :     outer_attrs = other.outer_attrs;
    4895              : 
    4896              :     if (other.guard_expr != nullptr)
    4897              :       guard_expr = other.guard_expr->clone_expr ();
    4898              :     else
    4899              :       guard_expr = nullptr;
    4900              : 
    4901              :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4902              : 
    4903              :     return *this;
    4904              :   }
    4905              : 
    4906              :   // move constructors
    4907         2602 :   MatchArm (MatchArm &&other) = default;
    4908            0 :   MatchArm &operator= (MatchArm &&other) = default;
    4909              : 
    4910              :   // Returns whether match arm is in an error state.
    4911        72560 :   bool is_error () const { return match_arm_pattern == nullptr; }
    4912              : 
    4913              :   // Creates a match arm in an error state.
    4914            0 :   static MatchArm create_error ()
    4915              :   {
    4916            0 :     location_t locus = UNDEF_LOCATION;
    4917            0 :     return MatchArm (nullptr, locus);
    4918              :   }
    4919              : 
    4920              :   std::string as_string () const;
    4921              : 
    4922              :   // TODO: is this better? Or is a "vis_block" better?
    4923           17 :   Expr &get_guard_expr ()
    4924              :   {
    4925           17 :     rust_assert (has_match_arm_guard ());
    4926           17 :     return *guard_expr;
    4927              :   }
    4928              : 
    4929            5 :   std::unique_ptr<Expr> &get_guard_expr_ptr ()
    4930              :   {
    4931            5 :     rust_assert (has_match_arm_guard ());
    4932            5 :     return guard_expr;
    4933              :   }
    4934              : 
    4935              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4936              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4937        65429 :   std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
    4938              : 
    4939              :   const std::unique_ptr<Pattern> &get_pattern () const
    4940              :   {
    4941              :     return match_arm_pattern;
    4942              :   }
    4943        65429 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4944              : 
    4945              :   location_t get_locus () const { return locus; }
    4946              : };
    4947              : 
    4948              : /* A "match case" - a correlated match arm and resulting expression. Not
    4949              :  * abstract. */
    4950              : struct MatchCase
    4951              : {
    4952              : private:
    4953              :   MatchArm arm;
    4954              :   std::unique_ptr<Expr> expr;
    4955              :   NodeId node_id;
    4956              : 
    4957              :   /* TODO: does whether trailing comma exists need to be stored? currently
    4958              :    * assuming it is only syntactical and has no effect on meaning. */
    4959              : 
    4960              : public:
    4961         2516 :   MatchCase (MatchArm arm, std::unique_ptr<Expr> expr)
    4962         2516 :     : arm (std::move (arm)), expr (std::move (expr)),
    4963         2516 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
    4964         2516 :   {}
    4965              : 
    4966         4945 :   MatchCase (const MatchCase &other)
    4967         4945 :     : arm (other.arm), expr (other.expr->clone_expr ()), node_id (other.node_id)
    4968         4945 :   {}
    4969              : 
    4970              :   MatchCase &operator= (const MatchCase &other)
    4971              :   {
    4972              :     arm = other.arm;
    4973              :     expr = other.expr->clone_expr ();
    4974              :     node_id = other.node_id;
    4975              : 
    4976              :     return *this;
    4977              :   }
    4978              : 
    4979         3775 :   MatchCase (MatchCase &&other) = default;
    4980            0 :   MatchCase &operator= (MatchCase &&other) = default;
    4981              : 
    4982         8828 :   ~MatchCase () = default;
    4983              : 
    4984              :   std::string as_string () const;
    4985              : 
    4986              :   // TODO: is this better? Or is a "vis_block" better?
    4987        51394 :   Expr &get_expr ()
    4988              :   {
    4989        51394 :     rust_assert (expr != nullptr);
    4990        51394 :     return *expr;
    4991              :   }
    4992              : 
    4993        14035 :   std::unique_ptr<Expr> &get_expr_ptr ()
    4994              :   {
    4995        14035 :     rust_assert (expr != nullptr);
    4996        14035 :     return expr;
    4997              :   }
    4998              : 
    4999              :   // TODO: is this better? Or is a "vis_block" better?
    5000        70409 :   MatchArm &get_arm ()
    5001              :   {
    5002        70409 :     rust_assert (!arm.is_error ());
    5003        70409 :     return arm;
    5004              :   }
    5005              : 
    5006        16228 :   NodeId get_node_id () const { return node_id; }
    5007              : };
    5008              : 
    5009              : // Match expression AST node
    5010              : class MatchExpr : public ExprWithBlock
    5011              : {
    5012              :   std::vector<Attribute> outer_attrs;
    5013              :   std::unique_ptr<Expr> branch_value;
    5014              :   std::vector<Attribute> inner_attrs;
    5015              :   std::vector<MatchCase> match_arms;
    5016              :   location_t locus;
    5017              : 
    5018              : public:
    5019              :   std::string as_string () const override;
    5020              : 
    5021              :   // Returns whether the match expression has any match arms.
    5022              :   bool has_match_arms () const { return !match_arms.empty (); }
    5023              : 
    5024         1087 :   MatchExpr (std::unique_ptr<Expr> branch_value,
    5025              :              std::vector<MatchCase> match_arms,
    5026              :              std::vector<Attribute> inner_attrs,
    5027              :              std::vector<Attribute> outer_attrs, location_t locus)
    5028         2174 :     : outer_attrs (std::move (outer_attrs)),
    5029         1087 :       branch_value (std::move (branch_value)),
    5030         1087 :       inner_attrs (std::move (inner_attrs)),
    5031         1087 :       match_arms (std::move (match_arms)), locus (locus)
    5032         1087 :   {}
    5033              : 
    5034              :   // Copy constructor requires clone due to unique_ptr
    5035         2182 :   MatchExpr (MatchExpr const &other)
    5036         2182 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    5037         2182 :       inner_attrs (other.inner_attrs), match_arms (other.match_arms),
    5038         2182 :       locus (other.locus)
    5039              :   {
    5040              :     // guard to prevent null dereference (only required if error state)
    5041         2182 :     if (other.branch_value != nullptr)
    5042         2182 :       branch_value = other.branch_value->clone_expr ();
    5043         2182 :   }
    5044              : 
    5045              :   // Overloaded assignment operator to clone due to unique_ptr
    5046              :   MatchExpr &operator= (MatchExpr const &other)
    5047              :   {
    5048              :     ExprWithBlock::operator= (other);
    5049              :     inner_attrs = other.inner_attrs;
    5050              :     match_arms = other.match_arms;
    5051              :     outer_attrs = other.outer_attrs;
    5052              :     locus = other.locus;
    5053              : 
    5054              :     // guard to prevent null dereference (only required if error state)
    5055              :     if (other.branch_value != nullptr)
    5056              :       branch_value = other.branch_value->clone_expr ();
    5057              :     else
    5058              :       branch_value = nullptr;
    5059              : 
    5060              :     return *this;
    5061              :   }
    5062              : 
    5063              :   // move constructors
    5064              :   MatchExpr (MatchExpr &&other) = default;
    5065              :   MatchExpr &operator= (MatchExpr &&other) = default;
    5066              : 
    5067         4711 :   location_t get_locus () const override final { return locus; }
    5068              : 
    5069              :   void accept_vis (ASTVisitor &vis) override;
    5070              : 
    5071              :   // Invalid if branch value is null, so base stripping on that.
    5072            0 :   void mark_for_strip () override { branch_value = nullptr; }
    5073        11431 :   bool is_marked_for_strip () const override { return branch_value == nullptr; }
    5074              : 
    5075              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    5076              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    5077        28264 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    5078              : 
    5079              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5080        34317 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5081              : 
    5082          846 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5083              :   {
    5084          846 :     outer_attrs = std::move (new_attrs);
    5085          846 :   }
    5086              : 
    5087              :   // TODO: is this better? Or is a "vis_block" better?
    5088        22219 :   Expr &get_scrutinee_expr ()
    5089              :   {
    5090        22219 :     rust_assert (branch_value != nullptr);
    5091        22219 :     return *branch_value;
    5092              :   }
    5093              : 
    5094         6045 :   std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
    5095              :   {
    5096         6045 :     rust_assert (branch_value != nullptr);
    5097         6045 :     return branch_value;
    5098              :   }
    5099              : 
    5100              :   const std::vector<MatchCase> &get_match_cases () const { return match_arms; }
    5101        28264 :   std::vector<MatchCase> &get_match_cases () { return match_arms; }
    5102              : 
    5103         1079 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Match; }
    5104              : 
    5105              : protected:
    5106              :   /* Use covariance to implement clone function as returning this object rather
    5107              :    * than base */
    5108         2182 :   MatchExpr *clone_expr_with_block_impl () const override
    5109              :   {
    5110         2182 :     return new MatchExpr (*this);
    5111              :   }
    5112              : };
    5113              : 
    5114              : // Await expression AST node (pseudo-member variable access)
    5115              : class AwaitExpr : public ExprWithoutBlock
    5116              : {
    5117              :   std::vector<Attribute> outer_attrs;
    5118              :   std::unique_ptr<Expr> awaited_expr;
    5119              :   location_t locus;
    5120              : 
    5121              : public:
    5122              :   // TODO: ensure outer attributes are actually allowed
    5123            0 :   AwaitExpr (std::unique_ptr<Expr> awaited_expr,
    5124              :              std::vector<Attribute> outer_attrs, location_t locus)
    5125            0 :     : outer_attrs (std::move (outer_attrs)),
    5126            0 :       awaited_expr (std::move (awaited_expr)), locus (locus)
    5127            0 :   {}
    5128              : 
    5129              :   // copy constructor with clone
    5130            0 :   AwaitExpr (AwaitExpr const &other)
    5131            0 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    5132            0 :       locus (other.locus)
    5133              :   {
    5134              :     // guard to prevent null dereference (only required if error state)
    5135            0 :     if (other.awaited_expr != nullptr)
    5136            0 :       awaited_expr = other.awaited_expr->clone_expr ();
    5137            0 :   }
    5138              : 
    5139              :   // overloaded assignment operator with clone
    5140              :   AwaitExpr &operator= (AwaitExpr const &other)
    5141              :   {
    5142              :     ExprWithoutBlock::operator= (other);
    5143              :     outer_attrs = other.outer_attrs;
    5144              :     locus = other.locus;
    5145              : 
    5146              :     // guard to prevent null dereference (only required if error state)
    5147              :     if (other.awaited_expr != nullptr)
    5148              :       awaited_expr = other.awaited_expr->clone_expr ();
    5149              :     else
    5150              :       awaited_expr = nullptr;
    5151              : 
    5152              :     return *this;
    5153              :   }
    5154              : 
    5155              :   // move constructors
    5156              :   AwaitExpr (AwaitExpr &&other) = default;
    5157              :   AwaitExpr &operator= (AwaitExpr &&other) = default;
    5158              : 
    5159              :   std::string as_string () const override;
    5160              : 
    5161            0 :   location_t get_locus () const override final { return locus; }
    5162              : 
    5163              :   void accept_vis (ASTVisitor &vis) override;
    5164              : 
    5165              :   // Invalid if awaited expr is null, so base stripping on that.
    5166            0 :   void mark_for_strip () override { awaited_expr = nullptr; }
    5167            0 :   bool is_marked_for_strip () const override { return awaited_expr == nullptr; }
    5168              : 
    5169              :   // TODO: is this better? Or is a "vis_block" better?
    5170            0 :   std::unique_ptr<Expr> &get_awaited_expr ()
    5171              :   {
    5172            0 :     rust_assert (awaited_expr != nullptr);
    5173            0 :     return awaited_expr;
    5174              :   }
    5175              : 
    5176              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5177            0 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5178              : 
    5179            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5180              :   {
    5181            0 :     outer_attrs = std::move (new_attrs);
    5182            0 :   }
    5183              : 
    5184            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Await; }
    5185              : 
    5186              : protected:
    5187              :   /* Use covariance to implement clone function as returning this object rather
    5188              :    * than base */
    5189            0 :   AwaitExpr *clone_expr_without_block_impl () const override
    5190              :   {
    5191            0 :     return new AwaitExpr (*this);
    5192              :   }
    5193              : };
    5194              : 
    5195              : // Async block expression AST node (block expr that evaluates to a future)
    5196              : class AsyncBlockExpr : public ExprWithBlock
    5197              : {
    5198              :   // TODO: should this extend BlockExpr rather than be a composite of it?
    5199              :   std::vector<Attribute> outer_attrs;
    5200              :   bool has_move;
    5201              :   std::unique_ptr<BlockExpr> block_expr;
    5202              :   location_t locus;
    5203              : 
    5204              : public:
    5205            0 :   AsyncBlockExpr (std::unique_ptr<BlockExpr> block_expr, bool has_move,
    5206              :                   std::vector<Attribute> outer_attrs, location_t locus)
    5207            0 :     : outer_attrs (std::move (outer_attrs)), has_move (has_move),
    5208            0 :       block_expr (std::move (block_expr)), locus (locus)
    5209            0 :   {}
    5210              : 
    5211              :   // copy constructor with clone
    5212            0 :   AsyncBlockExpr (AsyncBlockExpr const &other)
    5213            0 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    5214            0 :       has_move (other.has_move), locus (other.locus)
    5215              :   {
    5216              :     // guard to prevent null dereference (only required if error state)
    5217            0 :     if (other.block_expr != nullptr)
    5218            0 :       block_expr = other.block_expr->clone_block_expr ();
    5219            0 :   }
    5220              : 
    5221              :   // overloaded assignment operator to clone
    5222              :   AsyncBlockExpr &operator= (AsyncBlockExpr const &other)
    5223              :   {
    5224              :     ExprWithBlock::operator= (other);
    5225              :     outer_attrs = other.outer_attrs;
    5226              :     has_move = other.has_move;
    5227              :     locus = other.locus;
    5228              : 
    5229              :     // guard to prevent null dereference (only required if error state)
    5230              :     if (other.block_expr != nullptr)
    5231              :       block_expr = other.block_expr->clone_block_expr ();
    5232              :     else
    5233              :       block_expr = nullptr;
    5234              : 
    5235              :     return *this;
    5236              :   }
    5237              : 
    5238              :   // move constructors
    5239              :   AsyncBlockExpr (AsyncBlockExpr &&other) = default;
    5240              :   AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default;
    5241              : 
    5242              :   std::string as_string () const override;
    5243              : 
    5244            0 :   bool get_has_move () { return has_move; }
    5245            0 :   location_t get_locus () const override final { return locus; }
    5246              : 
    5247              :   void accept_vis (ASTVisitor &vis) override;
    5248              : 
    5249              :   // Invalid if block is null, so base stripping on that.
    5250            0 :   void mark_for_strip () override { block_expr = nullptr; }
    5251            0 :   bool is_marked_for_strip () const override { return block_expr == nullptr; }
    5252              : 
    5253              :   // TODO: is this better? Or is a "vis_block" better?
    5254            0 :   std::unique_ptr<BlockExpr> &get_block_expr ()
    5255              :   {
    5256            0 :     rust_assert (block_expr != nullptr);
    5257            0 :     return block_expr;
    5258              :   }
    5259              : 
    5260              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5261            0 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5262              : 
    5263            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5264              :   {
    5265            0 :     outer_attrs = std::move (new_attrs);
    5266            0 :   }
    5267              : 
    5268            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::AsyncBlock; }
    5269              : 
    5270              : protected:
    5271              :   /* Use covariance to implement clone function as returning this object rather
    5272              :    * than base */
    5273            0 :   AsyncBlockExpr *clone_expr_with_block_impl () const override
    5274              :   {
    5275            0 :     return new AsyncBlockExpr (*this);
    5276              :   }
    5277              : };
    5278              : 
    5279              : // Inline-assembly specific options
    5280              : enum class InlineAsmOption
    5281              : {
    5282              :   PURE = 1 << 0,
    5283              :   NOMEM = 1 << 1,
    5284              :   READONLY = 1 << 2,
    5285              :   PRESERVES_FLAGS = 1 << 3,
    5286              :   NORETURN = 1 << 4,
    5287              :   NOSTACK = 1 << 5,
    5288              :   ATT_SYNTAX = 1 << 6,
    5289              :   RAW = 1 << 7,
    5290              :   MAY_UNWIND = 1 << 8,
    5291              : };
    5292              : 
    5293              : struct InlineAsmRegOrRegClass
    5294              : {
    5295              :   enum Type
    5296              :   {
    5297              :     Reg,
    5298              :     RegClass,
    5299              :   };
    5300              : 
    5301              :   struct Reg
    5302              :   {
    5303              :     std::string Symbol;
    5304              :   };
    5305              : 
    5306              :   struct RegClass
    5307              :   {
    5308              :     std::string Symbol;
    5309              :   };
    5310              : 
    5311              :   Type type;
    5312              :   struct Reg reg;
    5313              :   struct RegClass reg_class;
    5314              : 
    5315              :   Identifier name;
    5316              :   location_t locus;
    5317              : };
    5318              : 
    5319           18 : struct LlvmOperand
    5320              : {
    5321              :   std::string constraint;
    5322              :   std::unique_ptr<Expr> expr;
    5323              : 
    5324            2 :   LlvmOperand (std::string constraint, std::unique_ptr<Expr> &&expr)
    5325            4 :     : constraint (constraint), expr (std::move (expr))
    5326              :   {}
    5327              : 
    5328           18 :   LlvmOperand (const LlvmOperand &other)
    5329           36 :     : constraint (other.constraint), expr (other.expr->clone_expr ())
    5330           18 :   {}
    5331              :   LlvmOperand &operator= (const LlvmOperand &other)
    5332              :   {
    5333              :     constraint = other.constraint;
    5334              :     expr = other.expr->clone_expr ();
    5335              : 
    5336              :     return *this;
    5337              :   }
    5338              : };
    5339              : 
    5340          676 : class InlineAsmOperand
    5341              : {
    5342              : public:
    5343              :   enum class RegisterType
    5344              :   {
    5345              :     In,
    5346              :     Out,
    5347              :     InOut,
    5348              :     SplitInOut,
    5349              :     Const,
    5350              :     Sym,
    5351              :     Label,
    5352              :   };
    5353              : 
    5354            0 :   class Register
    5355              :   {
    5356              :   public:
    5357          796 :     Register () {}
    5358            0 :     virtual ~Register () = default;
    5359              : 
    5360          673 :     std::unique_ptr<Register> clone () const
    5361              :     {
    5362          673 :       return std::unique_ptr<Register> (clone_impl ());
    5363              :     }
    5364              : 
    5365              :   protected:
    5366              :     virtual Register *clone_impl () const = 0;
    5367              :   };
    5368              : 
    5369              :   class In : public Register
    5370              :   {
    5371              :   public:
    5372              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5373              :     std::unique_ptr<Expr> expr;
    5374              : 
    5375           12 :     In (tl::optional<struct InlineAsmRegOrRegClass> &reg,
    5376              :         std::unique_ptr<Expr> expr)
    5377           12 :       : reg (reg), expr (std::move (expr))
    5378              :     {
    5379           12 :       rust_assert (this->expr != nullptr);
    5380           12 :     }
    5381              : 
    5382          261 :     In (const In &other)
    5383          261 :     {
    5384          261 :       reg = other.reg;
    5385              : 
    5386          261 :       expr = other.expr->clone_expr ();
    5387          261 :     }
    5388              : 
    5389              :     In operator= (const In &other)
    5390              :     {
    5391              :       reg = other.reg;
    5392              :       expr = other.expr->clone_expr ();
    5393              : 
    5394              :       return *this;
    5395              :     }
    5396              : 
    5397              :   private:
    5398          239 :     In *clone_impl () const { return new In (*this); }
    5399              :   };
    5400              : 
    5401              :   class Out : public Register
    5402              :   {
    5403              :   public:
    5404              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5405              :     bool late;
    5406              :     std::unique_ptr<Expr> expr; // can be null
    5407              : 
    5408           17 :     Out (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5409              :          std::unique_ptr<Expr> expr)
    5410           17 :       : reg (reg), late (late), expr (std::move (expr))
    5411              :     {
    5412           17 :       rust_assert (this->expr != nullptr);
    5413           17 :     }
    5414              : 
    5415          422 :     Out (const Out &other)
    5416          422 :     {
    5417          422 :       reg = other.reg;
    5418          422 :       late = other.late;
    5419          422 :       expr = other.expr->clone_expr ();
    5420          422 :     }
    5421              : 
    5422              :     Out operator= (const Out &other)
    5423              :     {
    5424              :       reg = other.reg;
    5425              :       late = other.late;
    5426              :       expr = other.expr->clone_expr ();
    5427              :       return *this;
    5428              :     }
    5429              : 
    5430              :   private:
    5431          388 :     Out *clone_impl () const { return new Out (*this); }
    5432              :   };
    5433              : 
    5434              :   class InOut : public Register
    5435              :   {
    5436              :   public:
    5437              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5438              :     bool late;
    5439              :     std::unique_ptr<Expr> expr; // this can't be null
    5440              : 
    5441            1 :     InOut (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5442              :            std::unique_ptr<Expr> expr)
    5443            1 :       : reg (reg), late (late), expr (std::move (expr))
    5444              :     {
    5445            1 :       rust_assert (this->expr != nullptr);
    5446            1 :     }
    5447              : 
    5448            1 :     InOut (const InOut &other)
    5449            1 :     {
    5450            1 :       reg = other.reg;
    5451            1 :       late = other.late;
    5452            1 :       expr = other.expr->clone_expr ();
    5453            1 :     }
    5454              : 
    5455              :     InOut operator= (const InOut &other)
    5456              :     {
    5457              :       reg = other.reg;
    5458              :       late = other.late;
    5459              :       expr = other.expr->clone_expr ();
    5460              : 
    5461              :       return *this;
    5462              :     }
    5463              : 
    5464              :   private:
    5465            0 :     InOut *clone_impl () const { return new InOut (*this); }
    5466              :   };
    5467              : 
    5468              :   class SplitInOut : public Register
    5469              :   {
    5470              :   public:
    5471              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5472              :     bool late;
    5473              :     std::unique_ptr<Expr> in_expr;
    5474              :     std::unique_ptr<Expr> out_expr; // could be null
    5475              : 
    5476            2 :     SplitInOut (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5477              :                 std::unique_ptr<Expr> in_expr, std::unique_ptr<Expr> out_expr)
    5478            2 :       : reg (reg), late (late), in_expr (std::move (in_expr)),
    5479            2 :         out_expr (std::move (out_expr))
    5480              :     {
    5481            2 :       rust_assert (this->in_expr != nullptr);
    5482            2 :       rust_assert (this->out_expr != nullptr);
    5483            2 :     }
    5484              : 
    5485           80 :     SplitInOut (const SplitInOut &other)
    5486           80 :     {
    5487           80 :       reg = other.reg;
    5488           80 :       late = other.late;
    5489           80 :       in_expr = other.in_expr->clone_expr ();
    5490           80 :       out_expr = other.out_expr->clone_expr ();
    5491           80 :     }
    5492              : 
    5493              :     SplitInOut operator= (const SplitInOut &other)
    5494              :     {
    5495              :       reg = other.reg;
    5496              :       late = other.late;
    5497              :       in_expr = other.in_expr->clone_expr ();
    5498              :       out_expr = other.out_expr->clone_expr ();
    5499              : 
    5500              :       return *this;
    5501              :     }
    5502              : 
    5503              :   private:
    5504           46 :     SplitInOut *clone_impl () const { return new SplitInOut (*this); }
    5505              :   };
    5506              : 
    5507            0 :   class Const : public Register
    5508              :   {
    5509              :   public:
    5510              :     AnonConst anon_const;
    5511              : 
    5512              :   private:
    5513            0 :     Const *clone_impl () const { return new Const (*this); }
    5514              :   };
    5515              : 
    5516            0 :   class Sym : public Register
    5517              :   {
    5518              :   public:
    5519              :     std::unique_ptr<Expr> expr;
    5520              : 
    5521              :     Sym (std::unique_ptr<Expr> expr) : expr (std::move (expr))
    5522              :     {
    5523              :       rust_assert (this->expr != nullptr);
    5524              :     }
    5525            0 :     Sym (const Sym &other)
    5526            0 :     {
    5527            0 :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5528            0 :     }
    5529              : 
    5530              :     Sym operator= (const Sym &other)
    5531              :     {
    5532              :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5533              :       return *this;
    5534              :     }
    5535              : 
    5536              :   private:
    5537            0 :     Sym *clone_impl () const { return new Sym (*this); }
    5538              :   };
    5539              : 
    5540              :   class Label : public Register
    5541              :   {
    5542              :   public:
    5543              :     std::string label_name;
    5544              :     std::unique_ptr<Expr> expr;
    5545              : 
    5546              :     Label (tl::optional<std::string> label_name, std::unique_ptr<Expr> expr)
    5547              :       : expr (std::move (expr))
    5548              :     {
    5549              :       rust_assert (this->expr != nullptr);
    5550              :       if (label_name.has_value ())
    5551              :         this->label_name = label_name.value ();
    5552              :     }
    5553            0 :     Label (const Label &other)
    5554            0 :     {
    5555            0 :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5556            0 :     }
    5557              : 
    5558              :     Label operator= (const Label &other)
    5559              :     {
    5560              :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5561              :       return *this;
    5562              :     }
    5563              : 
    5564              :   private:
    5565            0 :     Label *clone_impl () const { return new Label (*this); }
    5566              :   };
    5567              : 
    5568          673 :   InlineAsmOperand (const InlineAsmOperand &other)
    5569          673 :     : register_type (other.register_type), locus (other.locus),
    5570          673 :       reg (other.reg->clone ())
    5571          673 :   {}
    5572              : 
    5573           12 :   InlineAsmOperand (const In &reg, location_t locus)
    5574           12 :     : register_type (RegisterType::In), locus (locus), reg (new In (reg))
    5575           12 :   {}
    5576           17 :   InlineAsmOperand (const Out &reg, location_t locus)
    5577           17 :     : register_type (RegisterType::Out), locus (locus), reg (new Out (reg))
    5578           17 :   {}
    5579            1 :   InlineAsmOperand (const InOut &reg, location_t locus)
    5580            1 :     : register_type (RegisterType::InOut), locus (locus), reg (new InOut (reg))
    5581            1 :   {}
    5582            2 :   InlineAsmOperand (const SplitInOut &reg, location_t locus)
    5583            2 :     : register_type (RegisterType::SplitInOut), locus (locus),
    5584            2 :       reg (new SplitInOut (reg))
    5585            2 :   {}
    5586              :   InlineAsmOperand (const Const &reg, location_t locus)
    5587              :     : register_type (RegisterType::Const), locus (locus), reg (new Const (reg))
    5588              :   {}
    5589              :   InlineAsmOperand (const Sym &reg, location_t locus)
    5590              :     : register_type (RegisterType::Sym), locus (locus), reg (new Sym (reg))
    5591              :   {}
    5592              :   InlineAsmOperand (const Label &reg, location_t locus)
    5593              :     : register_type (RegisterType::Label), locus (locus), reg (new Label (reg))
    5594              :   {}
    5595              : 
    5596              :   location_t get_locus () const { return locus; }
    5597          460 :   RegisterType get_register_type () const { return register_type; }
    5598              : 
    5599              :   // Potentially fail immediately if you don't use get_register_type() to
    5600              :   // inspect the RegisterType first before calling the following functions Check
    5601              :   // first
    5602          150 :   In &get_in ()
    5603              :   {
    5604          150 :     rust_assert (register_type == RegisterType::In);
    5605          150 :     return static_cast<In &> (*reg);
    5606              :   }
    5607           10 :   const In &get_in () const
    5608              :   {
    5609           10 :     rust_assert (register_type == RegisterType::In);
    5610           10 :     return static_cast<const In &> (*reg);
    5611              :   }
    5612              : 
    5613          251 :   Out &get_out ()
    5614              :   {
    5615          251 :     rust_assert (register_type == RegisterType::Out);
    5616          251 :     return static_cast<Out &> (*reg);
    5617              :   }
    5618           17 :   const Out &get_out () const
    5619              :   {
    5620           17 :     rust_assert (register_type == RegisterType::Out);
    5621           17 :     return static_cast<const Out &> (*reg);
    5622              :   }
    5623              : 
    5624            0 :   InOut &get_in_out ()
    5625              :   {
    5626            0 :     rust_assert (register_type == RegisterType::InOut);
    5627            0 :     return static_cast<InOut &> (*reg);
    5628              :   }
    5629            0 :   const InOut &get_in_out () const
    5630              :   {
    5631            0 :     rust_assert (register_type == RegisterType::InOut);
    5632            0 :     return static_cast<const InOut &> (*reg);
    5633              :   }
    5634              : 
    5635           30 :   SplitInOut &get_split_in_out ()
    5636              :   {
    5637           30 :     rust_assert (register_type == RegisterType::SplitInOut);
    5638           30 :     return static_cast<SplitInOut &> (*reg);
    5639              :   }
    5640            2 :   const SplitInOut &get_split_in_out () const
    5641              :   {
    5642            2 :     rust_assert (register_type == RegisterType::SplitInOut);
    5643            2 :     return static_cast<const SplitInOut &> (*reg);
    5644              :   }
    5645              : 
    5646            0 :   Const &get_const ()
    5647              :   {
    5648            0 :     rust_assert (register_type == RegisterType::Const);
    5649            0 :     return static_cast<Const &> (*reg);
    5650              :   }
    5651            0 :   const Const &get_const () const
    5652              :   {
    5653            0 :     rust_assert (register_type == RegisterType::Const);
    5654            0 :     return static_cast<Const &> (*reg);
    5655              :   }
    5656              : 
    5657            0 :   Sym &get_sym ()
    5658              :   {
    5659            0 :     rust_assert (register_type == RegisterType::Sym);
    5660            0 :     return static_cast<Sym &> (*reg);
    5661              :   }
    5662            0 :   const Sym &get_sym () const
    5663              :   {
    5664            0 :     rust_assert (register_type == RegisterType::Sym);
    5665            0 :     return static_cast<const Sym &> (*reg);
    5666              :   }
    5667              : 
    5668            0 :   Label &get_label ()
    5669              :   {
    5670            0 :     rust_assert (register_type == RegisterType::Label);
    5671            0 :     return static_cast<Label &> (*reg);
    5672              :   }
    5673            0 :   const Label &get_label () const
    5674              :   {
    5675            0 :     rust_assert (register_type == RegisterType::Label);
    5676            0 :     return static_cast<const Label &> (*reg);
    5677              :   }
    5678              : 
    5679              : private:
    5680              :   RegisterType register_type;
    5681              : 
    5682              :   location_t locus;
    5683              :   std::unique_ptr<Register> reg;
    5684              : };
    5685              : 
    5686              : struct InlineAsmPlaceHolder
    5687              : {
    5688              :   size_t operand_idx;
    5689              :   char modifier; // can be null
    5690              :   location_t locus;
    5691              : };
    5692              : 
    5693            0 : struct InlineAsmTemplatePiece
    5694              : {
    5695              :   bool is_placeholder;
    5696              :   std::string string;
    5697              :   InlineAsmPlaceHolder placeholder;
    5698              : };
    5699              : 
    5700           20 : struct TupleClobber
    5701              : {
    5702            4 :   TupleClobber (std::string symbol, location_t loc) : symbol (symbol), loc (loc)
    5703              :   {}
    5704              :   // as gccrs still doesn't contain a symbol class I have put them as strings
    5705              :   std::string symbol;
    5706              :   location_t loc;
    5707              : };
    5708              : 
    5709          322 : struct TupleTemplateStr
    5710              : {
    5711              :   // as gccrs still doesn't contain a symbol class I have put them as strings
    5712              :   location_t loc;
    5713              :   std::string symbol;
    5714              : 
    5715            2 :   location_t get_locus () { return loc; }
    5716           40 :   TupleTemplateStr (location_t loc, const std::string &symbol)
    5717           80 :     : loc (loc), symbol (symbol)
    5718              :   {}
    5719              : };
    5720              : 
    5721              : // Inline Assembly Node
    5722              : class InlineAsm : public ExprWithoutBlock
    5723              : {
    5724              : public:
    5725              :   enum class Option
    5726              :   {
    5727              :     PURE = 1 << 0,
    5728              :     NOMEM = 1 << 1,
    5729              :     READONLY = 1 << 2,
    5730              :     PRESERVES_FLAGS = 1 << 3,
    5731              :     NORETURN = 1 << 4,
    5732              :     NOSTACK = 1 << 5,
    5733              :     ATT_SYNTAX = 1 << 6,
    5734              :     RAW = 1 << 7,
    5735              :     MAY_UNWIND = 1 << 8,
    5736              :   };
    5737              : 
    5738              : private:
    5739              :   location_t locus;
    5740              :   // TODO: Not sure how outer_attrs plays with InlineAsm, I put it here in order
    5741              :   // to override, very hacky.
    5742              :   std::vector<Attribute> outer_attrs;
    5743              : 
    5744              : public:
    5745              :   // https://github.com/rust-lang/rust/blob/55cac26a9ef17da1c9c77c0816e88e178b7cc5dd/compiler/rustc_builtin_macros/src/asm.rs#L56C1-L64C7
    5746              :   //   let mut args = AsmArgs {
    5747              :   //     templates: vec![first_template],
    5748              :   //     operands: vec![],
    5749              :   //     named_args: Default::default(),
    5750              :   //     reg_args: Default::default(),
    5751              :   //     clobber_abis: Vec::new(),
    5752              :   //     options: ast::InlineAsmOptions::empty(),
    5753              :   //     options_spans: vec![],
    5754              :   // };
    5755              :   std::vector<InlineAsmTemplatePiece> template_;
    5756              :   std::vector<TupleTemplateStr> template_strs;
    5757              :   std::vector<InlineAsmOperand> operands;
    5758              :   std::map<std::string, int> named_args;
    5759              :   std::set<int> reg_args;
    5760              :   std::vector<TupleClobber> clobber_abi;
    5761              :   std::set<InlineAsm::Option> options;
    5762              : 
    5763              :   std::vector<location_t> line_spans;
    5764              : 
    5765              :   bool is_global_asm;
    5766              : 
    5767           39 :   InlineAsm (location_t locus, bool is_global_asm)
    5768           39 :     : locus (locus), is_global_asm (is_global_asm)
    5769           39 :   {}
    5770              : 
    5771              :   void accept_vis (ASTVisitor &vis) override;
    5772            0 :   std::string as_string () const override { return "InlineAsm AST Node"; }
    5773              : 
    5774           54 :   location_t get_locus () const override { return locus; }
    5775              : 
    5776            0 :   void mark_for_strip () override {}
    5777              : 
    5778           92 :   bool is_marked_for_strip () const override { return false; }
    5779              : 
    5780          399 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5781              : 
    5782            0 :   void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
    5783              : 
    5784           27 :   std::vector<InlineAsmTemplatePiece> get_template_ () { return template_; }
    5785              : 
    5786           56 :   std::vector<TupleTemplateStr> get_template_strs () { return template_strs; }
    5787              : 
    5788          426 :   std::vector<InlineAsmOperand> get_operands () { return operands; }
    5789              : 
    5790           27 :   std::vector<TupleClobber> get_clobber_abi () { return clobber_abi; }
    5791              : 
    5792           27 :   std::set<InlineAsm::Option> get_options () { return options; }
    5793              : 
    5794          203 :   InlineAsm *clone_expr_without_block_impl () const override
    5795              :   {
    5796          203 :     return new InlineAsm (*this);
    5797              :   }
    5798              : 
    5799           27 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::InlineAsm; }
    5800              : 
    5801            2 :   static std::string option_to_string (Option option)
    5802              :   {
    5803            2 :     switch (option)
    5804              :       {
    5805            0 :       case Option::PURE:
    5806            0 :         return "pure";
    5807            1 :       case Option::NOMEM:
    5808            1 :         return "nomem";
    5809            0 :       case Option::READONLY:
    5810            0 :         return "readonly";
    5811            0 :       case Option::PRESERVES_FLAGS:
    5812            0 :         return "preserves_flags";
    5813            1 :       case Option::NORETURN:
    5814            1 :         return "noreturn";
    5815            0 :       case Option::NOSTACK:
    5816            0 :         return "nostack";
    5817            0 :       case Option::ATT_SYNTAX:
    5818            0 :         return "att_syntax";
    5819            0 :       case Option::RAW:
    5820            0 :         return "raw";
    5821            0 :       case Option::MAY_UNWIND:
    5822            0 :         return "may_unwind";
    5823            0 :       default:
    5824            0 :         rust_unreachable ();
    5825              :       }
    5826              :   }
    5827              : };
    5828              : 
    5829              : class LlvmInlineAsm : public ExprWithoutBlock
    5830              : {
    5831              :   // llvm_asm!("" :         : "r"(&mut dummy) : "memory" : "volatile");
    5832              :   //           Asm, Outputs, Inputs,            Clobbers, Options,
    5833              : 
    5834              : public:
    5835              :   enum class Dialect
    5836              :   {
    5837              :     Att,
    5838              :     Intel,
    5839              :   };
    5840              : 
    5841              : private:
    5842              :   location_t locus;
    5843              :   std::vector<Attribute> outer_attrs;
    5844              :   std::vector<LlvmOperand> inputs;
    5845              :   std::vector<LlvmOperand> outputs;
    5846              :   std::vector<TupleTemplateStr> templates;
    5847              :   std::vector<TupleClobber> clobbers;
    5848              :   bool volatility;
    5849              :   bool align_stack;
    5850              :   Dialect dialect;
    5851              : 
    5852              : public:
    5853            2 :   LlvmInlineAsm (location_t locus) : locus (locus) {}
    5854              : 
    5855            2 :   Dialect get_dialect () { return dialect; }
    5856              : 
    5857           28 :   location_t get_locus () const override { return locus; }
    5858              : 
    5859            0 :   void mark_for_strip () override {}
    5860              : 
    5861            8 :   bool is_marked_for_strip () const override { return false; }
    5862              : 
    5863            2 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5864              : 
    5865              :   void accept_vis (ASTVisitor &vis) override;
    5866              : 
    5867            0 :   std::string as_string () const override { return "InlineAsm AST Node"; }
    5868              : 
    5869            0 :   void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
    5870              : 
    5871           14 :   LlvmInlineAsm *clone_expr_without_block_impl () const override
    5872              :   {
    5873           14 :     return new LlvmInlineAsm (*this);
    5874              :   }
    5875              : 
    5876            6 :   std::vector<TupleTemplateStr> &get_templates () { return templates; }
    5877              :   const std::vector<TupleTemplateStr> &get_templates () const
    5878              :   {
    5879              :     return templates;
    5880              :   }
    5881              : 
    5882            2 :   Expr::Kind get_expr_kind () const override
    5883              :   {
    5884            2 :     return Expr::Kind::LlvmInlineAsm;
    5885              :   }
    5886              : 
    5887            0 :   void set_align_stack (bool align_stack) { this->align_stack = align_stack; }
    5888            2 :   bool is_stack_aligned () { return align_stack; }
    5889              : 
    5890            2 :   void set_volatile (bool volatility) { this->volatility = volatility; }
    5891            2 :   bool is_volatile () { return volatility; }
    5892              : 
    5893            0 :   void set_dialect (Dialect dialect) { this->dialect = dialect; }
    5894              : 
    5895              :   void set_inputs (std::vector<LlvmOperand> operands) { inputs = operands; }
    5896              :   void set_outputs (std::vector<LlvmOperand> operands) { outputs = operands; }
    5897              : 
    5898           36 :   std::vector<LlvmOperand> &get_inputs () { return inputs; }
    5899              :   const std::vector<LlvmOperand> &get_inputs () const { return inputs; }
    5900           36 :   std::vector<LlvmOperand> &get_outputs () { return outputs; }
    5901              :   const std::vector<LlvmOperand> &get_outputs () const { return outputs; }
    5902              : 
    5903            6 :   std::vector<TupleClobber> &get_clobbers () { return clobbers; }
    5904              :   const std::vector<TupleClobber> &get_clobbers () const { return clobbers; }
    5905              : };
    5906              : 
    5907              : } // namespace AST
    5908              : } // namespace Rust
    5909              : 
    5910              : #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.