LCOV - code coverage report
Current view: top level - gcc/rust/ast - rust-expr.h (source / functions) Coverage Total Hit
Test: gcc.info Lines: 82.9 % 1701 1410
Test Date: 2026-03-28 14:25:54 Functions: 78.8 % 576 454
Legend: Lines:     hit not hit

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