LCOV - code coverage report
Current view: top level - gcc/rust/ast - rust-expr.h (source / functions) Coverage Total Hit
Test: gcc.info Lines: 83.5 % 1693 1414
Test Date: 2026-07-25 15:27:52 Functions: 79.7 % 572 456
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          615 : 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           72 :   LoopLabel (Lifetime loop_label, location_t locus = UNDEF_LOCATION)
      30           72 :     : label (std::move (loop_label)), locus (locus),
      31           72 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
      32           72 :   {}
      33              : 
      34              :   // Returns whether the LoopLabel is in an error state.
      35           89 :   location_t get_locus () const { return locus; }
      36              : 
      37         1460 :   Lifetime &get_lifetime () { return label; }
      38              : 
      39           40 :   NodeId get_node_id () const { return node_id; }
      40              : };
      41              : 
      42              : // AST node for an expression with an accompanying block - abstract
      43        69652 : 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        18710 :   ExprWithBlock *clone_expr_impl () const final override
      51              :   {
      52        18710 :     return clone_expr_with_block_impl ();
      53              :   }
      54              : 
      55        19848 :   bool is_expr_without_block () const final override { return false; };
      56              : 
      57              : public:
      58              :   // Unique pointer custom clone function
      59         2806 :   std::unique_ptr<ExprWithBlock> clone_expr_with_block () const
      60              :   {
      61         2806 :     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         7146 :   std::string as_string () const override { return literal.as_string (); }
      74              : 
      75         7101 :   Literal::LitType get_lit_type () const { return literal.get_lit_type (); }
      76              : 
      77        39923 :   LiteralExpr (std::string value_as_string, Literal::LitType type,
      78              :                PrimitiveCoreType type_hint, std::vector<Attribute> outer_attrs,
      79              :                location_t locus)
      80        79846 :     : outer_attrs (std::move (outer_attrs)),
      81        39923 :       literal (std::move (value_as_string), type, type_hint), locus (locus)
      82        39923 :   {}
      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        47341 :   location_t get_locus () const override final { return locus; }
      97              : 
      98          239 :   bool is_literal () const override final { return true; }
      99              : 
     100        24537 :   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       621893 :   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      3492283 :   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        20808 :   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        51265 :   LiteralExpr *clone_expr_without_block_impl () const final override
     122              :   {
     123        51265 :     return clone_literal_expr_impl ();
     124              :   }
     125              : 
     126              :   /* not virtual as currently no subclasses of LiteralExpr, but could be in
     127              :    * future */
     128        51265 :   /*virtual*/ LiteralExpr *clone_literal_expr_impl () const
     129              :   {
     130        51265 :     return new LiteralExpr (*this);
     131              :   }
     132              : };
     133              : 
     134              : // Literal expression attribute body (non-macro attribute)
     135        72071 : class AttrInputLiteral : public AttrInput
     136              : {
     137              :   LiteralExpr literal_expr;
     138              : 
     139              : public:
     140        11981 :   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       253618 :   LiteralExpr &get_literal () { return literal_expr; }
     157              : 
     158        39833 :   AttrInputType get_attr_input_type () const final override
     159              :   {
     160        39833 :     return AttrInput::AttrInputType::LITERAL;
     161              :   }
     162              : 
     163              : protected:
     164              :   /* Use covariance to implement clone function as returning this object rather
     165              :    * than base */
     166        72071 :   AttrInputLiteral *clone_attr_input_impl () const override
     167              :   {
     168        72071 :     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        23759 :   OperatorExpr (std::unique_ptr<Expr> main_or_left_expr,
     328              :                 std::vector<Attribute> outer_attribs, location_t locus)
     329        47518 :     : locus (locus), outer_attrs (std::move (outer_attribs)),
     330        23759 :       main_or_left_expr (std::move (main_or_left_expr))
     331        23759 :   {}
     332              : 
     333              :   // Copy constructor (only for initialisation of expr purposes)
     334        55716 :   OperatorExpr (OperatorExpr const &other)
     335        55716 :     : locus (other.locus), outer_attrs (other.outer_attrs)
     336              :   {
     337              :     // guard to prevent null dereference (only required if error state)
     338        55716 :     if (other.main_or_left_expr != nullptr)
     339        55716 :       main_or_left_expr = other.main_or_left_expr->clone_expr ();
     340        55716 :   }
     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        41949 :   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       661801 :   bool is_marked_for_strip () const override
     368              :   {
     369       661801 :     return main_or_left_expr == nullptr;
     370              :   }
     371              : 
     372              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
     373      3269756 :   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         2025 :   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         2025 :     : OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs),
     398              :                     locus),
     399         2025 :       mutability (mutability), raw_borrow (raw_borrow),
     400         2025 :       double_borrow (is_double_borrow)
     401         2025 :   {}
     402              : 
     403              :   void accept_vis (ASTVisitor &vis) override;
     404              : 
     405              :   // TODO: is this better? Or is a "vis_block" better?
     406        58504 :   Expr &get_borrowed_expr ()
     407              :   {
     408        58504 :     rust_assert (main_or_left_expr != nullptr);
     409        58504 :     return *main_or_left_expr;
     410              :   }
     411              : 
     412        11452 :   std::unique_ptr<Expr> &get_borrowed_expr_ptr ()
     413              :   {
     414        11452 :     rust_assert (main_or_left_expr != nullptr);
     415        11452 :     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         2029 :   Mutability get_mutability () const { return mutability; }
     423              : 
     424         2243 :   bool get_is_double_borrow () const { return double_borrow; }
     425         4506 :   bool is_raw_borrow () const { return raw_borrow; }
     426              : 
     427         2016 :   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         3286 :   BorrowExpr *clone_expr_without_block_impl () const override
     433              :   {
     434         3286 :     return new BorrowExpr (*this);
     435              :   }
     436              : };
     437              : 
     438              : // Unary prefix * deference operator
     439        23962 : class DereferenceExpr : public OperatorExpr
     440              : {
     441              : public:
     442              :   std::string as_string () const override;
     443              : 
     444              :   // Constructor calls OperatorExpr's protected constructor
     445         3978 :   DereferenceExpr (std::unique_ptr<Expr> deref_lvalue,
     446              :                    std::vector<Attribute> outer_attribs, location_t locus)
     447         3978 :     : OperatorExpr (std::move (deref_lvalue), std::move (outer_attribs), locus)
     448         3978 :   {}
     449              : 
     450              :   void accept_vis (ASTVisitor &vis) override;
     451              : 
     452              :   // TODO: is this better? Or is a "vis_block" better?
     453        71891 :   Expr &get_dereferenced_expr ()
     454              :   {
     455        71891 :     rust_assert (main_or_left_expr != nullptr);
     456        71891 :     return *main_or_left_expr;
     457              :   }
     458              : 
     459        23926 :   std::unique_ptr<Expr> &get_dereferenced_expr_ptr ()
     460              :   {
     461        23926 :     rust_assert (main_or_left_expr != nullptr);
     462        23926 :     return main_or_left_expr;
     463              :   }
     464              : 
     465         3976 :   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        11981 :   DereferenceExpr *clone_expr_without_block_impl () const override
     471              :   {
     472        11981 :     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         2550 : 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          864 :   ExprType get_expr_type () const { return expr_type; }
     534              : 
     535              :   // Constructor calls OperatorExpr's protected constructor
     536          694 :   NegationExpr (std::unique_ptr<Expr> negated_value, ExprType expr_kind,
     537              :                 std::vector<Attribute> outer_attribs, location_t locus)
     538          694 :     : OperatorExpr (std::move (negated_value), std::move (outer_attribs),
     539              :                     locus),
     540          694 :       expr_type (expr_kind)
     541          694 :   {}
     542              : 
     543              :   void accept_vis (ASTVisitor &vis) override;
     544              : 
     545              :   // TODO: is this better? Or is a "vis_block" better?
     546        16596 :   Expr &get_negated_expr ()
     547              :   {
     548        16596 :     rust_assert (main_or_left_expr != nullptr);
     549        16596 :     return *main_or_left_expr;
     550              :   }
     551              : 
     552         3675 :   std::unique_ptr<Expr> &get_negated_expr_ptr ()
     553              :   {
     554         3675 :     rust_assert (main_or_left_expr != nullptr);
     555         3675 :     return main_or_left_expr;
     556              :   }
     557              : 
     558          691 :   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         1275 :   NegationExpr *clone_expr_without_block_impl () const override
     564              :   {
     565         1275 :     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         3737 :   ExprType get_expr_type () const { return expr_type; }
     585              : 
     586              :   // Constructor calls OperatorExpr's protected constructor
     587         4597 :   ArithmeticOrLogicalExpr (std::unique_ptr<Expr> left_value,
     588              :                            std::unique_ptr<Expr> right_value,
     589              :                            ExprType expr_kind, location_t locus)
     590        13791 :     : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
     591         4597 :       expr_type (expr_kind), right_expr (std::move (right_value))
     592         4597 :   {}
     593              :   // outer attributes not allowed
     594              : 
     595              :   // Copy constructor - probably required due to unique pointer
     596        13013 :   ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other)
     597        26026 :     : OperatorExpr (other), expr_type (other.expr_type),
     598        13013 :       right_expr (other.right_expr->clone_expr ())
     599        13013 :   {}
     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      2176569 :   Expr &get_left_expr ()
     621              :   {
     622      2176569 :     rust_assert (main_or_left_expr != nullptr);
     623      2176569 :     return *main_or_left_expr;
     624              :   }
     625              : 
     626      1066876 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     627              :   {
     628      1066876 :     rust_assert (main_or_left_expr != nullptr);
     629      1066876 :     return main_or_left_expr;
     630              :   }
     631              : 
     632              :   // TODO: is this better? Or is a "vis_block" better?
     633      2176569 :   Expr &get_right_expr ()
     634              :   {
     635      2176569 :     rust_assert (right_expr != nullptr);
     636      2176569 :     return *right_expr;
     637              :   }
     638              : 
     639      1066876 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     640              :   {
     641      1066876 :     rust_assert (right_expr != nullptr);
     642      1066876 :     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         3413 :   Expr::Kind get_expr_kind () const override
     649              :   {
     650         3413 :     return Expr::Kind::ArithmeticOrLogical;
     651              :   }
     652              : 
     653              : protected:
     654              :   /* Use covariance to implement clone function as returning this object rather
     655              :    * than base */
     656        13013 :   ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override
     657              :   {
     658        13013 :     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         3735 :   ExprType get_expr_type () const { return expr_type; }
     678              : 
     679              :   // Constructor requires pointers for polymorphism
     680         3561 :   ComparisonExpr (std::unique_ptr<Expr> left_value,
     681              :                   std::unique_ptr<Expr> right_value, ExprType comparison_kind,
     682              :                   location_t locus)
     683        10683 :     : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
     684         3561 :       expr_type (comparison_kind), right_expr (std::move (right_value))
     685         3561 :   {}
     686              :   // outer attributes not allowed
     687              : 
     688              :   // Copy constructor also calls OperatorExpr's protected constructor
     689         5473 :   ComparisonExpr (ComparisonExpr const &other)
     690        10946 :     : OperatorExpr (other), expr_type (other.expr_type),
     691         5473 :       right_expr (other.right_expr->clone_expr ())
     692         5473 :   {}
     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        79535 :   Expr &get_left_expr ()
     714              :   {
     715        79535 :     rust_assert (main_or_left_expr != nullptr);
     716        79535 :     return *main_or_left_expr;
     717              :   }
     718              : 
     719        20483 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     720              :   {
     721        20483 :     rust_assert (main_or_left_expr != nullptr);
     722        20483 :     return main_or_left_expr;
     723              :   }
     724              : 
     725              :   // TODO: is this better? Or is a "vis_block" better?
     726        79535 :   Expr &get_right_expr ()
     727              :   {
     728        79535 :     rust_assert (right_expr != nullptr);
     729        79535 :     return *right_expr;
     730              :   }
     731              : 
     732        20483 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     733              :   {
     734        20483 :     rust_assert (right_expr != nullptr);
     735        20483 :     return right_expr;
     736              :   }
     737              : 
     738              :   ExprType get_kind () { return expr_type; }
     739              : 
     740         3550 :   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         5473 :   ComparisonExpr *clone_expr_without_block_impl () const override
     748              :   {
     749         5473 :     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          403 :   LazyBooleanExpr (std::unique_ptr<Expr> left_bool_expr,
     767              :                    std::unique_ptr<Expr> right_bool_expr, ExprType expr_kind,
     768              :                    location_t locus)
     769         1209 :     : OperatorExpr (std::move (left_bool_expr), std::vector<Attribute> (),
     770              :                     locus),
     771          403 :       expr_type (expr_kind), right_expr (std::move (right_bool_expr))
     772          403 :   {}
     773              :   // outer attributes not allowed
     774              : 
     775              :   // Copy constructor also calls OperatorExpr's protected constructor
     776          439 :   LazyBooleanExpr (LazyBooleanExpr const &other)
     777          878 :     : OperatorExpr (other), expr_type (other.expr_type),
     778          439 :       right_expr (other.right_expr->clone_expr ())
     779          439 :   {}
     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          438 :   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         8049 :   Expr &get_left_expr ()
     804              :   {
     805         8049 :     rust_assert (main_or_left_expr != nullptr);
     806         8049 :     return *main_or_left_expr;
     807              :   }
     808              : 
     809         2098 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     810              :   {
     811         2098 :     rust_assert (main_or_left_expr != nullptr);
     812         2098 :     return main_or_left_expr;
     813              :   }
     814              : 
     815              :   // TODO: is this better? Or is a "vis_block" better?
     816         8049 :   Expr &get_right_expr ()
     817              :   {
     818         8049 :     rust_assert (right_expr != nullptr);
     819         8049 :     return *right_expr;
     820              :   }
     821              : 
     822         2098 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     823              :   {
     824         2098 :     rust_assert (right_expr != nullptr);
     825         2098 :     return right_expr;
     826              :   }
     827              : 
     828              :   ExprType get_kind () { return expr_type; }
     829              : 
     830          403 :   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          439 :   LazyBooleanExpr *clone_expr_without_block_impl () const override
     836              :   {
     837          439 :     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         5271 :   TypeCastExpr (std::unique_ptr<Expr> expr_to_cast,
     852              :                 std::unique_ptr<TypeNoBounds> type_to_cast_to, location_t locus)
     853        15813 :     : OperatorExpr (std::move (expr_to_cast), std::vector<Attribute> (), locus),
     854         5271 :       type_to_convert_to (std::move (type_to_cast_to))
     855         5271 :   {}
     856              :   // outer attributes not allowed
     857              : 
     858              :   // Copy constructor also requires calling protected constructor
     859        10626 :   TypeCastExpr (TypeCastExpr const &other)
     860        10626 :     : OperatorExpr (other),
     861        10626 :       type_to_convert_to (other.type_to_convert_to->clone_type_no_bounds ())
     862        10626 :   {}
     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       170967 :   Expr &get_casted_expr ()
     882              :   {
     883       170967 :     rust_assert (main_or_left_expr != nullptr);
     884       170967 :     return *main_or_left_expr;
     885              :   }
     886              : 
     887        29207 :   std::unique_ptr<Expr> &get_casted_expr_ptr ()
     888              :   {
     889        29207 :     rust_assert (main_or_left_expr != nullptr);
     890        29207 :     return main_or_left_expr;
     891              :   }
     892              : 
     893              :   // TODO: is this better? Or is a "vis_block" better?
     894       170967 :   TypeNoBounds &get_type_to_cast_to ()
     895              :   {
     896       170967 :     rust_assert (type_to_convert_to != nullptr);
     897       170967 :     return *type_to_convert_to;
     898              :   }
     899              : 
     900        29207 :   std::unique_ptr<TypeNoBounds> &get_type_to_cast_to_ptr ()
     901              :   {
     902        29207 :     rust_assert (type_to_convert_to != nullptr);
     903        29207 :     return type_to_convert_to;
     904              :   }
     905              : 
     906         5225 :   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        10626 :   TypeCastExpr *clone_expr_without_block_impl () const override
     912              :   {
     913        10626 :     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         2544 :   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         2544 :     : OperatorExpr (std::move (value_to_assign_to), std::move (outer_attribs),
     930              :                     locus),
     931         2544 :       right_expr (std::move (value_to_assign))
     932         2544 :   {}
     933              :   // outer attributes not allowed
     934              : 
     935              :   // Call OperatorExpr constructor in copy constructor, as well as clone
     936         7001 :   AssignmentExpr (AssignmentExpr const &other)
     937         7001 :     : OperatorExpr (other), right_expr (other.right_expr->clone_expr ())
     938         7001 :   {}
     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        60191 :   Expr &get_left_expr ()
     962              :   {
     963        60191 :     rust_assert (main_or_left_expr != nullptr);
     964        60191 :     return *main_or_left_expr;
     965              :   }
     966              : 
     967        11609 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
     968              :   {
     969        11609 :     rust_assert (main_or_left_expr != nullptr);
     970        11609 :     return main_or_left_expr;
     971              :   }
     972              : 
     973        11609 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
     974              :   {
     975        11609 :     rust_assert (right_expr != nullptr);
     976        11609 :     return right_expr;
     977              :   }
     978              : 
     979              :   // TODO: is this better? Or is a "vis_block" better?
     980        60191 :   Expr &get_right_expr ()
     981              :   {
     982        60191 :     rust_assert (right_expr != nullptr);
     983        60191 :     return *right_expr;
     984              :   }
     985              : 
     986         2507 :   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         7001 :   AssignmentExpr *clone_expr_without_block_impl () const override
     992              :   {
     993         7001 :     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          684 :   ExprType get_expr_type () const { return expr_type; }
    1013              : 
    1014              :   // Use pointers in constructor to enable polymorphism
    1015          685 :   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         2055 :     : OperatorExpr (std::move (value_to_assign_to), std::vector<Attribute> (),
    1019              :                     locus),
    1020          685 :       expr_type (expr_kind), right_expr (std::move (value_to_assign))
    1021          685 :   {}
    1022              :   // outer attributes not allowed
    1023              : 
    1024              :   // Have clone in copy constructor
    1025         2620 :   CompoundAssignmentExpr (CompoundAssignmentExpr const &other)
    1026         5240 :     : OperatorExpr (other), expr_type (other.expr_type),
    1027         2620 :       right_expr (other.right_expr->clone_expr ())
    1028         2620 :   {}
    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        20439 :   Expr &get_left_expr ()
    1050              :   {
    1051        20439 :     rust_assert (main_or_left_expr != nullptr);
    1052        20439 :     return *main_or_left_expr;
    1053              :   }
    1054              : 
    1055         3247 :   std::unique_ptr<Expr> &get_left_expr_ptr ()
    1056              :   {
    1057         3247 :     rust_assert (main_or_left_expr != nullptr);
    1058         3247 :     return main_or_left_expr;
    1059              :   }
    1060              : 
    1061              :   // TODO: is this better? Or is a "vis_block" better?
    1062        20439 :   Expr &get_right_expr ()
    1063              :   {
    1064        20439 :     rust_assert (right_expr != nullptr);
    1065        20439 :     return *right_expr;
    1066              :   }
    1067              : 
    1068         3247 :   std::unique_ptr<Expr> &get_right_expr_ptr ()
    1069              :   {
    1070         3247 :     rust_assert (right_expr != nullptr);
    1071         3247 :     return right_expr;
    1072              :   }
    1073              : 
    1074          685 :   Expr::Kind get_expr_kind () const override
    1075              :   {
    1076          685 :     return Expr::Kind::CompoundAssignment;
    1077              :   }
    1078              : 
    1079              : protected:
    1080              :   /* Use covariance to implement clone function as returning this object rather
    1081              :    * than base */
    1082         2620 :   CompoundAssignmentExpr *clone_expr_without_block_impl () const override
    1083              :   {
    1084         2620 :     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        10177 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1101              : 
    1102              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1103        12356 :   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          318 :   GroupedExpr (std::unique_ptr<Expr> parenthesised_expr,
    1111              :                std::vector<Attribute> inner_attribs,
    1112              :                std::vector<Attribute> outer_attribs, location_t locus)
    1113          636 :     : outer_attrs (std::move (outer_attribs)),
    1114          318 :       inner_attrs (std::move (inner_attribs)),
    1115          318 :       expr_in_parens (std::move (parenthesised_expr)), locus (locus)
    1116          318 :   {}
    1117              : 
    1118              :   // Copy constructor includes clone for expr_in_parens
    1119          555 :   GroupedExpr (GroupedExpr const &other)
    1120          555 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1121          555 :       inner_attrs (other.inner_attrs), locus (other.locus)
    1122              :   {
    1123              :     // guard to prevent null dereference (only required if error state)
    1124          555 :     if (other.expr_in_parens != nullptr)
    1125          555 :       expr_in_parens = other.expr_in_parens->clone_expr ();
    1126          555 :   }
    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          954 :   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         2163 :   bool is_marked_for_strip () const override
    1156              :   {
    1157         2163 :     return expr_in_parens == nullptr;
    1158              :   }
    1159              : 
    1160              :   // TODO: is this better? Or is a "vis_block" better?
    1161         8393 :   Expr &get_expr_in_parens ()
    1162              :   {
    1163         8393 :     rust_assert (expr_in_parens != nullptr);
    1164         8393 :     return *expr_in_parens;
    1165              :   }
    1166              : 
    1167         1842 :   std::unique_ptr<Expr> &get_expr_in_parens_ptr ()
    1168              :   {
    1169         1842 :     rust_assert (expr_in_parens != nullptr);
    1170         1842 :     return expr_in_parens;
    1171              :   }
    1172              : 
    1173          318 :   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          555 :   GroupedExpr *clone_expr_without_block_impl () const override
    1179              :   {
    1180          555 :     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          682 :   std::unique_ptr<ArrayElems> clone_array_elems () const
    1193              :   {
    1194          682 :     return std::unique_ptr<ArrayElems> (clone_array_elems_impl ());
    1195              :   }
    1196              : 
    1197              :   virtual std::string as_string () const = 0;
    1198              : 
    1199              :   virtual void accept_vis (ASTVisitor &vis) = 0;
    1200              : 
    1201          434 :   NodeId get_node_id () const { return node_id; }
    1202              : 
    1203              : protected:
    1204         1121 :   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          316 :   ArrayElemsValues (std::vector<std::unique_ptr<Expr>> elems, location_t locus)
    1220          316 :     : ArrayElems (), values (std::move (elems)), locus (locus)
    1221          316 :   {}
    1222              : 
    1223              :   // copy constructor with vector clone
    1224          527 :   ArrayElemsValues (ArrayElemsValues const &other)
    1225          527 :   {
    1226          527 :     values.reserve (other.values.size ());
    1227        14107 :     for (const auto &e : other.values)
    1228        13580 :       values.push_back (e->clone_expr ());
    1229          527 :   }
    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         6377 :   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          527 :   ArrayElemsValues *clone_array_elems_impl () const override
    1262              :   {
    1263          527 :     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        10560 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1358              : 
    1359              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1360        12237 :   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          439 :   ArrayExpr (std::unique_ptr<ArrayElems> array_elems,
    1369              :              std::vector<Attribute> inner_attribs,
    1370              :              std::vector<Attribute> outer_attribs, location_t locus)
    1371          878 :     : outer_attrs (std::move (outer_attribs)),
    1372          439 :       inner_attrs (std::move (inner_attribs)),
    1373          439 :       internal_elements (std::move (array_elems)), locus (locus)
    1374              :   {
    1375          439 :     rust_assert (internal_elements != nullptr);
    1376          439 :   }
    1377              : 
    1378              :   // Copy constructor requires cloning ArrayElems for polymorphism to hold
    1379          682 :   ArrayExpr (ArrayExpr const &other)
    1380          682 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1381          682 :       inner_attrs (other.inner_attrs), locus (other.locus),
    1382         1364 :       marked_for_strip (other.marked_for_strip)
    1383              :   {
    1384          682 :     internal_elements = other.internal_elements->clone_array_elems ();
    1385          682 :     rust_assert (internal_elements != nullptr);
    1386          682 :   }
    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          889 :   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         1639 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    1414              : 
    1415              :   // TODO: is this better? Or is a "vis_block" better?
    1416         8929 :   std::unique_ptr<ArrayElems> &get_array_elems ()
    1417              :   {
    1418         8929 :     rust_assert (internal_elements != nullptr);
    1419         8929 :     return internal_elements;
    1420              :   }
    1421              : 
    1422          434 :   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          682 :   ArrayExpr *clone_expr_without_block_impl () const override
    1428              :   {
    1429          682 :     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          303 :   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          606 :     : outer_attrs (std::move (outer_attribs)),
    1452          303 :       array_expr (std::move (array_expr)),
    1453          303 :       index_expr (std::move (array_index_expr)), locus (locus)
    1454          303 :   {}
    1455              : 
    1456              :   // Copy constructor requires special cloning due to unique_ptr
    1457          338 :   ArrayIndexExpr (ArrayIndexExpr const &other)
    1458          338 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1459          338 :       locus (other.locus)
    1460              :   {
    1461              :     // guard to prevent null dereference (only required if error state)
    1462          338 :     if (other.array_expr != nullptr)
    1463          338 :       array_expr = other.array_expr->clone_expr ();
    1464          338 :     if (other.index_expr != nullptr)
    1465          338 :       index_expr = other.index_expr->clone_expr ();
    1466          338 :   }
    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          696 :   location_t get_locus () const override final { return locus; }
    1493              : 
    1494              :   void accept_vis (ASTVisitor &vis) override;
    1495              : 
    1496              :   // Invalid if either expr is null, so base stripping on that.
    1497            1 :   void mark_for_strip () override
    1498              :   {
    1499            1 :     array_expr = nullptr;
    1500            1 :     index_expr = nullptr;
    1501            1 :   }
    1502         1405 :   bool is_marked_for_strip () const override
    1503              :   {
    1504         1405 :     return array_expr == nullptr && index_expr == nullptr;
    1505              :   }
    1506              : 
    1507              :   // TODO: is this better? Or is a "vis_block" better?
    1508         6368 :   Expr &get_array_expr ()
    1509              :   {
    1510         6368 :     rust_assert (array_expr != nullptr);
    1511         6368 :     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         6367 :   Expr &get_index_expr ()
    1522              :   {
    1523         6367 :     rust_assert (index_expr != nullptr);
    1524         6367 :     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         9302 :   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          338 :   ArrayIndexExpr *clone_expr_without_block_impl () const override
    1547              :   {
    1548          338 :     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        12614 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1568              : 
    1569              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1570        15792 :   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          590 :   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         1180 :     : outer_attrs (std::move (outer_attribs)),
    1581          590 :       inner_attrs (std::move (inner_attribs)),
    1582          590 :       tuple_elems (std::move (tuple_elements)), locus (locus)
    1583          590 :   {}
    1584              : 
    1585              :   // copy constructor with vector clone
    1586         1221 :   TupleExpr (TupleExpr const &other)
    1587         1221 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1588         1221 :       inner_attrs (other.inner_attrs), locus (other.locus),
    1589         2442 :       marked_for_strip (other.marked_for_strip)
    1590              :   {
    1591         1221 :     tuple_elems.reserve (other.tuple_elems.size ());
    1592         3441 :     for (const auto &e : other.tuple_elems)
    1593         2220 :       tuple_elems.push_back (e->clone_expr ());
    1594         1221 :   }
    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         1171 :   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         2434 :   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        12664 :   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          570 :   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         1221 :   TupleExpr *clone_expr_without_block_impl () const override
    1642              :   {
    1643         1221 :     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          963 :   TupleIndex get_tuple_index () const { return tuple_index; }
    1665              : 
    1666          939 :   TupleIndexExpr (std::unique_ptr<Expr> tuple_expr, TupleIndex index,
    1667              :                   std::vector<Attribute> outer_attribs, location_t locus)
    1668         1878 :     : outer_attrs (std::move (outer_attribs)),
    1669          939 :       tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus),
    1670          939 :       to_strip (false)
    1671          939 :   {}
    1672              : 
    1673              :   // Copy constructor requires a clone for tuple_expr
    1674         1428 :   TupleIndexExpr (TupleIndexExpr const &other)
    1675         1428 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    1676         1428 :       tuple_index (other.tuple_index), locus (other.locus),
    1677         1428 :       to_strip (other.to_strip)
    1678              :   {
    1679              :     // guard to prevent null dereference (only required if error state)
    1680         1428 :     if (other.tuple_expr != nullptr)
    1681         1428 :       tuple_expr = other.tuple_expr->clone_expr ();
    1682         1428 :   }
    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         2006 :   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         4740 :   bool is_marked_for_strip () const override { return to_strip; }
    1713              : 
    1714              :   // TODO: is this better? Or is a "vis_block" better?
    1715        22209 :   Expr &get_tuple_expr ()
    1716              :   {
    1717        22209 :     rust_assert (tuple_expr != nullptr);
    1718        22209 :     return *tuple_expr;
    1719              :   }
    1720              : 
    1721         4682 :   std::unique_ptr<Expr> &get_tuple_expr_ptr ()
    1722              :   {
    1723         4682 :     rust_assert (tuple_expr != nullptr);
    1724         4682 :     return tuple_expr;
    1725              :   }
    1726              : 
    1727              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    1728        32847 :   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          905 :   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         1428 :   TupleIndexExpr *clone_expr_without_block_impl () const override
    1741              :   {
    1742         1428 :     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         1454 :   StructExpr (PathInExpression struct_path,
    1755              :               std::vector<Attribute> outer_attribs)
    1756         2908 :     : outer_attrs (std::move (outer_attribs)),
    1757         1454 :       struct_name (std::move (struct_path))
    1758         1454 :   {}
    1759              : 
    1760              : public:
    1761            0 :   const PathInExpression &get_struct_name () const { return struct_name; }
    1762        37737 :   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         6194 :   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        44279 :   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         1453 :   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        37607 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    1796              : 
    1797              :   // Constructor has to call protected constructor of base class
    1798         1454 :   StructExprStruct (PathInExpression struct_path,
    1799              :                     std::vector<Attribute> inner_attribs,
    1800              :                     std::vector<Attribute> outer_attribs, location_t locus)
    1801         1454 :     : StructExpr (std::move (struct_path), std::move (outer_attribs)),
    1802         1454 :       inner_attrs (std::move (inner_attribs)), locus (locus)
    1803         1454 :   {}
    1804              : 
    1805         3280 :   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         1435 :   StructBase (std::unique_ptr<Expr> base_struct_ptr, location_t locus)
    1828         1435 :     : base_struct (std::move (base_struct_ptr)), locus (locus)
    1829              :   {}
    1830              : 
    1831              :   // Copy constructor requires clone
    1832         1699 :   StructBase (StructBase const &other)
    1833         1699 :   {
    1834              :     /* HACK: gets around base_struct pointer being null (e.g. if no struct base
    1835              :      * exists) */
    1836         1699 :     if (other.base_struct != nullptr)
    1837           63 :       base_struct = other.base_struct->clone_expr ();
    1838         1699 :   }
    1839              : 
    1840              :   // Destructor
    1841         2774 :   ~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         1372 :   StructBase (StructBase &&other) = default;
    1857           63 :   StructBase &operator= (StructBase &&other) = default;
    1858              : 
    1859              :   // Returns a null expr-ed StructBase - error state
    1860         1372 :   static StructBase error () { return StructBase (nullptr, UNDEF_LOCATION); }
    1861              : 
    1862              :   // Returns whether StructBase is in error state
    1863        36185 :   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         2975 :   std::unique_ptr<StructExprField> clone_struct_expr_field () const
    1892              :   {
    1893         2975 :     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         2552 :   NodeId get_node_id () const { return node_id; }
    1903              : 
    1904         2667 :   const std::vector<AST::Attribute> &get_outer_attrs () const
    1905              :   {
    1906         2667 :     return outer_attrs;
    1907              :   }
    1908              : 
    1909        10801 :   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         5006 :   StructExprField (AST::AttrVec outer_attrs)
    1919         5006 :     : outer_attrs (std::move (outer_attrs)),
    1920         5006 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
    1921         5006 :   {}
    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         2123 :   StructExprFieldWithVal (std::unique_ptr<Expr> field_value,
    1965              :                           AST::AttrVec outer_attrs)
    1966         2123 :     : StructExprField (std::move (outer_attrs)), value (std::move (field_value))
    1967         2123 :   {}
    1968              : 
    1969              :   // Copy constructor requires clone
    1970         2667 :   StructExprFieldWithVal (StructExprFieldWithVal const &other)
    1971         2667 :     : StructExprField (other.get_outer_attrs ()),
    1972         2667 :       value (other.value->clone_expr ())
    1973         2667 :   {}
    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        46176 :   Expr &get_value ()
    1993              :   {
    1994        46176 :     rust_assert (value != nullptr);
    1995        46176 :     return *value;
    1996              :   }
    1997              : 
    1998        11478 :   std::unique_ptr<Expr> &get_value_ptr ()
    1999              :   {
    2000        11478 :     rust_assert (value != nullptr);
    2001        11478 :     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         2044 :   StructExprFieldIdentifierValue (Identifier field_identifier,
    2013              :                                   std::unique_ptr<Expr> field_value,
    2014              :                                   AST::AttrVec outer_attrs, location_t locus)
    2015         2044 :     : StructExprFieldWithVal (std::move (field_value), std::move (outer_attrs)),
    2016         2044 :       field_name (std::move (field_identifier)), locus (locus)
    2017         2044 :   {}
    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         4798 :   std::string get_field_name () const { return field_name.as_string (); }
    2031              : 
    2032         4475 :   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         2623 :   StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override
    2038              :   {
    2039         2623 :     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        36185 :   bool has_struct_base () const { return !struct_base.is_invalid (); }
    2087              : 
    2088              :   // Constructor for StructExprStructFields when no struct base is used
    2089         1372 :   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         1372 :     : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
    2096              :                         std::move (outer_attribs), locus),
    2097         1372 :       fields (std::move (expr_fields)), struct_base (std::move (base_struct))
    2098         1372 :   {}
    2099              : 
    2100              :   // copy constructor with vector clone
    2101         1699 :   StructExprStructFields (StructExprStructFields const &other)
    2102         1699 :     : StructExprStruct (other), struct_base (other.struct_base)
    2103              :   {
    2104         1699 :     fields.reserve (other.fields.size ());
    2105         4674 :     for (const auto &e : other.fields)
    2106         2975 :       fields.push_back (e->clone_struct_expr_field ());
    2107         1699 :   }
    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         6047 :   std::vector<std::unique_ptr<StructExprField>> &get_fields ()
    2130              :   {
    2131        36185 :     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         1699 :   StructExprStructFields *clone_expr_without_block_impl () const override
    2145              :   {
    2146         1699 :     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        12776 :   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        25552 :     : outer_attrs (std::move (outer_attribs)),
    2202        12776 :       function (std::move (function_expr)),
    2203        12776 :       params (std::move (function_params)), locus (locus)
    2204        12776 :   {}
    2205              : 
    2206              :   // copy constructor requires clone
    2207        25495 :   CallExpr (CallExpr const &other)
    2208        25495 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2209        25495 :       locus (other.locus)
    2210              :   {
    2211              :     // guard to prevent null dereference (only required if error state)
    2212        25495 :     if (other.function != nullptr)
    2213        25495 :       function = other.function->clone_expr ();
    2214              : 
    2215        25495 :     params.reserve (other.params.size ());
    2216        54582 :     for (const auto &e : other.params)
    2217        29087 :       params.push_back (e->clone_expr ());
    2218        25495 :   }
    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        26229 :   location_t get_locus () const override final { return locus; }
    2248              : 
    2249              :   void accept_vis (ASTVisitor &vis) override;
    2250              : 
    2251              :   // Invalid if function expr is null, so base stripping on that.
    2252            2 :   void mark_for_strip () override { function = nullptr; }
    2253        74121 :   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       388313 :   std::vector<std::unique_ptr<Expr>> &get_params () { return params; }
    2261              : 
    2262              :   // TODO: is this better? Or is a "vis_block" better?
    2263       317199 :   Expr &get_function_expr ()
    2264              :   {
    2265       317199 :     rust_assert (function != nullptr);
    2266       317199 :     return *function;
    2267              :   }
    2268              : 
    2269        71116 :   std::unique_ptr<Expr> &get_function_expr_ptr () { return function; }
    2270              : 
    2271              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2272       475011 :   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        12674 :   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        25495 :   CallExpr *clone_expr_without_block_impl () const override
    2285              :   {
    2286        25495 :     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         3096 :   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         6192 :     : outer_attrs (std::move (outer_attribs)),
    2307         3096 :       receiver (std::move (call_receiver)),
    2308         3096 :       method_name (std::move (method_path)), params (std::move (method_params)),
    2309         3096 :       locus (locus)
    2310         3096 :   {}
    2311              : 
    2312              :   // copy constructor required due to cloning
    2313        10430 :   MethodCallExpr (MethodCallExpr const &other)
    2314        10430 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2315        10430 :       method_name (other.method_name), locus (other.locus)
    2316              :   {
    2317              :     // guard to prevent null dereference (only required if error state)
    2318        10430 :     if (other.receiver != nullptr)
    2319        10430 :       receiver = other.receiver->clone_expr ();
    2320              : 
    2321        10430 :     params.reserve (other.params.size ());
    2322        19161 :     for (const auto &e : other.params)
    2323         8731 :       params.push_back (e->clone_expr ());
    2324        10430 :   }
    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         6570 :   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        19969 :   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        92973 :   std::vector<std::unique_ptr<Expr>> &get_params () { return params; }
    2365              : 
    2366              :   // TODO: is this better? Or is a "vis_block" better?
    2367        77275 :   Expr &get_receiver_expr ()
    2368              :   {
    2369        77275 :     rust_assert (receiver != nullptr);
    2370        77275 :     return *receiver;
    2371              :   }
    2372              : 
    2373        15698 :   std::unique_ptr<Expr> &get_receiver_expr_ptr ()
    2374              :   {
    2375        15698 :     rust_assert (receiver != nullptr);
    2376        15698 :     return receiver;
    2377              :   }
    2378              : 
    2379              :   const PathExprSegment &get_method_name () const { return method_name; }
    2380        92973 :   PathExprSegment &get_method_name () { return method_name; }
    2381              : 
    2382              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2383       113850 :   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         3052 :   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        10430 :   MethodCallExpr *clone_expr_without_block_impl () const override
    2396              :   {
    2397        10430 :     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         5409 :   FieldAccessExpr (std::unique_ptr<Expr> field_access_receiver,
    2414              :                    Identifier field_name, std::vector<Attribute> outer_attribs,
    2415              :                    location_t locus)
    2416        10818 :     : outer_attrs (std::move (outer_attribs)),
    2417         5409 :       receiver (std::move (field_access_receiver)),
    2418         5409 :       field (std::move (field_name)), locus (locus)
    2419         5409 :   {}
    2420              : 
    2421              :   // Copy constructor required due to unique_ptr cloning
    2422        20462 :   FieldAccessExpr (FieldAccessExpr const &other)
    2423        20462 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    2424        20462 :       field (other.field), locus (other.locus)
    2425              :   {
    2426              :     // guard to prevent null dereference (only required if error state)
    2427        20462 :     if (other.receiver != nullptr)
    2428        20462 :       receiver = other.receiver->clone_expr ();
    2429        20462 :   }
    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        12996 :   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        37153 :   bool is_marked_for_strip () const override { return receiver == nullptr; }
    2459              : 
    2460              :   // TODO: is this better? Or is a "vis_block" better?
    2461       134774 :   Expr &get_receiver_expr ()
    2462              :   {
    2463       134774 :     rust_assert (receiver != nullptr);
    2464       134774 :     return *receiver;
    2465              :   }
    2466              : 
    2467        22457 :   std::unique_ptr<Expr> &get_receiver_expr_ptr ()
    2468              :   {
    2469        22457 :     rust_assert (receiver != nullptr);
    2470        22457 :     return receiver;
    2471              :   }
    2472              : 
    2473         5179 :   Identifier get_field_name () const { return field; }
    2474              : 
    2475              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    2476       197785 :   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         5056 :   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        20462 :   FieldAccessExpr *clone_expr_without_block_impl () const override
    2489              :   {
    2490        20462 :     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       762080 :   bool has_tail_expr () const { return expr != nullptr; }
    2731              : 
    2732        23867 :   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        47734 :     : outer_attrs (std::move (outer_attribs)),
    2739        23867 :       inner_attrs (std::move (inner_attribs)),
    2740        23867 :       statements (std::move (block_statements)), expr (std::move (block_expr)),
    2741        23867 :       label (std::move (label)), start_locus (start_locus),
    2742        23867 :       end_locus (end_locus)
    2743        23867 :   {}
    2744              : 
    2745              :   // Copy constructor with clone
    2746        54212 :   BlockExpr (BlockExpr const &other)
    2747        54212 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    2748        54212 :       inner_attrs (other.inner_attrs), label (other.label),
    2749        54212 :       start_locus (other.start_locus), end_locus (other.end_locus),
    2750       108424 :       marked_for_strip (other.marked_for_strip)
    2751              :   {
    2752              :     // guard to protect from null pointer dereference
    2753        54212 :     if (other.expr != nullptr)
    2754        45284 :       expr = other.expr->clone_expr ();
    2755              : 
    2756        54212 :     statements.reserve (other.statements.size ());
    2757        90347 :     for (const auto &e : other.statements)
    2758        36135 :       statements.push_back (e->clone_stmt ());
    2759        54212 :   }
    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        47600 :   std::unique_ptr<BlockExpr> clone_block_expr () const
    2790              :   {
    2791        47600 :     return std::unique_ptr<BlockExpr> (clone_block_expr_impl ());
    2792              :   }
    2793              : 
    2794         4831 :   location_t get_locus () const override final { return start_locus; }
    2795              : 
    2796        23265 :   location_t get_start_locus () const { return start_locus; }
    2797        23265 :   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       101626 :   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       613954 :   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       646901 :   std::vector<std::unique_ptr<Stmt>> &get_statements () { return statements; }
    2816              : 
    2817              :   // TODO: is this better? Or is a "vis_block" better?
    2818       363181 :   Expr &get_tail_expr ()
    2819              :   {
    2820       363181 :     rust_assert (has_tail_expr ());
    2821       363181 :     return *expr;
    2822              :   }
    2823              : 
    2824        98109 :   std::unique_ptr<Expr> &get_tail_expr_ptr ()
    2825              :   {
    2826        98109 :     rust_assert (has_tail_expr ());
    2827        98109 :     return expr;
    2828              :   }
    2829              : 
    2830        40905 :   std::unique_ptr<Expr> take_tail_expr ()
    2831              :   {
    2832        40905 :     rust_assert (has_tail_expr ());
    2833        40905 :     return std::move (expr);
    2834              :   }
    2835              : 
    2836        40905 :   void set_tail_expr (std::unique_ptr<Expr> expr)
    2837              :   {
    2838        40905 :     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       675814 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    2850              : 
    2851         1300 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    2852              :   {
    2853         1300 :     outer_attrs = std::move (new_attrs);
    2854         1300 :   }
    2855              : 
    2856       481541 :   bool has_label () { return label.has_value (); }
    2857           57 :   LoopLabel &get_label () { return label.value (); }
    2858              : 
    2859         1554 :   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         6612 :   BlockExpr *clone_expr_with_block_impl () const final override
    2865              :   {
    2866         6612 :     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        54212 :   /*virtual*/ BlockExpr *clone_block_expr_impl () const
    2872              :   {
    2873        54212 :     return new BlockExpr (*this);
    2874              :   }
    2875              : };
    2876              : 
    2877         4059 : class AnonConst : public ExprWithBlock
    2878              : {
    2879              : public:
    2880              :   enum class Kind
    2881              :   {
    2882              :     Explicit,
    2883              :     DeferredInference,
    2884              :   };
    2885              : 
    2886          680 :   AnonConst (std::unique_ptr<Expr> &&expr, location_t locus = UNKNOWN_LOCATION)
    2887         1360 :     : ExprWithBlock (), locus (locus), kind (Kind::Explicit),
    2888          680 :       expr (std::move (expr))
    2889              :   {
    2890          680 :     rust_assert (this->expr.value ());
    2891          680 :   }
    2892              : 
    2893           13 :   AnonConst (location_t locus = UNKNOWN_LOCATION)
    2894           26 :     : ExprWithBlock (), locus (locus), kind (Kind::DeferredInference),
    2895           13 :       expr (tl::nullopt)
    2896              :   {}
    2897              : 
    2898         4529 :   AnonConst (const AnonConst &other)
    2899         4529 :   {
    2900         4529 :     node_id = other.node_id;
    2901         4529 :     locus = other.locus;
    2902         4529 :     kind = other.kind;
    2903              : 
    2904         4529 :     if (other.expr)
    2905         4440 :       expr = other.expr.value ()->clone_expr ();
    2906         4529 :   }
    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         1384 :   location_t get_locus () const override { return locus; }
    2925              : 
    2926        13305 :   Expr &get_inner_expr ()
    2927              :   {
    2928        13305 :     rust_assert (expr.has_value ());
    2929        13305 :     return *expr.value ();
    2930              :   }
    2931              : 
    2932         3883 :   std::unique_ptr<Expr> &get_inner_expr_ptr ()
    2933              :   {
    2934         3883 :     rust_assert (expr.has_value ());
    2935         3883 :     return expr.value ();
    2936              :   }
    2937              : 
    2938          689 :   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        19800 :     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         4922 :   bool is_marked_for_strip () const override { return false; }
    2955              : 
    2956              :   void accept_vis (ASTVisitor &vis) override;
    2957              : 
    2958        17462 :   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         2387 :   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         3162 :   bool has_break_expr () const { return break_expr.has_value (); }
    3207              : 
    3208              :   // Constructor for a break expression
    3209          103 :   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          309 :     : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)),
    3213          103 :       break_expr (std::move (expr_in_break)), locus (locus)
    3214              :   {
    3215          103 :     if (this->has_break_expr ())
    3216           26 :       rust_assert (this->break_expr != nullptr);
    3217          103 :   }
    3218              : 
    3219              :   // Copy constructor defined to use clone for unique pointer
    3220          118 :   BreakExpr (BreakExpr const &other)
    3221          118 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3222          118 :       label (other.label), locus (other.locus),
    3223          236 :       marked_for_strip (other.marked_for_strip)
    3224              :   {
    3225          118 :     if (other.has_break_expr ())
    3226           26 :       break_expr = other.get_break_expr_unchecked ().clone_expr ();
    3227          118 :   }
    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          209 :   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          672 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3258              : 
    3259              :   // TODO: is this better? Or is a "vis_block" better?
    3260          932 :   Expr &get_break_expr_unchecked ()
    3261              :   {
    3262          932 :     rust_assert (has_break_expr ());
    3263          932 :     return *break_expr.value ();
    3264              :   }
    3265              : 
    3266           26 :   const Expr &get_break_expr_unchecked () const
    3267              :   {
    3268           26 :     rust_assert (has_break_expr ());
    3269           26 :     return *break_expr.value ();
    3270              :   }
    3271              : 
    3272          130 :   std::unique_ptr<Expr> &get_break_expr_ptr_unchecked ()
    3273              :   {
    3274          130 :     rust_assert (has_break_expr ());
    3275          130 :     return break_expr.value ();
    3276              :   }
    3277              : 
    3278              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3279         3613 :   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          627 :   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          101 :   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          118 :   BreakExpr *clone_expr_without_block_impl () const override
    3298              :   {
    3299          118 :     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              : // Box expression AST node representation
    3755              : class BoxExpr : public ExprWithoutBlock
    3756              : {
    3757              :   std::unique_ptr<Expr> expr;
    3758              :   std::vector<Attribute> outer_attrs;
    3759              :   location_t locus;
    3760              : 
    3761              : public:
    3762            5 :   BoxExpr (std::unique_ptr<Expr> expr, std::vector<Attribute> outer_attrs,
    3763              :            location_t locus)
    3764            5 :     : expr (std::move (expr)), outer_attrs (outer_attrs), locus (locus)
    3765            5 :   {}
    3766              : 
    3767              :   // Copy constructor with clone
    3768            5 :   BoxExpr (BoxExpr const &other)
    3769            5 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3770            5 :       locus (other.locus)
    3771              :   {
    3772              :     // guard to protect from null pointer dereference
    3773            5 :     if (other.expr != nullptr)
    3774            5 :       expr = other.expr->clone_expr ();
    3775            5 :   }
    3776              : 
    3777              :   BoxExpr &operator= (BoxExpr const &other)
    3778              :   {
    3779              :     ExprWithoutBlock::operator= (other);
    3780              :     locus = other.locus;
    3781              :     outer_attrs = other.outer_attrs;
    3782              : 
    3783              :     // guard to protect from null pointer dereference
    3784              :     if (other.expr != nullptr)
    3785              :       expr = other.expr->clone_expr ();
    3786              :     else
    3787              :       expr = nullptr;
    3788              : 
    3789              :     return *this;
    3790              :   }
    3791              : 
    3792              :   // move constructors
    3793              :   BoxExpr (BoxExpr &&other) = default;
    3794              :   BoxExpr &operator= (BoxExpr &&other) = default;
    3795              : 
    3796           10 :   location_t get_locus () const override final { return locus; }
    3797              : 
    3798              :   void accept_vis (ASTVisitor &vis) override;
    3799              : 
    3800            0 :   void mark_for_strip () override { expr = nullptr; }
    3801           16 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    3802              : 
    3803              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3804           83 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3805              : 
    3806            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3807              :   {
    3808            0 :     outer_attrs = std::move (new_attrs);
    3809            0 :   }
    3810              : 
    3811              :   std::string as_string () const override;
    3812              : 
    3813           64 :   Expr &get_boxed_expr ()
    3814              :   {
    3815           64 :     rust_assert (expr != nullptr);
    3816           64 :     return *expr;
    3817              :   }
    3818              : 
    3819           20 :   std::unique_ptr<Expr> &get_boxed_expr_ptr ()
    3820              :   {
    3821           20 :     rust_assert (expr != nullptr);
    3822           20 :     return expr;
    3823              :   }
    3824              : 
    3825            4 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Box; }
    3826              : 
    3827              : protected:
    3828              :   /* Use covariance to implement clone function as returning this object rather
    3829              :    * than base */
    3830            5 :   BoxExpr *clone_expr_without_block_impl () const override
    3831              :   {
    3832            5 :     return new BoxExpr (*this);
    3833              :   }
    3834              : };
    3835              : 
    3836              : // Return expression AST node representation
    3837              : class ReturnExpr : public ExprWithoutBlock
    3838              : {
    3839              :   std::vector<Attribute> outer_attrs;
    3840              :   tl::optional<std::unique_ptr<Expr>> return_expr;
    3841              :   location_t locus;
    3842              : 
    3843              :   // TODO: find another way to store this to save memory?
    3844              :   bool marked_for_strip = false;
    3845              : 
    3846              : public:
    3847              :   std::string as_string () const override;
    3848              : 
    3849              :   /* Returns whether the object has an expression returned (i.e. not void return
    3850              :    * type). */
    3851        15517 :   bool has_returned_expr () const { return return_expr.has_value (); }
    3852              : 
    3853              :   // Constructor for ReturnExpr.
    3854          550 :   ReturnExpr (tl::optional<std::unique_ptr<Expr>> returned_expr,
    3855              :               std::vector<Attribute> outer_attribs, location_t locus)
    3856         1100 :     : outer_attrs (std::move (outer_attribs)),
    3857          550 :       return_expr (std::move (returned_expr)), locus (locus)
    3858          550 :   {}
    3859              : 
    3860              :   // Copy constructor with clone
    3861          667 :   ReturnExpr (ReturnExpr const &other)
    3862          667 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    3863          667 :       locus (other.locus), marked_for_strip (other.marked_for_strip)
    3864              :   {
    3865              :     // guard to protect from null pointer dereference
    3866          667 :     if (other.return_expr)
    3867          606 :       return_expr = other.return_expr.value ()->clone_expr ();
    3868          667 :   }
    3869              : 
    3870              :   // Overloaded assignment operator to clone return_expr pointer
    3871              :   ReturnExpr &operator= (ReturnExpr const &other)
    3872              :   {
    3873              :     ExprWithoutBlock::operator= (other);
    3874              :     locus = other.locus;
    3875              :     marked_for_strip = other.marked_for_strip;
    3876              :     outer_attrs = other.outer_attrs;
    3877              : 
    3878              :     // guard to protect from null pointer dereference
    3879              :     if (other.return_expr)
    3880              :       return_expr = other.return_expr.value ()->clone_expr ();
    3881              :     else
    3882              :       return_expr = tl::nullopt;
    3883              : 
    3884              :     return *this;
    3885              :   }
    3886              : 
    3887              :   // move constructors
    3888              :   ReturnExpr (ReturnExpr &&other) = default;
    3889              :   ReturnExpr &operator= (ReturnExpr &&other) = default;
    3890              : 
    3891         1078 :   location_t get_locus () const override final { return locus; }
    3892              : 
    3893              :   void accept_vis (ASTVisitor &vis) override;
    3894              : 
    3895              :   // Can't think of any invalid invariants, so store boolean.
    3896            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3897         3132 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3898              : 
    3899              :   // TODO: is this better? Or is a "vis_block" better?
    3900        11676 :   Expr &get_returned_expr ()
    3901              :   {
    3902        11676 :     rust_assert (return_expr);
    3903        11676 :     return *return_expr.value ();
    3904              :   }
    3905              : 
    3906            0 :   const Expr &get_returned_expr () const
    3907              :   {
    3908            0 :     rust_assert (return_expr);
    3909            0 :     return *return_expr.value ();
    3910              :   }
    3911              : 
    3912         2551 :   std::unique_ptr<Expr> &get_returned_expr_ptr ()
    3913              :   {
    3914         2551 :     rust_assert (return_expr);
    3915         2551 :     return return_expr.value ();
    3916              :   }
    3917              : 
    3918              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3919        18146 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3920              : 
    3921            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3922              :   {
    3923            0 :     outer_attrs = std::move (new_attrs);
    3924            0 :   }
    3925              : 
    3926          545 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Return; }
    3927              : 
    3928              : protected:
    3929              :   /* Use covariance to implement clone function as returning this object rather
    3930              :    * than base */
    3931          667 :   ReturnExpr *clone_expr_without_block_impl () const override
    3932              :   {
    3933          667 :     return new ReturnExpr (*this);
    3934              :   }
    3935              : };
    3936              : 
    3937              : // Try expression AST node representation
    3938              : class TryExpr : public ExprWithBlock
    3939              : {
    3940              :   std::vector<Attribute> outer_attrs;
    3941              :   std::unique_ptr<BlockExpr> block_expr;
    3942              :   location_t locus;
    3943              : 
    3944              :   // TODO: find another way to store this to save memory?
    3945              :   bool marked_for_strip = false;
    3946              : 
    3947              : public:
    3948              :   std::string as_string () const override;
    3949              : 
    3950              :   // Constructor for ReturnExpr.
    3951            1 :   TryExpr (std::unique_ptr<BlockExpr> block_expr,
    3952              :            std::vector<Attribute> outer_attribs, location_t locus)
    3953            2 :     : outer_attrs (std::move (outer_attribs)),
    3954            1 :       block_expr (std::move (block_expr)), locus (locus)
    3955              :   {
    3956            1 :     rust_assert (this->block_expr);
    3957            1 :   }
    3958              : 
    3959              :   // Copy constructor with clone
    3960            2 :   TryExpr (TryExpr const &other)
    3961            2 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    3962            2 :       block_expr (other.block_expr->clone_block_expr ()), locus (other.locus),
    3963            2 :       marked_for_strip (other.marked_for_strip)
    3964            2 :   {}
    3965              : 
    3966              :   // Overloaded assignment operator to clone return_expr pointer
    3967              :   TryExpr &operator= (TryExpr const &other)
    3968              :   {
    3969              :     ExprWithBlock::operator= (other);
    3970              :     locus = other.locus;
    3971              :     marked_for_strip = other.marked_for_strip;
    3972              :     outer_attrs = other.outer_attrs;
    3973              : 
    3974              :     block_expr = other.block_expr->clone_block_expr ();
    3975              : 
    3976              :     return *this;
    3977              :   }
    3978              : 
    3979              :   // move constructors
    3980              :   TryExpr (TryExpr &&other) = default;
    3981              :   TryExpr &operator= (TryExpr &&other) = default;
    3982              : 
    3983            1 :   location_t get_locus () const override final { return locus; }
    3984              : 
    3985              :   void accept_vis (ASTVisitor &vis) override;
    3986              : 
    3987              :   // Can't think of any invalid invariants, so store boolean.
    3988            0 :   void mark_for_strip () override { marked_for_strip = true; }
    3989            4 :   bool is_marked_for_strip () const override { return marked_for_strip; }
    3990              : 
    3991              :   // TODO: is this better? Or is a "vis_block" better?
    3992           10 :   BlockExpr &get_block_expr () { return *block_expr; }
    3993            4 :   std::unique_ptr<BlockExpr> &get_block_expr_ptr () { return block_expr; }
    3994              : 
    3995              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    3996           13 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    3997              : 
    3998            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    3999              :   {
    4000            0 :     outer_attrs = std::move (new_attrs);
    4001            0 :   }
    4002              : 
    4003            2 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Try; }
    4004              : 
    4005              : protected:
    4006              :   /* Use covariance to implement clone function as returning this object rather
    4007              :    * than base */
    4008            1 :   TryExpr *clone_expr_with_block_impl () const override
    4009              :   {
    4010            1 :     return new TryExpr (*this);
    4011              :   }
    4012              : };
    4013              : 
    4014              : // Forward decl - defined in rust-macro.h
    4015              : class MacroInvocation;
    4016              : 
    4017              : // An unsafe block AST node
    4018              : class UnsafeBlockExpr : public ExprWithBlock
    4019              : {
    4020              :   std::vector<Attribute> outer_attrs;
    4021              :   // Or just have it extend BlockExpr
    4022              :   std::unique_ptr<BlockExpr> expr;
    4023              :   location_t locus;
    4024              : 
    4025              : public:
    4026              :   std::string as_string () const override;
    4027              : 
    4028         3690 :   UnsafeBlockExpr (std::unique_ptr<BlockExpr> block_expr,
    4029              :                    std::vector<Attribute> outer_attribs, location_t locus)
    4030         7380 :     : outer_attrs (std::move (outer_attribs)), expr (std::move (block_expr)),
    4031         3690 :       locus (locus)
    4032         3690 :   {}
    4033              : 
    4034              :   // Copy constructor with clone
    4035         8220 :   UnsafeBlockExpr (UnsafeBlockExpr const &other)
    4036         8220 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4037         8220 :       locus (other.locus)
    4038              :   {
    4039              :     // guard to prevent null dereference (only required if error state)
    4040         8220 :     if (other.expr != nullptr)
    4041         8220 :       expr = other.expr->clone_block_expr ();
    4042         8220 :   }
    4043              : 
    4044              :   // Overloaded assignment operator to clone
    4045              :   UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other)
    4046              :   {
    4047              :     ExprWithBlock::operator= (other);
    4048              :     locus = other.locus;
    4049              :     outer_attrs = other.outer_attrs;
    4050              : 
    4051              :     // guard to prevent null dereference (only required if error state)
    4052              :     if (other.expr != nullptr)
    4053              :       expr = other.expr->clone_block_expr ();
    4054              :     else
    4055              :       expr = nullptr;
    4056              : 
    4057              :     return *this;
    4058              :   }
    4059              : 
    4060              :   // move constructors
    4061              :   UnsafeBlockExpr (UnsafeBlockExpr &&other) = default;
    4062              :   UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default;
    4063              : 
    4064         7744 :   location_t get_locus () const override final { return locus; }
    4065              : 
    4066              :   void accept_vis (ASTVisitor &vis) override;
    4067              : 
    4068              :   // Invalid if block is null, so base stripping on that.
    4069            7 :   void mark_for_strip () override { expr = nullptr; }
    4070        17024 :   bool is_marked_for_strip () const override { return expr == nullptr; }
    4071              : 
    4072              :   // TODO: is this better? Or is a "vis_block" better?
    4073        80748 :   BlockExpr &get_block_expr ()
    4074              :   {
    4075        80748 :     rust_assert (expr != nullptr);
    4076        80748 :     return *expr;
    4077              :   }
    4078              : 
    4079        20319 :   std::unique_ptr<BlockExpr> &get_block_expr_ptr ()
    4080              :   {
    4081        20319 :     rust_assert (expr != nullptr);
    4082        20319 :     return expr;
    4083              :   }
    4084              : 
    4085              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4086       123481 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4087              : 
    4088         3275 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4089              :   {
    4090         3275 :     outer_attrs = std::move (new_attrs);
    4091         3275 :   }
    4092              : 
    4093         3663 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::UnsafeBlock; }
    4094              : 
    4095              : protected:
    4096              :   /* Use covariance to implement clone function as returning this object rather
    4097              :    * than base */
    4098         8220 :   UnsafeBlockExpr *clone_expr_with_block_impl () const override
    4099              :   {
    4100         8220 :     return new UnsafeBlockExpr (*this);
    4101              :   }
    4102              : };
    4103              : 
    4104              : // Base loop expression AST node - aka LoopExpr
    4105              : class BaseLoopExpr : public ExprWithBlock
    4106              : {
    4107              : protected:
    4108              :   // protected to allow subclasses better use of them
    4109              :   std::vector<Attribute> outer_attrs;
    4110              :   tl::optional<LoopLabel> loop_label;
    4111              :   std::unique_ptr<BlockExpr> loop_block;
    4112              : 
    4113              : private:
    4114              :   location_t locus;
    4115              : 
    4116              : protected:
    4117              :   // Constructor for BaseLoopExpr
    4118          240 :   BaseLoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4119              :                 tl::optional<LoopLabel> loop_label = tl::nullopt,
    4120              :                 std::vector<Attribute> outer_attribs
    4121              :                 = std::vector<Attribute> ())
    4122          480 :     : outer_attrs (std::move (outer_attribs)),
    4123          240 :       loop_label (std::move (loop_label)), loop_block (std::move (loop_block)),
    4124          240 :       locus (locus)
    4125          240 :   {}
    4126              : 
    4127              :   // Copy constructor for BaseLoopExpr with clone
    4128          277 :   BaseLoopExpr (BaseLoopExpr const &other)
    4129          277 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4130          277 :       loop_label (other.loop_label), locus (other.locus)
    4131              :   {
    4132              :     // guard to prevent null dereference (only required if error state)
    4133          277 :     if (other.loop_block != nullptr)
    4134          277 :       loop_block = other.loop_block->clone_block_expr ();
    4135          277 :   }
    4136              : 
    4137              :   // Overloaded assignment operator to clone
    4138              :   BaseLoopExpr &operator= (BaseLoopExpr const &other)
    4139              :   {
    4140              :     ExprWithBlock::operator= (other);
    4141              :     loop_label = other.loop_label;
    4142              :     locus = other.locus;
    4143              :     outer_attrs = other.outer_attrs;
    4144              : 
    4145              :     // guard to prevent null dereference (only required if error state)
    4146              :     if (other.loop_block != nullptr)
    4147              :       loop_block = other.loop_block->clone_block_expr ();
    4148              :     else
    4149              :       loop_block = nullptr;
    4150              : 
    4151              :     return *this;
    4152              :   }
    4153              : 
    4154              :   // move constructors
    4155              :   BaseLoopExpr (BaseLoopExpr &&other) = default;
    4156              :   BaseLoopExpr &operator= (BaseLoopExpr &&other) = default;
    4157              : 
    4158              : public:
    4159         5074 :   bool has_loop_label () const { return loop_label.has_value (); }
    4160              : 
    4161          799 :   LoopLabel &get_loop_label () { return loop_label.value (); }
    4162            0 :   const LoopLabel &get_loop_label () const { return loop_label.value (); }
    4163              : 
    4164          467 :   location_t get_locus () const override final { return locus; }
    4165              : 
    4166              :   // Invalid if loop block is null, so base stripping on that.
    4167            0 :   void mark_for_strip () override { loop_block = nullptr; }
    4168          942 :   bool is_marked_for_strip () const override { return loop_block == nullptr; }
    4169              : 
    4170              :   // TODO: is this better? Or is a "vis_block" better?
    4171         4737 :   BlockExpr &get_loop_block ()
    4172              :   {
    4173         4737 :     rust_assert (loop_block != nullptr);
    4174         4737 :     return *loop_block;
    4175              :   }
    4176              : 
    4177         1299 :   std::unique_ptr<BlockExpr> &get_loop_block_ptr ()
    4178              :   {
    4179         1299 :     rust_assert (loop_block != nullptr);
    4180         1299 :     return loop_block;
    4181              :   }
    4182              : 
    4183              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4184         7125 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4185              : 
    4186          183 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4187              :   {
    4188          183 :     outer_attrs = std::move (new_attrs);
    4189          183 :   }
    4190              : 
    4191          253 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Loop; }
    4192              : 
    4193              :   enum class Kind
    4194              :   {
    4195              :     Loop,
    4196              :     While,
    4197              :     WhileLet,
    4198              :     For
    4199              :   };
    4200              : 
    4201              :   virtual Kind get_loop_kind () const = 0;
    4202              : };
    4203              : 
    4204              : // 'Loop' expression (i.e. the infinite loop) AST node
    4205          272 : class LoopExpr : public BaseLoopExpr
    4206              : {
    4207              : public:
    4208              :   std::string as_string () const override;
    4209              : 
    4210              :   // Constructor for LoopExpr
    4211          137 :   LoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4212           40 :             tl::optional<LoopLabel> loop_label = tl::nullopt,
    4213              :             std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
    4214          137 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4215          176 :                     std::move (outer_attribs))
    4216          137 :   {}
    4217              : 
    4218              :   void accept_vis (ASTVisitor &vis) override;
    4219              : 
    4220          133 :   BaseLoopExpr::Kind get_loop_kind () const override
    4221              :   {
    4222          133 :     return BaseLoopExpr::Kind::Loop;
    4223              :   }
    4224              : 
    4225              : protected:
    4226              :   /* Use covariance to implement clone function as returning this object rather
    4227              :    * than base */
    4228          136 :   LoopExpr *clone_expr_with_block_impl () const override
    4229              :   {
    4230          136 :     return new LoopExpr (*this);
    4231              :   }
    4232              : };
    4233              : 
    4234              : // While loop expression AST node (predicate loop)
    4235              : class WhileLoopExpr : public BaseLoopExpr
    4236              : {
    4237              :   std::unique_ptr<Expr> condition;
    4238              : 
    4239              : public:
    4240              :   std::string as_string () const override;
    4241              : 
    4242              :   // Constructor for while loop with loop label
    4243           79 :   WhileLoopExpr (std::unique_ptr<Expr> loop_condition,
    4244              :                  std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4245              :                  tl::optional<LoopLabel> loop_label = tl::nullopt,
    4246              :                  std::vector<Attribute> outer_attribs
    4247              :                  = std::vector<Attribute> ())
    4248           79 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4249              :                     std::move (outer_attribs)),
    4250           81 :       condition (std::move (loop_condition))
    4251           79 :   {}
    4252              : 
    4253              :   // Copy constructor with clone
    4254          119 :   WhileLoopExpr (WhileLoopExpr const &other)
    4255          119 :     : BaseLoopExpr (other), condition (other.condition->clone_expr ())
    4256          119 :   {}
    4257              : 
    4258              :   // Overloaded assignment operator to clone
    4259              :   WhileLoopExpr &operator= (WhileLoopExpr const &other)
    4260              :   {
    4261              :     BaseLoopExpr::operator= (other);
    4262              :     condition = other.condition->clone_expr ();
    4263              :     // loop_block = other.loop_block->clone_block_expr();
    4264              :     // loop_label = other.loop_label;
    4265              :     // outer_attrs = other.outer_attrs;
    4266              : 
    4267              :     return *this;
    4268              :   }
    4269              : 
    4270              :   // move constructors
    4271              :   WhileLoopExpr (WhileLoopExpr &&other) = default;
    4272              :   WhileLoopExpr &operator= (WhileLoopExpr &&other) = default;
    4273              : 
    4274              :   void accept_vis (ASTVisitor &vis) override;
    4275              : 
    4276              :   // TODO: is this better? Or is a "vis_block" better?
    4277         2030 :   Expr &get_predicate_expr ()
    4278              :   {
    4279         2030 :     rust_assert (condition != nullptr);
    4280         2030 :     return *condition;
    4281              :   }
    4282              : 
    4283          523 :   std::unique_ptr<Expr> &get_predicate_expr_ptr ()
    4284              :   {
    4285          523 :     rust_assert (condition != nullptr);
    4286          523 :     return condition;
    4287              :   }
    4288              : 
    4289           79 :   BaseLoopExpr::Kind get_loop_kind () const override
    4290              :   {
    4291           79 :     return BaseLoopExpr::Kind::While;
    4292              :   }
    4293              : 
    4294              : protected:
    4295              :   /* Use covariance to implement clone function as returning this object rather
    4296              :    * than base */
    4297          119 :   WhileLoopExpr *clone_expr_with_block_impl () const override
    4298              :   {
    4299          119 :     return new WhileLoopExpr (*this);
    4300              :   }
    4301              : };
    4302              : 
    4303              : // While let loop expression AST node (predicate pattern loop)
    4304              : class WhileLetLoopExpr : public BaseLoopExpr
    4305              : {
    4306              :   // MatchArmPatterns patterns;
    4307              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4308              :   std::unique_ptr<Expr> scrutinee;
    4309              : 
    4310              : public:
    4311              :   std::string as_string () const override;
    4312              : 
    4313              :   // Constructor with a loop label
    4314            4 :   WhileLetLoopExpr (std::unique_ptr<Pattern> match_arm_pattern,
    4315              :                     std::unique_ptr<Expr> scrutinee,
    4316              :                     std::unique_ptr<BlockExpr> loop_block, location_t locus,
    4317              :                     tl::optional<LoopLabel> loop_label = tl::nullopt,
    4318              :                     std::vector<Attribute> outer_attribs
    4319              :                     = std::vector<Attribute> ())
    4320            4 :     : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
    4321              :                     std::move (outer_attribs)),
    4322            4 :       match_arm_pattern (std::move (match_arm_pattern)),
    4323            5 :       scrutinee (std::move (scrutinee))
    4324            4 :   {}
    4325              : 
    4326              :   // Copy constructor with clone
    4327            3 :   WhileLetLoopExpr (WhileLetLoopExpr const &other)
    4328            3 :     : BaseLoopExpr (other),
    4329            3 :       /*match_arm_patterns(other.match_arm_patterns),*/ scrutinee (
    4330            3 :         other.scrutinee->clone_expr ())
    4331              :   {
    4332            3 :     match_arm_pattern = other.get_pattern ()->clone_pattern ();
    4333            3 :   }
    4334              : 
    4335              :   // Overloaded assignment operator to clone pointers
    4336              :   WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other)
    4337              :   {
    4338              :     BaseLoopExpr::operator= (other);
    4339              :     scrutinee = other.scrutinee->clone_expr ();
    4340              :     match_arm_pattern = other.get_pattern ()->clone_pattern ();
    4341              :     return *this;
    4342              :   }
    4343              : 
    4344              :   // move constructors
    4345              :   WhileLetLoopExpr (WhileLetLoopExpr &&other) = default;
    4346              :   WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default;
    4347              : 
    4348              :   void accept_vis (ASTVisitor &vis) override;
    4349              : 
    4350              :   // TODO: is this better? Or is a "vis_block" better?
    4351           30 :   Expr &get_scrutinee_expr ()
    4352              :   {
    4353           30 :     rust_assert (scrutinee != nullptr);
    4354           30 :     return *scrutinee;
    4355              :   }
    4356              : 
    4357           12 :   std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
    4358              :   {
    4359           12 :     rust_assert (scrutinee != nullptr);
    4360           12 :     return scrutinee;
    4361              :   }
    4362              : 
    4363              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4364            3 :   const std::unique_ptr<Pattern> &get_pattern () const
    4365              :   {
    4366            3 :     return match_arm_pattern;
    4367              :   }
    4368           42 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4369              : 
    4370            6 :   BaseLoopExpr::Kind get_loop_kind () const override
    4371              :   {
    4372            6 :     return BaseLoopExpr::Kind::WhileLet;
    4373              :   }
    4374              : 
    4375              : protected:
    4376              :   /* Use covariance to implement clone function as returning this object rather
    4377              :    * than base */
    4378            3 :   WhileLetLoopExpr *clone_expr_with_block_impl () const override
    4379              :   {
    4380            3 :     return new WhileLetLoopExpr (*this);
    4381              :   }
    4382              : };
    4383              : 
    4384              : // For loop expression AST node (iterator loop)
    4385              : class ForLoopExpr : public BaseLoopExpr
    4386              : {
    4387              :   std::unique_ptr<Pattern> pattern;
    4388              :   std::unique_ptr<Expr> iterator_expr;
    4389              : 
    4390              : public:
    4391              :   std::string as_string () const override;
    4392              : 
    4393              :   // Constructor with loop label
    4394           20 :   ForLoopExpr (std::unique_ptr<Pattern> loop_pattern,
    4395              :                std::unique_ptr<Expr> iterator_expr,
    4396              :                std::unique_ptr<BlockExpr> loop_body, location_t locus,
    4397              :                tl::optional<LoopLabel> loop_label = tl::nullopt,
    4398              :                std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
    4399           20 :     : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label),
    4400              :                     std::move (outer_attribs)),
    4401           20 :       pattern (std::move (loop_pattern)),
    4402           20 :       iterator_expr (std::move (iterator_expr))
    4403           20 :   {}
    4404              : 
    4405              :   // Copy constructor with clone
    4406           19 :   ForLoopExpr (ForLoopExpr const &other)
    4407           57 :     : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()),
    4408           19 :       iterator_expr (other.iterator_expr->clone_expr ())
    4409           19 :   {}
    4410              : 
    4411              :   // Overloaded assignment operator to clone
    4412              :   ForLoopExpr &operator= (ForLoopExpr const &other)
    4413              :   {
    4414              :     BaseLoopExpr::operator= (other);
    4415              :     pattern = other.pattern->clone_pattern ();
    4416              :     iterator_expr = other.iterator_expr->clone_expr ();
    4417              :     /*loop_block = other.loop_block->clone_block_expr();
    4418              :     loop_label = other.loop_label;
    4419              :     outer_attrs = other.outer_attrs;*/
    4420              : 
    4421              :     return *this;
    4422              :   }
    4423              : 
    4424              :   // move constructors
    4425              :   ForLoopExpr (ForLoopExpr &&other) = default;
    4426              :   ForLoopExpr &operator= (ForLoopExpr &&other) = default;
    4427              : 
    4428              :   void accept_vis (ASTVisitor &vis) override;
    4429              : 
    4430              :   // TODO: is this better? Or is a "vis_block" better?
    4431          410 :   Expr &get_iterator_expr ()
    4432              :   {
    4433          410 :     rust_assert (iterator_expr != nullptr);
    4434          410 :     return *iterator_expr;
    4435              :   }
    4436              : 
    4437          132 :   std::unique_ptr<Expr> &get_iterator_expr_ptr ()
    4438              :   {
    4439          132 :     rust_assert (iterator_expr != nullptr);
    4440          132 :     return iterator_expr;
    4441              :   }
    4442              : 
    4443              :   // TODO: is this better? Or is a "vis_block" better?
    4444          410 :   Pattern &get_pattern ()
    4445              :   {
    4446          410 :     rust_assert (pattern != nullptr);
    4447          410 :     return *pattern;
    4448              :   }
    4449              : 
    4450          132 :   std::unique_ptr<Pattern> &get_pattern_ptr ()
    4451              :   {
    4452          132 :     rust_assert (pattern != nullptr);
    4453          132 :     return pattern;
    4454              :   }
    4455              : 
    4456           34 :   BaseLoopExpr::Kind get_loop_kind () const override
    4457              :   {
    4458           34 :     return BaseLoopExpr::Kind::For;
    4459              :   }
    4460              : 
    4461              : protected:
    4462              :   /* Use covariance to implement clone function as returning this object rather
    4463              :    * than base */
    4464           19 :   ForLoopExpr *clone_expr_with_block_impl () const override
    4465              :   {
    4466           19 :     return new ForLoopExpr (*this);
    4467              :   }
    4468              : };
    4469              : 
    4470              : // forward decl for IfExpr
    4471              : class IfLetExpr;
    4472              : 
    4473              : // Base if expression with no "else" or "if let" AST node
    4474              : class IfExpr : public ExprWithBlock
    4475              : {
    4476              :   std::vector<Attribute> outer_attrs;
    4477              :   std::unique_ptr<Expr> condition;
    4478              :   std::unique_ptr<BlockExpr> if_block;
    4479              :   location_t locus;
    4480              : 
    4481              : public:
    4482              :   std::string as_string () const override;
    4483              : 
    4484         2510 :   IfExpr (std::unique_ptr<Expr> condition, std::unique_ptr<BlockExpr> if_block,
    4485              :           std::vector<Attribute> outer_attrs, location_t locus)
    4486         5020 :     : outer_attrs (std::move (outer_attrs)), condition (std::move (condition)),
    4487         2510 :       if_block (std::move (if_block)), locus (locus)
    4488         2510 :   {}
    4489              :   // outer attributes are never allowed on IfExprs
    4490              : 
    4491              :   // Copy constructor with clone
    4492         4137 :   IfExpr (IfExpr const &other)
    4493         4137 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4494         4137 :       locus (other.locus)
    4495              :   {
    4496              :     // guard to prevent null dereference (only required if error state)
    4497         4137 :     if (other.condition != nullptr)
    4498         4137 :       condition = other.condition->clone_expr ();
    4499         4137 :     if (other.if_block != nullptr)
    4500         4137 :       if_block = other.if_block->clone_block_expr ();
    4501         4137 :   }
    4502              : 
    4503              :   // Overloaded assignment operator to clone expressions
    4504              :   IfExpr &operator= (IfExpr const &other)
    4505              :   {
    4506              :     ExprWithBlock::operator= (other);
    4507              :     outer_attrs = other.outer_attrs;
    4508              :     locus = other.locus;
    4509              : 
    4510              :     // guard to prevent null dereference (only required if error state)
    4511              :     if (other.condition != nullptr)
    4512              :       condition = other.condition->clone_expr ();
    4513              :     else
    4514              :       condition = nullptr;
    4515              :     if (other.if_block != nullptr)
    4516              :       if_block = other.if_block->clone_block_expr ();
    4517              :     else
    4518              :       if_block = nullptr;
    4519              : 
    4520              :     return *this;
    4521              :   }
    4522              : 
    4523              :   // move constructors
    4524              :   IfExpr (IfExpr &&other) = default;
    4525              :   IfExpr &operator= (IfExpr &&other) = default;
    4526              : 
    4527              :   // Unique pointer custom clone function
    4528              :   std::unique_ptr<IfExpr> clone_if_expr () const
    4529              :   {
    4530              :     return std::unique_ptr<IfExpr> (clone_if_expr_impl ());
    4531              :   }
    4532              : 
    4533              :   /* Note that multiple "else if"s are handled via nested ASTs rather than a
    4534              :    * vector of else ifs - i.e. not like a switch statement. TODO - is this a
    4535              :    * better approach? or does it not parse correctly and have downsides? */
    4536              : 
    4537         4860 :   location_t get_locus () const override final { return locus; }
    4538              : 
    4539              :   void accept_vis (ASTVisitor &vis) override;
    4540              : 
    4541              :   void vis_if_condition (ASTVisitor &vis) { condition->accept_vis (vis); }
    4542              :   void vis_if_block (ASTVisitor &vis) { if_block->accept_vis (vis); }
    4543              : 
    4544              :   // TODO: is this better? Or is a "vis_block" better?
    4545        62018 :   Expr &get_condition_expr ()
    4546              :   {
    4547        62018 :     rust_assert (condition != nullptr);
    4548        62018 :     return *condition;
    4549              :   }
    4550              : 
    4551        14204 :   std::unique_ptr<Expr> &get_condition_expr_ptr ()
    4552              :   {
    4553        14204 :     rust_assert (condition != nullptr);
    4554        14204 :     return condition;
    4555              :   }
    4556              : 
    4557              :   // TODO: is this better? Or is a "vis_block" better?
    4558        76222 :   BlockExpr &get_if_block ()
    4559              :   {
    4560        76222 :     rust_assert (if_block != nullptr);
    4561        76222 :     return *if_block;
    4562              :   }
    4563              : 
    4564              :   // Invalid if if block or condition is null, so base stripping on that.
    4565            0 :   void mark_for_strip () override
    4566              :   {
    4567            0 :     if_block = nullptr;
    4568            0 :     condition = nullptr;
    4569            0 :   }
    4570        12087 :   bool is_marked_for_strip () const override
    4571              :   {
    4572        12087 :     return if_block == nullptr && condition == nullptr;
    4573              :   }
    4574              : 
    4575         2108 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4576              :   {
    4577         2108 :     outer_attrs = std::move (new_attrs);
    4578         2108 :   }
    4579              : 
    4580              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4581              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4582        88067 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4583              : 
    4584         2139 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::If; }
    4585              : 
    4586              : protected:
    4587              :   // Base clone function but still concrete as concrete base class
    4588         1343 :   virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); }
    4589              : 
    4590              :   /* Use covariance to implement clone function as returning this object rather
    4591              :    * than base */
    4592         4137 :   IfExpr *clone_expr_with_block_impl () const final override
    4593              :   {
    4594         4137 :     return clone_if_expr_impl ();
    4595              :   }
    4596              : };
    4597              : 
    4598              : // If expression with an ending "else" expression AST node (trailing)
    4599              : class IfExprConseqElse : public IfExpr
    4600              : {
    4601              :   std::unique_ptr<ExprWithBlock> else_block;
    4602              : 
    4603              : public:
    4604              :   std::string as_string () const override;
    4605              : 
    4606         1269 :   IfExprConseqElse (std::unique_ptr<Expr> condition,
    4607              :                     std::unique_ptr<BlockExpr> if_block,
    4608              :                     std::unique_ptr<ExprWithBlock> else_block,
    4609              :                     std::vector<Attribute> outer_attrs, location_t locus)
    4610         1269 :     : IfExpr (std::move (condition), std::move (if_block),
    4611              :               std::move (outer_attrs), locus),
    4612         1269 :       else_block (std::move (else_block))
    4613         1269 :   {}
    4614              :   // again, outer attributes not allowed
    4615              : 
    4616              :   // Copy constructor with clone
    4617         2794 :   IfExprConseqElse (IfExprConseqElse const &other)
    4618         2794 :     : IfExpr (other), else_block (other.else_block->clone_expr_with_block ())
    4619         2794 :   {}
    4620              : 
    4621              :   // Overloaded assignment operator with cloning
    4622              :   IfExprConseqElse &operator= (IfExprConseqElse const &other)
    4623              :   {
    4624              :     IfExpr::operator= (other);
    4625              :     // condition = other.condition->clone_expr();
    4626              :     // if_block = other.if_block->clone_block_expr();
    4627              :     else_block = other.else_block->clone_expr_with_block ();
    4628              : 
    4629              :     return *this;
    4630              :   }
    4631              : 
    4632              :   // move constructors
    4633              :   IfExprConseqElse (IfExprConseqElse &&other) = default;
    4634              :   IfExprConseqElse &operator= (IfExprConseqElse &&other) = default;
    4635              : 
    4636              :   void accept_vis (ASTVisitor &vis) override;
    4637              : 
    4638              :   void vis_else_block (ASTVisitor &vis) { else_block->accept_vis (vis); }
    4639              : 
    4640              :   // TODO: is this better? Or is a "vis_block" better?
    4641        36451 :   ExprWithBlock &get_else_block ()
    4642              :   {
    4643        36451 :     rust_assert (else_block != nullptr);
    4644        36451 :     return *else_block;
    4645              :   }
    4646              : 
    4647              : protected:
    4648              :   /* Use covariance to implement clone function as returning this object rather
    4649              :    * than base */
    4650         2794 :   IfExprConseqElse *clone_if_expr_impl () const override
    4651              :   {
    4652         2794 :     return new IfExprConseqElse (*this);
    4653              :   }
    4654              : };
    4655              : 
    4656              : // Basic "if let" expression AST node with no else
    4657              : class IfLetExpr : public ExprWithBlock
    4658              : {
    4659              :   std::vector<Attribute> outer_attrs;
    4660              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4661              :   std::unique_ptr<Expr> value;
    4662              :   std::unique_ptr<BlockExpr> if_block;
    4663              :   location_t locus;
    4664              : 
    4665              : public:
    4666              :   std::string as_string () const override;
    4667              : 
    4668           31 :   IfLetExpr (std::unique_ptr<Pattern> match_arm_pattern,
    4669              :              std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
    4670              :              std::vector<Attribute> outer_attrs, location_t locus)
    4671           62 :     : outer_attrs (std::move (outer_attrs)),
    4672           31 :       match_arm_pattern (std::move (match_arm_pattern)),
    4673           31 :       value (std::move (value)), if_block (std::move (if_block)), locus (locus)
    4674           31 :   {}
    4675              : 
    4676              :   // copy constructor with clone
    4677           30 :   IfLetExpr (IfLetExpr const &other)
    4678           30 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    4679           30 :       locus (other.locus)
    4680              :   {
    4681              :     // guard to prevent null dereference (only required if error state)
    4682           30 :     if (other.value != nullptr)
    4683           30 :       value = other.value->clone_expr ();
    4684           30 :     if (other.if_block != nullptr)
    4685           30 :       if_block = other.if_block->clone_block_expr ();
    4686           30 :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4687           30 :   }
    4688              : 
    4689              :   // overload assignment operator to clone
    4690              :   IfLetExpr &operator= (IfLetExpr const &other)
    4691              :   {
    4692              :     ExprWithBlock::operator= (other);
    4693              :     outer_attrs = other.outer_attrs;
    4694              :     locus = other.locus;
    4695              : 
    4696              :     // guard to prevent null dereference (only required if error state)
    4697              :     if (other.value != nullptr)
    4698              :       value = other.value->clone_expr ();
    4699              :     else
    4700              :       value = nullptr;
    4701              :     if (other.if_block != nullptr)
    4702              :       if_block = other.if_block->clone_block_expr ();
    4703              :     else
    4704              :       if_block = nullptr;
    4705              : 
    4706              :     if (other.if_block != nullptr)
    4707              :       if_block = other.if_block->clone_block_expr ();
    4708              : 
    4709              :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4710              :     return *this;
    4711              :   }
    4712              : 
    4713              :   // move constructors
    4714              :   IfLetExpr (IfLetExpr &&other) = default;
    4715              :   IfLetExpr &operator= (IfLetExpr &&other) = default;
    4716              : 
    4717              :   // Unique pointer custom clone function
    4718              :   std::unique_ptr<IfLetExpr> clone_if_let_expr () const
    4719              :   {
    4720              :     return std::unique_ptr<IfLetExpr> (clone_if_let_expr_impl ());
    4721              :   }
    4722              : 
    4723          137 :   location_t get_locus () const override final { return locus; }
    4724              : 
    4725              :   void accept_vis (ASTVisitor &vis) override;
    4726              : 
    4727              :   // Invalid if block or value is null, so base stripping on that.
    4728            0 :   void mark_for_strip () override
    4729              :   {
    4730            0 :     if_block = nullptr;
    4731            0 :     value = nullptr;
    4732            0 :   }
    4733          102 :   bool is_marked_for_strip () const override
    4734              :   {
    4735          102 :     return if_block == nullptr && value == nullptr;
    4736              :   }
    4737              : 
    4738              :   // TODO: is this better? Or is a "vis_block" better?
    4739          596 :   Expr &get_value_expr ()
    4740              :   {
    4741          596 :     rust_assert (value != nullptr);
    4742          596 :     return *value;
    4743              :   }
    4744              : 
    4745          150 :   std::unique_ptr<Expr> &get_value_expr_ptr ()
    4746              :   {
    4747          150 :     rust_assert (value != nullptr);
    4748          150 :     return value;
    4749              :   }
    4750              : 
    4751              :   // TODO: is this better? Or is a "vis_block" better?
    4752          746 :   BlockExpr &get_if_block ()
    4753              :   {
    4754          746 :     rust_assert (if_block != nullptr);
    4755          746 :     return *if_block;
    4756              :   }
    4757              : 
    4758              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4759              :   const std::unique_ptr<Pattern> &get_pattern () const
    4760              :   {
    4761              :     return match_arm_pattern;
    4762              :   }
    4763          686 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4764              : 
    4765           29 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    4766              :   {
    4767           29 :     outer_attrs = std::move (new_attrs);
    4768           29 :   }
    4769              : 
    4770              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4771              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4772          804 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    4773              : 
    4774           29 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::IfLet; }
    4775              : 
    4776              : protected:
    4777              :   /* Use covariance to implement clone function as returning this object rather
    4778              :    * than base (or rather this or any derived object) */
    4779           30 :   IfLetExpr *clone_expr_with_block_impl () const final override
    4780              :   {
    4781           30 :     return clone_if_let_expr_impl ();
    4782              :   }
    4783              : 
    4784              :   // Base clone function but still concrete as concrete base class
    4785           18 :   virtual IfLetExpr *clone_if_let_expr_impl () const
    4786              :   {
    4787           18 :     return new IfLetExpr (*this);
    4788              :   }
    4789              : };
    4790              : 
    4791              : /* AST node representing "if let" expression with an "else" expression at the
    4792              :  * end */
    4793              : class IfLetExprConseqElse : public IfLetExpr
    4794              : {
    4795              :   std::unique_ptr<ExprWithBlock> else_block;
    4796              : 
    4797              : public:
    4798              :   std::string as_string () const override;
    4799              : 
    4800           12 :   IfLetExprConseqElse (std::unique_ptr<Pattern> match_arm_pattern,
    4801              :                        std::unique_ptr<Expr> value,
    4802              :                        std::unique_ptr<BlockExpr> if_block,
    4803              :                        std::unique_ptr<ExprWithBlock> else_block,
    4804              :                        std::vector<Attribute> outer_attrs, location_t locus)
    4805           12 :     : IfLetExpr (std::move (match_arm_pattern), std::move (value),
    4806              :                  std::move (if_block), std::move (outer_attrs), locus),
    4807           12 :       else_block (std::move (else_block))
    4808           12 :   {}
    4809              :   // outer attributes not allowed
    4810              : 
    4811              :   // copy constructor with clone
    4812           12 :   IfLetExprConseqElse (IfLetExprConseqElse const &other)
    4813           12 :     : IfLetExpr (other), else_block (other.else_block->clone_expr_with_block ())
    4814           12 :   {}
    4815              : 
    4816              :   // overload assignment operator to clone
    4817              :   IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other)
    4818              :   {
    4819              :     IfLetExpr::operator= (other);
    4820              :     // match_arm_patterns = other.match_arm_patterns;
    4821              :     // value = other.value->clone_expr();
    4822              :     // if_block = other.if_block->clone_block_expr();
    4823              :     else_block = other.else_block->clone_expr_with_block ();
    4824              :     // outer_attrs = other.outer_attrs;
    4825              : 
    4826              :     return *this;
    4827              :   }
    4828              : 
    4829              :   // move constructors
    4830              :   IfLetExprConseqElse (IfLetExprConseqElse &&other) = default;
    4831              :   IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default;
    4832              : 
    4833              :   void accept_vis (ASTVisitor &vis) override;
    4834              : 
    4835              :   // TODO: is this better? Or is a "vis_block" better?
    4836          266 :   ExprWithBlock &get_else_block ()
    4837              :   {
    4838          266 :     rust_assert (else_block != nullptr);
    4839          266 :     return *else_block;
    4840              :   }
    4841              : 
    4842              : protected:
    4843              :   /* Use covariance to implement clone function as returning this object rather
    4844              :    * than base */
    4845           12 :   IfLetExprConseqElse *clone_if_let_expr_impl () const override
    4846              :   {
    4847           12 :     return new IfLetExprConseqElse (*this);
    4848              :   }
    4849              : };
    4850              : 
    4851              : // Match arm expression
    4852              : struct MatchArm
    4853              : {
    4854              : private:
    4855              :   std::vector<Attribute> outer_attrs;
    4856              :   // MatchArmPatterns patterns;
    4857              :   std::unique_ptr<Pattern> match_arm_pattern; // inlined
    4858              : 
    4859              :   // bool has_match_arm_guard;
    4860              :   // inlined from MatchArmGuard
    4861              :   std::unique_ptr<Expr> guard_expr;
    4862              : 
    4863              :   location_t locus;
    4864              : 
    4865              : public:
    4866              :   // Returns whether the MatchArm has a match arm guard expression
    4867        65682 :   bool has_match_arm_guard () const { return guard_expr != nullptr; }
    4868              : 
    4869              :   // Constructor for match arm with a guard expression
    4870         2534 :   MatchArm (std::unique_ptr<Pattern> match_arm_pattern, location_t locus,
    4871              :             std::unique_ptr<Expr> guard_expr = nullptr,
    4872              :             std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
    4873         2534 :     : outer_attrs (std::move (outer_attrs)),
    4874          372 :       match_arm_pattern (std::move (match_arm_pattern)),
    4875         2534 :       guard_expr (std::move (guard_expr)), locus (locus)
    4876              :   {}
    4877              : 
    4878              :   // Copy constructor with clone
    4879         4963 :   MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs)
    4880              :   {
    4881              :     // guard to protect from null pointer dereference
    4882         4963 :     if (other.guard_expr != nullptr)
    4883            1 :       guard_expr = other.guard_expr->clone_expr ();
    4884              : 
    4885         4963 :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4886              : 
    4887         4963 :     locus = other.locus;
    4888         4963 :   }
    4889              : 
    4890        14135 :   ~MatchArm () = default;
    4891              : 
    4892              :   // Overload assignment operator to clone
    4893              :   MatchArm &operator= (MatchArm const &other)
    4894              :   {
    4895              :     outer_attrs = other.outer_attrs;
    4896              : 
    4897              :     if (other.guard_expr != nullptr)
    4898              :       guard_expr = other.guard_expr->clone_expr ();
    4899              :     else
    4900              :       guard_expr = nullptr;
    4901              : 
    4902              :     match_arm_pattern = other.match_arm_pattern->clone_pattern ();
    4903              : 
    4904              :     return *this;
    4905              :   }
    4906              : 
    4907              :   // move constructors
    4908         2618 :   MatchArm (MatchArm &&other) = default;
    4909            0 :   MatchArm &operator= (MatchArm &&other) = default;
    4910              : 
    4911              :   // Returns whether match arm is in an error state.
    4912        72842 :   bool is_error () const { return match_arm_pattern == nullptr; }
    4913              : 
    4914              :   // Creates a match arm in an error state.
    4915            0 :   static MatchArm create_error ()
    4916              :   {
    4917            0 :     location_t locus = UNDEF_LOCATION;
    4918            0 :     return MatchArm (nullptr, locus);
    4919              :   }
    4920              : 
    4921              :   std::string as_string () const;
    4922              : 
    4923              :   // TODO: is this better? Or is a "vis_block" better?
    4924           17 :   Expr &get_guard_expr ()
    4925              :   {
    4926           17 :     rust_assert (has_match_arm_guard ());
    4927           17 :     return *guard_expr;
    4928              :   }
    4929              : 
    4930            5 :   std::unique_ptr<Expr> &get_guard_expr_ptr ()
    4931              :   {
    4932            5 :     rust_assert (has_match_arm_guard ());
    4933            5 :     return guard_expr;
    4934              :   }
    4935              : 
    4936              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    4937              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    4938        65682 :   std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
    4939              : 
    4940              :   const std::unique_ptr<Pattern> &get_pattern () const
    4941              :   {
    4942              :     return match_arm_pattern;
    4943              :   }
    4944        65682 :   std::unique_ptr<Pattern> &get_pattern () { return match_arm_pattern; }
    4945              : 
    4946              :   location_t get_locus () const { return locus; }
    4947              : };
    4948              : 
    4949              : /* A "match case" - a correlated match arm and resulting expression. Not
    4950              :  * abstract. */
    4951              : struct MatchCase
    4952              : {
    4953              : private:
    4954              :   MatchArm arm;
    4955              :   std::unique_ptr<Expr> expr;
    4956              :   NodeId node_id;
    4957              : 
    4958              :   /* TODO: does whether trailing comma exists need to be stored? currently
    4959              :    * assuming it is only syntactical and has no effect on meaning. */
    4960              : 
    4961              : public:
    4962         2532 :   MatchCase (MatchArm arm, std::unique_ptr<Expr> expr)
    4963         2532 :     : arm (std::move (arm)), expr (std::move (expr)),
    4964         2532 :       node_id (Analysis::Mappings::get ().get_next_node_id ())
    4965         2532 :   {}
    4966              : 
    4967         4963 :   MatchCase (const MatchCase &other)
    4968         4963 :     : arm (other.arm), expr (other.expr->clone_expr ()), node_id (other.node_id)
    4969         4963 :   {}
    4970              : 
    4971              :   MatchCase &operator= (const MatchCase &other)
    4972              :   {
    4973              :     arm = other.arm;
    4974              :     expr = other.expr->clone_expr ();
    4975              :     node_id = other.node_id;
    4976              : 
    4977              :     return *this;
    4978              :   }
    4979              : 
    4980         3798 :   MatchCase (MatchCase &&other) = default;
    4981            0 :   MatchCase &operator= (MatchCase &&other) = default;
    4982              : 
    4983         8876 :   ~MatchCase () = default;
    4984              : 
    4985              :   std::string as_string () const;
    4986              : 
    4987              :   // TODO: is this better? Or is a "vis_block" better?
    4988        51593 :   Expr &get_expr ()
    4989              :   {
    4990        51593 :     rust_assert (expr != nullptr);
    4991        51593 :     return *expr;
    4992              :   }
    4993              : 
    4994        14089 :   std::unique_ptr<Expr> &get_expr_ptr ()
    4995              :   {
    4996        14089 :     rust_assert (expr != nullptr);
    4997        14089 :     return expr;
    4998              :   }
    4999              : 
    5000              :   // TODO: is this better? Or is a "vis_block" better?
    5001        70680 :   MatchArm &get_arm ()
    5002              :   {
    5003        70680 :     rust_assert (!arm.is_error ());
    5004        70680 :     return arm;
    5005              :   }
    5006              : 
    5007        16294 :   NodeId get_node_id () const { return node_id; }
    5008              : };
    5009              : 
    5010              : // Match expression AST node
    5011              : class MatchExpr : public ExprWithBlock
    5012              : {
    5013              :   std::vector<Attribute> outer_attrs;
    5014              :   std::unique_ptr<Expr> branch_value;
    5015              :   std::vector<Attribute> inner_attrs;
    5016              :   std::vector<MatchCase> match_arms;
    5017              :   location_t locus;
    5018              : 
    5019              : public:
    5020              :   std::string as_string () const override;
    5021              : 
    5022              :   // Returns whether the match expression has any match arms.
    5023              :   bool has_match_arms () const { return !match_arms.empty (); }
    5024              : 
    5025         1095 :   MatchExpr (std::unique_ptr<Expr> branch_value,
    5026              :              std::vector<MatchCase> match_arms,
    5027              :              std::vector<Attribute> inner_attrs,
    5028              :              std::vector<Attribute> outer_attrs, location_t locus)
    5029         2190 :     : outer_attrs (std::move (outer_attrs)),
    5030         1095 :       branch_value (std::move (branch_value)),
    5031         1095 :       inner_attrs (std::move (inner_attrs)),
    5032         1095 :       match_arms (std::move (match_arms)), locus (locus)
    5033         1095 :   {}
    5034              : 
    5035              :   // Copy constructor requires clone due to unique_ptr
    5036         2188 :   MatchExpr (MatchExpr const &other)
    5037         2188 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    5038         2188 :       inner_attrs (other.inner_attrs), match_arms (other.match_arms),
    5039         2188 :       locus (other.locus)
    5040              :   {
    5041              :     // guard to prevent null dereference (only required if error state)
    5042         2188 :     if (other.branch_value != nullptr)
    5043         2188 :       branch_value = other.branch_value->clone_expr ();
    5044         2188 :   }
    5045              : 
    5046              :   // Overloaded assignment operator to clone due to unique_ptr
    5047              :   MatchExpr &operator= (MatchExpr const &other)
    5048              :   {
    5049              :     ExprWithBlock::operator= (other);
    5050              :     inner_attrs = other.inner_attrs;
    5051              :     match_arms = other.match_arms;
    5052              :     outer_attrs = other.outer_attrs;
    5053              :     locus = other.locus;
    5054              : 
    5055              :     // guard to prevent null dereference (only required if error state)
    5056              :     if (other.branch_value != nullptr)
    5057              :       branch_value = other.branch_value->clone_expr ();
    5058              :     else
    5059              :       branch_value = nullptr;
    5060              : 
    5061              :     return *this;
    5062              :   }
    5063              : 
    5064              :   // move constructors
    5065              :   MatchExpr (MatchExpr &&other) = default;
    5066              :   MatchExpr &operator= (MatchExpr &&other) = default;
    5067              : 
    5068         4732 :   location_t get_locus () const override final { return locus; }
    5069              : 
    5070              :   void accept_vis (ASTVisitor &vis) override;
    5071              : 
    5072              :   // Invalid if branch value is null, so base stripping on that.
    5073            0 :   void mark_for_strip () override { branch_value = nullptr; }
    5074        11472 :   bool is_marked_for_strip () const override { return branch_value == nullptr; }
    5075              : 
    5076              :   // TODO: this mutable getter seems really dodgy. Think up better way.
    5077              :   const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
    5078        28384 :   std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
    5079              : 
    5080              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5081        34451 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5082              : 
    5083          851 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5084              :   {
    5085          851 :     outer_attrs = std::move (new_attrs);
    5086          851 :   }
    5087              : 
    5088              :   // TODO: is this better? Or is a "vis_block" better?
    5089        22314 :   Expr &get_scrutinee_expr ()
    5090              :   {
    5091        22314 :     rust_assert (branch_value != nullptr);
    5092        22314 :     return *branch_value;
    5093              :   }
    5094              : 
    5095         6070 :   std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
    5096              :   {
    5097         6070 :     rust_assert (branch_value != nullptr);
    5098         6070 :     return branch_value;
    5099              :   }
    5100              : 
    5101              :   const std::vector<MatchCase> &get_match_cases () const { return match_arms; }
    5102        28383 :   std::vector<MatchCase> &get_match_cases () { return match_arms; }
    5103              : 
    5104         1086 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Match; }
    5105              : 
    5106              : protected:
    5107              :   /* Use covariance to implement clone function as returning this object rather
    5108              :    * than base */
    5109         2188 :   MatchExpr *clone_expr_with_block_impl () const override
    5110              :   {
    5111         2188 :     return new MatchExpr (*this);
    5112              :   }
    5113              : };
    5114              : 
    5115              : // Await expression AST node (pseudo-member variable access)
    5116              : class AwaitExpr : public ExprWithoutBlock
    5117              : {
    5118              :   std::vector<Attribute> outer_attrs;
    5119              :   std::unique_ptr<Expr> awaited_expr;
    5120              :   location_t locus;
    5121              : 
    5122              : public:
    5123              :   // TODO: ensure outer attributes are actually allowed
    5124            0 :   AwaitExpr (std::unique_ptr<Expr> awaited_expr,
    5125              :              std::vector<Attribute> outer_attrs, location_t locus)
    5126            0 :     : outer_attrs (std::move (outer_attrs)),
    5127            0 :       awaited_expr (std::move (awaited_expr)), locus (locus)
    5128            0 :   {}
    5129              : 
    5130              :   // copy constructor with clone
    5131            0 :   AwaitExpr (AwaitExpr const &other)
    5132            0 :     : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
    5133            0 :       locus (other.locus)
    5134              :   {
    5135              :     // guard to prevent null dereference (only required if error state)
    5136            0 :     if (other.awaited_expr != nullptr)
    5137            0 :       awaited_expr = other.awaited_expr->clone_expr ();
    5138            0 :   }
    5139              : 
    5140              :   // overloaded assignment operator with clone
    5141              :   AwaitExpr &operator= (AwaitExpr const &other)
    5142              :   {
    5143              :     ExprWithoutBlock::operator= (other);
    5144              :     outer_attrs = other.outer_attrs;
    5145              :     locus = other.locus;
    5146              : 
    5147              :     // guard to prevent null dereference (only required if error state)
    5148              :     if (other.awaited_expr != nullptr)
    5149              :       awaited_expr = other.awaited_expr->clone_expr ();
    5150              :     else
    5151              :       awaited_expr = nullptr;
    5152              : 
    5153              :     return *this;
    5154              :   }
    5155              : 
    5156              :   // move constructors
    5157              :   AwaitExpr (AwaitExpr &&other) = default;
    5158              :   AwaitExpr &operator= (AwaitExpr &&other) = default;
    5159              : 
    5160              :   std::string as_string () const override;
    5161              : 
    5162            0 :   location_t get_locus () const override final { return locus; }
    5163              : 
    5164              :   void accept_vis (ASTVisitor &vis) override;
    5165              : 
    5166              :   // Invalid if awaited expr is null, so base stripping on that.
    5167            0 :   void mark_for_strip () override { awaited_expr = nullptr; }
    5168            0 :   bool is_marked_for_strip () const override { return awaited_expr == nullptr; }
    5169              : 
    5170              :   // TODO: is this better? Or is a "vis_block" better?
    5171            0 :   std::unique_ptr<Expr> &get_awaited_expr ()
    5172              :   {
    5173            0 :     rust_assert (awaited_expr != nullptr);
    5174            0 :     return awaited_expr;
    5175              :   }
    5176              : 
    5177              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5178            0 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5179              : 
    5180            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5181              :   {
    5182            0 :     outer_attrs = std::move (new_attrs);
    5183            0 :   }
    5184              : 
    5185            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::Await; }
    5186              : 
    5187              : protected:
    5188              :   /* Use covariance to implement clone function as returning this object rather
    5189              :    * than base */
    5190            0 :   AwaitExpr *clone_expr_without_block_impl () const override
    5191              :   {
    5192            0 :     return new AwaitExpr (*this);
    5193              :   }
    5194              : };
    5195              : 
    5196              : // Async block expression AST node (block expr that evaluates to a future)
    5197              : class AsyncBlockExpr : public ExprWithBlock
    5198              : {
    5199              :   // TODO: should this extend BlockExpr rather than be a composite of it?
    5200              :   std::vector<Attribute> outer_attrs;
    5201              :   bool has_move;
    5202              :   std::unique_ptr<BlockExpr> block_expr;
    5203              :   location_t locus;
    5204              : 
    5205              : public:
    5206            0 :   AsyncBlockExpr (std::unique_ptr<BlockExpr> block_expr, bool has_move,
    5207              :                   std::vector<Attribute> outer_attrs, location_t locus)
    5208            0 :     : outer_attrs (std::move (outer_attrs)), has_move (has_move),
    5209            0 :       block_expr (std::move (block_expr)), locus (locus)
    5210            0 :   {}
    5211              : 
    5212              :   // copy constructor with clone
    5213            0 :   AsyncBlockExpr (AsyncBlockExpr const &other)
    5214            0 :     : ExprWithBlock (other), outer_attrs (other.outer_attrs),
    5215            0 :       has_move (other.has_move), locus (other.locus)
    5216              :   {
    5217              :     // guard to prevent null dereference (only required if error state)
    5218            0 :     if (other.block_expr != nullptr)
    5219            0 :       block_expr = other.block_expr->clone_block_expr ();
    5220            0 :   }
    5221              : 
    5222              :   // overloaded assignment operator to clone
    5223              :   AsyncBlockExpr &operator= (AsyncBlockExpr const &other)
    5224              :   {
    5225              :     ExprWithBlock::operator= (other);
    5226              :     outer_attrs = other.outer_attrs;
    5227              :     has_move = other.has_move;
    5228              :     locus = other.locus;
    5229              : 
    5230              :     // guard to prevent null dereference (only required if error state)
    5231              :     if (other.block_expr != nullptr)
    5232              :       block_expr = other.block_expr->clone_block_expr ();
    5233              :     else
    5234              :       block_expr = nullptr;
    5235              : 
    5236              :     return *this;
    5237              :   }
    5238              : 
    5239              :   // move constructors
    5240              :   AsyncBlockExpr (AsyncBlockExpr &&other) = default;
    5241              :   AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default;
    5242              : 
    5243              :   std::string as_string () const override;
    5244              : 
    5245            0 :   bool get_has_move () { return has_move; }
    5246            0 :   location_t get_locus () const override final { return locus; }
    5247              : 
    5248              :   void accept_vis (ASTVisitor &vis) override;
    5249              : 
    5250              :   // Invalid if block is null, so base stripping on that.
    5251            0 :   void mark_for_strip () override { block_expr = nullptr; }
    5252            0 :   bool is_marked_for_strip () const override { return block_expr == nullptr; }
    5253              : 
    5254              :   // TODO: is this better? Or is a "vis_block" better?
    5255            0 :   std::unique_ptr<BlockExpr> &get_block_expr ()
    5256              :   {
    5257            0 :     rust_assert (block_expr != nullptr);
    5258            0 :     return block_expr;
    5259              :   }
    5260              : 
    5261              :   const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
    5262            0 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5263              : 
    5264            0 :   void set_outer_attrs (std::vector<Attribute> new_attrs) override
    5265              :   {
    5266            0 :     outer_attrs = std::move (new_attrs);
    5267            0 :   }
    5268              : 
    5269            0 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::AsyncBlock; }
    5270              : 
    5271              : protected:
    5272              :   /* Use covariance to implement clone function as returning this object rather
    5273              :    * than base */
    5274            0 :   AsyncBlockExpr *clone_expr_with_block_impl () const override
    5275              :   {
    5276            0 :     return new AsyncBlockExpr (*this);
    5277              :   }
    5278              : };
    5279              : 
    5280              : // Inline-assembly specific options
    5281              : enum class InlineAsmOption
    5282              : {
    5283              :   PURE = 1 << 0,
    5284              :   NOMEM = 1 << 1,
    5285              :   READONLY = 1 << 2,
    5286              :   PRESERVES_FLAGS = 1 << 3,
    5287              :   NORETURN = 1 << 4,
    5288              :   NOSTACK = 1 << 5,
    5289              :   ATT_SYNTAX = 1 << 6,
    5290              :   RAW = 1 << 7,
    5291              :   MAY_UNWIND = 1 << 8,
    5292              : };
    5293              : 
    5294              : struct InlineAsmRegOrRegClass
    5295              : {
    5296              :   enum Type
    5297              :   {
    5298              :     Reg,
    5299              :     RegClass,
    5300              :   };
    5301              : 
    5302              :   struct Reg
    5303              :   {
    5304              :     std::string Symbol;
    5305              :   };
    5306              : 
    5307              :   struct RegClass
    5308              :   {
    5309              :     std::string Symbol;
    5310              :   };
    5311              : 
    5312              :   Type type;
    5313              :   struct Reg reg;
    5314              :   struct RegClass reg_class;
    5315              : 
    5316              :   Identifier name;
    5317              :   location_t locus;
    5318              : };
    5319              : 
    5320           18 : struct LlvmOperand
    5321              : {
    5322              :   std::string constraint;
    5323              :   std::unique_ptr<Expr> expr;
    5324              : 
    5325            2 :   LlvmOperand (std::string constraint, std::unique_ptr<Expr> &&expr)
    5326            4 :     : constraint (constraint), expr (std::move (expr))
    5327              :   {}
    5328              : 
    5329           18 :   LlvmOperand (const LlvmOperand &other)
    5330           36 :     : constraint (other.constraint), expr (other.expr->clone_expr ())
    5331           18 :   {}
    5332              :   LlvmOperand &operator= (const LlvmOperand &other)
    5333              :   {
    5334              :     constraint = other.constraint;
    5335              :     expr = other.expr->clone_expr ();
    5336              : 
    5337              :     return *this;
    5338              :   }
    5339              : };
    5340              : 
    5341          676 : class InlineAsmOperand
    5342              : {
    5343              : public:
    5344              :   enum class RegisterType
    5345              :   {
    5346              :     In,
    5347              :     Out,
    5348              :     InOut,
    5349              :     SplitInOut,
    5350              :     Const,
    5351              :     Sym,
    5352              :     Label,
    5353              :   };
    5354              : 
    5355            0 :   class Register
    5356              :   {
    5357              :   public:
    5358          796 :     Register () {}
    5359            0 :     virtual ~Register () = default;
    5360              : 
    5361          673 :     std::unique_ptr<Register> clone () const
    5362              :     {
    5363          673 :       return std::unique_ptr<Register> (clone_impl ());
    5364              :     }
    5365              : 
    5366              :   protected:
    5367              :     virtual Register *clone_impl () const = 0;
    5368              :   };
    5369              : 
    5370              :   class In : public Register
    5371              :   {
    5372              :   public:
    5373              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5374              :     std::unique_ptr<Expr> expr;
    5375              : 
    5376           12 :     In (tl::optional<struct InlineAsmRegOrRegClass> &reg,
    5377              :         std::unique_ptr<Expr> expr)
    5378           12 :       : reg (reg), expr (std::move (expr))
    5379              :     {
    5380           12 :       rust_assert (this->expr != nullptr);
    5381           12 :     }
    5382              : 
    5383          261 :     In (const In &other)
    5384          261 :     {
    5385          261 :       reg = other.reg;
    5386              : 
    5387          261 :       expr = other.expr->clone_expr ();
    5388          261 :     }
    5389              : 
    5390              :     In operator= (const In &other)
    5391              :     {
    5392              :       reg = other.reg;
    5393              :       expr = other.expr->clone_expr ();
    5394              : 
    5395              :       return *this;
    5396              :     }
    5397              : 
    5398              :   private:
    5399          239 :     In *clone_impl () const { return new In (*this); }
    5400              :   };
    5401              : 
    5402              :   class Out : public Register
    5403              :   {
    5404              :   public:
    5405              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5406              :     bool late;
    5407              :     std::unique_ptr<Expr> expr; // can be null
    5408              : 
    5409           17 :     Out (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5410              :          std::unique_ptr<Expr> expr)
    5411           17 :       : reg (reg), late (late), expr (std::move (expr))
    5412              :     {
    5413           17 :       rust_assert (this->expr != nullptr);
    5414           17 :     }
    5415              : 
    5416          422 :     Out (const Out &other)
    5417          422 :     {
    5418          422 :       reg = other.reg;
    5419          422 :       late = other.late;
    5420          422 :       expr = other.expr->clone_expr ();
    5421          422 :     }
    5422              : 
    5423              :     Out operator= (const Out &other)
    5424              :     {
    5425              :       reg = other.reg;
    5426              :       late = other.late;
    5427              :       expr = other.expr->clone_expr ();
    5428              :       return *this;
    5429              :     }
    5430              : 
    5431              :   private:
    5432          388 :     Out *clone_impl () const { return new Out (*this); }
    5433              :   };
    5434              : 
    5435              :   class InOut : public Register
    5436              :   {
    5437              :   public:
    5438              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5439              :     bool late;
    5440              :     std::unique_ptr<Expr> expr; // this can't be null
    5441              : 
    5442            1 :     InOut (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5443              :            std::unique_ptr<Expr> expr)
    5444            1 :       : reg (reg), late (late), expr (std::move (expr))
    5445              :     {
    5446            1 :       rust_assert (this->expr != nullptr);
    5447            1 :     }
    5448              : 
    5449            1 :     InOut (const InOut &other)
    5450            1 :     {
    5451            1 :       reg = other.reg;
    5452            1 :       late = other.late;
    5453            1 :       expr = other.expr->clone_expr ();
    5454            1 :     }
    5455              : 
    5456              :     InOut operator= (const InOut &other)
    5457              :     {
    5458              :       reg = other.reg;
    5459              :       late = other.late;
    5460              :       expr = other.expr->clone_expr ();
    5461              : 
    5462              :       return *this;
    5463              :     }
    5464              : 
    5465              :   private:
    5466            0 :     InOut *clone_impl () const { return new InOut (*this); }
    5467              :   };
    5468              : 
    5469              :   class SplitInOut : public Register
    5470              :   {
    5471              :   public:
    5472              :     tl::optional<InlineAsmRegOrRegClass> reg;
    5473              :     bool late;
    5474              :     std::unique_ptr<Expr> in_expr;
    5475              :     std::unique_ptr<Expr> out_expr; // could be null
    5476              : 
    5477            2 :     SplitInOut (tl::optional<struct InlineAsmRegOrRegClass> &reg, bool late,
    5478              :                 std::unique_ptr<Expr> in_expr, std::unique_ptr<Expr> out_expr)
    5479            2 :       : reg (reg), late (late), in_expr (std::move (in_expr)),
    5480            2 :         out_expr (std::move (out_expr))
    5481              :     {
    5482            2 :       rust_assert (this->in_expr != nullptr);
    5483            2 :       rust_assert (this->out_expr != nullptr);
    5484            2 :     }
    5485              : 
    5486           80 :     SplitInOut (const SplitInOut &other)
    5487           80 :     {
    5488           80 :       reg = other.reg;
    5489           80 :       late = other.late;
    5490           80 :       in_expr = other.in_expr->clone_expr ();
    5491           80 :       out_expr = other.out_expr->clone_expr ();
    5492           80 :     }
    5493              : 
    5494              :     SplitInOut operator= (const SplitInOut &other)
    5495              :     {
    5496              :       reg = other.reg;
    5497              :       late = other.late;
    5498              :       in_expr = other.in_expr->clone_expr ();
    5499              :       out_expr = other.out_expr->clone_expr ();
    5500              : 
    5501              :       return *this;
    5502              :     }
    5503              : 
    5504              :   private:
    5505           46 :     SplitInOut *clone_impl () const { return new SplitInOut (*this); }
    5506              :   };
    5507              : 
    5508            0 :   class Const : public Register
    5509              :   {
    5510              :   public:
    5511              :     AnonConst anon_const;
    5512              : 
    5513              :   private:
    5514            0 :     Const *clone_impl () const { return new Const (*this); }
    5515              :   };
    5516              : 
    5517            0 :   class Sym : public Register
    5518              :   {
    5519              :   public:
    5520              :     std::unique_ptr<Expr> expr;
    5521              : 
    5522              :     Sym (std::unique_ptr<Expr> expr) : expr (std::move (expr))
    5523              :     {
    5524              :       rust_assert (this->expr != nullptr);
    5525              :     }
    5526            0 :     Sym (const Sym &other)
    5527            0 :     {
    5528            0 :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5529            0 :     }
    5530              : 
    5531              :     Sym operator= (const Sym &other)
    5532              :     {
    5533              :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5534              :       return *this;
    5535              :     }
    5536              : 
    5537              :   private:
    5538            0 :     Sym *clone_impl () const { return new Sym (*this); }
    5539              :   };
    5540              : 
    5541              :   class Label : public Register
    5542              :   {
    5543              :   public:
    5544              :     std::string label_name;
    5545              :     std::unique_ptr<Expr> expr;
    5546              : 
    5547              :     Label (tl::optional<std::string> label_name, std::unique_ptr<Expr> expr)
    5548              :       : expr (std::move (expr))
    5549              :     {
    5550              :       rust_assert (this->expr != nullptr);
    5551              :       if (label_name.has_value ())
    5552              :         this->label_name = label_name.value ();
    5553              :     }
    5554            0 :     Label (const Label &other)
    5555            0 :     {
    5556            0 :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5557            0 :     }
    5558              : 
    5559              :     Label operator= (const Label &other)
    5560              :     {
    5561              :       expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
    5562              :       return *this;
    5563              :     }
    5564              : 
    5565              :   private:
    5566            0 :     Label *clone_impl () const { return new Label (*this); }
    5567              :   };
    5568              : 
    5569          673 :   InlineAsmOperand (const InlineAsmOperand &other)
    5570          673 :     : register_type (other.register_type), locus (other.locus),
    5571          673 :       reg (other.reg->clone ())
    5572          673 :   {}
    5573              : 
    5574           12 :   InlineAsmOperand (const In &reg, location_t locus)
    5575           12 :     : register_type (RegisterType::In), locus (locus), reg (new In (reg))
    5576           12 :   {}
    5577           17 :   InlineAsmOperand (const Out &reg, location_t locus)
    5578           17 :     : register_type (RegisterType::Out), locus (locus), reg (new Out (reg))
    5579           17 :   {}
    5580            1 :   InlineAsmOperand (const InOut &reg, location_t locus)
    5581            1 :     : register_type (RegisterType::InOut), locus (locus), reg (new InOut (reg))
    5582            1 :   {}
    5583            2 :   InlineAsmOperand (const SplitInOut &reg, location_t locus)
    5584            2 :     : register_type (RegisterType::SplitInOut), locus (locus),
    5585            2 :       reg (new SplitInOut (reg))
    5586            2 :   {}
    5587              :   InlineAsmOperand (const Const &reg, location_t locus)
    5588              :     : register_type (RegisterType::Const), locus (locus), reg (new Const (reg))
    5589              :   {}
    5590              :   InlineAsmOperand (const Sym &reg, location_t locus)
    5591              :     : register_type (RegisterType::Sym), locus (locus), reg (new Sym (reg))
    5592              :   {}
    5593              :   InlineAsmOperand (const Label &reg, location_t locus)
    5594              :     : register_type (RegisterType::Label), locus (locus), reg (new Label (reg))
    5595              :   {}
    5596              : 
    5597              :   location_t get_locus () const { return locus; }
    5598          460 :   RegisterType get_register_type () const { return register_type; }
    5599              : 
    5600              :   // Potentially fail immediately if you don't use get_register_type() to
    5601              :   // inspect the RegisterType first before calling the following functions Check
    5602              :   // first
    5603          150 :   In &get_in ()
    5604              :   {
    5605          150 :     rust_assert (register_type == RegisterType::In);
    5606          150 :     return static_cast<In &> (*reg);
    5607              :   }
    5608           10 :   const In &get_in () const
    5609              :   {
    5610           10 :     rust_assert (register_type == RegisterType::In);
    5611           10 :     return static_cast<const In &> (*reg);
    5612              :   }
    5613              : 
    5614          251 :   Out &get_out ()
    5615              :   {
    5616          251 :     rust_assert (register_type == RegisterType::Out);
    5617          251 :     return static_cast<Out &> (*reg);
    5618              :   }
    5619           17 :   const Out &get_out () const
    5620              :   {
    5621           17 :     rust_assert (register_type == RegisterType::Out);
    5622           17 :     return static_cast<const Out &> (*reg);
    5623              :   }
    5624              : 
    5625            0 :   InOut &get_in_out ()
    5626              :   {
    5627            0 :     rust_assert (register_type == RegisterType::InOut);
    5628            0 :     return static_cast<InOut &> (*reg);
    5629              :   }
    5630            0 :   const InOut &get_in_out () const
    5631              :   {
    5632            0 :     rust_assert (register_type == RegisterType::InOut);
    5633            0 :     return static_cast<const InOut &> (*reg);
    5634              :   }
    5635              : 
    5636           30 :   SplitInOut &get_split_in_out ()
    5637              :   {
    5638           30 :     rust_assert (register_type == RegisterType::SplitInOut);
    5639           30 :     return static_cast<SplitInOut &> (*reg);
    5640              :   }
    5641            2 :   const SplitInOut &get_split_in_out () const
    5642              :   {
    5643            2 :     rust_assert (register_type == RegisterType::SplitInOut);
    5644            2 :     return static_cast<const SplitInOut &> (*reg);
    5645              :   }
    5646              : 
    5647            0 :   Const &get_const ()
    5648              :   {
    5649            0 :     rust_assert (register_type == RegisterType::Const);
    5650            0 :     return static_cast<Const &> (*reg);
    5651              :   }
    5652            0 :   const Const &get_const () const
    5653              :   {
    5654            0 :     rust_assert (register_type == RegisterType::Const);
    5655            0 :     return static_cast<Const &> (*reg);
    5656              :   }
    5657              : 
    5658            0 :   Sym &get_sym ()
    5659              :   {
    5660            0 :     rust_assert (register_type == RegisterType::Sym);
    5661            0 :     return static_cast<Sym &> (*reg);
    5662              :   }
    5663            0 :   const Sym &get_sym () const
    5664              :   {
    5665            0 :     rust_assert (register_type == RegisterType::Sym);
    5666            0 :     return static_cast<const Sym &> (*reg);
    5667              :   }
    5668              : 
    5669            0 :   Label &get_label ()
    5670              :   {
    5671            0 :     rust_assert (register_type == RegisterType::Label);
    5672            0 :     return static_cast<Label &> (*reg);
    5673              :   }
    5674            0 :   const Label &get_label () const
    5675              :   {
    5676            0 :     rust_assert (register_type == RegisterType::Label);
    5677            0 :     return static_cast<const Label &> (*reg);
    5678              :   }
    5679              : 
    5680              : private:
    5681              :   RegisterType register_type;
    5682              : 
    5683              :   location_t locus;
    5684              :   std::unique_ptr<Register> reg;
    5685              : };
    5686              : 
    5687              : struct InlineAsmPlaceHolder
    5688              : {
    5689              :   size_t operand_idx;
    5690              :   char modifier; // can be null
    5691              :   location_t locus;
    5692              : };
    5693              : 
    5694            0 : struct InlineAsmTemplatePiece
    5695              : {
    5696              :   bool is_placeholder;
    5697              :   std::string string;
    5698              :   InlineAsmPlaceHolder placeholder;
    5699              : };
    5700              : 
    5701           20 : struct TupleClobber
    5702              : {
    5703            4 :   TupleClobber (std::string symbol, location_t loc) : symbol (symbol), loc (loc)
    5704              :   {}
    5705              :   // as gccrs still doesn't contain a symbol class I have put them as strings
    5706              :   std::string symbol;
    5707              :   location_t loc;
    5708              : };
    5709              : 
    5710          322 : struct TupleTemplateStr
    5711              : {
    5712              :   // as gccrs still doesn't contain a symbol class I have put them as strings
    5713              :   location_t loc;
    5714              :   std::string symbol;
    5715              : 
    5716            2 :   location_t get_locus () { return loc; }
    5717           40 :   TupleTemplateStr (location_t loc, const std::string &symbol)
    5718           80 :     : loc (loc), symbol (symbol)
    5719              :   {}
    5720              : };
    5721              : 
    5722              : // Inline Assembly Node
    5723              : class InlineAsm : public ExprWithoutBlock
    5724              : {
    5725              : public:
    5726              :   enum class Option
    5727              :   {
    5728              :     PURE = 1 << 0,
    5729              :     NOMEM = 1 << 1,
    5730              :     READONLY = 1 << 2,
    5731              :     PRESERVES_FLAGS = 1 << 3,
    5732              :     NORETURN = 1 << 4,
    5733              :     NOSTACK = 1 << 5,
    5734              :     ATT_SYNTAX = 1 << 6,
    5735              :     RAW = 1 << 7,
    5736              :     MAY_UNWIND = 1 << 8,
    5737              :   };
    5738              : 
    5739              : private:
    5740              :   location_t locus;
    5741              :   // TODO: Not sure how outer_attrs plays with InlineAsm, I put it here in order
    5742              :   // to override, very hacky.
    5743              :   std::vector<Attribute> outer_attrs;
    5744              : 
    5745              : public:
    5746              :   // https://github.com/rust-lang/rust/blob/55cac26a9ef17da1c9c77c0816e88e178b7cc5dd/compiler/rustc_builtin_macros/src/asm.rs#L56C1-L64C7
    5747              :   //   let mut args = AsmArgs {
    5748              :   //     templates: vec![first_template],
    5749              :   //     operands: vec![],
    5750              :   //     named_args: Default::default(),
    5751              :   //     reg_args: Default::default(),
    5752              :   //     clobber_abis: Vec::new(),
    5753              :   //     options: ast::InlineAsmOptions::empty(),
    5754              :   //     options_spans: vec![],
    5755              :   // };
    5756              :   std::vector<InlineAsmTemplatePiece> template_;
    5757              :   std::vector<TupleTemplateStr> template_strs;
    5758              :   std::vector<InlineAsmOperand> operands;
    5759              :   std::map<std::string, int> named_args;
    5760              :   std::set<int> reg_args;
    5761              :   std::vector<TupleClobber> clobber_abi;
    5762              :   std::set<InlineAsm::Option> options;
    5763              : 
    5764              :   std::vector<location_t> line_spans;
    5765              : 
    5766              :   bool is_global_asm;
    5767              : 
    5768           39 :   InlineAsm (location_t locus, bool is_global_asm)
    5769           39 :     : locus (locus), is_global_asm (is_global_asm)
    5770           39 :   {}
    5771              : 
    5772              :   void accept_vis (ASTVisitor &vis) override;
    5773            0 :   std::string as_string () const override { return "InlineAsm AST Node"; }
    5774              : 
    5775           54 :   location_t get_locus () const override { return locus; }
    5776              : 
    5777            0 :   void mark_for_strip () override {}
    5778              : 
    5779           92 :   bool is_marked_for_strip () const override { return false; }
    5780              : 
    5781          399 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5782              : 
    5783            0 :   void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
    5784              : 
    5785           27 :   std::vector<InlineAsmTemplatePiece> get_template_ () { return template_; }
    5786              : 
    5787           56 :   std::vector<TupleTemplateStr> get_template_strs () { return template_strs; }
    5788              : 
    5789          426 :   std::vector<InlineAsmOperand> get_operands () { return operands; }
    5790              : 
    5791           27 :   std::vector<TupleClobber> get_clobber_abi () { return clobber_abi; }
    5792              : 
    5793           27 :   std::set<InlineAsm::Option> get_options () { return options; }
    5794              : 
    5795          203 :   InlineAsm *clone_expr_without_block_impl () const override
    5796              :   {
    5797          203 :     return new InlineAsm (*this);
    5798              :   }
    5799              : 
    5800           27 :   Expr::Kind get_expr_kind () const override { return Expr::Kind::InlineAsm; }
    5801              : 
    5802            2 :   static std::string option_to_string (Option option)
    5803              :   {
    5804            2 :     switch (option)
    5805              :       {
    5806            0 :       case Option::PURE:
    5807            0 :         return "pure";
    5808            1 :       case Option::NOMEM:
    5809            1 :         return "nomem";
    5810            0 :       case Option::READONLY:
    5811            0 :         return "readonly";
    5812            0 :       case Option::PRESERVES_FLAGS:
    5813            0 :         return "preserves_flags";
    5814            1 :       case Option::NORETURN:
    5815            1 :         return "noreturn";
    5816            0 :       case Option::NOSTACK:
    5817            0 :         return "nostack";
    5818            0 :       case Option::ATT_SYNTAX:
    5819            0 :         return "att_syntax";
    5820            0 :       case Option::RAW:
    5821            0 :         return "raw";
    5822            0 :       case Option::MAY_UNWIND:
    5823            0 :         return "may_unwind";
    5824            0 :       default:
    5825            0 :         rust_unreachable ();
    5826              :       }
    5827              :   }
    5828              : };
    5829              : 
    5830              : class LlvmInlineAsm : public ExprWithoutBlock
    5831              : {
    5832              :   // llvm_asm!("" :         : "r"(&mut dummy) : "memory" : "volatile");
    5833              :   //           Asm, Outputs, Inputs,            Clobbers, Options,
    5834              : 
    5835              : public:
    5836              :   enum class Dialect
    5837              :   {
    5838              :     Att,
    5839              :     Intel,
    5840              :   };
    5841              : 
    5842              : private:
    5843              :   location_t locus;
    5844              :   std::vector<Attribute> outer_attrs;
    5845              :   std::vector<LlvmOperand> inputs;
    5846              :   std::vector<LlvmOperand> outputs;
    5847              :   std::vector<TupleTemplateStr> templates;
    5848              :   std::vector<TupleClobber> clobbers;
    5849              :   bool volatility;
    5850              :   bool align_stack;
    5851              :   Dialect dialect;
    5852              : 
    5853              : public:
    5854            2 :   LlvmInlineAsm (location_t locus) : locus (locus) {}
    5855              : 
    5856            2 :   Dialect get_dialect () { return dialect; }
    5857              : 
    5858           28 :   location_t get_locus () const override { return locus; }
    5859              : 
    5860            0 :   void mark_for_strip () override {}
    5861              : 
    5862            8 :   bool is_marked_for_strip () const override { return false; }
    5863              : 
    5864            2 :   std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
    5865              : 
    5866              :   void accept_vis (ASTVisitor &vis) override;
    5867              : 
    5868            0 :   std::string as_string () const override { return "InlineAsm AST Node"; }
    5869              : 
    5870            0 :   void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
    5871              : 
    5872           14 :   LlvmInlineAsm *clone_expr_without_block_impl () const override
    5873              :   {
    5874           14 :     return new LlvmInlineAsm (*this);
    5875              :   }
    5876              : 
    5877            6 :   std::vector<TupleTemplateStr> &get_templates () { return templates; }
    5878              :   const std::vector<TupleTemplateStr> &get_templates () const
    5879              :   {
    5880              :     return templates;
    5881              :   }
    5882              : 
    5883            2 :   Expr::Kind get_expr_kind () const override
    5884              :   {
    5885            2 :     return Expr::Kind::LlvmInlineAsm;
    5886              :   }
    5887              : 
    5888            0 :   void set_align_stack (bool align_stack) { this->align_stack = align_stack; }
    5889            2 :   bool is_stack_aligned () { return align_stack; }
    5890              : 
    5891            2 :   void set_volatile (bool volatility) { this->volatility = volatility; }
    5892            2 :   bool is_volatile () { return volatility; }
    5893              : 
    5894            0 :   void set_dialect (Dialect dialect) { this->dialect = dialect; }
    5895              : 
    5896              :   void set_inputs (std::vector<LlvmOperand> operands) { inputs = operands; }
    5897              :   void set_outputs (std::vector<LlvmOperand> operands) { outputs = operands; }
    5898              : 
    5899           36 :   std::vector<LlvmOperand> &get_inputs () { return inputs; }
    5900              :   const std::vector<LlvmOperand> &get_inputs () const { return inputs; }
    5901           36 :   std::vector<LlvmOperand> &get_outputs () { return outputs; }
    5902              :   const std::vector<LlvmOperand> &get_outputs () const { return outputs; }
    5903              : 
    5904            6 :   std::vector<TupleClobber> &get_clobbers () { return clobbers; }
    5905              :   const std::vector<TupleClobber> &get_clobbers () const { return clobbers; }
    5906              : };
    5907              : 
    5908              : } // namespace AST
    5909              : } // namespace Rust
    5910              : 
    5911              : #endif
        

Generated by: LCOV version 2.4-beta

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