--- /dev/null
+#ifndef RUST_AST_EXPR_H
+#define RUST_AST_EXPR_H
+
+#include "rust-ast.h"
+#include "rust-path.h"
+#include "operator.h"
+
+namespace Rust {
+namespace AST {
+/* TODO: if GCC moves to C++17 or allows boost, replace some boolean
+ * "has_whatever" pairs with
+ * optional types (std::optional or boost::optional)? */
+
+// AST node for an expression with an accompanying block - abstract
+class ExprWithBlock : public Expr
+{
+protected:
+ // pure virtual clone implementation
+ virtual ExprWithBlock *clone_expr_with_block_impl () const = 0;
+
+ // prevent having to define multiple clone expressions
+ ExprWithBlock *clone_expr_impl () const final override
+ {
+ return clone_expr_with_block_impl ();
+ }
+
+ bool is_expr_without_block () const final override { return false; };
+
+public:
+ // Unique pointer custom clone function
+ std::unique_ptr<ExprWithBlock> clone_expr_with_block () const
+ {
+ return std::unique_ptr<ExprWithBlock> (clone_expr_with_block_impl ());
+ }
+};
+
+// Literals? Or literal base?
+class LiteralExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ Literal literal;
+ Location locus;
+
+public:
+ std::string as_string () const override { return literal.as_string (); }
+
+ Literal::LitType get_lit_type () const { return literal.get_lit_type (); }
+
+ LiteralExpr (std::string value_as_string, Literal::LitType type,
+ PrimitiveCoreType type_hint, std::vector<Attribute> outer_attrs,
+ Location locus)
+ : outer_attrs (std::move (outer_attrs)),
+ literal (std::move (value_as_string), type, type_hint), locus (locus)
+ {}
+
+ LiteralExpr (Literal literal, std::vector<Attribute> outer_attrs,
+ Location locus)
+ : outer_attrs (std::move (outer_attrs)), literal (std::move (literal)),
+ locus (locus)
+ {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<LiteralExpr> clone_literal_expr () const
+ {
+ return std::unique_ptr<LiteralExpr> (clone_literal_expr_impl ());
+ }
+
+ Location get_locus () const override final { return locus; }
+
+ Literal get_literal () const { return literal; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if literal is in error state, so base stripping on that.
+ void mark_for_strip () override { literal = Literal::create_error (); }
+ bool is_marked_for_strip () const override { return literal.is_error (); }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ LiteralExpr *clone_expr_without_block_impl () const final override
+ {
+ return clone_literal_expr_impl ();
+ }
+
+ /* not virtual as currently no subclasses of LiteralExpr, but could be in
+ * future */
+ /*virtual*/ LiteralExpr *clone_literal_expr_impl () const
+ {
+ return new LiteralExpr (*this);
+ }
+};
+
+// Literal expression attribute body (non-macro attribute)
+class AttrInputLiteral : public AttrInput
+{
+ LiteralExpr literal_expr;
+
+public:
+ AttrInputLiteral (LiteralExpr lit_expr) : literal_expr (std::move (lit_expr))
+ {}
+
+ std::string as_string () const override
+ {
+ return " = " + literal_expr.as_string ();
+ }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ /* this can never be a cfg predicate - cfg and cfg_attr require a token-tree
+ * cfg */
+ bool check_cfg_predicate (const Session &) const override { return false; }
+
+ bool is_meta_item () const override { return false; }
+
+ LiteralExpr &get_literal () { return literal_expr; }
+
+ AttrInputType get_attr_input_type () const final override
+ {
+ return AttrInput::AttrInputType::LITERAL;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ AttrInputLiteral *clone_attr_input_impl () const override
+ {
+ return new AttrInputLiteral (*this);
+ }
+};
+
+/* literal expr only meta item inner - TODO possibly replace with inheritance of
+ * LiteralExpr itself? */
+class MetaItemLitExpr : public MetaItemInner
+{
+ LiteralExpr lit_expr;
+
+public:
+ MetaItemLitExpr (LiteralExpr lit_expr) : lit_expr (std::move (lit_expr)) {}
+
+ std::string as_string () const override { return lit_expr.as_string (); }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaItemLitExpr *clone_meta_item_inner_impl () const override
+ {
+ return new MetaItemLitExpr (*this);
+ }
+};
+
+// more generic meta item "path = lit" form
+class MetaItemPathLit : public MetaItem
+{
+ SimplePath path;
+ LiteralExpr lit;
+
+public:
+ MetaItemPathLit (SimplePath path, LiteralExpr lit_expr)
+ : path (std::move (path)), lit (std::move (lit_expr))
+ {}
+
+ std::string as_string () const override
+ {
+ return path.as_string () + " = " + lit.as_string ();
+ }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &session) const override;
+ /* TODO: return true if "ident" is defined and value of it is "lit", return
+ * false otherwise */
+
+ Attribute to_attribute () const override;
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaItemPathLit *clone_meta_item_inner_impl () const override
+ {
+ return new MetaItemPathLit (*this);
+ }
+};
+
+/* Represents an expression using unary or binary operators as AST node. Can be
+ * overloaded. */
+class OperatorExpr : public ExprWithoutBlock
+{
+ // TODO: create binary and unary operator subclasses?
+public:
+ Location locus;
+
+protected:
+ /* Variables must be protected to allow derived classes to use them as first
+ * class citizens */
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> main_or_left_expr;
+
+ // Constructor (only for initialisation of expr purposes)
+ OperatorExpr (std::unique_ptr<Expr> main_or_left_expr,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : locus (locus), outer_attrs (std::move (outer_attribs)),
+ main_or_left_expr (std::move (main_or_left_expr))
+ {}
+
+ // Copy constructor (only for initialisation of expr purposes)
+ OperatorExpr (OperatorExpr const &other)
+ : locus (other.locus), outer_attrs (other.outer_attrs)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.main_or_left_expr != nullptr)
+ main_or_left_expr = other.main_or_left_expr->clone_expr ();
+ }
+
+ // Overload assignment operator to deep copy expr
+ OperatorExpr &operator= (OperatorExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.main_or_left_expr != nullptr)
+ main_or_left_expr = other.main_or_left_expr->clone_expr ();
+ else
+ main_or_left_expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ OperatorExpr (OperatorExpr &&other) = default;
+ OperatorExpr &operator= (OperatorExpr &&other) = default;
+
+public:
+ Location get_locus () const override final { return locus; }
+
+ // Invalid if expr is null, so base stripping on that.
+ void mark_for_strip () override { main_or_left_expr = nullptr; }
+ bool is_marked_for_strip () const override
+ {
+ return main_or_left_expr == nullptr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+};
+
+/* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be
+ * overloaded. */
+class BorrowExpr : public OperatorExpr
+{
+ bool is_mut;
+ bool double_borrow;
+
+public:
+ std::string as_string () const override;
+
+ BorrowExpr (std::unique_ptr<Expr> borrow_lvalue, bool is_mut_borrow,
+ bool is_double_borrow, std::vector<Attribute> outer_attribs,
+ Location locus)
+ : OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs),
+ locus),
+ is_mut (is_mut_borrow), double_borrow (is_double_borrow)
+ {}
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_borrowed_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+ bool get_is_mut () const { return is_mut; }
+
+ bool get_is_double_borrow () const { return double_borrow; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ BorrowExpr *clone_expr_without_block_impl () const override
+ {
+ return new BorrowExpr (*this);
+ }
+};
+
+// Unary prefix * deference operator
+class DereferenceExpr : public OperatorExpr
+{
+public:
+ std::string as_string () const override;
+
+ // Constructor calls OperatorExpr's protected constructor
+ DereferenceExpr (std::unique_ptr<Expr> deref_lvalue,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : OperatorExpr (std::move (deref_lvalue), std::move (outer_attribs), locus)
+ {}
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_dereferenced_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ DereferenceExpr *clone_expr_without_block_impl () const override
+ {
+ return new DereferenceExpr (*this);
+ }
+};
+
+// Unary postfix ? error propogation operator. Cannot be overloaded.
+class ErrorPropagationExpr : public OperatorExpr
+{
+public:
+ std::string as_string () const override;
+
+ // Constructor calls OperatorExpr's protected constructor
+ ErrorPropagationExpr (std::unique_ptr<Expr> potential_error_value,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : OperatorExpr (std::move (potential_error_value),
+ std::move (outer_attribs), locus)
+ {}
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_propagating_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ErrorPropagationExpr *clone_expr_without_block_impl () const override
+ {
+ return new ErrorPropagationExpr (*this);
+ }
+};
+
+// Unary prefix - or ! negation or NOT operators.
+class NegationExpr : public OperatorExpr
+{
+public:
+ using ExprType = NegationOperator;
+
+private:
+ /* Note: overload negation via std::ops::Neg and not via std::ops::Not
+ * Negation only works for signed integer and floating-point types, NOT only
+ * works for boolean and integer types (via bitwise NOT) */
+ ExprType expr_type;
+
+public:
+ std::string as_string () const override;
+
+ ExprType get_expr_type () const { return expr_type; }
+
+ // Constructor calls OperatorExpr's protected constructor
+ NegationExpr (std::unique_ptr<Expr> negated_value, ExprType expr_kind,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : OperatorExpr (std::move (negated_value), std::move (outer_attribs),
+ locus),
+ expr_type (expr_kind)
+ {}
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_negated_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ NegationExpr *clone_expr_without_block_impl () const override
+ {
+ return new NegationExpr (*this);
+ }
+};
+
+// Infix binary operators. +, -, *, /, %, &, |, ^, <<, >>
+class ArithmeticOrLogicalExpr : public OperatorExpr
+{
+public:
+ using ExprType = ArithmeticOrLogicalOperator;
+
+private:
+ // Note: overloading trait specified in comments
+ ExprType expr_type;
+
+ std::unique_ptr<Expr> right_expr;
+
+public:
+ std::string as_string () const override;
+
+ ExprType get_expr_type () const { return expr_type; }
+
+ // Constructor calls OperatorExpr's protected constructor
+ ArithmeticOrLogicalExpr (std::unique_ptr<Expr> left_value,
+ std::unique_ptr<Expr> right_value,
+ ExprType expr_kind, Location locus)
+ : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
+ expr_type (expr_kind), right_expr (std::move (right_value))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor - probably required due to unique pointer
+ ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other)
+ : OperatorExpr (other), expr_type (other.expr_type),
+ right_expr (other.right_expr->clone_expr ())
+ {}
+
+ // Overload assignment operator
+ ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr const &other)
+ {
+ OperatorExpr::operator= (other);
+ // main_or_left_expr = other.main_or_left_expr->clone_expr();
+ right_expr = other.right_expr->clone_expr ();
+ expr_type = other.expr_type;
+
+ return *this;
+ }
+
+ // move constructors
+ ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr &&other) = default;
+ ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr &&other)
+ = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_left_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_right_expr ()
+ {
+ rust_assert (right_expr != nullptr);
+ return right_expr;
+ }
+
+ void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+ void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override
+ {
+ return new ArithmeticOrLogicalExpr (*this);
+ }
+};
+
+// Infix binary comparison operators. ==, !=, <, <=, >, >=
+class ComparisonExpr : public OperatorExpr
+{
+public:
+ using ExprType = ComparisonOperator;
+
+private:
+ // Note: overloading trait specified in comments
+ ExprType expr_type;
+
+ std::unique_ptr<Expr> right_expr;
+
+public:
+ std::string as_string () const override;
+
+ ExprType get_expr_type () const { return expr_type; }
+
+ // Constructor requires pointers for polymorphism
+ ComparisonExpr (std::unique_ptr<Expr> left_value,
+ std::unique_ptr<Expr> right_value, ExprType comparison_kind,
+ Location locus)
+ : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
+ expr_type (comparison_kind), right_expr (std::move (right_value))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor also calls OperatorExpr's protected constructor
+ ComparisonExpr (ComparisonExpr const &other)
+ : OperatorExpr (other), expr_type (other.expr_type),
+ right_expr (other.right_expr->clone_expr ())
+ {}
+
+ // Overload assignment operator to deep copy
+ ComparisonExpr &operator= (ComparisonExpr const &other)
+ {
+ OperatorExpr::operator= (other);
+ // main_or_left_expr = other.main_or_left_expr->clone_expr();
+ right_expr = other.right_expr->clone_expr ();
+ expr_type = other.expr_type;
+ // outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ ComparisonExpr (ComparisonExpr &&other) = default;
+ ComparisonExpr &operator= (ComparisonExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_left_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_right_expr ()
+ {
+ rust_assert (right_expr != nullptr);
+ return right_expr;
+ }
+
+ ExprType get_kind () { return expr_type; }
+
+ /* TODO: implement via a function call to std::cmp::PartialEq::eq(&op1, &op2)
+ * maybe? */
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ComparisonExpr *clone_expr_without_block_impl () const override
+ {
+ return new ComparisonExpr (*this);
+ }
+};
+
+// Infix binary lazy boolean logical operators && and ||.
+class LazyBooleanExpr : public OperatorExpr
+{
+public:
+ using ExprType = LazyBooleanOperator;
+
+private:
+ ExprType expr_type;
+
+ std::unique_ptr<Expr> right_expr;
+
+public:
+ // Constructor calls OperatorExpr's protected constructor
+ LazyBooleanExpr (std::unique_ptr<Expr> left_bool_expr,
+ std::unique_ptr<Expr> right_bool_expr, ExprType expr_kind,
+ Location locus)
+ : OperatorExpr (std::move (left_bool_expr), std::vector<Attribute> (),
+ locus),
+ expr_type (expr_kind), right_expr (std::move (right_bool_expr))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor also calls OperatorExpr's protected constructor
+ LazyBooleanExpr (LazyBooleanExpr const &other)
+ : OperatorExpr (other), expr_type (other.expr_type),
+ right_expr (other.right_expr->clone_expr ())
+ {}
+
+ // Overload assignment operator to deep copy
+ LazyBooleanExpr &operator= (LazyBooleanExpr const &other)
+ {
+ OperatorExpr::operator= (other);
+ // main_or_left_expr = other.main_or_left_expr->clone_expr();
+ right_expr = other.right_expr->clone_expr ();
+ expr_type = other.expr_type;
+
+ return *this;
+ }
+
+ // move constructors
+ LazyBooleanExpr (LazyBooleanExpr &&other) = default;
+ LazyBooleanExpr &operator= (LazyBooleanExpr &&other) = default;
+
+ std::string as_string () const override;
+
+ ExprType get_expr_type () const { return expr_type; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_left_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_right_expr ()
+ {
+ rust_assert (right_expr != nullptr);
+ return right_expr;
+ }
+
+ ExprType get_kind () { return expr_type; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ LazyBooleanExpr *clone_expr_without_block_impl () const override
+ {
+ return new LazyBooleanExpr (*this);
+ }
+};
+
+// Binary infix "as" cast expression.
+class TypeCastExpr : public OperatorExpr
+{
+ std::unique_ptr<TypeNoBounds> type_to_convert_to;
+
+ // Note: only certain type casts allowed, outlined in reference
+public:
+ std::string as_string () const override;
+
+ // Constructor requires calling protected constructor of OperatorExpr
+ TypeCastExpr (std::unique_ptr<Expr> expr_to_cast,
+ std::unique_ptr<TypeNoBounds> type_to_cast_to, Location locus)
+ : OperatorExpr (std::move (expr_to_cast), std::vector<Attribute> (), locus),
+ type_to_convert_to (std::move (type_to_cast_to))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor also requires calling protected constructor
+ TypeCastExpr (TypeCastExpr const &other)
+ : OperatorExpr (other),
+ type_to_convert_to (other.type_to_convert_to->clone_type_no_bounds ())
+ {}
+
+ // Overload assignment operator to deep copy
+ TypeCastExpr &operator= (TypeCastExpr const &other)
+ {
+ OperatorExpr::operator= (other);
+ // main_or_left_expr = other.main_or_left_expr->clone_expr();
+ type_to_convert_to = other.type_to_convert_to->clone_type_no_bounds ();
+
+ return *this;
+ }
+
+ // move constructors
+ TypeCastExpr (TypeCastExpr &&other) = default;
+ TypeCastExpr &operator= (TypeCastExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_casted_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<TypeNoBounds> &get_type_to_cast_to ()
+ {
+ rust_assert (type_to_convert_to != nullptr);
+ return type_to_convert_to;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TypeCastExpr *clone_expr_without_block_impl () const override
+ {
+ return new TypeCastExpr (*this);
+ }
+};
+
+// Binary assignment expression.
+class AssignmentExpr : public OperatorExpr
+{
+ std::unique_ptr<Expr> right_expr;
+
+public:
+ std::string as_string () const override;
+
+ // Call OperatorExpr constructor to initialise left_expr
+ AssignmentExpr (std::unique_ptr<Expr> value_to_assign_to,
+ std::unique_ptr<Expr> value_to_assign,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : OperatorExpr (std::move (value_to_assign_to), std::move (outer_attribs),
+ locus),
+ right_expr (std::move (value_to_assign))
+ {}
+ // outer attributes not allowed
+
+ // Call OperatorExpr constructor in copy constructor, as well as clone
+ AssignmentExpr (AssignmentExpr const &other)
+ : OperatorExpr (other), right_expr (other.right_expr->clone_expr ())
+ {}
+
+ // Overload assignment operator to clone unique_ptr right_expr
+ AssignmentExpr &operator= (AssignmentExpr const &other)
+ {
+ OperatorExpr::operator= (other);
+ // main_or_left_expr = other.main_or_left_expr->clone_expr();
+ right_expr = other.right_expr->clone_expr ();
+ // outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ AssignmentExpr (AssignmentExpr &&other) = default;
+ AssignmentExpr &operator= (AssignmentExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+ void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_left_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_right_expr ()
+ {
+ rust_assert (right_expr != nullptr);
+ return right_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ AssignmentExpr *clone_expr_without_block_impl () const override
+ {
+ return new AssignmentExpr (*this);
+ }
+};
+
+/* Binary infix compound assignment (arithmetic or logic then assignment)
+ * expressions. */
+class CompoundAssignmentExpr : public OperatorExpr
+{
+public:
+ using ExprType = CompoundAssignmentOperator;
+
+private:
+ // Note: overloading trait specified in comments
+ ExprType expr_type;
+ std::unique_ptr<Expr> right_expr;
+
+public:
+ std::string as_string () const override;
+
+ ExprType get_expr_type () const { return expr_type; }
+
+ // Use pointers in constructor to enable polymorphism
+ CompoundAssignmentExpr (std::unique_ptr<Expr> value_to_assign_to,
+ std::unique_ptr<Expr> value_to_assign,
+ ExprType expr_kind, Location locus)
+ : OperatorExpr (std::move (value_to_assign_to), std::vector<Attribute> (),
+ locus),
+ expr_type (expr_kind), right_expr (std::move (value_to_assign))
+ {}
+ // outer attributes not allowed
+
+ // Have clone in copy constructor
+ CompoundAssignmentExpr (CompoundAssignmentExpr const &other)
+ : OperatorExpr (other), expr_type (other.expr_type),
+ right_expr (other.right_expr->clone_expr ())
+ {}
+
+ // Overload assignment operator to clone
+ CompoundAssignmentExpr &operator= (CompoundAssignmentExpr const &other)
+ {
+ OperatorExpr::operator= (other);
+ // main_or_left_expr = other.main_or_left_expr->clone_expr();
+ right_expr = other.right_expr->clone_expr ();
+ expr_type = other.expr_type;
+ // outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ CompoundAssignmentExpr (CompoundAssignmentExpr &&other) = default;
+ CompoundAssignmentExpr &operator= (CompoundAssignmentExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_left_expr ()
+ {
+ rust_assert (main_or_left_expr != nullptr);
+ return main_or_left_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_right_expr ()
+ {
+ rust_assert (right_expr != nullptr);
+ return right_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ CompoundAssignmentExpr *clone_expr_without_block_impl () const override
+ {
+ return new CompoundAssignmentExpr (*this);
+ }
+};
+
+// Expression in parentheses (i.e. like literally just any 3 + (2 * 6))
+class GroupedExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::vector<Attribute> inner_attrs;
+ std::unique_ptr<Expr> expr_in_parens;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ GroupedExpr (std::unique_ptr<Expr> parenthesised_expr,
+ std::vector<Attribute> inner_attribs,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ inner_attrs (std::move (inner_attribs)),
+ expr_in_parens (std::move (parenthesised_expr)), locus (locus)
+ {}
+
+ // Copy constructor includes clone for expr_in_parens
+ GroupedExpr (GroupedExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ inner_attrs (other.inner_attrs), locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr_in_parens != nullptr)
+ expr_in_parens = other.expr_in_parens->clone_expr ();
+ }
+
+ // Overloaded assignment operator to clone expr_in_parens
+ GroupedExpr &operator= (GroupedExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ inner_attrs = other.inner_attrs;
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr_in_parens != nullptr)
+ expr_in_parens = other.expr_in_parens->clone_expr ();
+ else
+ expr_in_parens = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ GroupedExpr (GroupedExpr &&other) = default;
+ GroupedExpr &operator= (GroupedExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if inner expr is null, so base stripping on that.
+ void mark_for_strip () override { expr_in_parens = nullptr; }
+ bool is_marked_for_strip () const override
+ {
+ return expr_in_parens == nullptr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_expr_in_parens ()
+ {
+ rust_assert (expr_in_parens != nullptr);
+ return expr_in_parens;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ GroupedExpr *clone_expr_without_block_impl () const override
+ {
+ return new GroupedExpr (*this);
+ }
+};
+
+// Base array initialisation internal element representation thing (abstract)
+// aka ArrayElements
+class ArrayElems
+{
+public:
+ virtual ~ArrayElems () {}
+
+ // Unique pointer custom clone ArrayElems function
+ std::unique_ptr<ArrayElems> clone_array_elems () const
+ {
+ return std::unique_ptr<ArrayElems> (clone_array_elems_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ NodeId get_node_id () const { return node_id; }
+
+protected:
+ ArrayElems () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+ // pure virtual clone implementation
+ virtual ArrayElems *clone_array_elems_impl () const = 0;
+
+ NodeId node_id;
+};
+
+// Value array elements
+class ArrayElemsValues : public ArrayElems
+{
+ std::vector<std::unique_ptr<Expr> > values;
+ Location locus;
+
+public:
+ ArrayElemsValues (std::vector<std::unique_ptr<Expr> > elems, Location locus)
+ : ArrayElems (), values (std::move (elems)), locus (locus)
+ {}
+
+ // copy constructor with vector clone
+ ArrayElemsValues (ArrayElemsValues const &other)
+ {
+ values.reserve (other.values.size ());
+ for (const auto &e : other.values)
+ values.push_back (e->clone_expr ());
+ }
+
+ // overloaded assignment operator with vector clone
+ ArrayElemsValues &operator= (ArrayElemsValues const &other)
+ {
+ values.reserve (other.values.size ());
+ for (const auto &e : other.values)
+ values.push_back (e->clone_expr ());
+
+ return *this;
+ }
+
+ // move constructors
+ ArrayElemsValues (ArrayElemsValues &&other) = default;
+ ArrayElemsValues &operator= (ArrayElemsValues &&other) = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<std::unique_ptr<Expr> > &get_values () const
+ {
+ return values;
+ }
+ std::vector<std::unique_ptr<Expr> > &get_values () { return values; }
+
+ size_t get_num_values () const { return values.size (); }
+
+protected:
+ ArrayElemsValues *clone_array_elems_impl () const override
+ {
+ return new ArrayElemsValues (*this);
+ }
+};
+
+// Copied array element and number of copies
+class ArrayElemsCopied : public ArrayElems
+{
+ std::unique_ptr<Expr> elem_to_copy;
+ std::unique_ptr<Expr> num_copies;
+ Location locus;
+
+public:
+ // Constructor requires pointers for polymorphism
+ ArrayElemsCopied (std::unique_ptr<Expr> copied_elem,
+ std::unique_ptr<Expr> copy_amount, Location locus)
+ : ArrayElems (), elem_to_copy (std::move (copied_elem)),
+ num_copies (std::move (copy_amount)), locus (locus)
+ {}
+
+ // Copy constructor required due to unique_ptr - uses custom clone
+ ArrayElemsCopied (ArrayElemsCopied const &other)
+ : elem_to_copy (other.elem_to_copy->clone_expr ()),
+ num_copies (other.num_copies->clone_expr ())
+ {}
+
+ // Overloaded assignment operator for deep copying
+ ArrayElemsCopied &operator= (ArrayElemsCopied const &other)
+ {
+ elem_to_copy = other.elem_to_copy->clone_expr ();
+ num_copies = other.num_copies->clone_expr ();
+
+ return *this;
+ }
+
+ // move constructors
+ ArrayElemsCopied (ArrayElemsCopied &&other) = default;
+ ArrayElemsCopied &operator= (ArrayElemsCopied &&other) = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_elem_to_copy ()
+ {
+ rust_assert (elem_to_copy != nullptr);
+ return elem_to_copy;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_num_copies ()
+ {
+ rust_assert (num_copies != nullptr);
+ return num_copies;
+ }
+
+protected:
+ ArrayElemsCopied *clone_array_elems_impl () const override
+ {
+ return new ArrayElemsCopied (*this);
+ }
+};
+
+// Array definition-ish expression
+class ArrayExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::vector<Attribute> inner_attrs;
+ std::unique_ptr<ArrayElems> internal_elements;
+ Location locus;
+
+ // TODO: find another way to store this to save memory?
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override;
+
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ // Constructor requires ArrayElems pointer
+ ArrayExpr (std::unique_ptr<ArrayElems> array_elems,
+ std::vector<Attribute> inner_attribs,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ inner_attrs (std::move (inner_attribs)),
+ internal_elements (std::move (array_elems)), locus (locus)
+ {
+ rust_assert (internal_elements != nullptr);
+ }
+
+ // Copy constructor requires cloning ArrayElems for polymorphism to hold
+ ArrayExpr (ArrayExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ inner_attrs (other.inner_attrs), locus (other.locus),
+ marked_for_strip (other.marked_for_strip)
+ {
+ internal_elements = other.internal_elements->clone_array_elems ();
+ rust_assert (internal_elements != nullptr);
+ }
+
+ // Overload assignment operator to clone internal_elements
+ ArrayExpr &operator= (ArrayExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ inner_attrs = other.inner_attrs;
+ locus = other.locus;
+ marked_for_strip = other.marked_for_strip;
+ outer_attrs = other.outer_attrs;
+
+ internal_elements = other.internal_elements->clone_array_elems ();
+
+ rust_assert (internal_elements != nullptr);
+ return *this;
+ }
+
+ // move constructors
+ ArrayExpr (ArrayExpr &&other) = default;
+ ArrayExpr &operator= (ArrayExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can't think of any invalid invariants, so store boolean.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<ArrayElems> &get_array_elems ()
+ {
+ rust_assert (internal_elements != nullptr);
+ return internal_elements;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ArrayExpr *clone_expr_without_block_impl () const override
+ {
+ return new ArrayExpr (*this);
+ }
+};
+
+// Aka IndexExpr (also applies to slices)
+/* Apparently a[b] is equivalent to *std::ops::Index::index(&a, b) or
+ * *std::ops::Index::index_mut(&mut a, b) */
+/* Also apparently deref operations on a will be repeatedly applied to find an
+ * implementation */
+class ArrayIndexExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> array_expr;
+ std::unique_ptr<Expr> index_expr;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ ArrayIndexExpr (std::unique_ptr<Expr> array_expr,
+ std::unique_ptr<Expr> array_index_expr,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ array_expr (std::move (array_expr)),
+ index_expr (std::move (array_index_expr)), locus (locus)
+ {}
+
+ // Copy constructor requires special cloning due to unique_ptr
+ ArrayIndexExpr (ArrayIndexExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.array_expr != nullptr)
+ array_expr = other.array_expr->clone_expr ();
+ if (other.index_expr != nullptr)
+ index_expr = other.index_expr->clone_expr ();
+ }
+
+ // Overload assignment operator to clone unique_ptrs
+ ArrayIndexExpr &operator= (ArrayIndexExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.array_expr != nullptr)
+ array_expr = other.array_expr->clone_expr ();
+ else
+ array_expr = nullptr;
+ if (other.index_expr != nullptr)
+ index_expr = other.index_expr->clone_expr ();
+ else
+ index_expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ ArrayIndexExpr (ArrayIndexExpr &&other) = default;
+ ArrayIndexExpr &operator= (ArrayIndexExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if either expr is null, so base stripping on that.
+ void mark_for_strip () override
+ {
+ array_expr = nullptr;
+ index_expr = nullptr;
+ }
+ bool is_marked_for_strip () const override
+ {
+ return array_expr == nullptr && index_expr == nullptr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_array_expr ()
+ {
+ rust_assert (array_expr != nullptr);
+ return array_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_index_expr ()
+ {
+ rust_assert (index_expr != nullptr);
+ return index_expr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ArrayIndexExpr *clone_expr_without_block_impl () const override
+ {
+ return new ArrayIndexExpr (*this);
+ }
+};
+
+// AST representation of a tuple
+class TupleExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::vector<Attribute> inner_attrs;
+ std::vector<std::unique_ptr<Expr> > tuple_elems;
+ Location locus;
+
+ // TODO: find another way to store this to save memory?
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override;
+
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ TupleExpr (std::vector<std::unique_ptr<Expr> > tuple_elements,
+ std::vector<Attribute> inner_attribs,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ inner_attrs (std::move (inner_attribs)),
+ tuple_elems (std::move (tuple_elements)), locus (locus)
+ {}
+
+ // copy constructor with vector clone
+ TupleExpr (TupleExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ inner_attrs (other.inner_attrs), locus (other.locus),
+ marked_for_strip (other.marked_for_strip)
+ {
+ tuple_elems.reserve (other.tuple_elems.size ());
+ for (const auto &e : other.tuple_elems)
+ tuple_elems.push_back (e->clone_expr ());
+ }
+
+ // overloaded assignment operator to vector clone
+ TupleExpr &operator= (TupleExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ outer_attrs = other.outer_attrs;
+ inner_attrs = other.inner_attrs;
+ locus = other.locus;
+ marked_for_strip = other.marked_for_strip;
+
+ tuple_elems.reserve (other.tuple_elems.size ());
+ for (const auto &e : other.tuple_elems)
+ tuple_elems.push_back (e->clone_expr ());
+
+ return *this;
+ }
+
+ // move constructors
+ TupleExpr (TupleExpr &&other) = default;
+ TupleExpr &operator= (TupleExpr &&other) = default;
+
+ /* Note: syntactically, can disambiguate single-element tuple from parens with
+ * comma, i.e. (0,) rather than (0) */
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can't think of any invalid invariants, so store boolean.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<std::unique_ptr<Expr> > &get_tuple_elems () const
+ {
+ return tuple_elems;
+ }
+ std::vector<std::unique_ptr<Expr> > &get_tuple_elems ()
+ {
+ return tuple_elems;
+ }
+
+ bool is_unit () const { return tuple_elems.size () == 0; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TupleExpr *clone_expr_without_block_impl () const override
+ {
+ return new TupleExpr (*this);
+ }
+};
+
+// aka TupleIndexingExpr
+// AST representation of a tuple indexing expression
+class TupleIndexExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> tuple_expr;
+ // TupleIndex is a decimal int literal with no underscores or suffix
+ TupleIndex tuple_index;
+
+ Location locus;
+
+ // i.e. pair.0
+
+public:
+ std::string as_string () const override;
+
+ TupleIndex get_tuple_index () const { return tuple_index; }
+
+ TupleIndexExpr (std::unique_ptr<Expr> tuple_expr, TupleIndex index,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus)
+ {}
+
+ // Copy constructor requires a clone for tuple_expr
+ TupleIndexExpr (TupleIndexExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ tuple_index (other.tuple_index), locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.tuple_expr != nullptr)
+ tuple_expr = other.tuple_expr->clone_expr ();
+ }
+
+ // Overload assignment operator in order to clone
+ TupleIndexExpr &operator= (TupleIndexExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ tuple_index = other.tuple_index;
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.tuple_expr != nullptr)
+ tuple_expr = other.tuple_expr->clone_expr ();
+ else
+ tuple_expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ TupleIndexExpr (TupleIndexExpr &&other) = default;
+ TupleIndexExpr &operator= (TupleIndexExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if tuple expr is null, so base stripping on that.
+ void mark_for_strip () override { tuple_expr = nullptr; }
+ bool is_marked_for_strip () const override { return tuple_expr == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_tuple_expr ()
+ {
+ rust_assert (tuple_expr != nullptr);
+ return tuple_expr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TupleIndexExpr *clone_expr_without_block_impl () const override
+ {
+ return new TupleIndexExpr (*this);
+ }
+};
+
+// Base struct/tuple/union value creator AST node (abstract)
+class StructExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ PathInExpression struct_name;
+
+protected:
+ // Protected constructor to allow initialising struct_name
+ StructExpr (PathInExpression struct_path,
+ std::vector<Attribute> outer_attribs)
+ : outer_attrs (std::move (outer_attribs)),
+ struct_name (std::move (struct_path))
+ {}
+
+public:
+ const PathInExpression &get_struct_name () const { return struct_name; }
+ PathInExpression &get_struct_name () { return struct_name; }
+
+ std::string as_string () const override;
+
+ // Invalid if path is empty, so base stripping on that.
+ void mark_for_strip () override
+ {
+ struct_name = PathInExpression::create_error ();
+ }
+ bool is_marked_for_strip () const override { return struct_name.is_error (); }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+};
+
+// Actual AST node of the struct creator (with no fields). Not abstract!
+class StructExprStruct : public StructExpr
+{
+ std::vector<Attribute> inner_attrs;
+
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+ // Constructor has to call protected constructor of base class
+ StructExprStruct (PathInExpression struct_path,
+ std::vector<Attribute> inner_attribs,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : StructExpr (std::move (struct_path), std::move (outer_attribs)),
+ inner_attrs (std::move (inner_attribs)), locus (locus)
+ {}
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructExprStruct *clone_expr_without_block_impl () const override
+ {
+ return new StructExprStruct (*this);
+ }
+};
+
+/* AST node representing expression used to fill a struct's fields from another
+ * struct */
+struct StructBase
+{
+private:
+ std::unique_ptr<Expr> base_struct;
+ Location locus;
+
+public:
+ StructBase (std::unique_ptr<Expr> base_struct_ptr, Location locus)
+ : base_struct (std::move (base_struct_ptr)), locus (locus)
+ {}
+
+ // Copy constructor requires clone
+ StructBase (StructBase const &other)
+ {
+ /* HACK: gets around base_struct pointer being null (e.g. if no struct base
+ * exists) */
+ if (other.base_struct != nullptr)
+ base_struct = other.base_struct->clone_expr ();
+ }
+
+ // Destructor
+ ~StructBase () = default;
+
+ // Overload assignment operator to clone base_struct
+ StructBase &operator= (StructBase const &other)
+ {
+ // prevent null pointer dereference
+ if (other.base_struct != nullptr)
+ base_struct = other.base_struct->clone_expr ();
+ else
+ base_struct = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ StructBase (StructBase &&other) = default;
+ StructBase &operator= (StructBase &&other) = default;
+
+ // Returns a null expr-ed StructBase - error state
+ static StructBase error () { return StructBase (nullptr, Location ()); }
+
+ // Returns whether StructBase is in error state
+ bool is_invalid () const { return base_struct == nullptr; }
+
+ std::string as_string () const;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_base_struct ()
+ {
+ rust_assert (base_struct != nullptr);
+ return base_struct;
+ }
+};
+
+/* Base AST node for a single struct expression field (in struct instance
+ * creation) - abstract */
+class StructExprField
+{
+public:
+ virtual ~StructExprField () {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<StructExprField> clone_struct_expr_field () const
+ {
+ return std::unique_ptr<StructExprField> (clone_struct_expr_field_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual Location get_locus () const = 0;
+
+ NodeId get_node_id () const { return node_id; }
+
+protected:
+ // pure virtual clone implementation
+ virtual StructExprField *clone_struct_expr_field_impl () const = 0;
+
+ StructExprField () : node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ NodeId node_id;
+};
+
+// Identifier-only variant of StructExprField AST node
+class StructExprFieldIdentifier : public StructExprField
+{
+ Identifier field_name;
+ Location locus;
+
+public:
+ StructExprFieldIdentifier (Identifier field_identifier, Location locus)
+ : StructExprField (), field_name (std::move (field_identifier)),
+ locus (locus)
+ {}
+
+ std::string as_string () const override { return field_name; }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ Identifier get_field_name () const { return field_name; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructExprFieldIdentifier *clone_struct_expr_field_impl () const override
+ {
+ return new StructExprFieldIdentifier (*this);
+ }
+};
+
+/* Base AST node for a single struct expression field with an assigned value -
+ * abstract */
+class StructExprFieldWithVal : public StructExprField
+{
+ std::unique_ptr<Expr> value;
+
+protected:
+ StructExprFieldWithVal (std::unique_ptr<Expr> field_value)
+ : StructExprField (), value (std::move (field_value))
+ {}
+
+ // Copy constructor requires clone
+ StructExprFieldWithVal (StructExprFieldWithVal const &other)
+ : value (other.value->clone_expr ())
+ {}
+
+ // Overload assignment operator to clone unique_ptr
+ StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other)
+ {
+ value = other.value->clone_expr ();
+
+ return *this;
+ }
+
+ // move constructors
+ StructExprFieldWithVal (StructExprFieldWithVal &&other) = default;
+ StructExprFieldWithVal &operator= (StructExprFieldWithVal &&other) = default;
+
+public:
+ std::string as_string () const override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_value ()
+ {
+ rust_assert (value != nullptr);
+ return value;
+ }
+};
+
+// Identifier and value variant of StructExprField AST node
+class StructExprFieldIdentifierValue : public StructExprFieldWithVal
+{
+ Identifier field_name;
+ Location locus;
+
+public:
+ StructExprFieldIdentifierValue (Identifier field_identifier,
+ std::unique_ptr<Expr> field_value,
+ Location locus)
+ : StructExprFieldWithVal (std::move (field_value)),
+ field_name (std::move (field_identifier)), locus (locus)
+ {}
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ std::string get_field_name () const { return field_name; }
+
+ Location get_locus () const override final { return locus; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override
+ {
+ return new StructExprFieldIdentifierValue (*this);
+ }
+};
+
+// Tuple index and value variant of StructExprField AST node
+class StructExprFieldIndexValue : public StructExprFieldWithVal
+{
+ TupleIndex index;
+ Location locus;
+
+public:
+ StructExprFieldIndexValue (TupleIndex tuple_index,
+ std::unique_ptr<Expr> field_value, Location locus)
+ : StructExprFieldWithVal (std::move (field_value)), index (tuple_index),
+ locus (locus)
+ {}
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ TupleIndex get_index () const { return index; }
+
+ Location get_locus () const override final { return locus; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructExprFieldIndexValue *clone_struct_expr_field_impl () const override
+ {
+ return new StructExprFieldIndexValue (*this);
+ }
+};
+
+// AST node of a struct creator with fields
+class StructExprStructFields : public StructExprStruct
+{
+ // std::vector<StructExprField> fields;
+ std::vector<std::unique_ptr<StructExprField> > fields;
+
+ // bool has_struct_base;
+ StructBase struct_base;
+
+public:
+ std::string as_string () const override;
+
+ bool has_struct_base () const { return !struct_base.is_invalid (); }
+
+ // Constructor for StructExprStructFields when no struct base is used
+ StructExprStructFields (
+ PathInExpression struct_path,
+ std::vector<std::unique_ptr<StructExprField> > expr_fields, Location locus,
+ StructBase base_struct = StructBase::error (),
+ std::vector<Attribute> inner_attribs = std::vector<Attribute> (),
+ std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
+ : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
+ std::move (outer_attribs), locus),
+ fields (std::move (expr_fields)), struct_base (std::move (base_struct))
+ {}
+
+ // copy constructor with vector clone
+ StructExprStructFields (StructExprStructFields const &other)
+ : StructExprStruct (other), struct_base (other.struct_base)
+ {
+ fields.reserve (other.fields.size ());
+ for (const auto &e : other.fields)
+ fields.push_back (e->clone_struct_expr_field ());
+ }
+
+ // overloaded assignment operator with vector clone
+ StructExprStructFields &operator= (StructExprStructFields const &other)
+ {
+ StructExprStruct::operator= (other);
+ struct_base = other.struct_base;
+
+ fields.reserve (other.fields.size ());
+ for (const auto &e : other.fields)
+ fields.push_back (e->clone_struct_expr_field ());
+
+ return *this;
+ }
+
+ // move constructors
+ StructExprStructFields (StructExprStructFields &&other) = default;
+ StructExprStructFields &operator= (StructExprStructFields &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ std::vector<std::unique_ptr<StructExprField> > &get_fields ()
+ {
+ return fields;
+ }
+ const std::vector<std::unique_ptr<StructExprField> > &get_fields () const
+ {
+ return fields;
+ }
+
+ StructBase &get_struct_base () { return struct_base; }
+ const StructBase &get_struct_base () const { return struct_base; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructExprStructFields *clone_expr_without_block_impl () const override
+ {
+ return new StructExprStructFields (*this);
+ }
+};
+
+// AST node of the functional update struct creator
+/* TODO: remove and replace with StructExprStructFields, except with empty
+ * vector of fields? */
+class StructExprStructBase : public StructExprStruct
+{
+ StructBase struct_base;
+
+public:
+ std::string as_string () const override;
+
+ StructExprStructBase (PathInExpression struct_path, StructBase base_struct,
+ std::vector<Attribute> inner_attribs,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
+ std::move (outer_attribs), locus),
+ struct_base (std::move (base_struct))
+ {}
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ StructBase &get_struct_base () { return struct_base; }
+ const StructBase &get_struct_base () const { return struct_base; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructExprStructBase *clone_expr_without_block_impl () const override
+ {
+ return new StructExprStructBase (*this);
+ }
+};
+
+// Forward decl for Function - used in CallExpr
+class Function;
+
+// Function call expression AST node
+class CallExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> function;
+ std::vector<std::unique_ptr<Expr> > params;
+ Location locus;
+
+public:
+ Function *fndeclRef;
+
+ std::string as_string () const override;
+
+ CallExpr (std::unique_ptr<Expr> function_expr,
+ std::vector<std::unique_ptr<Expr> > function_params,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ function (std::move (function_expr)),
+ params (std::move (function_params)), locus (locus)
+ {}
+
+ // copy constructor requires clone
+ CallExpr (CallExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.function != nullptr)
+ function = other.function->clone_expr ();
+
+ params.reserve (other.params.size ());
+ for (const auto &e : other.params)
+ params.push_back (e->clone_expr ());
+ }
+
+ // Overload assignment operator to clone
+ CallExpr &operator= (CallExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.function != nullptr)
+ function = other.function->clone_expr ();
+ else
+ function = nullptr;
+
+ params.reserve (other.params.size ());
+ for (const auto &e : other.params)
+ params.push_back (e->clone_expr ());
+
+ return *this;
+ }
+
+ // move constructors
+ CallExpr (CallExpr &&other) = default;
+ CallExpr &operator= (CallExpr &&other) = default;
+
+ // Returns whether function call has parameters.
+ bool has_params () const { return !params.empty (); }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if function expr is null, so base stripping on that.
+ void mark_for_strip () override { function = nullptr; }
+ bool is_marked_for_strip () const override { return function == nullptr; }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<std::unique_ptr<Expr> > &get_params () const
+ {
+ return params;
+ }
+ std::vector<std::unique_ptr<Expr> > &get_params () { return params; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_function_expr ()
+ {
+ rust_assert (function != nullptr);
+ return function;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ CallExpr *clone_expr_without_block_impl () const override
+ {
+ return new CallExpr (*this);
+ }
+};
+
+// Method call expression AST node
+class MethodCallExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> receiver;
+ PathExprSegment method_name;
+ std::vector<std::unique_ptr<Expr> > params;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ MethodCallExpr (std::unique_ptr<Expr> call_receiver,
+ PathExprSegment method_path,
+ std::vector<std::unique_ptr<Expr> > method_params,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ receiver (std::move (call_receiver)),
+ method_name (std::move (method_path)), params (std::move (method_params)),
+ locus (locus)
+ {}
+
+ // copy constructor required due to cloning
+ MethodCallExpr (MethodCallExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ method_name (other.method_name), locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.receiver != nullptr)
+ receiver = other.receiver->clone_expr ();
+
+ params.reserve (other.params.size ());
+ for (const auto &e : other.params)
+ params.push_back (e->clone_expr ());
+ }
+
+ // Overload assignment operator to clone receiver object
+ MethodCallExpr &operator= (MethodCallExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ method_name = other.method_name;
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.receiver != nullptr)
+ receiver = other.receiver->clone_expr ();
+ else
+ receiver = nullptr;
+
+ params.reserve (other.params.size ());
+ for (const auto &e : other.params)
+ params.push_back (e->clone_expr ());
+
+ return *this;
+ }
+
+ // move constructors
+ MethodCallExpr (MethodCallExpr &&other) = default;
+ MethodCallExpr &operator= (MethodCallExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if receiver expr is null, so base stripping on that.
+ void mark_for_strip () override { receiver = nullptr; }
+ bool is_marked_for_strip () const override { return receiver == nullptr; }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<std::unique_ptr<Expr> > &get_params () const
+ {
+ return params;
+ }
+ std::vector<std::unique_ptr<Expr> > &get_params () { return params; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_receiver_expr ()
+ {
+ rust_assert (receiver != nullptr);
+ return receiver;
+ }
+
+ const PathExprSegment &get_method_name () const { return method_name; }
+ PathExprSegment &get_method_name () { return method_name; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MethodCallExpr *clone_expr_without_block_impl () const override
+ {
+ return new MethodCallExpr (*this);
+ }
+};
+
+// aka FieldExpression
+// Struct or union field access expression AST node
+class FieldAccessExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> receiver;
+ Identifier field;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ FieldAccessExpr (std::unique_ptr<Expr> field_access_receiver,
+ Identifier field_name, std::vector<Attribute> outer_attribs,
+ Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ receiver (std::move (field_access_receiver)),
+ field (std::move (field_name)), locus (locus)
+ {}
+
+ // Copy constructor required due to unique_ptr cloning
+ FieldAccessExpr (FieldAccessExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ field (other.field), locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.receiver != nullptr)
+ receiver = other.receiver->clone_expr ();
+ }
+
+ // Overload assignment operator to clone unique_ptr
+ FieldAccessExpr &operator= (FieldAccessExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ field = other.field;
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.receiver != nullptr)
+ receiver = other.receiver->clone_expr ();
+ else
+ receiver = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ FieldAccessExpr (FieldAccessExpr &&other) = default;
+ FieldAccessExpr &operator= (FieldAccessExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if receiver expr is null, so base stripping on that.
+ void mark_for_strip () override { receiver = nullptr; }
+ bool is_marked_for_strip () const override { return receiver == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_receiver_expr ()
+ {
+ rust_assert (receiver != nullptr);
+ return receiver;
+ }
+
+ Identifier get_field_name () const { return field; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ FieldAccessExpr *clone_expr_without_block_impl () const override
+ {
+ return new FieldAccessExpr (*this);
+ }
+};
+
+// Closure parameter data structure
+struct ClosureParam
+{
+private:
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Pattern> pattern;
+
+ // bool has_type_given;
+ std::unique_ptr<Type> type;
+ Location locus;
+
+public:
+ // Returns whether the type of the parameter has been given.
+ bool has_type_given () const { return type != nullptr; }
+
+ bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+ // Constructor for closure parameter
+ ClosureParam (std::unique_ptr<Pattern> param_pattern, Location locus,
+ std::unique_ptr<Type> param_type = nullptr,
+ std::vector<Attribute> outer_attrs = {})
+ : outer_attrs (std::move (outer_attrs)),
+ pattern (std::move (param_pattern)), type (std::move (param_type)),
+ locus (locus)
+ {}
+
+ // Copy constructor required due to cloning as a result of unique_ptrs
+ ClosureParam (ClosureParam const &other) : outer_attrs (other.outer_attrs)
+ {
+ // guard to protect from null pointer dereference
+ if (other.pattern != nullptr)
+ pattern = other.pattern->clone_pattern ();
+ if (other.type != nullptr)
+ type = other.type->clone_type ();
+ }
+
+ ~ClosureParam () = default;
+
+ // Assignment operator must be overloaded to clone as well
+ ClosureParam &operator= (ClosureParam const &other)
+ {
+ outer_attrs = other.outer_attrs;
+
+ // guard to protect from null pointer dereference
+ if (other.pattern != nullptr)
+ pattern = other.pattern->clone_pattern ();
+ else
+ pattern = nullptr;
+ if (other.type != nullptr)
+ type = other.type->clone_type ();
+ else
+ type = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ ClosureParam (ClosureParam &&other) = default;
+ ClosureParam &operator= (ClosureParam &&other) = default;
+
+ // Returns whether closure parameter is in an error state.
+ bool is_error () const { return pattern == nullptr; }
+
+ // Creates an error state closure parameter.
+ static ClosureParam create_error ()
+ {
+ return ClosureParam (nullptr, Location ());
+ }
+
+ std::string as_string () const;
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Pattern> &get_pattern ()
+ {
+ rust_assert (pattern != nullptr);
+ return pattern;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Type> &get_type ()
+ {
+ rust_assert (has_type_given ());
+ return type;
+ }
+};
+
+// Base closure definition expression AST node - abstract
+class ClosureExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ bool has_move;
+ std::vector<ClosureParam> params; // may be empty
+ Location locus;
+
+protected:
+ ClosureExpr (std::vector<ClosureParam> closure_params, bool has_move,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)), has_move (has_move),
+ params (std::move (closure_params)), locus (locus)
+ {}
+
+public:
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<ClosureParam> &get_params () const { return params; }
+ std::vector<ClosureParam> &get_params () { return params; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+};
+
+// Represents a non-type-specified closure expression AST node
+class ClosureExprInner : public ClosureExpr
+{
+ std::unique_ptr<Expr> closure_inner;
+
+public:
+ std::string as_string () const override;
+
+ // Constructor for a ClosureExprInner
+ ClosureExprInner (std::unique_ptr<Expr> closure_inner_expr,
+ std::vector<ClosureParam> closure_params, Location locus,
+ bool is_move = false,
+ std::vector<Attribute> outer_attribs
+ = std::vector<Attribute> ())
+ : ClosureExpr (std::move (closure_params), is_move,
+ std::move (outer_attribs), locus),
+ closure_inner (std::move (closure_inner_expr))
+ {}
+
+ // Copy constructor must be defined to allow copying via cloning of unique_ptr
+ ClosureExprInner (ClosureExprInner const &other) : ClosureExpr (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.closure_inner != nullptr)
+ closure_inner = other.closure_inner->clone_expr ();
+ }
+
+ // Overload assignment operator to clone closure_inner
+ ClosureExprInner &operator= (ClosureExprInner const &other)
+ {
+ ClosureExpr::operator= (other);
+ // params = other.params;
+ // has_move = other.has_move;
+ // outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.closure_inner != nullptr)
+ closure_inner = other.closure_inner->clone_expr ();
+ else
+ closure_inner = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ ClosureExprInner (ClosureExprInner &&other) = default;
+ ClosureExprInner &operator= (ClosureExprInner &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if inner expr is null, so base stripping on that.
+ void mark_for_strip () override { closure_inner = nullptr; }
+ bool is_marked_for_strip () const override
+ {
+ return closure_inner == nullptr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_definition_expr ()
+ {
+ rust_assert (closure_inner != nullptr);
+ return closure_inner;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ClosureExprInner *clone_expr_without_block_impl () const override
+ {
+ return new ClosureExprInner (*this);
+ }
+};
+
+// A block AST node
+class BlockExpr : public ExprWithBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::vector<Attribute> inner_attrs;
+ std::vector<std::unique_ptr<Stmt> > statements;
+ std::unique_ptr<Expr> expr;
+ Location start_locus;
+ Location end_locus;
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override;
+
+ // Returns whether the block contains statements.
+ bool has_statements () const { return !statements.empty (); }
+
+ // Returns whether the block contains a final expression.
+ bool has_tail_expr () const { return expr != nullptr; }
+
+ BlockExpr (std::vector<std::unique_ptr<Stmt> > block_statements,
+ std::unique_ptr<Expr> block_expr,
+ std::vector<Attribute> inner_attribs,
+ std::vector<Attribute> outer_attribs, Location start_locus,
+ Location end_locus)
+ : outer_attrs (std::move (outer_attribs)),
+ inner_attrs (std::move (inner_attribs)),
+ statements (std::move (block_statements)), expr (std::move (block_expr)),
+ start_locus (start_locus), end_locus (end_locus)
+ {}
+
+ // Copy constructor with clone
+ BlockExpr (BlockExpr const &other)
+ : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+ inner_attrs (other.inner_attrs), start_locus (other.start_locus),
+ end_locus (other.end_locus), marked_for_strip (other.marked_for_strip)
+ {
+ // guard to protect from null pointer dereference
+ if (other.expr != nullptr)
+ expr = other.expr->clone_expr ();
+
+ statements.reserve (other.statements.size ());
+ for (const auto &e : other.statements)
+ statements.push_back (e->clone_stmt ());
+ }
+
+ // Overloaded assignment operator to clone pointer
+ BlockExpr &operator= (BlockExpr const &other)
+ {
+ ExprWithBlock::operator= (other);
+ inner_attrs = other.inner_attrs;
+ start_locus = other.start_locus;
+ end_locus = other.end_locus;
+ marked_for_strip = other.marked_for_strip;
+ outer_attrs = other.outer_attrs;
+
+ // guard to protect from null pointer dereference
+ if (other.expr != nullptr)
+ expr = other.expr->clone_expr ();
+ else
+ expr = nullptr;
+
+ statements.reserve (other.statements.size ());
+ for (const auto &e : other.statements)
+ statements.push_back (e->clone_stmt ());
+
+ return *this;
+ }
+
+ // move constructors
+ BlockExpr (BlockExpr &&other) = default;
+ BlockExpr &operator= (BlockExpr &&other) = default;
+
+ // Unique pointer custom clone function
+ std::unique_ptr<BlockExpr> clone_block_expr () const
+ {
+ return std::unique_ptr<BlockExpr> (clone_block_expr_impl ());
+ }
+
+ Location get_locus () const override final { return start_locus; }
+
+ Location get_start_locus () const { return start_locus; }
+ Location get_end_locus () const { return end_locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can be completely empty, so have to have a separate flag.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+ size_t num_statements () const { return statements.size (); }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+ const std::vector<std::unique_ptr<Stmt> > &get_statements () const
+ {
+ return statements;
+ }
+ std::vector<std::unique_ptr<Stmt> > &get_statements () { return statements; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_tail_expr ()
+ {
+ rust_assert (has_tail_expr ());
+ return expr;
+ }
+
+ // Removes the tail expression from the block.
+ void strip_tail_expr () { expr = nullptr; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ BlockExpr *clone_expr_with_block_impl () const final override
+ {
+ return clone_block_expr_impl ();
+ }
+
+ /* This is the base method as not an abstract class - not virtual but could be
+ * in future if required. */
+ /*virtual*/ BlockExpr *clone_block_expr_impl () const
+ {
+ return new BlockExpr (*this);
+ }
+};
+
+// Represents a type-specified closure expression AST node
+class ClosureExprInnerTyped : public ClosureExpr
+{
+ // TODO: spec says typenobounds
+ std::unique_ptr<Type> return_type;
+ std::unique_ptr<BlockExpr>
+ expr; // only used because may be polymorphic in future
+
+public:
+ std::string as_string () const override;
+
+ // Constructor potentially with a move
+ ClosureExprInnerTyped (std::unique_ptr<Type> closure_return_type,
+ std::unique_ptr<BlockExpr> closure_expr,
+ std::vector<ClosureParam> closure_params,
+ Location locus, bool is_move = false,
+ std::vector<Attribute> outer_attribs
+ = std::vector<Attribute> ())
+ : ClosureExpr (std::move (closure_params), is_move,
+ std::move (outer_attribs), locus),
+ return_type (std::move (closure_return_type)),
+ expr (std::move (closure_expr))
+ {}
+
+ // Copy constructor requires cloning
+ ClosureExprInnerTyped (ClosureExprInnerTyped const &other)
+ : ClosureExpr (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_block_expr ();
+ if (other.return_type != nullptr)
+ return_type = other.return_type->clone_type ();
+ }
+
+ // Overload assignment operator to clone unique_ptrs
+ ClosureExprInnerTyped &operator= (ClosureExprInnerTyped const &other)
+ {
+ ClosureExpr::operator= (other);
+ // params = other.params;
+ // has_move = other.has_move;
+ // outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_block_expr ();
+ else
+ expr = nullptr;
+ if (other.return_type != nullptr)
+ return_type = other.return_type->clone_type ();
+ else
+ return_type = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ ClosureExprInnerTyped (ClosureExprInnerTyped &&other) = default;
+ ClosureExprInnerTyped &operator= (ClosureExprInnerTyped &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ /* Invalid if inner expr is null, so base stripping on that. Technically,
+ * type should also not be null. */
+ void mark_for_strip () override { expr = nullptr; }
+ bool is_marked_for_strip () const override { return expr == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_definition_block ()
+ {
+ rust_assert (expr != nullptr);
+ return expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Type> &get_return_type ()
+ {
+ rust_assert (return_type != nullptr);
+ return return_type;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ClosureExprInnerTyped *clone_expr_without_block_impl () const override
+ {
+ return new ClosureExprInnerTyped (*this);
+ }
+};
+
+// AST node representing continue expression within loops
+class ContinueExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ Lifetime label;
+ Location locus;
+
+ // TODO: find another way to store this to save memory?
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override;
+
+ // Returns true if the continue expr has a label.
+ bool has_label () const { return !label.is_error (); }
+
+ // Constructor for a ContinueExpr with a label.
+ ContinueExpr (Lifetime label, std::vector<Attribute> outer_attribs,
+ Location locus)
+ : outer_attrs (std::move (outer_attribs)), label (std::move (label)),
+ locus (locus)
+ {}
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can't think of any invalid invariants, so store boolean.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ Lifetime &get_label () { return label; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ContinueExpr *clone_expr_without_block_impl () const override
+ {
+ return new ContinueExpr (*this);
+ }
+};
+// TODO: merge "break" and "continue"? Or even merge in "return"?
+
+// AST node representing break expression within loops
+class BreakExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ Lifetime label;
+ std::unique_ptr<Expr> break_expr;
+ Location locus;
+
+ // TODO: find another way to store this to save memory?
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override;
+
+ // Returns whether the break expression has a label or not.
+ bool has_label () const { return !label.is_error (); }
+
+ /* Returns whether the break expression has an expression used in the break or
+ * not. */
+ bool has_break_expr () const { return break_expr != nullptr; }
+
+ // Constructor for a break expression
+ BreakExpr (Lifetime break_label, std::unique_ptr<Expr> expr_in_break,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)),
+ break_expr (std::move (expr_in_break)), locus (locus)
+ {}
+
+ // Copy constructor defined to use clone for unique pointer
+ BreakExpr (BreakExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ label (other.label), locus (other.locus),
+ marked_for_strip (other.marked_for_strip)
+ {
+ // guard to protect from null pointer dereference
+ if (other.break_expr != nullptr)
+ break_expr = other.break_expr->clone_expr ();
+ }
+
+ // Overload assignment operator to clone unique pointer
+ BreakExpr &operator= (BreakExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ label = other.label;
+ locus = other.locus;
+ marked_for_strip = other.marked_for_strip;
+ outer_attrs = other.outer_attrs;
+
+ // guard to protect from null pointer dereference
+ if (other.break_expr != nullptr)
+ break_expr = other.break_expr->clone_expr ();
+ else
+ break_expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ BreakExpr (BreakExpr &&other) = default;
+ BreakExpr &operator= (BreakExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can't think of any invalid invariants, so store boolean.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_break_expr ()
+ {
+ rust_assert (has_break_expr ());
+ return break_expr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ Lifetime &get_label () { return label; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ BreakExpr *clone_expr_without_block_impl () const override
+ {
+ return new BreakExpr (*this);
+ }
+};
+
+// Base range expression AST node object - abstract
+class RangeExpr : public ExprWithoutBlock
+{
+ Location locus;
+
+protected:
+ // outer attributes not allowed before range expressions
+ RangeExpr (Location locus) : locus (locus) {}
+
+public:
+ Location get_locus () const override final { return locus; }
+
+ // should never be called - error if called
+ void set_outer_attrs (std::vector<Attribute> /* new_attrs */) override
+ {
+ rust_assert (false);
+ }
+};
+
+// Range from (inclusive) and to (exclusive) expression AST node object
+// aka RangeExpr; constructs a std::ops::Range object
+class RangeFromToExpr : public RangeExpr
+{
+ std::unique_ptr<Expr> from;
+ std::unique_ptr<Expr> to;
+
+public:
+ std::string as_string () const override;
+
+ RangeFromToExpr (std::unique_ptr<Expr> range_from,
+ std::unique_ptr<Expr> range_to, Location locus)
+ : RangeExpr (locus), from (std::move (range_from)),
+ to (std::move (range_to))
+ {}
+
+ // Copy constructor with cloning
+ RangeFromToExpr (RangeFromToExpr const &other) : RangeExpr (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.from != nullptr)
+ from = other.from->clone_expr ();
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ }
+
+ // Overload assignment operator to clone unique pointers
+ RangeFromToExpr &operator= (RangeFromToExpr const &other)
+ {
+ RangeExpr::operator= (other);
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.from != nullptr)
+ from = other.from->clone_expr ();
+ else
+ from = nullptr;
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ else
+ to = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ RangeFromToExpr (RangeFromToExpr &&other) = default;
+ RangeFromToExpr &operator= (RangeFromToExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if either expr is null, so base stripping on that.
+ void mark_for_strip () override
+ {
+ from = nullptr;
+ to = nullptr;
+ }
+ bool is_marked_for_strip () const override
+ {
+ return from == nullptr && to == nullptr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_from_expr ()
+ {
+ rust_assert (from != nullptr);
+ return from;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_to_expr ()
+ {
+ rust_assert (to != nullptr);
+ return to;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangeFromToExpr *clone_expr_without_block_impl () const override
+ {
+ return new RangeFromToExpr (*this);
+ }
+};
+
+// Range from (inclusive) expression AST node object
+// constructs a std::ops::RangeFrom object
+class RangeFromExpr : public RangeExpr
+{
+ std::unique_ptr<Expr> from;
+
+public:
+ std::string as_string () const override;
+
+ RangeFromExpr (std::unique_ptr<Expr> range_from, Location locus)
+ : RangeExpr (locus), from (std::move (range_from))
+ {}
+
+ // Copy constructor with clone
+ RangeFromExpr (RangeFromExpr const &other) : RangeExpr (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.from != nullptr)
+ from = other.from->clone_expr ();
+ }
+
+ // Overload assignment operator to clone unique_ptr
+ RangeFromExpr &operator= (RangeFromExpr const &other)
+ {
+ RangeExpr::operator= (other);
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.from != nullptr)
+ from = other.from->clone_expr ();
+ else
+ from = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ RangeFromExpr (RangeFromExpr &&other) = default;
+ RangeFromExpr &operator= (RangeFromExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if expr is null, so base stripping on that.
+ void mark_for_strip () override { from = nullptr; }
+ bool is_marked_for_strip () const override { return from == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_from_expr ()
+ {
+ rust_assert (from != nullptr);
+ return from;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangeFromExpr *clone_expr_without_block_impl () const override
+ {
+ return new RangeFromExpr (*this);
+ }
+};
+
+// Range to (exclusive) expression AST node object
+// constructs a std::ops::RangeTo object
+class RangeToExpr : public RangeExpr
+{
+ std::unique_ptr<Expr> to;
+
+public:
+ std::string as_string () const override;
+
+ // outer attributes not allowed
+ RangeToExpr (std::unique_ptr<Expr> range_to, Location locus)
+ : RangeExpr (locus), to (std::move (range_to))
+ {}
+
+ // Copy constructor with clone
+ RangeToExpr (RangeToExpr const &other) : RangeExpr (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ }
+
+ // Overload assignment operator to clone unique_ptr
+ RangeToExpr &operator= (RangeToExpr const &other)
+ {
+ RangeExpr::operator= (other);
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ else
+ to = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ RangeToExpr (RangeToExpr &&other) = default;
+ RangeToExpr &operator= (RangeToExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if expr is null, so base stripping on that.
+ void mark_for_strip () override { to = nullptr; }
+ bool is_marked_for_strip () const override { return to == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_to_expr ()
+ {
+ rust_assert (to != nullptr);
+ return to;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangeToExpr *clone_expr_without_block_impl () const override
+ {
+ return new RangeToExpr (*this);
+ }
+};
+
+// Full range expression AST node object
+// constructs a std::ops::RangeFull object
+class RangeFullExpr : public RangeExpr
+{
+ // TODO: find another way to store this to save memory?
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override;
+
+ RangeFullExpr (Location locus) : RangeExpr (locus) {}
+ // outer attributes not allowed
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can't think of any invalid invariants, so store boolean.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangeFullExpr *clone_expr_without_block_impl () const override
+ {
+ return new RangeFullExpr (*this);
+ }
+};
+
+// Range from (inclusive) and to (inclusive) expression AST node object
+// aka RangeInclusiveExpr; constructs a std::ops::RangeInclusive object
+class RangeFromToInclExpr : public RangeExpr
+{
+ std::unique_ptr<Expr> from;
+ std::unique_ptr<Expr> to;
+
+public:
+ std::string as_string () const override;
+
+ RangeFromToInclExpr (std::unique_ptr<Expr> range_from,
+ std::unique_ptr<Expr> range_to, Location locus)
+ : RangeExpr (locus), from (std::move (range_from)),
+ to (std::move (range_to))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor with clone
+ RangeFromToInclExpr (RangeFromToInclExpr const &other) : RangeExpr (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.from != nullptr)
+ from = other.from->clone_expr ();
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ }
+
+ // Overload assignment operator to use clone
+ RangeFromToInclExpr &operator= (RangeFromToInclExpr const &other)
+ {
+ RangeExpr::operator= (other);
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.from != nullptr)
+ from = other.from->clone_expr ();
+ else
+ from = nullptr;
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ else
+ to = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ RangeFromToInclExpr (RangeFromToInclExpr &&other) = default;
+ RangeFromToInclExpr &operator= (RangeFromToInclExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if either expr is null, so base stripping on that.
+ void mark_for_strip () override
+ {
+ from = nullptr;
+ to = nullptr;
+ }
+ bool is_marked_for_strip () const override
+ {
+ return from == nullptr && to == nullptr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_from_expr ()
+ {
+ rust_assert (from != nullptr);
+ return from;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_to_expr ()
+ {
+ rust_assert (to != nullptr);
+ return to;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangeFromToInclExpr *clone_expr_without_block_impl () const override
+ {
+ return new RangeFromToInclExpr (*this);
+ }
+};
+
+// Range to (inclusive) expression AST node object
+// aka RangeToInclusiveExpr; constructs a std::ops::RangeToInclusive object
+class RangeToInclExpr : public RangeExpr
+{
+ std::unique_ptr<Expr> to;
+
+public:
+ std::string as_string () const override;
+
+ RangeToInclExpr (std::unique_ptr<Expr> range_to, Location locus)
+ : RangeExpr (locus), to (std::move (range_to))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor with clone
+ RangeToInclExpr (RangeToInclExpr const &other) : RangeExpr (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ }
+
+ // Overload assignment operator to clone pointer
+ RangeToInclExpr &operator= (RangeToInclExpr const &other)
+ {
+ RangeExpr::operator= (other);
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.to != nullptr)
+ to = other.to->clone_expr ();
+ else
+ to = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ RangeToInclExpr (RangeToInclExpr &&other) = default;
+ RangeToInclExpr &operator= (RangeToInclExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if expr is null, so base stripping on that.
+ void mark_for_strip () override { to = nullptr; }
+ bool is_marked_for_strip () const override { return to == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_to_expr ()
+ {
+ rust_assert (to != nullptr);
+ return to;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangeToInclExpr *clone_expr_without_block_impl () const override
+ {
+ return new RangeToInclExpr (*this);
+ }
+};
+
+// Return expression AST node representation
+class ReturnExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> return_expr;
+ Location locus;
+
+ // TODO: find another way to store this to save memory?
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override;
+
+ /* Returns whether the object has an expression returned (i.e. not void return
+ * type). */
+ bool has_returned_expr () const { return return_expr != nullptr; }
+
+ // Constructor for ReturnExpr.
+ ReturnExpr (std::unique_ptr<Expr> returned_expr,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)),
+ return_expr (std::move (returned_expr)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ ReturnExpr (ReturnExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ locus (other.locus), marked_for_strip (other.marked_for_strip)
+ {
+ // guard to protect from null pointer dereference
+ if (other.return_expr != nullptr)
+ return_expr = other.return_expr->clone_expr ();
+ }
+
+ // Overloaded assignment operator to clone return_expr pointer
+ ReturnExpr &operator= (ReturnExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ locus = other.locus;
+ marked_for_strip = other.marked_for_strip;
+ outer_attrs = other.outer_attrs;
+
+ // guard to protect from null pointer dereference
+ if (other.return_expr != nullptr)
+ return_expr = other.return_expr->clone_expr ();
+ else
+ return_expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ ReturnExpr (ReturnExpr &&other) = default;
+ ReturnExpr &operator= (ReturnExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can't think of any invalid invariants, so store boolean.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_returned_expr ()
+ {
+ rust_assert (return_expr != nullptr);
+ return return_expr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ReturnExpr *clone_expr_without_block_impl () const override
+ {
+ return new ReturnExpr (*this);
+ }
+};
+
+// Forward decl - defined in rust-macro.h
+class MacroInvocation;
+
+// An unsafe block AST node
+class UnsafeBlockExpr : public ExprWithBlock
+{
+ std::vector<Attribute> outer_attrs;
+ // Or just have it extend BlockExpr
+ std::unique_ptr<BlockExpr> expr;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ UnsafeBlockExpr (std::unique_ptr<BlockExpr> block_expr,
+ std::vector<Attribute> outer_attribs, Location locus)
+ : outer_attrs (std::move (outer_attribs)), expr (std::move (block_expr)),
+ locus (locus)
+ {}
+
+ // Copy constructor with clone
+ UnsafeBlockExpr (UnsafeBlockExpr const &other)
+ : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_block_expr ();
+ }
+
+ // Overloaded assignment operator to clone
+ UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other)
+ {
+ ExprWithBlock::operator= (other);
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_block_expr ();
+ else
+ expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ UnsafeBlockExpr (UnsafeBlockExpr &&other) = default;
+ UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if block is null, so base stripping on that.
+ void mark_for_strip () override { expr = nullptr; }
+ bool is_marked_for_strip () const override { return expr == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_block_expr ()
+ {
+ rust_assert (expr != nullptr);
+ return expr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ UnsafeBlockExpr *clone_expr_with_block_impl () const override
+ {
+ return new UnsafeBlockExpr (*this);
+ }
+};
+
+// Loop label expression AST node used with break and continue expressions
+// TODO: inline?
+class LoopLabel /*: public Node*/
+{
+ Lifetime label; // or type LIFETIME_OR_LABEL
+ Location locus;
+
+ NodeId node_id;
+
+public:
+ std::string as_string () const;
+
+ LoopLabel (Lifetime loop_label, Location locus = Location ())
+ : label (std::move (loop_label)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Returns whether the LoopLabel is in an error state.
+ bool is_error () const { return label.is_error (); }
+
+ // Creates an error state LoopLabel.
+ static LoopLabel error () { return LoopLabel (Lifetime::error ()); }
+
+ Location get_locus () const { return locus; }
+
+ Lifetime &get_lifetime () { return label; }
+
+ NodeId get_node_id () const { return node_id; }
+};
+
+// Base loop expression AST node - aka LoopExpr
+class BaseLoopExpr : public ExprWithBlock
+{
+protected:
+ // protected to allow subclasses better use of them
+ std::vector<Attribute> outer_attrs;
+ LoopLabel loop_label;
+ std::unique_ptr<BlockExpr> loop_block;
+
+private:
+ Location locus;
+
+protected:
+ // Constructor for BaseLoopExpr
+ BaseLoopExpr (std::unique_ptr<BlockExpr> loop_block, Location locus,
+ LoopLabel loop_label = LoopLabel::error (),
+ std::vector<Attribute> outer_attribs
+ = std::vector<Attribute> ())
+ : outer_attrs (std::move (outer_attribs)),
+ loop_label (std::move (loop_label)), loop_block (std::move (loop_block)),
+ locus (locus)
+ {}
+
+ // Copy constructor for BaseLoopExpr with clone
+ BaseLoopExpr (BaseLoopExpr const &other)
+ : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+ loop_label (other.loop_label), locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.loop_block != nullptr)
+ loop_block = other.loop_block->clone_block_expr ();
+ }
+
+ // Overloaded assignment operator to clone
+ BaseLoopExpr &operator= (BaseLoopExpr const &other)
+ {
+ ExprWithBlock::operator= (other);
+ loop_label = other.loop_label;
+ locus = other.locus;
+ outer_attrs = other.outer_attrs;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.loop_block != nullptr)
+ loop_block = other.loop_block->clone_block_expr ();
+ else
+ loop_block = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ BaseLoopExpr (BaseLoopExpr &&other) = default;
+ BaseLoopExpr &operator= (BaseLoopExpr &&other) = default;
+
+public:
+ bool has_loop_label () const { return !loop_label.is_error (); }
+
+ LoopLabel &get_loop_label () { return loop_label; }
+
+ Location get_locus () const override final { return locus; }
+
+ // Invalid if loop block is null, so base stripping on that.
+ void mark_for_strip () override { loop_block = nullptr; }
+ bool is_marked_for_strip () const override { return loop_block == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_loop_block ()
+ {
+ rust_assert (loop_block != nullptr);
+ return loop_block;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+};
+
+// 'Loop' expression (i.e. the infinite loop) AST node
+class LoopExpr : public BaseLoopExpr
+{
+public:
+ std::string as_string () const override;
+
+ // Constructor for LoopExpr
+ LoopExpr (std::unique_ptr<BlockExpr> loop_block, Location locus,
+ LoopLabel loop_label = LoopLabel::error (),
+ std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
+ : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
+ std::move (outer_attribs))
+ {}
+
+ void accept_vis (ASTVisitor &vis) override;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ LoopExpr *clone_expr_with_block_impl () const override
+ {
+ return new LoopExpr (*this);
+ }
+};
+
+// While loop expression AST node (predicate loop)
+class WhileLoopExpr : public BaseLoopExpr
+{
+ std::unique_ptr<Expr> condition;
+
+public:
+ std::string as_string () const override;
+
+ // Constructor for while loop with loop label
+ WhileLoopExpr (std::unique_ptr<Expr> loop_condition,
+ std::unique_ptr<BlockExpr> loop_block, Location locus,
+ LoopLabel loop_label = LoopLabel::error (),
+ std::vector<Attribute> outer_attribs
+ = std::vector<Attribute> ())
+ : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
+ std::move (outer_attribs)),
+ condition (std::move (loop_condition))
+ {}
+
+ // Copy constructor with clone
+ WhileLoopExpr (WhileLoopExpr const &other)
+ : BaseLoopExpr (other), condition (other.condition->clone_expr ())
+ {}
+
+ // Overloaded assignment operator to clone
+ WhileLoopExpr &operator= (WhileLoopExpr const &other)
+ {
+ BaseLoopExpr::operator= (other);
+ condition = other.condition->clone_expr ();
+ // loop_block = other.loop_block->clone_block_expr();
+ // loop_label = other.loop_label;
+ // outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ WhileLoopExpr (WhileLoopExpr &&other) = default;
+ WhileLoopExpr &operator= (WhileLoopExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_predicate_expr ()
+ {
+ rust_assert (condition != nullptr);
+ return condition;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ WhileLoopExpr *clone_expr_with_block_impl () const override
+ {
+ return new WhileLoopExpr (*this);
+ }
+};
+
+// While let loop expression AST node (predicate pattern loop)
+class WhileLetLoopExpr : public BaseLoopExpr
+{
+ // MatchArmPatterns patterns;
+ std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+ std::unique_ptr<Expr> scrutinee;
+
+public:
+ std::string as_string () const override;
+
+ // Constructor with a loop label
+ WhileLetLoopExpr (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+ std::unique_ptr<Expr> scrutinee,
+ std::unique_ptr<BlockExpr> loop_block, Location locus,
+ LoopLabel loop_label = LoopLabel::error (),
+ std::vector<Attribute> outer_attribs
+ = std::vector<Attribute> ())
+ : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
+ std::move (outer_attribs)),
+ match_arm_patterns (std::move (match_arm_patterns)),
+ scrutinee (std::move (scrutinee))
+ {}
+
+ // Copy constructor with clone
+ WhileLetLoopExpr (WhileLetLoopExpr const &other)
+ : BaseLoopExpr (other),
+ /*match_arm_patterns(other.match_arm_patterns),*/ scrutinee (
+ other.scrutinee->clone_expr ())
+ {
+ match_arm_patterns.reserve (other.match_arm_patterns.size ());
+ for (const auto &e : other.match_arm_patterns)
+ match_arm_patterns.push_back (e->clone_pattern ());
+ }
+
+ // Overloaded assignment operator to clone pointers
+ WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other)
+ {
+ BaseLoopExpr::operator= (other);
+ // match_arm_patterns = other.match_arm_patterns;
+ scrutinee = other.scrutinee->clone_expr ();
+ // loop_block = other.loop_block->clone_block_expr();
+ // loop_label = other.loop_label;
+ // outer_attrs = other.outer_attrs;
+
+ match_arm_patterns.reserve (other.match_arm_patterns.size ());
+ for (const auto &e : other.match_arm_patterns)
+ match_arm_patterns.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ WhileLetLoopExpr (WhileLetLoopExpr &&other) = default;
+ WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_scrutinee_expr ()
+ {
+ rust_assert (scrutinee != nullptr);
+ return scrutinee;
+ }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+ {
+ return match_arm_patterns;
+ }
+ std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+ {
+ return match_arm_patterns;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ WhileLetLoopExpr *clone_expr_with_block_impl () const override
+ {
+ return new WhileLetLoopExpr (*this);
+ }
+};
+
+// For loop expression AST node (iterator loop)
+class ForLoopExpr : public BaseLoopExpr
+{
+ std::unique_ptr<Pattern> pattern;
+ std::unique_ptr<Expr> iterator_expr;
+
+public:
+ std::string as_string () const override;
+
+ // Constructor with loop label
+ ForLoopExpr (std::unique_ptr<Pattern> loop_pattern,
+ std::unique_ptr<Expr> iterator_expr,
+ std::unique_ptr<BlockExpr> loop_body, Location locus,
+ LoopLabel loop_label = LoopLabel::error (),
+ std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
+ : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label),
+ std::move (outer_attribs)),
+ pattern (std::move (loop_pattern)),
+ iterator_expr (std::move (iterator_expr))
+ {}
+
+ // Copy constructor with clone
+ ForLoopExpr (ForLoopExpr const &other)
+ : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()),
+ iterator_expr (other.iterator_expr->clone_expr ())
+ {}
+
+ // Overloaded assignment operator to clone
+ ForLoopExpr &operator= (ForLoopExpr const &other)
+ {
+ BaseLoopExpr::operator= (other);
+ pattern = other.pattern->clone_pattern ();
+ iterator_expr = other.iterator_expr->clone_expr ();
+ /*loop_block = other.loop_block->clone_block_expr();
+ loop_label = other.loop_label;
+ outer_attrs = other.outer_attrs;*/
+
+ return *this;
+ }
+
+ // move constructors
+ ForLoopExpr (ForLoopExpr &&other) = default;
+ ForLoopExpr &operator= (ForLoopExpr &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_iterator_expr ()
+ {
+ rust_assert (iterator_expr != nullptr);
+ return iterator_expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Pattern> &get_pattern ()
+ {
+ rust_assert (pattern != nullptr);
+ return pattern;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ForLoopExpr *clone_expr_with_block_impl () const override
+ {
+ return new ForLoopExpr (*this);
+ }
+};
+
+// forward decl for IfExpr
+class IfLetExpr;
+
+// Base if expression with no "else" or "if let" AST node
+class IfExpr : public ExprWithBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> condition;
+ std::unique_ptr<BlockExpr> if_block;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ IfExpr (std::unique_ptr<Expr> condition, std::unique_ptr<BlockExpr> if_block,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)), condition (std::move (condition)),
+ if_block (std::move (if_block)), locus (locus)
+ {}
+ // outer attributes are never allowed on IfExprs
+
+ // Copy constructor with clone
+ IfExpr (IfExpr const &other)
+ : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.condition != nullptr)
+ condition = other.condition->clone_expr ();
+ if (other.if_block != nullptr)
+ if_block = other.if_block->clone_block_expr ();
+ }
+
+ // Overloaded assignment operator to clone expressions
+ IfExpr &operator= (IfExpr const &other)
+ {
+ ExprWithBlock::operator= (other);
+ outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.condition != nullptr)
+ condition = other.condition->clone_expr ();
+ else
+ condition = nullptr;
+ if (other.if_block != nullptr)
+ if_block = other.if_block->clone_block_expr ();
+ else
+ if_block = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ IfExpr (IfExpr &&other) = default;
+ IfExpr &operator= (IfExpr &&other) = default;
+
+ // Unique pointer custom clone function
+ std::unique_ptr<IfExpr> clone_if_expr () const
+ {
+ return std::unique_ptr<IfExpr> (clone_if_expr_impl ());
+ }
+
+ /* Note that multiple "else if"s are handled via nested ASTs rather than a
+ * vector of else ifs - i.e. not like a switch statement. TODO - is this a
+ * better approach? or does it not parse correctly and have downsides? */
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ void vis_if_condition (ASTVisitor &vis) { condition->accept_vis (vis); }
+ void vis_if_block (ASTVisitor &vis) { if_block->accept_vis (vis); }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_condition_expr ()
+ {
+ rust_assert (condition != nullptr);
+ return condition;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_if_block ()
+ {
+ rust_assert (if_block != nullptr);
+ return if_block;
+ }
+
+ // Invalid if if block or condition is null, so base stripping on that.
+ void mark_for_strip () override
+ {
+ if_block = nullptr;
+ condition = nullptr;
+ }
+ bool is_marked_for_strip () const override
+ {
+ return if_block == nullptr && condition == nullptr;
+ }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+protected:
+ // Base clone function but still concrete as concrete base class
+ virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); }
+
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IfExpr *clone_expr_with_block_impl () const final override
+ {
+ return clone_if_expr_impl ();
+ }
+};
+
+// If expression with an ending "else" expression AST node (trailing)
+class IfExprConseqElse : public IfExpr
+{
+ std::unique_ptr<BlockExpr> else_block;
+
+public:
+ std::string as_string () const override;
+
+ IfExprConseqElse (std::unique_ptr<Expr> condition,
+ std::unique_ptr<BlockExpr> if_block,
+ std::unique_ptr<BlockExpr> else_block,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : IfExpr (std::move (condition), std::move (if_block),
+ std::move (outer_attrs), locus),
+ else_block (std::move (else_block))
+ {}
+ // again, outer attributes not allowed
+
+ // Copy constructor with clone
+ IfExprConseqElse (IfExprConseqElse const &other)
+ : IfExpr (other), else_block (other.else_block->clone_block_expr ())
+ {}
+
+ // Overloaded assignment operator with cloning
+ IfExprConseqElse &operator= (IfExprConseqElse const &other)
+ {
+ IfExpr::operator= (other);
+ // condition = other.condition->clone_expr();
+ // if_block = other.if_block->clone_block_expr();
+ else_block = other.else_block->clone_block_expr ();
+
+ return *this;
+ }
+
+ // move constructors
+ IfExprConseqElse (IfExprConseqElse &&other) = default;
+ IfExprConseqElse &operator= (IfExprConseqElse &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ void vis_else_block (ASTVisitor &vis) { else_block->accept_vis (vis); }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_else_block ()
+ {
+ rust_assert (else_block != nullptr);
+ return else_block;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IfExprConseqElse *clone_if_expr_impl () const override
+ {
+ return new IfExprConseqElse (*this);
+ }
+};
+
+// If expression with an ending "else if" expression AST node
+class IfExprConseqIf : public IfExpr
+{
+ std::unique_ptr<IfExpr> conseq_if_expr;
+
+public:
+ std::string as_string () const override;
+
+ IfExprConseqIf (std::unique_ptr<Expr> condition,
+ std::unique_ptr<BlockExpr> if_block,
+ std::unique_ptr<IfExpr> conseq_if_expr,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : IfExpr (std::move (condition), std::move (if_block),
+ std::move (outer_attrs), locus),
+ conseq_if_expr (std::move (conseq_if_expr))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor with clone
+ IfExprConseqIf (IfExprConseqIf const &other)
+ : IfExpr (other), conseq_if_expr (other.conseq_if_expr->clone_if_expr ())
+ {}
+
+ // Overloaded assignment operator to use clone
+ IfExprConseqIf &operator= (IfExprConseqIf const &other)
+ {
+ IfExpr::operator= (other);
+ // condition = other.condition->clone_expr();
+ // if_block = other.if_block->clone_block_expr();
+ conseq_if_expr = other.conseq_if_expr->clone_if_expr ();
+
+ return *this;
+ }
+
+ // move constructors
+ IfExprConseqIf (IfExprConseqIf &&other) = default;
+ IfExprConseqIf &operator= (IfExprConseqIf &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ void vis_conseq_if_expr (ASTVisitor &vis)
+ {
+ conseq_if_expr->accept_vis (vis);
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<IfExpr> &get_conseq_if_expr ()
+ {
+ rust_assert (conseq_if_expr != nullptr);
+ return conseq_if_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IfExprConseqIf *clone_if_expr_impl () const override
+ {
+ return new IfExprConseqIf (*this);
+ }
+};
+
+// Basic "if let" expression AST node with no else
+class IfLetExpr : public ExprWithBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+ std::unique_ptr<Expr> value;
+ std::unique_ptr<BlockExpr> if_block;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ IfLetExpr (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+ std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)),
+ match_arm_patterns (std::move (match_arm_patterns)),
+ value (std::move (value)), if_block (std::move (if_block)), locus (locus)
+ {}
+
+ // copy constructor with clone
+ IfLetExpr (IfLetExpr const &other)
+ : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.value != nullptr)
+ value = other.value->clone_expr ();
+ if (other.if_block != nullptr)
+ if_block = other.if_block->clone_block_expr ();
+
+ match_arm_patterns.reserve (other.match_arm_patterns.size ());
+ for (const auto &e : other.match_arm_patterns)
+ match_arm_patterns.push_back (e->clone_pattern ());
+ }
+
+ // overload assignment operator to clone
+ IfLetExpr &operator= (IfLetExpr const &other)
+ {
+ ExprWithBlock::operator= (other);
+ outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.value != nullptr)
+ value = other.value->clone_expr ();
+ else
+ value = nullptr;
+ if (other.if_block != nullptr)
+ if_block = other.if_block->clone_block_expr ();
+ else
+ if_block = nullptr;
+
+ match_arm_patterns.reserve (other.match_arm_patterns.size ());
+ for (const auto &e : other.match_arm_patterns)
+ match_arm_patterns.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ IfLetExpr (IfLetExpr &&other) = default;
+ IfLetExpr &operator= (IfLetExpr &&other) = default;
+
+ // Unique pointer custom clone function
+ std::unique_ptr<IfLetExpr> clone_if_let_expr () const
+ {
+ return std::unique_ptr<IfLetExpr> (clone_if_let_expr_impl ());
+ }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if block or value is null, so base stripping on that.
+ void mark_for_strip () override
+ {
+ if_block = nullptr;
+ value = nullptr;
+ }
+ bool is_marked_for_strip () const override
+ {
+ return if_block == nullptr && value == nullptr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_value_expr ()
+ {
+ rust_assert (value != nullptr);
+ return value;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_if_block ()
+ {
+ rust_assert (if_block != nullptr);
+ return if_block;
+ }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+ {
+ return match_arm_patterns;
+ }
+ std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+ {
+ return match_arm_patterns;
+ }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base (or rather this or any derived object) */
+ IfLetExpr *clone_expr_with_block_impl () const final override
+ {
+ return clone_if_let_expr_impl ();
+ }
+
+ // Base clone function but still concrete as concrete base class
+ virtual IfLetExpr *clone_if_let_expr_impl () const
+ {
+ return new IfLetExpr (*this);
+ }
+};
+
+// If expression with an ending "else if let" expression AST node
+class IfExprConseqIfLet : public IfExpr
+{
+ std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+ std::string as_string () const override;
+
+ IfExprConseqIfLet (std::unique_ptr<Expr> condition,
+ std::unique_ptr<BlockExpr> if_block,
+ std::unique_ptr<IfLetExpr> conseq_if_let_expr,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : IfExpr (std::move (condition), std::move (if_block),
+ std::move (outer_attrs), locus),
+ if_let_expr (std::move (conseq_if_let_expr))
+ {}
+ // outer attributes not allowed
+
+ // Copy constructor with clone
+ IfExprConseqIfLet (IfExprConseqIfLet const &other)
+ : IfExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+ {}
+
+ // Overloaded assignment operator to use clone
+ IfExprConseqIfLet &operator= (IfExprConseqIfLet const &other)
+ {
+ IfExpr::operator= (other);
+ // condition = other.condition->clone_expr();
+ // if_block = other.if_block->clone_block_expr();
+ if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+ return *this;
+ }
+
+ // move constructors
+ IfExprConseqIfLet (IfExprConseqIfLet &&other) = default;
+ IfExprConseqIfLet &operator= (IfExprConseqIfLet &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<IfLetExpr> &get_conseq_if_let_expr ()
+ {
+ rust_assert (if_let_expr != nullptr);
+ return if_let_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IfExprConseqIfLet *clone_if_expr_impl () const override
+ {
+ return new IfExprConseqIfLet (*this);
+ }
+};
+
+/* AST node representing "if let" expression with an "else" expression at the
+ * end */
+class IfLetExprConseqElse : public IfLetExpr
+{
+ std::unique_ptr<BlockExpr> else_block;
+
+public:
+ std::string as_string () const override;
+
+ IfLetExprConseqElse (
+ std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+ std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+ std::unique_ptr<BlockExpr> else_block, std::vector<Attribute> outer_attrs,
+ Location locus)
+ : IfLetExpr (std::move (match_arm_patterns), std::move (value),
+ std::move (if_block), std::move (outer_attrs), locus),
+ else_block (std::move (else_block))
+ {}
+ // outer attributes not allowed
+
+ // copy constructor with clone
+ IfLetExprConseqElse (IfLetExprConseqElse const &other)
+ : IfLetExpr (other), else_block (other.else_block->clone_block_expr ())
+ {}
+
+ // overload assignment operator to clone
+ IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other)
+ {
+ IfLetExpr::operator= (other);
+ // match_arm_patterns = other.match_arm_patterns;
+ // value = other.value->clone_expr();
+ // if_block = other.if_block->clone_block_expr();
+ else_block = other.else_block->clone_block_expr ();
+ // outer_attrs = other.outer_attrs;
+
+ return *this;
+ }
+
+ // move constructors
+ IfLetExprConseqElse (IfLetExprConseqElse &&other) = default;
+ IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_else_block ()
+ {
+ rust_assert (else_block != nullptr);
+ return else_block;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IfLetExprConseqElse *clone_if_let_expr_impl () const override
+ {
+ return new IfLetExprConseqElse (*this);
+ }
+};
+
+/* AST node representing "if let" expression with an "else if" expression at the
+ * end */
+class IfLetExprConseqIf : public IfLetExpr
+{
+ std::unique_ptr<IfExpr> if_expr;
+
+public:
+ std::string as_string () const override;
+
+ IfLetExprConseqIf (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+ std::unique_ptr<Expr> value,
+ std::unique_ptr<BlockExpr> if_block,
+ std::unique_ptr<IfExpr> if_expr,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : IfLetExpr (std::move (match_arm_patterns), std::move (value),
+ std::move (if_block), std::move (outer_attrs), locus),
+ if_expr (std::move (if_expr))
+ {}
+ // again, outer attributes not allowed
+
+ // copy constructor with clone
+ IfLetExprConseqIf (IfLetExprConseqIf const &other)
+ : IfLetExpr (other), if_expr (other.if_expr->clone_if_expr ())
+ {}
+
+ // overload assignment operator to clone
+ IfLetExprConseqIf &operator= (IfLetExprConseqIf const &other)
+ {
+ IfLetExpr::operator= (other);
+ // match_arm_patterns = other.match_arm_patterns;
+ // value = other.value->clone_expr();
+ // if_block = other.if_block->clone_block_expr();
+ if_expr = other.if_expr->clone_if_expr ();
+
+ return *this;
+ }
+
+ // move constructors
+ IfLetExprConseqIf (IfLetExprConseqIf &&other) = default;
+ IfLetExprConseqIf &operator= (IfLetExprConseqIf &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<IfExpr> &get_conseq_if_expr ()
+ {
+ rust_assert (if_expr != nullptr);
+ return if_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IfLetExprConseqIf *clone_if_let_expr_impl () const override
+ {
+ return new IfLetExprConseqIf (*this);
+ }
+};
+
+/* AST node representing "if let" expression with an "else if let" expression at
+ * the end */
+class IfLetExprConseqIfLet : public IfLetExpr
+{
+ std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+ std::string as_string () const override;
+
+ IfLetExprConseqIfLet (
+ std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+ std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+ std::unique_ptr<IfLetExpr> if_let_expr, std::vector<Attribute> outer_attrs,
+ Location locus)
+ : IfLetExpr (std::move (match_arm_patterns), std::move (value),
+ std::move (if_block), std::move (outer_attrs), locus),
+ if_let_expr (std::move (if_let_expr))
+ {}
+ // outer attributes not allowed
+
+ // copy constructor with clone
+ IfLetExprConseqIfLet (IfLetExprConseqIfLet const &other)
+ : IfLetExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+ {}
+
+ // overload assignment operator to clone
+ IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet const &other)
+ {
+ IfLetExpr::operator= (other);
+ // match_arm_patterns = other.match_arm_patterns;
+ // value = other.value->clone_expr();
+ // if_block = other.if_block->clone_block_expr();
+ if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+ return *this;
+ }
+
+ // move constructors
+ IfLetExprConseqIfLet (IfLetExprConseqIfLet &&other) = default;
+ IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<IfLetExpr> &get_conseq_if_let_expr ()
+ {
+ rust_assert (if_let_expr != nullptr);
+ return if_let_expr;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IfLetExprConseqIfLet *clone_if_let_expr_impl () const override
+ {
+ return new IfLetExprConseqIfLet (*this);
+ }
+};
+
+// Match arm expression
+struct MatchArm
+{
+private:
+ std::vector<Attribute> outer_attrs;
+ // MatchArmPatterns patterns;
+ std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+
+ // bool has_match_arm_guard;
+ // inlined from MatchArmGuard
+ std::unique_ptr<Expr> guard_expr;
+
+ Location locus;
+
+public:
+ // Returns whether the MatchArm has a match arm guard expression
+ bool has_match_arm_guard () const { return guard_expr != nullptr; }
+
+ // Constructor for match arm with a guard expression
+ MatchArm (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+ Location locus, std::unique_ptr<Expr> guard_expr = nullptr,
+ std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
+ : outer_attrs (std::move (outer_attrs)),
+ match_arm_patterns (std::move (match_arm_patterns)),
+ guard_expr (std::move (guard_expr)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs)
+ {
+ // guard to protect from null pointer dereference
+ if (other.guard_expr != nullptr)
+ guard_expr = other.guard_expr->clone_expr ();
+
+ match_arm_patterns.reserve (other.match_arm_patterns.size ());
+ for (const auto &e : other.match_arm_patterns)
+ match_arm_patterns.push_back (e->clone_pattern ());
+
+ locus = other.locus;
+ }
+
+ ~MatchArm () = default;
+
+ // Overload assignment operator to clone
+ MatchArm &operator= (MatchArm const &other)
+ {
+ outer_attrs = other.outer_attrs;
+
+ if (other.guard_expr != nullptr)
+ guard_expr = other.guard_expr->clone_expr ();
+ else
+ guard_expr = nullptr;
+
+ match_arm_patterns.reserve (other.match_arm_patterns.size ());
+ for (const auto &e : other.match_arm_patterns)
+ match_arm_patterns.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ MatchArm (MatchArm &&other) = default;
+ MatchArm &operator= (MatchArm &&other) = default;
+
+ // Returns whether match arm is in an error state.
+ bool is_error () const { return match_arm_patterns.empty (); }
+
+ // Creates a match arm in an error state.
+ static MatchArm create_error ()
+ {
+ Location locus = Location ();
+ return MatchArm (std::vector<std::unique_ptr<Pattern> > (), locus);
+ }
+
+ std::string as_string () const;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_guard_expr ()
+ {
+ rust_assert (has_match_arm_guard ());
+ return guard_expr;
+ }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+ {
+ return match_arm_patterns;
+ }
+ std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+ {
+ return match_arm_patterns;
+ }
+
+ Location get_locus () const { return locus; }
+};
+
+/* A "match case" - a correlated match arm and resulting expression. Not
+ * abstract. */
+struct MatchCase
+{
+private:
+ MatchArm arm;
+ std::unique_ptr<Expr> expr;
+ NodeId node_id;
+
+ /* TODO: does whether trailing comma exists need to be stored? currently
+ * assuming it is only syntactical and has no effect on meaning. */
+
+public:
+ MatchCase (MatchArm arm, std::unique_ptr<Expr> expr)
+ : arm (std::move (arm)), expr (std::move (expr)),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ MatchCase (const MatchCase &other)
+ : arm (other.arm), expr (other.expr->clone_expr ()), node_id (other.node_id)
+ {}
+
+ MatchCase &operator= (const MatchCase &other)
+ {
+ arm = other.arm;
+ expr = other.expr->clone_expr ();
+ node_id = other.node_id;
+
+ return *this;
+ }
+
+ MatchCase (MatchCase &&other) = default;
+ MatchCase &operator= (MatchCase &&other) = default;
+
+ ~MatchCase () = default;
+
+ std::string as_string () const;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_expr ()
+ {
+ rust_assert (expr != nullptr);
+ return expr;
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ MatchArm &get_arm ()
+ {
+ rust_assert (!arm.is_error ());
+ return arm;
+ }
+
+ NodeId get_node_id () const { return node_id; }
+};
+
+// Match expression AST node
+class MatchExpr : public ExprWithBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> branch_value;
+ std::vector<Attribute> inner_attrs;
+ std::vector<MatchCase> match_arms;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ // Returns whether the match expression has any match arms.
+ bool has_match_arms () const { return !match_arms.empty (); }
+
+ MatchExpr (std::unique_ptr<Expr> branch_value,
+ std::vector<MatchCase> match_arms,
+ std::vector<Attribute> inner_attrs,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)),
+ branch_value (std::move (branch_value)),
+ inner_attrs (std::move (inner_attrs)),
+ match_arms (std::move (match_arms)), locus (locus)
+ {}
+
+ // Copy constructor requires clone due to unique_ptr
+ MatchExpr (MatchExpr const &other)
+ : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+ inner_attrs (other.inner_attrs), match_arms (other.match_arms),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.branch_value != nullptr)
+ branch_value = other.branch_value->clone_expr ();
+ }
+
+ // Overloaded assignment operator to clone due to unique_ptr
+ MatchExpr &operator= (MatchExpr const &other)
+ {
+ ExprWithBlock::operator= (other);
+ inner_attrs = other.inner_attrs;
+ match_arms = other.match_arms;
+ outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.branch_value != nullptr)
+ branch_value = other.branch_value->clone_expr ();
+ else
+ branch_value = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ MatchExpr (MatchExpr &&other) = default;
+ MatchExpr &operator= (MatchExpr &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if branch value is null, so base stripping on that.
+ void mark_for_strip () override { branch_value = nullptr; }
+ bool is_marked_for_strip () const override { return branch_value == nullptr; }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_scrutinee_expr ()
+ {
+ rust_assert (branch_value != nullptr);
+ return branch_value;
+ }
+
+ const std::vector<MatchCase> &get_match_cases () const { return match_arms; }
+ std::vector<MatchCase> &get_match_cases () { return match_arms; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MatchExpr *clone_expr_with_block_impl () const override
+ {
+ return new MatchExpr (*this);
+ }
+};
+
+// Await expression AST node (pseudo-member variable access)
+class AwaitExpr : public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ std::unique_ptr<Expr> awaited_expr;
+ Location locus;
+
+public:
+ // TODO: ensure outer attributes are actually allowed
+ AwaitExpr (std::unique_ptr<Expr> awaited_expr,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)),
+ awaited_expr (std::move (awaited_expr)), locus (locus)
+ {}
+
+ // copy constructor with clone
+ AwaitExpr (AwaitExpr const &other)
+ : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.awaited_expr != nullptr)
+ awaited_expr = other.awaited_expr->clone_expr ();
+ }
+
+ // overloaded assignment operator with clone
+ AwaitExpr &operator= (AwaitExpr const &other)
+ {
+ ExprWithoutBlock::operator= (other);
+ outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.awaited_expr != nullptr)
+ awaited_expr = other.awaited_expr->clone_expr ();
+ else
+ awaited_expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ AwaitExpr (AwaitExpr &&other) = default;
+ AwaitExpr &operator= (AwaitExpr &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if awaited expr is null, so base stripping on that.
+ void mark_for_strip () override { awaited_expr = nullptr; }
+ bool is_marked_for_strip () const override { return awaited_expr == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_awaited_expr ()
+ {
+ rust_assert (awaited_expr != nullptr);
+ return awaited_expr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ AwaitExpr *clone_expr_without_block_impl () const override
+ {
+ return new AwaitExpr (*this);
+ }
+};
+
+// Async block expression AST node (block expr that evaluates to a future)
+class AsyncBlockExpr : public ExprWithBlock
+{
+ // TODO: should this extend BlockExpr rather than be a composite of it?
+ std::vector<Attribute> outer_attrs;
+ bool has_move;
+ std::unique_ptr<BlockExpr> block_expr;
+ Location locus;
+
+public:
+ AsyncBlockExpr (std::unique_ptr<BlockExpr> block_expr, bool has_move,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)), has_move (has_move),
+ block_expr (std::move (block_expr)), locus (locus)
+ {}
+
+ // copy constructor with clone
+ AsyncBlockExpr (AsyncBlockExpr const &other)
+ : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+ has_move (other.has_move), locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.block_expr != nullptr)
+ block_expr = other.block_expr->clone_block_expr ();
+ }
+
+ // overloaded assignment operator to clone
+ AsyncBlockExpr &operator= (AsyncBlockExpr const &other)
+ {
+ ExprWithBlock::operator= (other);
+ outer_attrs = other.outer_attrs;
+ has_move = other.has_move;
+ locus = other.locus;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.block_expr != nullptr)
+ block_expr = other.block_expr->clone_block_expr ();
+ else
+ block_expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ AsyncBlockExpr (AsyncBlockExpr &&other) = default;
+ AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if block is null, so base stripping on that.
+ void mark_for_strip () override { block_expr = nullptr; }
+ bool is_marked_for_strip () const override { return block_expr == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<BlockExpr> &get_block_expr ()
+ {
+ rust_assert (block_expr != nullptr);
+ return block_expr;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ AsyncBlockExpr *clone_expr_with_block_impl () const override
+ {
+ return new AsyncBlockExpr (*this);
+ }
+};
+} // namespace AST
+} // namespace Rust
+
+#endif
--- /dev/null
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AST_MACRO_H
+#define RUST_AST_MACRO_H
+
+#include "rust-system.h"
+#include "rust-ast.h"
+#include "rust-location.h"
+
+namespace Rust {
+namespace AST {
+
+// Decls as definitions moved to rust-ast.h
+class MacroItem;
+
+class MacroFragSpec
+{
+public:
+ enum Kind
+ {
+ BLOCK,
+ EXPR,
+ IDENT,
+ ITEM,
+ LIFETIME,
+ LITERAL,
+ META,
+ PAT,
+ PATH,
+ STMT,
+ TT,
+ TY,
+ VIS,
+ INVALID // not really a specifier, but used to mark invalid one passed in
+ };
+
+ MacroFragSpec (Kind kind) : kind (kind) {}
+
+ static MacroFragSpec get_frag_spec_from_str (const std::string &str)
+ {
+ if (str == "block")
+ return MacroFragSpec (BLOCK);
+ else if (str == "expr")
+ return MacroFragSpec (EXPR);
+ else if (str == "ident")
+ return MacroFragSpec (IDENT);
+ else if (str == "item")
+ return MacroFragSpec (ITEM);
+ else if (str == "lifetime")
+ return MacroFragSpec (LIFETIME);
+ else if (str == "literal")
+ return MacroFragSpec (LITERAL);
+ else if (str == "meta")
+ return MacroFragSpec (META);
+ else if (str == "pat" || str == "pat_param")
+ return MacroFragSpec (PAT);
+ else if (str == "path")
+ return MacroFragSpec (PATH);
+ else if (str == "stmt")
+ return MacroFragSpec (STMT);
+ else if (str == "tt")
+ return MacroFragSpec (TT);
+ else if (str == "ty")
+ return MacroFragSpec (TY);
+ else if (str == "vis")
+ return MacroFragSpec (VIS);
+ else
+ {
+ // error_at("invalid string '%s' used as fragment specifier",
+ // str->c_str()));
+ return MacroFragSpec (INVALID);
+ }
+ }
+
+ Kind get_kind () const { return kind; }
+ bool is_error () const { return kind == Kind::INVALID; }
+
+ // Converts a frag spec enum item to a string form.
+ std::string as_string () const
+ {
+ switch (kind)
+ {
+ case BLOCK:
+ return "block";
+ case EXPR:
+ return "expr";
+ case IDENT:
+ return "ident";
+ case ITEM:
+ return "item";
+ case LIFETIME:
+ return "lifetime";
+ case LITERAL:
+ return "literal";
+ case META:
+ return "meta";
+ case PAT:
+ return "pat";
+ case PATH:
+ return "path";
+ case STMT:
+ return "stmt";
+ case TT:
+ return "tt";
+ case TY:
+ return "ty";
+ case VIS:
+ return "vis";
+ case INVALID:
+ return "INVALID_FRAG_SPEC";
+ default:
+ return "ERROR_MARK_STRING - unknown frag spec";
+ }
+ }
+
+ bool has_follow_set_restrictions () const
+ {
+ switch (kind)
+ {
+ case EXPR:
+ case STMT:
+ case PAT:
+ case PATH:
+ case TY:
+ case VIS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ bool has_follow_set_fragment_restrictions () const
+ {
+ switch (kind)
+ {
+ case PAT:
+ case TY:
+ case VIS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+private:
+ Kind kind;
+};
+
+// A macro match that has an identifier and fragment spec
+class MacroMatchFragment : public MacroMatch
+{
+ Identifier ident;
+ MacroFragSpec frag_spec;
+ Location locus;
+
+public:
+ MacroMatchFragment (Identifier ident, MacroFragSpec frag_spec, Location locus)
+ : ident (std::move (ident)), frag_spec (frag_spec), locus (locus)
+ {}
+
+ // Returns whether macro match fragment is in an error state.
+ bool is_error () const
+ {
+ return frag_spec.get_kind () == MacroFragSpec::INVALID;
+ }
+
+ // Creates an error state macro match fragment.
+ static MacroMatchFragment create_error (Location locus)
+ {
+ return MacroMatchFragment (std::string (""),
+ MacroFragSpec (MacroFragSpec::Kind::INVALID),
+ locus);
+ }
+
+ std::string as_string () const override;
+ Location get_match_locus () const override { return locus; };
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ MacroMatchType get_macro_match_type () const override
+ {
+ return MacroMatchType::Fragment;
+ }
+
+ Identifier get_ident () const { return ident; }
+ const MacroFragSpec &get_frag_spec () const { return frag_spec; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MacroMatchFragment *clone_macro_match_impl () const override
+ {
+ return new MacroMatchFragment (*this);
+ }
+};
+
+// A repetition macro match
+class MacroMatchRepetition : public MacroMatch
+{
+public:
+ enum MacroRepOp
+ {
+ NONE,
+ ANY,
+ ONE_OR_MORE,
+ ZERO_OR_ONE,
+ };
+
+private:
+ std::vector<std::unique_ptr<MacroMatch> > matches;
+ MacroRepOp op;
+
+ // bool has_sep;
+ typedef Token MacroRepSep;
+ // any token except delimiters and repetition operators
+ std::unique_ptr<MacroRepSep> sep;
+ Location locus;
+
+public:
+ // Returns whether macro match repetition has separator token.
+ bool has_sep () const { return sep != nullptr; }
+
+ MacroMatchRepetition (std::vector<std::unique_ptr<MacroMatch> > matches,
+ MacroRepOp op, std::unique_ptr<MacroRepSep> sep,
+ Location locus)
+ : matches (std::move (matches)), op (op), sep (std::move (sep)),
+ locus (locus)
+ {}
+
+ // Copy constructor with clone
+ MacroMatchRepetition (MacroMatchRepetition const &other)
+ : op (other.op), locus (other.locus)
+ {
+ // guard to protect from null pointer dereference
+ if (other.sep != nullptr)
+ sep = other.sep->clone_token ();
+
+ matches.reserve (other.matches.size ());
+ for (const auto &e : other.matches)
+ matches.push_back (e->clone_macro_match ());
+ }
+
+ // Overloaded assignment operator to clone
+ MacroMatchRepetition &operator= (MacroMatchRepetition const &other)
+ {
+ op = other.op;
+ locus = other.locus;
+
+ // guard to protect from null pointer dereference
+ if (other.sep != nullptr)
+ sep = other.sep->clone_token ();
+ else
+ sep = nullptr;
+
+ matches.reserve (other.matches.size ());
+ for (const auto &e : other.matches)
+ matches.push_back (e->clone_macro_match ());
+
+ return *this;
+ }
+
+ // move constructors
+ MacroMatchRepetition (MacroMatchRepetition &&other) = default;
+ MacroMatchRepetition &operator= (MacroMatchRepetition &&other) = default;
+
+ std::string as_string () const override;
+ Location get_match_locus () const override { return locus; };
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ MacroMatchType get_macro_match_type () const override
+ {
+ return MacroMatchType::Repetition;
+ }
+
+ MacroRepOp get_op () const { return op; }
+ const std::unique_ptr<MacroRepSep> &get_sep () const { return sep; }
+ std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; }
+ const std::vector<std::unique_ptr<MacroMatch> > &get_matches () const
+ {
+ return matches;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MacroMatchRepetition *clone_macro_match_impl () const override
+ {
+ return new MacroMatchRepetition (*this);
+ }
+};
+
+// can't inline due to polymorphism
+class MacroMatcher : public MacroMatch
+{
+ DelimType delim_type;
+ std::vector<std::unique_ptr<MacroMatch> > matches;
+ Location locus;
+
+ // TODO: think of way to mark invalid that doesn't take up more space
+ bool is_invalid;
+
+public:
+ MacroMatcher (DelimType delim_type,
+ std::vector<std::unique_ptr<MacroMatch> > matches,
+ Location locus)
+ : delim_type (delim_type), matches (std::move (matches)), locus (locus),
+ is_invalid (false)
+ {}
+
+ // copy constructor with vector clone
+ MacroMatcher (MacroMatcher const &other)
+ : delim_type (other.delim_type), locus (other.locus)
+ {
+ matches.reserve (other.matches.size ());
+ for (const auto &e : other.matches)
+ matches.push_back (e->clone_macro_match ());
+ }
+
+ // overloaded assignment operator with vector clone
+ MacroMatcher &operator= (MacroMatcher const &other)
+ {
+ delim_type = other.delim_type;
+ locus = other.locus;
+
+ matches.reserve (other.matches.size ());
+ for (const auto &e : other.matches)
+ matches.push_back (e->clone_macro_match ());
+
+ return *this;
+ }
+
+ // move constructors
+ MacroMatcher (MacroMatcher &&other) = default;
+ MacroMatcher &operator= (MacroMatcher &&other) = default;
+
+ // Creates an error state macro matcher.
+ static MacroMatcher create_error (Location locus)
+ {
+ return MacroMatcher (true, locus);
+ }
+
+ // Returns whether MacroMatcher is in an error state.
+ bool is_error () const { return is_invalid; }
+ Location get_match_locus () const override { return locus; }
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ MacroMatchType get_macro_match_type () const override
+ {
+ return MacroMatchType::Matcher;
+ }
+
+ DelimType get_delim_type () const { return delim_type; }
+ std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; }
+ const std::vector<std::unique_ptr<MacroMatch> > &get_matches () const
+ {
+ return matches;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MacroMatcher *clone_macro_match_impl () const override
+ {
+ return new MacroMatcher (*this);
+ }
+
+ // constructor only used to create error matcher
+ MacroMatcher (bool is_invalid, Location locus)
+ : delim_type (PARENS), locus (locus), is_invalid (is_invalid)
+ {}
+};
+
+// TODO: inline?
+struct MacroTranscriber
+{
+private:
+ DelimTokenTree token_tree;
+ Location locus;
+
+public:
+ MacroTranscriber (DelimTokenTree token_tree, Location locus)
+ : token_tree (std::move (token_tree)), locus (locus)
+ {}
+
+ std::string as_string () const { return token_tree.as_string (); }
+
+ Location get_locus () const { return locus; }
+
+ DelimTokenTree &get_token_tree () { return token_tree; }
+};
+
+// A macro rule? Matcher and transcriber pair?
+struct MacroRule
+{
+private:
+ MacroMatcher matcher;
+ MacroTranscriber transcriber;
+ Location locus;
+
+public:
+ MacroRule (MacroMatcher matcher, MacroTranscriber transcriber, Location locus)
+ : matcher (std::move (matcher)), transcriber (std::move (transcriber)),
+ locus (locus)
+ {}
+
+ // Returns whether macro rule is in error state.
+ bool is_error () const { return matcher.is_error (); }
+
+ // Creates an error state macro rule.
+ static MacroRule create_error (Location locus)
+ {
+ return MacroRule (MacroMatcher::create_error (locus),
+ MacroTranscriber (DelimTokenTree::create_empty (),
+ Location ()),
+ locus);
+ }
+
+ Location get_locus () const { return locus; }
+
+ std::string as_string () const;
+
+ MacroMatcher &get_matcher () { return matcher; }
+ MacroTranscriber &get_transcriber () { return transcriber; }
+};
+
+// A macro rules definition item AST node
+class MacroRulesDefinition : public MacroItem
+{
+ std::vector<Attribute> outer_attrs;
+ Identifier rule_name;
+ // MacroRulesDef rules_def;
+ // only curly without required semicolon at end
+ DelimType delim_type;
+ // MacroRules rules;
+ std::vector<MacroRule> rules; // inlined form
+ Location locus;
+
+ std::function<ASTFragment (Location, MacroInvocData &)>
+ associated_transcriber;
+ // Since we can't compare std::functions, we need to use an extra boolean
+ bool is_builtin_rule;
+
+ /**
+ * Default function to use as an associated transcriber. This function should
+ * never be called, hence the gcc_unreachable().
+ * If this function is used, then the macro is not builtin and the compiler
+ * should make use of the actual rules. If the macro is builtin, then another
+ * associated transcriber should be used
+ */
+ static ASTFragment dummy_builtin (Location, MacroInvocData &)
+ {
+ gcc_unreachable ();
+ return ASTFragment::create_error ();
+ }
+
+ /* NOTE: in rustc, macro definitions are considered (and parsed as) a type
+ * of macro, whereas here they are considered part of the language itself.
+ * I am not aware of the implications of this decision. The rustc spec does
+ * mention that using the same parser for macro definitions and invocations
+ * is "extremely self-referential and non-intuitive". */
+
+public:
+ std::string as_string () const override;
+
+ MacroRulesDefinition (Identifier rule_name, DelimType delim_type,
+ std::vector<MacroRule> rules,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)), rule_name (std::move (rule_name)),
+ delim_type (delim_type), rules (std::move (rules)), locus (locus),
+ associated_transcriber (dummy_builtin), is_builtin_rule (false)
+ {}
+
+ MacroRulesDefinition (Identifier builtin_name, DelimType delim_type,
+ std::function<ASTFragment (Location, MacroInvocData &)>
+ associated_transcriber)
+ : outer_attrs (std::vector<Attribute> ()), rule_name (builtin_name),
+ delim_type (delim_type), rules (std::vector<MacroRule> ()),
+ locus (Location ()), associated_transcriber (associated_transcriber),
+ is_builtin_rule (true)
+ {}
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if rule name is empty, so base stripping on that.
+ void mark_for_strip () override { rule_name = ""; }
+ bool is_marked_for_strip () const override { return rule_name.empty (); }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+ std::vector<MacroRule> &get_macro_rules () { return rules; }
+ const std::vector<MacroRule> &get_macro_rules () const { return rules; }
+
+ Location get_locus () const override final { return locus; }
+
+ Identifier get_rule_name () const { return rule_name; }
+
+ std::vector<MacroRule> &get_rules () { return rules; }
+ const std::vector<MacroRule> &get_rules () const { return rules; }
+
+ bool is_builtin () const { return is_builtin_rule; }
+ const std::function<ASTFragment (Location, MacroInvocData &)> &
+ get_builtin_transcriber () const
+ {
+ rust_assert (is_builtin ());
+ return associated_transcriber;
+ }
+ void set_builtin_transcriber (
+ std::function<ASTFragment (Location, MacroInvocData &)> transcriber)
+ {
+ associated_transcriber = transcriber;
+ is_builtin_rule = true;
+ }
+
+ Kind get_ast_kind () const override { return Kind::MACRO_RULES_DEFINITION; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MacroRulesDefinition *clone_item_impl () const override
+ {
+ return new MacroRulesDefinition (*this);
+ }
+};
+
+/* AST node of a macro invocation, which is replaced by the macro result at
+ * compile time */
+class MacroInvocation : public TypeNoBounds,
+ public Pattern,
+ public MacroItem,
+ public TraitItem,
+ public TraitImplItem,
+ public InherentImplItem,
+ public ExternalItem,
+ public ExprWithoutBlock
+{
+ std::vector<Attribute> outer_attrs;
+ MacroInvocData invoc_data;
+ Location locus;
+
+ // Important for when we actually expand the macro
+ bool is_semi_coloned;
+
+ NodeId node_id;
+
+public:
+ std::string as_string () const override;
+
+ MacroInvocation (MacroInvocData invoc_data,
+ std::vector<Attribute> outer_attrs, Location locus,
+ bool is_semi_coloned = false)
+ : outer_attrs (std::move (outer_attrs)),
+ invoc_data (std::move (invoc_data)), locus (locus),
+ is_semi_coloned (is_semi_coloned),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if path is empty, so base stripping on that.
+ void mark_for_strip () override { invoc_data.mark_for_strip (); }
+ bool is_marked_for_strip () const override
+ {
+ return invoc_data.is_marked_for_strip ();
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ NodeId get_pattern_node_id () const override final
+ {
+ return ExprWithoutBlock::get_node_id ();
+ }
+
+ Kind get_ast_kind () const override { return Kind::MACRO_INVOCATION; }
+
+ NodeId get_macro_node_id () const { return node_id; }
+
+ MacroInvocData &get_invoc_data () { return invoc_data; }
+
+ bool has_semicolon () const { return is_semi_coloned; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MacroInvocation *clone_pattern_impl () const final override
+ {
+ return clone_macro_invocation_impl ();
+ }
+
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MacroInvocation *clone_expr_without_block_impl () const final override
+ {
+ return clone_macro_invocation_impl ();
+ }
+
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ MacroInvocation *clone_type_no_bounds_impl () const final override
+ {
+ return clone_macro_invocation_impl ();
+ }
+
+ MacroInvocation *clone_external_item_impl () const final override
+ {
+ return clone_macro_invocation_impl ();
+ }
+
+ /*virtual*/ MacroInvocation *clone_macro_invocation_impl () const
+ {
+ return new MacroInvocation (*this);
+ }
+
+ Item *clone_item_impl () const override
+ {
+ return clone_macro_invocation_impl ();
+ }
+
+ bool is_item () const override { return !has_semicolon (); }
+
+ TraitItem *clone_trait_item_impl () const override
+ {
+ return clone_macro_invocation_impl ();
+ };
+
+ TraitImplItem *clone_trait_impl_item_impl () const override
+ {
+ return clone_macro_invocation_impl ();
+ };
+
+ InherentImplItem *clone_inherent_impl_item_impl () const override
+ {
+ return clone_macro_invocation_impl ();
+ }
+
+ ExprWithoutBlock *to_stmt () const override
+
+ {
+ auto new_impl = clone_macro_invocation_impl ();
+ new_impl->is_semi_coloned = true;
+
+ return new_impl;
+ }
+};
+
+// more generic meta item path-only form
+class MetaItemPath : public MetaItem
+{
+ SimplePath path;
+
+public:
+ MetaItemPath (SimplePath path) : path (std::move (path)) {}
+
+ std::string as_string () const override { return path.as_string (); }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // HACK: used to simplify parsing - returns non-empty only in this case
+ SimplePath to_path_item () const override
+ {
+ // this should copy construct - TODO ensure it does
+ return path;
+ }
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+ Attribute to_attribute () const override;
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaItemPath *clone_meta_item_inner_impl () const override
+ {
+ return new MetaItemPath (*this);
+ }
+};
+
+// more generic meta item sequence form
+class MetaItemSeq : public MetaItem
+{
+ SimplePath path;
+ std::vector<std::unique_ptr<MetaItemInner> > seq;
+
+public:
+ MetaItemSeq (SimplePath path,
+ std::vector<std::unique_ptr<MetaItemInner> > seq)
+ : path (std::move (path)), seq (std::move (seq))
+ {}
+
+ // copy constructor with vector clone
+ MetaItemSeq (const MetaItemSeq &other) : path (other.path)
+ {
+ seq.reserve (other.seq.size ());
+ for (const auto &e : other.seq)
+ seq.push_back (e->clone_meta_item_inner ());
+ }
+
+ // overloaded assignment operator with vector clone
+ MetaItemSeq &operator= (const MetaItemSeq &other)
+ {
+ MetaItem::operator= (other);
+ path = other.path;
+
+ seq.reserve (other.seq.size ());
+ for (const auto &e : other.seq)
+ seq.push_back (e->clone_meta_item_inner ());
+
+ return *this;
+ }
+
+ // default move constructors
+ MetaItemSeq (MetaItemSeq &&other) = default;
+ MetaItemSeq &operator= (MetaItemSeq &&other) = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+ Attribute to_attribute () const override;
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaItemSeq *clone_meta_item_inner_impl () const override
+ {
+ return new MetaItemSeq (*this);
+ }
+};
+
+// Preferred specialisation for single-identifier meta items.
+class MetaWord : public MetaItem
+{
+ Identifier ident;
+ Location ident_locus;
+
+public:
+ MetaWord (Identifier ident, Location ident_locus)
+ : ident (std::move (ident)), ident_locus (ident_locus)
+ {}
+
+ std::string as_string () const override { return ident; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+ Attribute to_attribute () const override;
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaWord *clone_meta_item_inner_impl () const override
+ {
+ return new MetaWord (*this);
+ }
+};
+
+// Preferred specialisation for "identifier '=' string literal" meta items.
+class MetaNameValueStr : public MetaItem
+{
+ Identifier ident;
+ Location ident_locus;
+
+ // NOTE: str stored without quotes
+ std::string str;
+ Location str_locus;
+
+public:
+ MetaNameValueStr (Identifier ident, Location ident_locus, std::string str,
+ Location str_locus)
+ : ident (std::move (ident)), ident_locus (ident_locus),
+ str (std::move (str)), str_locus (str_locus)
+ {}
+
+ std::string as_string () const override
+ {
+ return ident + " = \"" + str + "\"";
+ }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // HACK: used to simplify parsing - creates a copy of this
+ std::unique_ptr<MetaNameValueStr> to_meta_name_value_str () const override
+ {
+ return std::unique_ptr<MetaNameValueStr> (clone_meta_item_inner_impl ());
+ }
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+ Attribute to_attribute () const override;
+
+ inline std::pair<Identifier, std::string> get_name_value_pair () const
+ {
+ return std::pair<Identifier, std::string> (ident, str);
+ }
+
+ bool is_key_value_pair () const override { return true; }
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaNameValueStr *clone_meta_item_inner_impl () const override
+ {
+ return new MetaNameValueStr (*this);
+ }
+};
+
+// doubles up as MetaListIdents - determine via iterating through each path?
+// Preferred specialisation for "identifier '(' SimplePath, SimplePath, ... ')'"
+class MetaListPaths : public MetaItem
+{
+ Identifier ident;
+ Location ident_locus;
+ std::vector<SimplePath> paths;
+
+public:
+ MetaListPaths (Identifier ident, Location ident_locus,
+ std::vector<SimplePath> paths)
+ : ident (std::move (ident)), ident_locus (ident_locus),
+ paths (std::move (paths))
+ {}
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+ Attribute to_attribute () const override;
+
+private:
+ bool check_path_exists_in_cfg (const Session &session,
+ const SimplePath &path) const;
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaListPaths *clone_meta_item_inner_impl () const override
+ {
+ return new MetaListPaths (*this);
+ }
+};
+
+// Preferred specialisation for "identifier '(' MetaNameValueStr, ... ')'"
+class MetaListNameValueStr : public MetaItem
+{
+ Identifier ident;
+ Location ident_locus;
+ std::vector<MetaNameValueStr> strs;
+
+public:
+ MetaListNameValueStr (Identifier ident, Location ident_locus,
+ std::vector<MetaNameValueStr> strs)
+ : ident (std::move (ident)), ident_locus (ident_locus),
+ strs (std::move (strs))
+ {}
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool check_cfg_predicate (const Session &session) const override;
+
+ Attribute to_attribute () const override;
+
+protected:
+ // Use covariance to implement clone function as returning this type
+ MetaListNameValueStr *clone_meta_item_inner_impl () const override
+ {
+ return new MetaListNameValueStr (*this);
+ }
+};
+
+// Object that parses macros from a token stream.
+/* TODO: would "AttributeParser" be a better name? MetaItems are only for
+ * attributes, I believe */
+struct AttributeParser
+{
+private:
+ // TODO: might as well rewrite to use lexer tokens
+ std::vector<std::unique_ptr<Token> > token_stream;
+ int stream_pos;
+
+public:
+ AttributeParser (std::vector<std::unique_ptr<Token> > token_stream,
+ int stream_start_pos = 0)
+ : token_stream (std::move (token_stream)), stream_pos (stream_start_pos)
+ {}
+
+ ~AttributeParser () = default;
+
+ std::vector<std::unique_ptr<MetaItemInner> > parse_meta_item_seq ();
+
+private:
+ // Parses a MetaItemInner.
+ std::unique_ptr<MetaItemInner> parse_meta_item_inner ();
+ // Returns whether token can end a meta item.
+ bool is_end_meta_item_tok (TokenId id) const;
+ // Parses a simple path.
+ SimplePath parse_simple_path ();
+ // Parses a segment of a simple path (but not scope resolution operator).
+ SimplePathSegment parse_simple_path_segment ();
+ // Parses a MetaItemLitExpr.
+ std::unique_ptr<MetaItemLitExpr> parse_meta_item_lit ();
+ // Parses a literal.
+ Literal parse_literal ();
+ // Parses a meta item that begins with a simple path.
+ std::unique_ptr<MetaItem> parse_path_meta_item ();
+
+ // TODO: should this be const?
+ std::unique_ptr<Token> &peek_token (int i = 0)
+ {
+ return token_stream[stream_pos + i];
+ }
+
+ void skip_token (int i = 0) { stream_pos += 1 + i; }
+};
+} // namespace AST
+} // namespace Rust
+
+/* <https://stackoverflow.com/a/35304501> */
+namespace std {
+template <> struct hash<Rust::AST::MacroFragSpec::Kind>
+{
+ size_t operator() (const Rust::AST::MacroFragSpec::Kind &t) const noexcept
+ {
+ return size_t (t);
+ }
+};
+} // namespace std
+
+#endif
--- /dev/null
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AST_PATH_H
+#define RUST_AST_PATH_H
+/* "Path" (identifier within namespaces, essentially) handling. Required include
+ * for virtually all AST-related functionality. */
+
+#include "rust-ast.h"
+#include "system.h"
+
+namespace Rust {
+namespace AST {
+
+// The "identifier" (not generic args) aspect of each path expression segment
+class PathIdentSegment
+{
+ std::string segment_name;
+ Location locus;
+
+ // only allow identifiers, "super", "self", "Self", "crate", or "$crate"
+public:
+ PathIdentSegment (std::string segment_name, Location locus)
+ : segment_name (std::move (segment_name)), locus (locus)
+ {}
+
+ // Creates an error PathIdentSegment.
+ static PathIdentSegment create_error ()
+ {
+ return PathIdentSegment ("", Location ());
+ }
+
+ // Returns whether PathIdentSegment is in an error state.
+ bool is_error () const { return segment_name.empty (); }
+
+ std::string as_string () const { return segment_name; }
+
+ Location get_locus () const { return locus; }
+
+ bool is_super_segment () const { return as_string ().compare ("super") == 0; }
+ bool is_crate_segment () const { return as_string ().compare ("crate") == 0; }
+ bool is_lower_self () const { return as_string ().compare ("self") == 0; }
+ bool is_big_self () const { return as_string ().compare ("Self") == 0; }
+};
+
+// A binding of an identifier to a type used in generic arguments in paths
+struct GenericArgsBinding
+{
+private:
+ Identifier identifier;
+ std::unique_ptr<Type> type;
+ Location locus;
+
+public:
+ // Returns whether binding is in an error state.
+ bool is_error () const
+ {
+ return type == nullptr;
+ // and also identifier is empty, but cheaper computation
+ }
+
+ // Creates an error state generic args binding.
+ static GenericArgsBinding create_error ()
+ {
+ return GenericArgsBinding ("", nullptr);
+ }
+
+ // Pointer type for type in constructor to enable polymorphism
+ GenericArgsBinding (Identifier ident, std::unique_ptr<Type> type_ptr,
+ Location locus = Location ())
+ : identifier (std::move (ident)), type (std::move (type_ptr)), locus (locus)
+ {}
+
+ // Copy constructor has to deep copy the type as it is a unique pointer
+ GenericArgsBinding (GenericArgsBinding const &other)
+ : identifier (other.identifier), locus (other.locus)
+ {
+ // guard to protect from null pointer dereference
+ if (other.type != nullptr)
+ type = other.type->clone_type ();
+ }
+
+ // default destructor
+ ~GenericArgsBinding () = default;
+
+ // Overload assignment operator to deep copy the pointed-to type
+ GenericArgsBinding &operator= (GenericArgsBinding const &other)
+ {
+ identifier = other.identifier;
+ locus = other.locus;
+
+ // guard to protect from null pointer dereference
+ if (other.type != nullptr)
+ type = other.type->clone_type ();
+ else
+ type = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ GenericArgsBinding (GenericArgsBinding &&other) = default;
+ GenericArgsBinding &operator= (GenericArgsBinding &&other) = default;
+
+ std::string as_string () const;
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::unique_ptr<Type> &get_type ()
+ {
+ rust_assert (type != nullptr);
+ return type;
+ }
+
+ Location get_locus () const { return locus; }
+
+ Identifier get_identifier () const { return identifier; }
+};
+
+/* Class representing a const generic application */
+class GenericArg
+{
+public:
+ /**
+ * const generic arguments cannot always be differentiated with generic type
+ * arguments during parsing, e.g:
+ * ```rust
+ * let a: Foo<N>;
+ * ```
+ *
+ * Is N a type? A constant defined elsewhere? The parser cannot know, and must
+ * not draw any conclusions. We must wait until later passes of the compiler
+ * to decide whether this refers to a constant item or a type.
+ *
+ * On the other hand, simple expressions like literals or block expressions
+ * will always be constant expressions: There is no ambiguity at all.
+ */
+ enum class Kind
+ {
+ Error,
+ Const, // A const value
+ Type, // A type argument (not discernable during parsing)
+ Either, // Either a type or a const value, cleared up during resolving
+ };
+
+ static GenericArg create_error ()
+ {
+ return GenericArg (nullptr, nullptr, "", Kind::Error, Location ());
+ }
+
+ static GenericArg create_const (std::unique_ptr<Expr> expression)
+ {
+ auto locus = expression->get_locus ();
+ return GenericArg (std::move (expression), nullptr, "", Kind::Const, locus);
+ }
+
+ static GenericArg create_type (std::unique_ptr<Type> type)
+ {
+ auto locus = type->get_locus ();
+ return GenericArg (nullptr, std::move (type), "", Kind::Type, locus);
+ }
+
+ static GenericArg create_ambiguous (Identifier path, Location locus)
+ {
+ return GenericArg (nullptr, nullptr, std::move (path), Kind::Either, locus);
+ }
+
+ GenericArg (const GenericArg &other)
+ : path (other.path), kind (other.kind), locus (other.locus)
+ {
+ if (other.expression)
+ expression = other.expression->clone_expr ();
+ if (other.type)
+ type = other.type->clone_type ();
+ }
+
+ GenericArg operator= (const GenericArg &other)
+ {
+ kind = other.kind;
+ path = other.path;
+ locus = other.locus;
+
+ if (other.expression)
+ expression = other.expression->clone_expr ();
+ if (other.type)
+ type = other.type->clone_type ();
+
+ return *this;
+ }
+
+ bool is_error () const { return kind == Kind::Error; }
+
+ Kind get_kind () const { return kind; }
+ const Location &get_locus () const { return locus; }
+
+ std::unique_ptr<Expr> &get_expression ()
+ {
+ rust_assert (kind == Kind::Const);
+
+ return expression;
+ }
+
+ std::unique_ptr<Type> &get_type ()
+ {
+ rust_assert (kind == Kind::Type);
+
+ return type;
+ }
+
+ const std::string &get_path () const
+ {
+ rust_assert (kind == Kind::Either);
+
+ return path;
+ }
+
+ std::string as_string () const
+ {
+ switch (get_kind ())
+ {
+ case Kind::Error:
+ gcc_unreachable ();
+ case Kind::Either:
+ return "Ambiguous: " + path;
+ case Kind::Const:
+ return "Const: { " + expression->as_string () + " }";
+ case Kind::Type:
+ return "Type: " + type->as_string ();
+ }
+
+ return "";
+ }
+
+ /**
+ * Disambiguate an ambiguous generic argument to a const generic argument,
+ * unequivocally
+ */
+ GenericArg disambiguate_to_const () const;
+
+ /**
+ * Disambiguate an ambiguous generic argument to a type argument,
+ * unequivocally
+ */
+ GenericArg disambiguate_to_type () const;
+
+private:
+ GenericArg (std::unique_ptr<Expr> expression, std::unique_ptr<Type> type,
+ Identifier path, Kind kind, Location locus)
+ : expression (std::move (expression)), type (std::move (type)),
+ path (std::move (path)), kind (kind), locus (locus)
+ {}
+
+ /**
+ * Expression associated with a `Clear` const generic application
+ * A null pointer here is allowed in the case that the const argument is
+ * ambiguous.
+ */
+ std::unique_ptr<Expr> expression;
+
+ /**
+ * If the argument ends up being a type argument instead. A null pointer will
+ * be present here until the resolving phase.
+ */
+ std::unique_ptr<Type> type;
+
+ /**
+ * Optional path which cannot be differentiated between a constant item and
+ * a type. Only used for ambiguous const generic arguments, otherwise
+ * empty.
+ */
+ Identifier path;
+
+ /* Which kind of const generic application are we dealing with */
+ Kind kind;
+
+ Location locus;
+};
+
+/**
+ * Representation of const generic parameters
+ */
+class ConstGenericParam : public GenericParam
+{
+ /* Name of the parameter */
+ Identifier name;
+
+ /* Mandatory type of the const parameter - a null pointer is an error */
+ std::unique_ptr<AST::Type> type;
+
+ /**
+ * Default value for the const generic parameter
+ */
+ GenericArg default_value;
+
+ Attribute outer_attr;
+ Location locus;
+
+public:
+ ConstGenericParam (Identifier name, std::unique_ptr<AST::Type> type,
+ GenericArg default_value, Attribute outer_attr,
+ Location locus)
+ : name (name), type (std::move (type)),
+ default_value (std::move (default_value)), outer_attr (outer_attr),
+ locus (locus)
+ {}
+
+ ConstGenericParam (const ConstGenericParam &other)
+ : GenericParam (), name (other.name), type (other.type->clone_type ()),
+ default_value (other.default_value), outer_attr (other.outer_attr),
+ locus (other.locus)
+ {}
+
+ bool has_type () const { return type != nullptr; }
+ bool has_default_value () const { return !default_value.is_error (); }
+
+ const Identifier &get_name () const { return name; }
+
+ std::unique_ptr<AST::Type> &get_type ()
+ {
+ rust_assert (has_type ());
+
+ return type;
+ }
+
+ GenericArg &get_default_value ()
+ {
+ rust_assert (has_default_value ());
+
+ return default_value;
+ }
+
+ const GenericArg &get_default_value () const
+ {
+ rust_assert (has_default_value ());
+
+ return default_value;
+ }
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ Location get_locus () const override final { return locus; }
+
+ Kind get_kind () const override final { return Kind::Const; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ConstGenericParam *clone_generic_param_impl () const override
+ {
+ return new ConstGenericParam (*this);
+ }
+};
+
+// Generic arguments allowed in each path expression segment - inline?
+struct GenericArgs
+{
+ std::vector<Lifetime> lifetime_args;
+ std::vector<GenericArg> generic_args;
+ std::vector<GenericArgsBinding> binding_args;
+ Location locus;
+
+public:
+ // Returns true if there are any generic arguments
+ bool has_generic_args () const
+ {
+ return !(lifetime_args.empty () && generic_args.empty ()
+ && binding_args.empty ());
+ }
+
+ GenericArgs (std::vector<Lifetime> lifetime_args,
+ std::vector<GenericArg> generic_args,
+ std::vector<GenericArgsBinding> binding_args,
+ Location locus = Location ())
+ : lifetime_args (std::move (lifetime_args)),
+ generic_args (std::move (generic_args)),
+ binding_args (std::move (binding_args)), locus (locus)
+ {}
+
+ // copy constructor with vector clone
+ GenericArgs (GenericArgs const &other)
+ : lifetime_args (other.lifetime_args), generic_args (other.generic_args),
+ binding_args (other.binding_args), locus (other.locus)
+ {}
+
+ ~GenericArgs () = default;
+
+ // overloaded assignment operator to vector clone
+ GenericArgs &operator= (GenericArgs const &other)
+ {
+ lifetime_args = other.lifetime_args;
+ generic_args = other.generic_args;
+ binding_args = other.binding_args;
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ GenericArgs (GenericArgs &&other) = default;
+ GenericArgs &operator= (GenericArgs &&other) = default;
+
+ // Creates an empty GenericArgs (no arguments)
+ static GenericArgs create_empty () { return GenericArgs ({}, {}, {}); }
+
+ std::string as_string () const;
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::vector<GenericArg> &get_generic_args () { return generic_args; }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::vector<GenericArgsBinding> &get_binding_args () { return binding_args; }
+
+ std::vector<Lifetime> &get_lifetime_args () { return lifetime_args; };
+
+ Location get_locus () { return locus; }
+};
+
+/* A segment of a path in expression, including an identifier aspect and maybe
+ * generic args */
+class PathExprSegment
+{ // or should this extend PathIdentSegment?
+private:
+ PathIdentSegment segment_name;
+ GenericArgs generic_args;
+ Location locus;
+ NodeId node_id;
+
+public:
+ // Returns true if there are any generic arguments
+ bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+ // Constructor for segment (from IdentSegment and GenericArgs)
+ PathExprSegment (PathIdentSegment segment_name, Location locus,
+ GenericArgs generic_args = GenericArgs::create_empty ())
+ : segment_name (std::move (segment_name)),
+ generic_args (std::move (generic_args)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ /* Constructor for segment with generic arguments (from segment name and all
+ * args) */
+ PathExprSegment (std::string segment_name, Location locus,
+ std::vector<Lifetime> lifetime_args = {},
+ std::vector<GenericArg> generic_args = {},
+ std::vector<GenericArgsBinding> binding_args = {})
+ : segment_name (PathIdentSegment (std::move (segment_name), locus)),
+ generic_args (GenericArgs (std::move (lifetime_args),
+ std::move (generic_args),
+ std::move (binding_args))),
+ locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Returns whether path expression segment is in an error state.
+ bool is_error () const { return segment_name.is_error (); }
+
+ // Creates an error-state path expression segment.
+ static PathExprSegment create_error ()
+ {
+ return PathExprSegment (PathIdentSegment::create_error (), Location ());
+ }
+
+ std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ GenericArgs &get_generic_args ()
+ {
+ rust_assert (has_generic_args ());
+ return generic_args;
+ }
+
+ PathIdentSegment &get_ident_segment () { return segment_name; }
+ const PathIdentSegment &get_ident_segment () const { return segment_name; }
+
+ NodeId get_node_id () const { return node_id; }
+
+ bool is_super_path_seg () const
+ {
+ return !has_generic_args () && get_ident_segment ().is_super_segment ();
+ }
+
+ bool is_crate_path_seg () const
+ {
+ return !has_generic_args () && get_ident_segment ().is_crate_segment ();
+ }
+ bool is_lower_self_seg () const
+ {
+ return !has_generic_args () && get_ident_segment ().is_lower_self ();
+ }
+};
+
+// AST node representing a pattern that involves a "path" - abstract base
+// class
+class PathPattern : public Pattern
+{
+ std::vector<PathExprSegment> segments;
+
+protected:
+ PathPattern (std::vector<PathExprSegment> segments)
+ : segments (std::move (segments))
+ {}
+
+ // Returns whether path has segments.
+ bool has_segments () const { return !segments.empty (); }
+
+ /* Converts path segments to their equivalent SimplePath segments if
+ * possible, and creates a SimplePath from them. */
+ SimplePath convert_to_simple_path (bool with_opening_scope_resolution) const;
+
+ // Removes all segments of the path.
+ void remove_all_segments ()
+ {
+ segments.clear ();
+ segments.shrink_to_fit ();
+ }
+
+public:
+ /* Returns whether the path is a single segment (excluding qualified path
+ * initial as segment). */
+ bool is_single_segment () const { return segments.size () == 1; }
+
+ std::string as_string () const override;
+
+ // TODO: this seems kinda dodgy
+ std::vector<PathExprSegment> &get_segments () { return segments; }
+ const std::vector<PathExprSegment> &get_segments () const { return segments; }
+};
+
+/* AST node representing a path-in-expression pattern (path that allows
+ * generic arguments) */
+class PathInExpression : public PathPattern, public PathExpr
+{
+ std::vector<Attribute> outer_attrs;
+ bool has_opening_scope_resolution;
+ Location locus;
+ NodeId _node_id;
+
+public:
+ std::string as_string () const override;
+
+ // Constructor
+ PathInExpression (std::vector<PathExprSegment> path_segments,
+ std::vector<Attribute> outer_attrs, Location locus,
+ bool has_opening_scope_resolution = false)
+ : PathPattern (std::move (path_segments)),
+ outer_attrs (std::move (outer_attrs)),
+ has_opening_scope_resolution (has_opening_scope_resolution),
+ locus (locus), _node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Creates an error state path in expression.
+ static PathInExpression create_error ()
+ {
+ return PathInExpression ({}, {}, Location ());
+ }
+
+ // Returns whether path in expression is in an error state.
+ bool is_error () const { return !has_segments (); }
+
+ /* Converts PathInExpression to SimplePath if possible (i.e. no generic
+ * arguments). Otherwise returns an empty SimplePath. */
+ SimplePath as_simple_path () const
+ {
+ /* delegate to parent class as can't access segments. however,
+ * QualifiedPathInExpression conversion to simple path wouldn't make
+ * sense, so the method in the parent class should be protected, not
+ * public. Have to pass in opening scope resolution as parent class has no
+ * access to it.
+ */
+ return convert_to_simple_path (has_opening_scope_resolution);
+ }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if path is empty (error state), so base stripping on that.
+ void mark_for_strip () override { remove_all_segments (); }
+ bool is_marked_for_strip () const override { return is_error (); }
+
+ bool opening_scope_resolution () const
+ {
+ return has_opening_scope_resolution;
+ }
+
+ NodeId get_node_id () const override { return _node_id; }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ NodeId get_pattern_node_id () const override final { return get_node_id (); }
+
+protected:
+ /* Use covariance to implement clone function as returning this object
+ * rather than base */
+ PathInExpression *clone_pattern_impl () const final override
+ {
+ return clone_path_in_expression_impl ();
+ }
+
+ /* Use covariance to implement clone function as returning this object
+ * rather than base */
+ PathInExpression *clone_expr_without_block_impl () const final override
+ {
+ return clone_path_in_expression_impl ();
+ }
+
+ /*virtual*/ PathInExpression *clone_path_in_expression_impl () const
+ {
+ return new PathInExpression (*this);
+ }
+};
+
+/* Base class for segments used in type paths - not abstract (represents an
+ * ident-only segment) */
+class TypePathSegment
+{
+public:
+ enum SegmentType
+ {
+ REG,
+ GENERIC,
+ FUNCTION
+ };
+
+private:
+ PathIdentSegment ident_segment;
+ Location locus;
+
+protected:
+ /* This is protected because it is only really used by derived classes, not
+ * the base. */
+ bool has_separating_scope_resolution;
+ NodeId node_id;
+
+ // Clone function implementation - not pure virtual as overrided by
+ // subclasses
+ virtual TypePathSegment *clone_type_path_segment_impl () const
+ {
+ return new TypePathSegment (*this);
+ }
+
+public:
+ virtual ~TypePathSegment () {}
+
+ virtual SegmentType get_type () const { return SegmentType::REG; }
+
+ // Unique pointer custom clone function
+ std::unique_ptr<TypePathSegment> clone_type_path_segment () const
+ {
+ return std::unique_ptr<TypePathSegment> (clone_type_path_segment_impl ());
+ }
+
+ TypePathSegment (PathIdentSegment ident_segment,
+ bool has_separating_scope_resolution, Location locus)
+ : ident_segment (std::move (ident_segment)), locus (locus),
+ has_separating_scope_resolution (has_separating_scope_resolution),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ TypePathSegment (std::string segment_name,
+ bool has_separating_scope_resolution, Location locus)
+ : ident_segment (PathIdentSegment (std::move (segment_name), locus)),
+ locus (locus),
+ has_separating_scope_resolution (has_separating_scope_resolution),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ virtual std::string as_string () const { return ident_segment.as_string (); }
+
+ /* Returns whether the type path segment is in an error state. May be
+ * virtual in future. */
+ bool is_error () const { return ident_segment.is_error (); }
+
+ /* Returns whether segment is identifier only (as opposed to generic args or
+ * function). Overridden in derived classes with other segments. */
+ virtual bool is_ident_only () const { return true; }
+
+ Location get_locus () const { return locus; }
+
+ // not pure virtual as class not abstract
+ virtual void accept_vis (ASTVisitor &vis);
+
+ bool get_separating_scope_resolution () const
+ {
+ return has_separating_scope_resolution;
+ }
+
+ PathIdentSegment &get_ident_segment () { return ident_segment; };
+ const PathIdentSegment &get_ident_segment () const { return ident_segment; };
+
+ NodeId get_node_id () const { return node_id; }
+
+ bool is_crate_path_seg () const
+ {
+ return get_ident_segment ().is_crate_segment ();
+ }
+ bool is_super_path_seg () const
+ {
+ return get_ident_segment ().is_super_segment ();
+ }
+ bool is_big_self_seg () const { return get_ident_segment ().is_big_self (); }
+ bool is_lower_self_seg () const
+ {
+ return get_ident_segment ().is_lower_self ();
+ }
+};
+
+// Segment used in type path with generic args
+class TypePathSegmentGeneric : public TypePathSegment
+{
+ GenericArgs generic_args;
+
+public:
+ SegmentType get_type () const override { return SegmentType::GENERIC; }
+
+ bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+ bool is_ident_only () const override { return false; }
+
+ // Constructor with PathIdentSegment and GenericArgs
+ TypePathSegmentGeneric (PathIdentSegment ident_segment,
+ bool has_separating_scope_resolution,
+ GenericArgs generic_args, Location locus)
+ : TypePathSegment (std::move (ident_segment),
+ has_separating_scope_resolution, locus),
+ generic_args (std::move (generic_args))
+ {}
+
+ // Constructor from segment name and all args
+ TypePathSegmentGeneric (std::string segment_name,
+ bool has_separating_scope_resolution,
+ std::vector<Lifetime> lifetime_args,
+ std::vector<GenericArg> generic_args,
+ std::vector<GenericArgsBinding> binding_args,
+ Location locus)
+ : TypePathSegment (std::move (segment_name),
+ has_separating_scope_resolution, locus),
+ generic_args (GenericArgs (std::move (lifetime_args),
+ std::move (generic_args),
+ std::move (binding_args)))
+ {}
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ GenericArgs &get_generic_args ()
+ {
+ rust_assert (has_generic_args ());
+ return generic_args;
+ }
+
+protected:
+ // Use covariance to override base class method
+ TypePathSegmentGeneric *clone_type_path_segment_impl () const override
+ {
+ return new TypePathSegmentGeneric (*this);
+ }
+};
+
+// A function as represented in a type path
+struct TypePathFunction
+{
+private:
+ // TODO: remove
+ /*bool has_inputs;
+ TypePathFnInputs inputs;*/
+ // inlined from TypePathFnInputs
+ std::vector<std::unique_ptr<Type> > inputs;
+
+ // bool has_type;
+ std::unique_ptr<Type> return_type;
+
+ // FIXME: think of better way to mark as invalid than taking up storage
+ bool is_invalid;
+
+ Location locus;
+
+protected:
+ // Constructor only used to create invalid type path functions.
+ TypePathFunction (bool is_invalid, Location locus)
+ : is_invalid (is_invalid), locus (locus)
+ {}
+
+public:
+ // Returns whether the return type of the function has been specified.
+ bool has_return_type () const { return return_type != nullptr; }
+
+ // Returns whether the function has inputs.
+ bool has_inputs () const { return !inputs.empty (); }
+
+ // Returns whether function is in an error state.
+ bool is_error () const { return is_invalid; }
+
+ // Creates an error state function.
+ static TypePathFunction create_error ()
+ {
+ return TypePathFunction (true, Location ());
+ }
+
+ // Constructor
+ TypePathFunction (std::vector<std::unique_ptr<Type> > inputs, Location locus,
+ std::unique_ptr<Type> type = nullptr)
+ : inputs (std::move (inputs)), return_type (std::move (type)),
+ is_invalid (false), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ TypePathFunction (TypePathFunction const &other)
+ : is_invalid (other.is_invalid)
+ {
+ // guard to protect from null pointer dereference
+ if (other.return_type != nullptr)
+ return_type = other.return_type->clone_type ();
+
+ inputs.reserve (other.inputs.size ());
+ for (const auto &e : other.inputs)
+ inputs.push_back (e->clone_type ());
+ }
+
+ ~TypePathFunction () = default;
+
+ // Overloaded assignment operator to clone type
+ TypePathFunction &operator= (TypePathFunction const &other)
+ {
+ is_invalid = other.is_invalid;
+
+ // guard to protect from null pointer dereference
+ if (other.return_type != nullptr)
+ return_type = other.return_type->clone_type ();
+ else
+ return_type = nullptr;
+
+ inputs.reserve (other.inputs.size ());
+ for (const auto &e : other.inputs)
+ inputs.push_back (e->clone_type ());
+
+ return *this;
+ }
+
+ // move constructors
+ TypePathFunction (TypePathFunction &&other) = default;
+ TypePathFunction &operator= (TypePathFunction &&other) = default;
+
+ std::string as_string () const;
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ const std::vector<std::unique_ptr<Type> > &get_params () const
+ {
+ return inputs;
+ }
+ std::vector<std::unique_ptr<Type> > &get_params () { return inputs; }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::unique_ptr<Type> &get_return_type ()
+ {
+ rust_assert (has_return_type ());
+ return return_type;
+ }
+};
+
+// Segment used in type path with a function argument
+class TypePathSegmentFunction : public TypePathSegment
+{
+ TypePathFunction function_path;
+
+public:
+ SegmentType get_type () const override { return SegmentType::FUNCTION; }
+
+ // Constructor with PathIdentSegment and TypePathFn
+ TypePathSegmentFunction (PathIdentSegment ident_segment,
+ bool has_separating_scope_resolution,
+ TypePathFunction function_path, Location locus)
+ : TypePathSegment (std::move (ident_segment),
+ has_separating_scope_resolution, locus),
+ function_path (std::move (function_path))
+ {}
+
+ // Constructor with segment name and TypePathFn
+ TypePathSegmentFunction (std::string segment_name,
+ bool has_separating_scope_resolution,
+ TypePathFunction function_path, Location locus)
+ : TypePathSegment (std::move (segment_name),
+ has_separating_scope_resolution, locus),
+ function_path (std::move (function_path))
+ {}
+
+ std::string as_string () const override;
+
+ bool is_ident_only () const override { return false; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ TypePathFunction &get_type_path_function ()
+ {
+ rust_assert (!function_path.is_error ());
+ return function_path;
+ }
+
+protected:
+ // Use covariance to override base class method
+ TypePathSegmentFunction *clone_type_path_segment_impl () const override
+ {
+ return new TypePathSegmentFunction (*this);
+ }
+};
+
+// Path used inside types
+class TypePath : public TypeNoBounds
+{
+ bool has_opening_scope_resolution;
+ std::vector<std::unique_ptr<TypePathSegment> > segments;
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object
+ * rather than base */
+ TypePath *clone_type_no_bounds_impl () const override
+ {
+ return new TypePath (*this);
+ }
+
+public:
+ /* Returns whether the TypePath has an opening scope resolution operator
+ * (i.e. is global path or crate-relative path, not module-relative) */
+ bool has_opening_scope_resolution_op () const
+ {
+ return has_opening_scope_resolution;
+ }
+
+ // Returns whether the TypePath is in an invalid state.
+ bool is_error () const { return segments.empty (); }
+
+ // Creates an error state TypePath.
+ static TypePath create_error ()
+ {
+ return TypePath (std::vector<std::unique_ptr<TypePathSegment> > (),
+ Location ());
+ }
+
+ // Constructor
+ TypePath (std::vector<std::unique_ptr<TypePathSegment> > segments,
+ Location locus, bool has_opening_scope_resolution = false)
+ : TypeNoBounds (),
+ has_opening_scope_resolution (has_opening_scope_resolution),
+ segments (std::move (segments)), locus (locus)
+ {}
+
+ // Copy constructor with vector clone
+ TypePath (TypePath const &other)
+ : has_opening_scope_resolution (other.has_opening_scope_resolution),
+ locus (other.locus)
+ {
+ segments.reserve (other.segments.size ());
+ for (const auto &e : other.segments)
+ segments.push_back (e->clone_type_path_segment ());
+ }
+
+ // Overloaded assignment operator with clone
+ TypePath &operator= (TypePath const &other)
+ {
+ has_opening_scope_resolution = other.has_opening_scope_resolution;
+ locus = other.locus;
+
+ segments.reserve (other.segments.size ());
+ for (const auto &e : other.segments)
+ segments.push_back (e->clone_type_path_segment ());
+
+ return *this;
+ }
+
+ // move constructors
+ TypePath (TypePath &&other) = default;
+ TypePath &operator= (TypePath &&other) = default;
+
+ std::string as_string () const override;
+
+ /* Converts TypePath to SimplePath if possible (i.e. no generic or function
+ * arguments). Otherwise returns an empty SimplePath. */
+ SimplePath as_simple_path () const;
+
+ // Creates a trait bound with a clone of this type path as its only element.
+ TraitBound *to_trait_bound (bool in_parens) const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: this seems kinda dodgy
+ std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+ {
+ return segments;
+ }
+ const std::vector<std::unique_ptr<TypePathSegment> > &get_segments () const
+ {
+ return segments;
+ }
+
+ size_t get_num_segments () const { return segments.size (); }
+};
+
+struct QualifiedPathType
+{
+private:
+ std::unique_ptr<Type> type_to_invoke_on;
+ TypePath trait_path;
+ Location locus;
+ NodeId node_id;
+
+public:
+ // Constructor
+ QualifiedPathType (std::unique_ptr<Type> invoke_on_type,
+ Location locus = Location (),
+ TypePath trait_path = TypePath::create_error ())
+ : type_to_invoke_on (std::move (invoke_on_type)),
+ trait_path (std::move (trait_path)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor uses custom deep copy for Type to preserve polymorphism
+ QualifiedPathType (QualifiedPathType const &other)
+ : trait_path (other.trait_path), locus (other.locus)
+ {
+ node_id = other.node_id;
+ // guard to prevent null dereference
+ if (other.type_to_invoke_on != nullptr)
+ type_to_invoke_on = other.type_to_invoke_on->clone_type ();
+ }
+
+ // default destructor
+ ~QualifiedPathType () = default;
+
+ // overload assignment operator to use custom clone method
+ QualifiedPathType &operator= (QualifiedPathType const &other)
+ {
+ node_id = other.node_id;
+ trait_path = other.trait_path;
+ locus = other.locus;
+
+ // guard to prevent null dereference
+ if (other.type_to_invoke_on != nullptr)
+ type_to_invoke_on = other.type_to_invoke_on->clone_type ();
+ else
+ type_to_invoke_on = nullptr;
+
+ return *this;
+ }
+
+ // move constructor
+ QualifiedPathType (QualifiedPathType &&other) = default;
+ QualifiedPathType &operator= (QualifiedPathType &&other) = default;
+
+ // Returns whether the qualified path type has a rebind as clause.
+ bool has_as_clause () const { return !trait_path.is_error (); }
+
+ // Returns whether the qualified path type is in an error state.
+ bool is_error () const { return type_to_invoke_on == nullptr; }
+
+ // Creates an error state qualified path type.
+ static QualifiedPathType create_error ()
+ {
+ return QualifiedPathType (nullptr);
+ }
+
+ std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::unique_ptr<Type> &get_type ()
+ {
+ rust_assert (type_to_invoke_on != nullptr);
+ return type_to_invoke_on;
+ }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ TypePath &get_as_type_path ()
+ {
+ rust_assert (has_as_clause ());
+ return trait_path;
+ }
+
+ NodeId get_node_id () const { return node_id; }
+};
+
+/* AST node representing a qualified path-in-expression pattern (path that
+ * allows specifying trait functions) */
+class QualifiedPathInExpression : public PathPattern, public PathExpr
+{
+ std::vector<Attribute> outer_attrs;
+ QualifiedPathType path_type;
+ Location locus;
+ NodeId _node_id;
+
+public:
+ std::string as_string () const override;
+
+ QualifiedPathInExpression (QualifiedPathType qual_path_type,
+ std::vector<PathExprSegment> path_segments,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : PathPattern (std::move (path_segments)),
+ outer_attrs (std::move (outer_attrs)),
+ path_type (std::move (qual_path_type)), locus (locus),
+ _node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ /* TODO: maybe make a shortcut constructor that has QualifiedPathType
+ * elements as params */
+
+ // Returns whether qualified path in expression is in an error state.
+ bool is_error () const { return path_type.is_error (); }
+
+ // Creates an error qualified path in expression.
+ static QualifiedPathInExpression create_error ()
+ {
+ return QualifiedPathInExpression (QualifiedPathType::create_error (), {},
+ {}, Location ());
+ }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if path_type is error, so base stripping on that.
+ void mark_for_strip () override
+ {
+ path_type = QualifiedPathType::create_error ();
+ }
+ bool is_marked_for_strip () const override { return is_error (); }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ QualifiedPathType &get_qualified_path_type ()
+ {
+ rust_assert (!path_type.is_error ());
+ return path_type;
+ }
+
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+ void set_outer_attrs (std::vector<Attribute> new_attrs) override
+ {
+ outer_attrs = std::move (new_attrs);
+ }
+
+ NodeId get_node_id () const override { return _node_id; }
+
+ NodeId get_pattern_node_id () const override final { return get_node_id (); }
+
+protected:
+ /* Use covariance to implement clone function as returning this object
+ * rather than base */
+ QualifiedPathInExpression *clone_pattern_impl () const final override
+ {
+ return clone_qual_path_in_expression_impl ();
+ }
+
+ /* Use covariance to implement clone function as returning this object
+ * rather than base */
+ QualifiedPathInExpression *
+ clone_expr_without_block_impl () const final override
+ {
+ return clone_qual_path_in_expression_impl ();
+ }
+
+ /*virtual*/ QualifiedPathInExpression *
+ clone_qual_path_in_expression_impl () const
+ {
+ return new QualifiedPathInExpression (*this);
+ }
+};
+
+/* Represents a qualified path in a type; used for disambiguating trait
+ * function calls */
+class QualifiedPathInType : public TypeNoBounds
+{
+ QualifiedPathType path_type;
+ std::unique_ptr<TypePathSegment> associated_segment;
+ std::vector<std::unique_ptr<TypePathSegment> > segments;
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object
+ * rather than base */
+ QualifiedPathInType *clone_type_no_bounds_impl () const override
+ {
+ return new QualifiedPathInType (*this);
+ }
+
+public:
+ QualifiedPathInType (
+ QualifiedPathType qual_path_type,
+ std::unique_ptr<TypePathSegment> associated_segment,
+ std::vector<std::unique_ptr<TypePathSegment> > path_segments,
+ Location locus)
+ : path_type (std::move (qual_path_type)),
+ associated_segment (std::move (associated_segment)),
+ segments (std::move (path_segments)), locus (locus)
+ {}
+
+ /* TODO: maybe make a shortcut constructor that has QualifiedPathType
+ * elements as params */
+
+ // Copy constructor with vector clone
+ QualifiedPathInType (QualifiedPathInType const &other)
+ : path_type (other.path_type), locus (other.locus)
+ {
+ segments.reserve (other.segments.size ());
+ for (const auto &e : other.segments)
+ segments.push_back (e->clone_type_path_segment ());
+ }
+
+ // Overloaded assignment operator with vector clone
+ QualifiedPathInType &operator= (QualifiedPathInType const &other)
+ {
+ path_type = other.path_type;
+ locus = other.locus;
+
+ segments.reserve (other.segments.size ());
+ for (const auto &e : other.segments)
+ segments.push_back (e->clone_type_path_segment ());
+
+ return *this;
+ }
+
+ // move constructors
+ QualifiedPathInType (QualifiedPathInType &&other) = default;
+ QualifiedPathInType &operator= (QualifiedPathInType &&other) = default;
+
+ // Returns whether qualified path in type is in an error state.
+ bool is_error () const { return path_type.is_error (); }
+
+ // Creates an error state qualified path in type.
+ static QualifiedPathInType create_error ()
+ {
+ return QualifiedPathInType (
+ QualifiedPathType::create_error (), nullptr,
+ std::vector<std::unique_ptr<TypePathSegment> > (), Location ());
+ }
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ QualifiedPathType &get_qualified_path_type ()
+ {
+ rust_assert (!path_type.is_error ());
+ return path_type;
+ }
+
+ std::unique_ptr<TypePathSegment> &get_associated_segment ()
+ {
+ return associated_segment;
+ }
+
+ // TODO: this seems kinda dodgy
+ std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+ {
+ return segments;
+ }
+ const std::vector<std::unique_ptr<TypePathSegment> > &get_segments () const
+ {
+ return segments;
+ }
+
+ Location get_locus () const override final { return locus; }
+};
+} // namespace AST
+} // namespace Rust
+
+#endif
--- /dev/null
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AST_PATTERN_H
+#define RUST_AST_PATTERN_H
+
+#include "rust-ast.h"
+
+namespace Rust {
+namespace AST {
+// Literal pattern AST node (comparing to a literal)
+class LiteralPattern : public Pattern
+{
+ Literal lit;
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override;
+
+ // Constructor for a literal pattern
+ LiteralPattern (Literal lit, Location locus)
+ : lit (std::move (lit)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ LiteralPattern (std::string val, Literal::LitType type, Location locus)
+ : lit (Literal (std::move (val), type, PrimitiveCoreType::CORETYPE_STR)),
+ locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+ Literal &get_literal () { return lit; }
+
+ const Literal &get_literal () const { return lit; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ virtual LiteralPattern *clone_pattern_impl () const override
+ {
+ return new LiteralPattern (*this);
+ }
+};
+
+// Identifier pattern AST node (bind value matched to a variable)
+class IdentifierPattern : public Pattern
+{
+ Identifier variable_ident;
+ bool is_ref;
+ bool is_mut;
+
+ // bool has_pattern;
+ std::unique_ptr<Pattern> to_bind;
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override;
+
+ // Returns whether the IdentifierPattern has a pattern to bind.
+ bool has_pattern_to_bind () const { return to_bind != nullptr; }
+
+ // Constructor
+ IdentifierPattern (Identifier ident, Location locus, bool is_ref = false,
+ bool is_mut = false,
+ std::unique_ptr<Pattern> to_bind = nullptr)
+ : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref),
+ is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ IdentifierPattern (NodeId node_id, Identifier ident, Location locus,
+ bool is_ref = false, bool is_mut = false,
+ std::unique_ptr<Pattern> to_bind = nullptr)
+ : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref),
+ is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus),
+ node_id (node_id)
+ {}
+
+ // Copy constructor with clone
+ IdentifierPattern (IdentifierPattern const &other)
+ : variable_ident (other.variable_ident), is_ref (other.is_ref),
+ is_mut (other.is_mut), locus (other.locus), node_id (other.node_id)
+ {
+ // fix to get prevent null pointer dereference
+ if (other.to_bind != nullptr)
+ to_bind = other.to_bind->clone_pattern ();
+ }
+
+ // Overload assignment operator to use clone
+ IdentifierPattern &operator= (IdentifierPattern const &other)
+ {
+ variable_ident = other.variable_ident;
+ is_ref = other.is_ref;
+ is_mut = other.is_mut;
+ locus = other.locus;
+ node_id = other.node_id;
+
+ // fix to prevent null pointer dereference
+ if (other.to_bind != nullptr)
+ to_bind = other.to_bind->clone_pattern ();
+ else
+ to_bind = nullptr;
+
+ return *this;
+ }
+
+ // default move semantics
+ IdentifierPattern (IdentifierPattern &&other) = default;
+ IdentifierPattern &operator= (IdentifierPattern &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::unique_ptr<Pattern> &get_pattern_to_bind ()
+ {
+ rust_assert (has_pattern_to_bind ());
+ return to_bind;
+ }
+
+ Identifier get_ident () const { return variable_ident; }
+
+ bool get_is_mut () const { return is_mut; }
+ bool get_is_ref () const { return is_ref; }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ IdentifierPattern *clone_pattern_impl () const override
+ {
+ return new IdentifierPattern (*this);
+ }
+};
+
+// AST node for using the '_' wildcard "match any value" pattern
+class WildcardPattern : public Pattern
+{
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override { return std::string (1, '_'); }
+
+ WildcardPattern (Location locus)
+ : locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ WildcardPattern *clone_pattern_impl () const override
+ {
+ return new WildcardPattern (*this);
+ }
+};
+
+// Base range pattern bound (lower or upper limit) - abstract
+class RangePatternBound
+{
+public:
+ enum RangePatternBoundType
+ {
+ LITERAL,
+ PATH,
+ QUALPATH
+ };
+
+ virtual ~RangePatternBound () {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<RangePatternBound> clone_range_pattern_bound () const
+ {
+ return std::unique_ptr<RangePatternBound> (
+ clone_range_pattern_bound_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual RangePatternBoundType get_bound_type () const = 0;
+
+protected:
+ // pure virtual as RangePatternBound is abstract
+ virtual RangePatternBound *clone_range_pattern_bound_impl () const = 0;
+};
+
+// Literal-based pattern bound
+class RangePatternBoundLiteral : public RangePatternBound
+{
+ Literal literal;
+ /* Can only be a char, byte, int, or float literal - same impl here as
+ * previously */
+
+ // Minus prefixed to literal (if integer or floating-point)
+ bool has_minus;
+
+ Location locus;
+
+public:
+ // Constructor
+ RangePatternBoundLiteral (Literal literal, Location locus,
+ bool has_minus = false)
+ : literal (literal), has_minus (has_minus), locus (locus)
+ {}
+
+ std::string as_string () const override;
+
+ Literal get_literal () const { return literal; }
+
+ bool get_has_minus () const { return has_minus; }
+
+ Location get_locus () const { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ RangePatternBoundType get_bound_type () const override
+ {
+ return RangePatternBoundType::LITERAL;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangePatternBoundLiteral *clone_range_pattern_bound_impl () const override
+ {
+ return new RangePatternBoundLiteral (*this);
+ }
+};
+
+// Path-based pattern bound
+class RangePatternBoundPath : public RangePatternBound
+{
+ PathInExpression path;
+
+ /* TODO: should this be refactored so that PathInExpression is a subclass of
+ * RangePatternBound? */
+
+public:
+ RangePatternBoundPath (PathInExpression path) : path (std::move (path)) {}
+
+ std::string as_string () const override { return path.as_string (); }
+
+ Location get_locus () const { return path.get_locus (); }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: this mutable getter seems kinda dodgy
+ PathInExpression &get_path () { return path; }
+ const PathInExpression &get_path () const { return path; }
+
+ RangePatternBoundType get_bound_type () const override
+ {
+ return RangePatternBoundType::PATH;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangePatternBoundPath *clone_range_pattern_bound_impl () const override
+ {
+ return new RangePatternBoundPath (*this);
+ }
+};
+
+// Qualified path-based pattern bound
+class RangePatternBoundQualPath : public RangePatternBound
+{
+ QualifiedPathInExpression path;
+
+ /* TODO: should this be refactored so that QualifiedPathInExpression is a
+ * subclass of RangePatternBound? */
+
+public:
+ RangePatternBoundQualPath (QualifiedPathInExpression path)
+ : path (std::move (path))
+ {}
+
+ std::string as_string () const override { return path.as_string (); }
+
+ Location get_locus () const { return path.get_locus (); }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: this mutable getter seems kinda dodgy
+ QualifiedPathInExpression &get_qualified_path () { return path; }
+ const QualifiedPathInExpression &get_qualified_path () const { return path; }
+
+ RangePatternBoundType get_bound_type () const override
+ {
+ return RangePatternBoundType::QUALPATH;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangePatternBoundQualPath *clone_range_pattern_bound_impl () const override
+ {
+ return new RangePatternBoundQualPath (*this);
+ }
+};
+
+// AST node for matching within a certain range (range pattern)
+class RangePattern : public Pattern
+{
+ std::unique_ptr<RangePatternBound> lower;
+ std::unique_ptr<RangePatternBound> upper;
+
+ bool has_ellipsis_syntax;
+
+ /* location only stored to avoid a dereference - lower pattern should give
+ * correct location so maybe change in future */
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override;
+
+ // Constructor
+ RangePattern (std::unique_ptr<RangePatternBound> lower,
+ std::unique_ptr<RangePatternBound> upper, Location locus,
+ bool has_ellipsis_syntax = false)
+ : lower (std::move (lower)), upper (std::move (upper)),
+ has_ellipsis_syntax (has_ellipsis_syntax), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor with clone
+ RangePattern (RangePattern const &other)
+ : lower (other.lower->clone_range_pattern_bound ()),
+ upper (other.upper->clone_range_pattern_bound ()),
+ has_ellipsis_syntax (other.has_ellipsis_syntax), locus (other.locus),
+ node_id (other.node_id)
+ {}
+
+ // Overloaded assignment operator to clone
+ RangePattern &operator= (RangePattern const &other)
+ {
+ lower = other.lower->clone_range_pattern_bound ();
+ upper = other.upper->clone_range_pattern_bound ();
+ has_ellipsis_syntax = other.has_ellipsis_syntax;
+ locus = other.locus;
+ node_id = other.node_id;
+
+ return *this;
+ }
+
+ // default move semantics
+ RangePattern (RangePattern &&other) = default;
+ RangePattern &operator= (RangePattern &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? or is a "vis_bound" better?
+ std::unique_ptr<RangePatternBound> &get_lower_bound ()
+ {
+ rust_assert (lower != nullptr);
+ return lower;
+ }
+
+ std::unique_ptr<RangePatternBound> &get_upper_bound ()
+ {
+ rust_assert (upper != nullptr);
+ return upper;
+ }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RangePattern *clone_pattern_impl () const override
+ {
+ return new RangePattern (*this);
+ }
+};
+
+// AST node for pattern based on dereferencing the pointers given
+class ReferencePattern : public Pattern
+{
+ bool has_two_amps;
+ bool is_mut;
+ std::unique_ptr<Pattern> pattern;
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override;
+
+ ReferencePattern (std::unique_ptr<Pattern> pattern, bool is_mut_reference,
+ bool ref_has_two_amps, Location locus)
+ : has_two_amps (ref_has_two_amps), is_mut (is_mut_reference),
+ pattern (std::move (pattern)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor requires clone
+ ReferencePattern (ReferencePattern const &other)
+ : has_two_amps (other.has_two_amps), is_mut (other.is_mut),
+ pattern (other.pattern->clone_pattern ()), locus (other.locus),
+ node_id (other.node_id)
+ {}
+
+ // Overload assignment operator to clone
+ ReferencePattern &operator= (ReferencePattern const &other)
+ {
+ pattern = other.pattern->clone_pattern ();
+ is_mut = other.is_mut;
+ has_two_amps = other.has_two_amps;
+ locus = other.locus;
+ node_id = other.node_id;
+
+ return *this;
+ }
+
+ // default move semantics
+ ReferencePattern (ReferencePattern &&other) = default;
+ ReferencePattern &operator= (ReferencePattern &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::unique_ptr<Pattern> &get_referenced_pattern ()
+ {
+ rust_assert (pattern != nullptr);
+ return pattern;
+ }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ReferencePattern *clone_pattern_impl () const override
+ {
+ return new ReferencePattern (*this);
+ }
+};
+
+#if 0
+// aka StructPatternEtCetera; potential element in struct pattern
+struct StructPatternEtc
+{
+private:
+ std::vector<Attribute> outer_attrs;
+
+ // should this store location data?
+
+public:
+ StructPatternEtc (std::vector<Attribute> outer_attribs)
+ : outer_attrs (std::move (outer_attribs))
+ {}
+
+ // Creates an empty StructPatternEtc
+ static StructPatternEtc create_empty ()
+ {
+ return StructPatternEtc (std::vector<Attribute> ());
+ }
+};
+#endif
+
+// Base class for a single field in a struct pattern - abstract
+class StructPatternField
+{
+ std::vector<Attribute> outer_attrs;
+ Location locus;
+
+protected:
+ NodeId node_id;
+
+public:
+ enum ItemType
+ {
+ TUPLE_PAT,
+ IDENT_PAT,
+ IDENT
+ };
+
+ virtual ~StructPatternField () {}
+
+ // Unique pointer custom clone function
+ std::unique_ptr<StructPatternField> clone_struct_pattern_field () const
+ {
+ return std::unique_ptr<StructPatternField> (
+ clone_struct_pattern_field_impl ());
+ }
+
+ virtual std::string as_string () const;
+
+ Location get_locus () const { return locus; }
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual void mark_for_strip () = 0;
+ virtual bool is_marked_for_strip () const = 0;
+ virtual ItemType get_item_type () const = 0;
+
+ NodeId get_node_id () const { return node_id; }
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+protected:
+ StructPatternField (std::vector<Attribute> outer_attribs, Location locus,
+ NodeId node_id)
+ : outer_attrs (std::move (outer_attribs)), locus (locus), node_id (node_id)
+ {}
+
+ // Clone function implementation as pure virtual method
+ virtual StructPatternField *clone_struct_pattern_field_impl () const = 0;
+};
+
+// Tuple pattern single field in a struct pattern
+class StructPatternFieldTuplePat : public StructPatternField
+{
+ TupleIndex index;
+ std::unique_ptr<Pattern> tuple_pattern;
+
+public:
+ StructPatternFieldTuplePat (TupleIndex index,
+ std::unique_ptr<Pattern> tuple_pattern,
+ std::vector<Attribute> outer_attribs,
+ Location locus)
+ : StructPatternField (std::move (outer_attribs), locus,
+ Analysis::Mappings::get ()->get_next_node_id ()),
+ index (index), tuple_pattern (std::move (tuple_pattern))
+ {}
+
+ // Copy constructor requires clone
+ StructPatternFieldTuplePat (StructPatternFieldTuplePat const &other)
+ : StructPatternField (other), index (other.index)
+ {
+ // guard to prevent null dereference (only required if error state)
+ node_id = other.get_node_id ();
+ if (other.tuple_pattern != nullptr)
+ tuple_pattern = other.tuple_pattern->clone_pattern ();
+ }
+
+ // Overload assignment operator to perform clone
+ StructPatternFieldTuplePat &
+ operator= (StructPatternFieldTuplePat const &other)
+ {
+ StructPatternField::operator= (other);
+ index = other.index;
+ // outer_attrs = other.outer_attrs;
+ node_id = other.get_node_id ();
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.tuple_pattern != nullptr)
+ tuple_pattern = other.tuple_pattern->clone_pattern ();
+ else
+ tuple_pattern = nullptr;
+
+ return *this;
+ }
+
+ // default move semantics
+ StructPatternFieldTuplePat (StructPatternFieldTuplePat &&other) = default;
+ StructPatternFieldTuplePat &operator= (StructPatternFieldTuplePat &&other)
+ = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // based on idea of tuple pattern no longer existing
+ void mark_for_strip () override { tuple_pattern = nullptr; }
+ bool is_marked_for_strip () const override
+ {
+ return tuple_pattern == nullptr;
+ }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::unique_ptr<Pattern> &get_index_pattern ()
+ {
+ rust_assert (tuple_pattern != nullptr);
+ return tuple_pattern;
+ }
+
+ ItemType get_item_type () const override final { return ItemType::TUPLE_PAT; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructPatternFieldTuplePat *clone_struct_pattern_field_impl () const override
+ {
+ return new StructPatternFieldTuplePat (*this);
+ }
+};
+
+// Identifier pattern single field in a struct pattern
+class StructPatternFieldIdentPat : public StructPatternField
+{
+ Identifier ident;
+ std::unique_ptr<Pattern> ident_pattern;
+
+public:
+ StructPatternFieldIdentPat (Identifier ident,
+ std::unique_ptr<Pattern> ident_pattern,
+ std::vector<Attribute> outer_attrs,
+ Location locus)
+ : StructPatternField (std::move (outer_attrs), locus,
+ Analysis::Mappings::get ()->get_next_node_id ()),
+ ident (std::move (ident)), ident_pattern (std::move (ident_pattern))
+ {}
+
+ // Copy constructor requires clone
+ StructPatternFieldIdentPat (StructPatternFieldIdentPat const &other)
+ : StructPatternField (other), ident (other.ident)
+ {
+ // guard to prevent null dereference (only required if error state)
+ node_id = other.get_node_id ();
+ if (other.ident_pattern != nullptr)
+ ident_pattern = other.ident_pattern->clone_pattern ();
+ }
+
+ // Overload assignment operator to clone
+ StructPatternFieldIdentPat &
+ operator= (StructPatternFieldIdentPat const &other)
+ {
+ StructPatternField::operator= (other);
+ ident = other.ident;
+ // outer_attrs = other.outer_attrs;
+ node_id = other.get_node_id ();
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.ident_pattern != nullptr)
+ ident_pattern = other.ident_pattern->clone_pattern ();
+ else
+ ident_pattern = nullptr;
+
+ return *this;
+ }
+
+ // default move semantics
+ StructPatternFieldIdentPat (StructPatternFieldIdentPat &&other) = default;
+ StructPatternFieldIdentPat &operator= (StructPatternFieldIdentPat &&other)
+ = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // based on idea of identifier pattern no longer existing
+ void mark_for_strip () override { ident_pattern = nullptr; }
+ bool is_marked_for_strip () const override
+ {
+ return ident_pattern == nullptr;
+ }
+
+ const Identifier &get_identifier () const { return ident; }
+
+ // TODO: is this better? Or is a "vis_pattern" better?
+ std::unique_ptr<Pattern> &get_ident_pattern ()
+ {
+ rust_assert (ident_pattern != nullptr);
+ return ident_pattern;
+ }
+
+ ItemType get_item_type () const override final { return ItemType::IDENT_PAT; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructPatternFieldIdentPat *clone_struct_pattern_field_impl () const override
+ {
+ return new StructPatternFieldIdentPat (*this);
+ }
+};
+
+// Identifier only (with no pattern) single field in a struct pattern
+class StructPatternFieldIdent : public StructPatternField
+{
+ bool has_ref;
+ bool has_mut;
+ Identifier ident;
+
+public:
+ StructPatternFieldIdent (Identifier ident, bool is_ref, bool is_mut,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : StructPatternField (std::move (outer_attrs), locus,
+ Analysis::Mappings::get ()->get_next_node_id ()),
+ has_ref (is_ref), has_mut (is_mut), ident (std::move (ident))
+ {}
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // based on idea of identifier no longer existing
+ void mark_for_strip () override { ident = {}; }
+ bool is_marked_for_strip () const override { return ident.empty (); }
+
+ const Identifier &get_identifier () const { return ident; }
+
+ ItemType get_item_type () const override final { return ItemType::IDENT; }
+
+ bool is_ref () const { return has_ref; }
+
+ bool is_mut () const { return has_mut; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructPatternFieldIdent *clone_struct_pattern_field_impl () const override
+ {
+ return new StructPatternFieldIdent (*this);
+ }
+};
+
+// Elements of a struct pattern
+struct StructPatternElements
+{
+private:
+ // bool has_struct_pattern_fields;
+ std::vector<std::unique_ptr<StructPatternField> > fields;
+
+ bool has_struct_pattern_etc;
+ std::vector<Attribute> struct_pattern_etc_attrs;
+ // StructPatternEtc etc;
+
+ // must have at least one of the two and maybe both
+
+ // should this store location data?
+
+public:
+ // Returns whether there are any struct pattern fields
+ bool has_struct_pattern_fields () const { return !fields.empty (); }
+
+ /* Returns whether the struct pattern elements is entirely empty (no fields,
+ * no etc). */
+ bool is_empty () const
+ {
+ return !has_struct_pattern_fields () && !has_struct_pattern_etc;
+ }
+
+ bool has_etc () const { return has_struct_pattern_etc; }
+
+ // Constructor for StructPatternElements with both (potentially)
+ StructPatternElements (
+ std::vector<std::unique_ptr<StructPatternField> > fields,
+ std::vector<Attribute> etc_attrs)
+ : fields (std::move (fields)), has_struct_pattern_etc (true),
+ struct_pattern_etc_attrs (std::move (etc_attrs))
+ {}
+
+ // Constructor for StructPatternElements with no StructPatternEtc
+ StructPatternElements (
+ std::vector<std::unique_ptr<StructPatternField> > fields)
+ : fields (std::move (fields)), has_struct_pattern_etc (false),
+ struct_pattern_etc_attrs ()
+ {}
+
+ // Copy constructor with vector clone
+ StructPatternElements (StructPatternElements const &other)
+ : has_struct_pattern_etc (other.has_struct_pattern_etc),
+ struct_pattern_etc_attrs (other.struct_pattern_etc_attrs)
+ {
+ fields.reserve (other.fields.size ());
+ for (const auto &e : other.fields)
+ fields.push_back (e->clone_struct_pattern_field ());
+ }
+
+ // Overloaded assignment operator with vector clone
+ StructPatternElements &operator= (StructPatternElements const &other)
+ {
+ struct_pattern_etc_attrs = other.struct_pattern_etc_attrs;
+ has_struct_pattern_etc = other.has_struct_pattern_etc;
+
+ fields.reserve (other.fields.size ());
+ for (const auto &e : other.fields)
+ fields.push_back (e->clone_struct_pattern_field ());
+
+ return *this;
+ }
+
+ // move constructors
+ StructPatternElements (StructPatternElements &&other) = default;
+ StructPatternElements &operator= (StructPatternElements &&other) = default;
+
+ // Creates an empty StructPatternElements
+ static StructPatternElements create_empty ()
+ {
+ return StructPatternElements (
+ std::vector<std::unique_ptr<StructPatternField> > ());
+ }
+
+ std::string as_string () const;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<StructPatternField> > &
+ get_struct_pattern_fields ()
+ {
+ return fields;
+ }
+ const std::vector<std::unique_ptr<StructPatternField> > &
+ get_struct_pattern_fields () const
+ {
+ return fields;
+ }
+
+ std::vector<Attribute> &get_etc_outer_attrs ()
+ {
+ return struct_pattern_etc_attrs;
+ }
+ const std::vector<Attribute> &get_etc_outer_attrs () const
+ {
+ return struct_pattern_etc_attrs;
+ }
+
+ void strip_etc ()
+ {
+ has_struct_pattern_etc = false;
+ struct_pattern_etc_attrs.clear ();
+ struct_pattern_etc_attrs.shrink_to_fit ();
+ }
+};
+
+// Struct pattern AST node representation
+class StructPattern : public Pattern
+{
+ PathInExpression path;
+
+ // bool has_struct_pattern_elements;
+ StructPatternElements elems;
+
+ NodeId node_id;
+ Location locus;
+
+public:
+ std::string as_string () const override;
+
+ // Constructs a struct pattern from specified StructPatternElements
+ StructPattern (PathInExpression struct_path, Location locus,
+ StructPatternElements elems
+ = StructPatternElements::create_empty ())
+ : path (std::move (struct_path)), elems (std::move (elems)),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+ {}
+
+ /* TODO: constructor to construct via elements included in
+ * StructPatternElements */
+
+ /* Returns whether struct pattern has any struct pattern elements (if not, it
+ * is empty). */
+ bool has_struct_pattern_elems () const { return !elems.is_empty (); }
+
+ Location get_locus () const { return path.get_locus (); }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ StructPatternElements &get_struct_pattern_elems () { return elems; }
+ const StructPatternElements &get_struct_pattern_elems () const
+ {
+ return elems;
+ }
+
+ PathInExpression &get_path () { return path; }
+ const PathInExpression &get_path () const { return path; }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ StructPattern *clone_pattern_impl () const override
+ {
+ return new StructPattern (*this);
+ }
+};
+
+// Base abstract class for patterns used in TupleStructPattern
+class TupleStructItems
+{
+public:
+ enum ItemType
+ {
+ RANGE,
+ NO_RANGE
+ };
+
+ virtual ~TupleStructItems () {}
+
+ // TODO: should this store location data?
+
+ // Unique pointer custom clone function
+ std::unique_ptr<TupleStructItems> clone_tuple_struct_items () const
+ {
+ return std::unique_ptr<TupleStructItems> (clone_tuple_struct_items_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual ItemType get_item_type () const = 0;
+
+protected:
+ // pure virtual clone implementation
+ virtual TupleStructItems *clone_tuple_struct_items_impl () const = 0;
+};
+
+// Class for non-ranged tuple struct pattern patterns
+class TupleStructItemsNoRange : public TupleStructItems
+{
+ std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+ TupleStructItemsNoRange (std::vector<std::unique_ptr<Pattern> > patterns)
+ : patterns (std::move (patterns))
+ {}
+
+ // Copy constructor with vector clone
+ TupleStructItemsNoRange (TupleStructItemsNoRange const &other)
+ {
+ patterns.reserve (other.patterns.size ());
+ for (const auto &e : other.patterns)
+ patterns.push_back (e->clone_pattern ());
+ }
+
+ // Overloaded assignment operator with vector clone
+ TupleStructItemsNoRange &operator= (TupleStructItemsNoRange const &other)
+ {
+ patterns.reserve (other.patterns.size ());
+ for (const auto &e : other.patterns)
+ patterns.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ TupleStructItemsNoRange (TupleStructItemsNoRange &&other) = default;
+ TupleStructItemsNoRange &operator= (TupleStructItemsNoRange &&other)
+ = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+ const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+ {
+ return patterns;
+ }
+
+ ItemType get_item_type () const override final { return ItemType::NO_RANGE; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TupleStructItemsNoRange *clone_tuple_struct_items_impl () const override
+ {
+ return new TupleStructItemsNoRange (*this);
+ }
+};
+
+// Class for ranged tuple struct pattern patterns
+class TupleStructItemsRange : public TupleStructItems
+{
+ std::vector<std::unique_ptr<Pattern> > lower_patterns;
+ std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+ TupleStructItemsRange (std::vector<std::unique_ptr<Pattern> > lower_patterns,
+ std::vector<std::unique_ptr<Pattern> > upper_patterns)
+ : lower_patterns (std::move (lower_patterns)),
+ upper_patterns (std::move (upper_patterns))
+ {}
+
+ // Copy constructor with vector clone
+ TupleStructItemsRange (TupleStructItemsRange const &other)
+ {
+ lower_patterns.reserve (other.lower_patterns.size ());
+ for (const auto &e : other.lower_patterns)
+ lower_patterns.push_back (e->clone_pattern ());
+
+ upper_patterns.reserve (other.upper_patterns.size ());
+ for (const auto &e : other.upper_patterns)
+ upper_patterns.push_back (e->clone_pattern ());
+ }
+
+ // Overloaded assignment operator to clone
+ TupleStructItemsRange &operator= (TupleStructItemsRange const &other)
+ {
+ lower_patterns.reserve (other.lower_patterns.size ());
+ for (const auto &e : other.lower_patterns)
+ lower_patterns.push_back (e->clone_pattern ());
+
+ upper_patterns.reserve (other.upper_patterns.size ());
+ for (const auto &e : other.upper_patterns)
+ upper_patterns.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ TupleStructItemsRange (TupleStructItemsRange &&other) = default;
+ TupleStructItemsRange &operator= (TupleStructItemsRange &&other) = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<Pattern> > &get_lower_patterns ()
+ {
+ return lower_patterns;
+ }
+ const std::vector<std::unique_ptr<Pattern> > &get_lower_patterns () const
+ {
+ return lower_patterns;
+ }
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<Pattern> > &get_upper_patterns ()
+ {
+ return upper_patterns;
+ }
+ const std::vector<std::unique_ptr<Pattern> > &get_upper_patterns () const
+ {
+ return upper_patterns;
+ }
+
+ ItemType get_item_type () const override final { return ItemType::RANGE; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TupleStructItemsRange *clone_tuple_struct_items_impl () const override
+ {
+ return new TupleStructItemsRange (*this);
+ }
+};
+
+// AST node representing a tuple struct pattern
+class TupleStructPattern : public Pattern
+{
+ PathInExpression path;
+ std::unique_ptr<TupleStructItems> items;
+ NodeId node_id;
+
+ /* TOOD: should this store location data? current accessor uses path location
+ * data */
+
+public:
+ std::string as_string () const override;
+
+ // Returns whether the pattern has tuple struct items.
+ bool has_items () const { return items != nullptr; }
+
+ TupleStructPattern (PathInExpression tuple_struct_path,
+ std::unique_ptr<TupleStructItems> items)
+ : path (std::move (tuple_struct_path)), items (std::move (items)),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor required to clone
+ TupleStructPattern (TupleStructPattern const &other) : path (other.path)
+ {
+ // guard to protect from null dereference
+ node_id = other.node_id;
+ if (other.items != nullptr)
+ items = other.items->clone_tuple_struct_items ();
+ }
+
+ // Operator overload assignment operator to clone
+ TupleStructPattern &operator= (TupleStructPattern const &other)
+ {
+ path = other.path;
+ node_id = other.node_id;
+
+ // guard to protect from null dereference
+ if (other.items != nullptr)
+ items = other.items->clone_tuple_struct_items ();
+ else
+ items = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ TupleStructPattern (TupleStructPattern &&other) = default;
+ TupleStructPattern &operator= (TupleStructPattern &&other) = default;
+
+ Location get_locus () const override { return path.get_locus (); }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::unique_ptr<TupleStructItems> &get_items ()
+ {
+ rust_assert (has_items ());
+ return items;
+ }
+
+ PathInExpression &get_path () { return path; }
+ const PathInExpression &get_path () const { return path; }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TupleStructPattern *clone_pattern_impl () const override
+ {
+ return new TupleStructPattern (*this);
+ }
+};
+
+// Base abstract class representing TuplePattern patterns
+class TuplePatternItems
+{
+public:
+ enum TuplePatternItemType
+ {
+ MULTIPLE,
+ RANGED,
+ };
+
+ virtual ~TuplePatternItems () {}
+
+ // TODO: should this store location data?
+
+ // Unique pointer custom clone function
+ std::unique_ptr<TuplePatternItems> clone_tuple_pattern_items () const
+ {
+ return std::unique_ptr<TuplePatternItems> (
+ clone_tuple_pattern_items_impl ());
+ }
+
+ virtual std::string as_string () const = 0;
+
+ virtual void accept_vis (ASTVisitor &vis) = 0;
+
+ virtual TuplePatternItemType get_pattern_type () const = 0;
+
+protected:
+ // pure virtual clone implementation
+ virtual TuplePatternItems *clone_tuple_pattern_items_impl () const = 0;
+};
+
+// Class representing TuplePattern patterns where there is only a single pattern
+/*class TuplePatternItemsSingle : public TuplePatternItems {
+ // Pattern pattern;
+ std::unique_ptr<Pattern> pattern;
+
+ public:
+ TuplePatternItemsSingle(Pattern* pattern) : pattern(pattern) {}
+
+ // Copy constructor uses clone
+ TuplePatternItemsSingle(TuplePatternItemsSingle const& other) :
+ pattern(other.pattern->clone_pattern()) {}
+
+ // Destructor - define here if required
+
+ // Overload assignment operator to clone
+ TuplePatternItemsSingle& operator=(TuplePatternItemsSingle const& other) {
+ pattern = other.pattern->clone_pattern();
+
+ return *this;
+ }
+
+ // move constructors
+ TuplePatternItemsSingle(TuplePatternItemsSingle&& other) = default;
+ TuplePatternItemsSingle& operator=(TuplePatternItemsSingle&& other) =
+default;
+
+ protected:
+ // Use covariance to implement clone function as returning this object
+rather than base virtual TuplePatternItemsSingle*
+clone_tuple_pattern_items_impl() const override { return new
+TuplePatternItemsSingle(*this);
+ }
+};*/
+// removed in favour of single-element TuplePatternItemsMultiple
+
+// Class representing TuplePattern patterns where there are multiple patterns
+class TuplePatternItemsMultiple : public TuplePatternItems
+{
+ std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+ TuplePatternItemsMultiple (std::vector<std::unique_ptr<Pattern> > patterns)
+ : patterns (std::move (patterns))
+ {}
+
+ // Copy constructor with vector clone
+ TuplePatternItemsMultiple (TuplePatternItemsMultiple const &other)
+ {
+ patterns.reserve (other.patterns.size ());
+ for (const auto &e : other.patterns)
+ patterns.push_back (e->clone_pattern ());
+ }
+
+ // Overloaded assignment operator to vector clone
+ TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple const &other)
+ {
+ patterns.reserve (other.patterns.size ());
+ for (const auto &e : other.patterns)
+ patterns.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ TuplePatternItemsMultiple (TuplePatternItemsMultiple &&other) = default;
+ TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple &&other)
+ = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+ const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+ {
+ return patterns;
+ }
+
+ TuplePatternItemType get_pattern_type () const override
+ {
+ return TuplePatternItemType::MULTIPLE;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TuplePatternItemsMultiple *clone_tuple_pattern_items_impl () const override
+ {
+ return new TuplePatternItemsMultiple (*this);
+ }
+};
+
+// Class representing TuplePattern patterns where there are a range of patterns
+class TuplePatternItemsRanged : public TuplePatternItems
+{
+ std::vector<std::unique_ptr<Pattern> > lower_patterns;
+ std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+ TuplePatternItemsRanged (
+ std::vector<std::unique_ptr<Pattern> > lower_patterns,
+ std::vector<std::unique_ptr<Pattern> > upper_patterns)
+ : lower_patterns (std::move (lower_patterns)),
+ upper_patterns (std::move (upper_patterns))
+ {}
+
+ // Copy constructor with vector clone
+ TuplePatternItemsRanged (TuplePatternItemsRanged const &other)
+ {
+ lower_patterns.reserve (other.lower_patterns.size ());
+ for (const auto &e : other.lower_patterns)
+ lower_patterns.push_back (e->clone_pattern ());
+
+ upper_patterns.reserve (other.upper_patterns.size ());
+ for (const auto &e : other.upper_patterns)
+ upper_patterns.push_back (e->clone_pattern ());
+ }
+
+ // Overloaded assignment operator to clone
+ TuplePatternItemsRanged &operator= (TuplePatternItemsRanged const &other)
+ {
+ lower_patterns.reserve (other.lower_patterns.size ());
+ for (const auto &e : other.lower_patterns)
+ lower_patterns.push_back (e->clone_pattern ());
+
+ upper_patterns.reserve (other.upper_patterns.size ());
+ for (const auto &e : other.upper_patterns)
+ upper_patterns.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ TuplePatternItemsRanged (TuplePatternItemsRanged &&other) = default;
+ TuplePatternItemsRanged &operator= (TuplePatternItemsRanged &&other)
+ = default;
+
+ std::string as_string () const override;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<Pattern> > &get_lower_patterns ()
+ {
+ return lower_patterns;
+ }
+ const std::vector<std::unique_ptr<Pattern> > &get_lower_patterns () const
+ {
+ return lower_patterns;
+ }
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<Pattern> > &get_upper_patterns ()
+ {
+ return upper_patterns;
+ }
+ const std::vector<std::unique_ptr<Pattern> > &get_upper_patterns () const
+ {
+ return upper_patterns;
+ }
+
+ TuplePatternItemType get_pattern_type () const override
+ {
+ return TuplePatternItemType::RANGED;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TuplePatternItemsRanged *clone_tuple_pattern_items_impl () const override
+ {
+ return new TuplePatternItemsRanged (*this);
+ }
+};
+
+// AST node representing a tuple pattern
+class TuplePattern : public Pattern
+{
+ // bool has_tuple_pattern_items;
+ std::unique_ptr<TuplePatternItems> items;
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override;
+
+ // Returns true if the tuple pattern has items
+ bool has_tuple_pattern_items () const { return items != nullptr; }
+
+ TuplePattern (std::unique_ptr<TuplePatternItems> items, Location locus)
+ : items (std::move (items)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor requires clone
+ TuplePattern (TuplePattern const &other) : locus (other.locus)
+ {
+ // guard to prevent null dereference
+ node_id = other.node_id;
+ if (other.items != nullptr)
+ items = other.items->clone_tuple_pattern_items ();
+ }
+
+ // Overload assignment operator to clone
+ TuplePattern &operator= (TuplePattern const &other)
+ {
+ locus = other.locus;
+ node_id = other.node_id;
+
+ // guard to prevent null dereference
+ if (other.items != nullptr)
+ items = other.items->clone_tuple_pattern_items ();
+ else
+ items = nullptr;
+
+ return *this;
+ }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::unique_ptr<TuplePatternItems> &get_items ()
+ {
+ rust_assert (has_tuple_pattern_items ());
+ return items;
+ }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TuplePattern *clone_pattern_impl () const override
+ {
+ return new TuplePattern (*this);
+ }
+};
+
+// AST node representing a pattern in parentheses, used to control precedence
+class GroupedPattern : public Pattern
+{
+ std::unique_ptr<Pattern> pattern_in_parens;
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override
+ {
+ return "(" + pattern_in_parens->as_string () + ")";
+ }
+
+ GroupedPattern (std::unique_ptr<Pattern> pattern_in_parens, Location locus)
+ : pattern_in_parens (std::move (pattern_in_parens)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor uses clone
+ GroupedPattern (GroupedPattern const &other)
+ : pattern_in_parens (other.pattern_in_parens->clone_pattern ()),
+ locus (other.locus), node_id (other.node_id)
+ {}
+
+ // Overload assignment operator to clone
+ GroupedPattern &operator= (GroupedPattern const &other)
+ {
+ pattern_in_parens = other.pattern_in_parens->clone_pattern ();
+ locus = other.locus;
+ node_id = other.node_id;
+
+ return *this;
+ }
+
+ // default move semantics
+ GroupedPattern (GroupedPattern &&other) = default;
+ GroupedPattern &operator= (GroupedPattern &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::unique_ptr<Pattern> &get_pattern_in_parens ()
+ {
+ rust_assert (pattern_in_parens != nullptr);
+ return pattern_in_parens;
+ }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ GroupedPattern *clone_pattern_impl () const override
+ {
+ return new GroupedPattern (*this);
+ }
+};
+
+// AST node representing patterns that can match slices and arrays
+class SlicePattern : public Pattern
+{
+ std::vector<std::unique_ptr<Pattern> > items;
+ Location locus;
+ NodeId node_id;
+
+public:
+ std::string as_string () const override;
+
+ SlicePattern (std::vector<std::unique_ptr<Pattern> > items, Location locus)
+ : items (std::move (items)), locus (locus),
+ node_id (Analysis::Mappings::get ()->get_next_node_id ())
+ {}
+
+ // Copy constructor with vector clone
+ SlicePattern (SlicePattern const &other) : locus (other.locus)
+ {
+ node_id = other.node_id;
+ items.reserve (other.items.size ());
+ for (const auto &e : other.items)
+ items.push_back (e->clone_pattern ());
+ }
+
+ // Overloaded assignment operator to vector clone
+ SlicePattern &operator= (SlicePattern const &other)
+ {
+ locus = other.locus;
+ node_id = other.node_id;
+
+ items.reserve (other.items.size ());
+ for (const auto &e : other.items)
+ items.push_back (e->clone_pattern ());
+
+ return *this;
+ }
+
+ // move constructors
+ SlicePattern (SlicePattern &&other) = default;
+ SlicePattern &operator= (SlicePattern &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: seems kinda dodgy. Think of better way.
+ std::vector<std::unique_ptr<Pattern> > &get_items () { return items; }
+ const std::vector<std::unique_ptr<Pattern> > &get_items () const
+ {
+ return items;
+ }
+
+ NodeId get_node_id () const { return node_id; }
+
+ NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ SlicePattern *clone_pattern_impl () const override
+ {
+ return new SlicePattern (*this);
+ }
+};
+
+// Moved definition to rust-path.h
+class PathPattern;
+
+// Forward decls for paths (defined in rust-path.h)
+class PathInExpression;
+class QualifiedPathInExpression;
+
+// Replaced with forward decl - defined in rust-macro.h
+class MacroInvocation;
+} // namespace AST
+} // namespace Rust
+
+#endif
--- /dev/null
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AST_STATEMENT_H
+#define RUST_AST_STATEMENT_H
+
+#include "rust-ast.h"
+#include "rust-path.h"
+#include "rust-expr.h"
+
+namespace Rust {
+namespace AST {
+// Just a semi-colon, which apparently is a statement.
+class EmptyStmt : public Stmt
+{
+ Location locus;
+
+ // TODO: find another way to store this to save memory?
+ bool marked_for_strip = false;
+
+public:
+ std::string as_string () const override { return std::string (1, ';'); }
+
+ EmptyStmt (Location locus) : locus (locus) {}
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Can't think of any invalid invariants, so store boolean.
+ void mark_for_strip () override { marked_for_strip = true; }
+ bool is_marked_for_strip () const override { return marked_for_strip; }
+
+ bool is_item () const override final { return false; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ EmptyStmt *clone_stmt_impl () const override { return new EmptyStmt (*this); }
+};
+
+/* Variable assignment let statement - type of "declaration statement" as it
+ * introduces new name into scope */
+class LetStmt : public Stmt
+{
+ // bool has_outer_attrs;
+ std::vector<Attribute> outer_attrs;
+
+ std::unique_ptr<Pattern> variables_pattern;
+
+ // bool has_type;
+ std::unique_ptr<Type> type;
+
+ // bool has_init_expr;
+ std::unique_ptr<Expr> init_expr;
+
+ Location locus;
+
+public:
+ Type *inferedType;
+
+ // Returns whether let statement has outer attributes.
+ bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+ // Returns whether let statement has a given return type.
+ bool has_type () const { return type != nullptr; }
+
+ // Returns whether let statement has an initialisation expression.
+ bool has_init_expr () const { return init_expr != nullptr; }
+
+ std::string as_string () const override;
+
+ LetStmt (std::unique_ptr<Pattern> variables_pattern,
+ std::unique_ptr<Expr> init_expr, std::unique_ptr<Type> type,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)),
+ variables_pattern (std::move (variables_pattern)),
+ type (std::move (type)), init_expr (std::move (init_expr)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ LetStmt (LetStmt const &other)
+ : outer_attrs (other.outer_attrs), locus (other.locus)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.variables_pattern != nullptr)
+ variables_pattern = other.variables_pattern->clone_pattern ();
+
+ // guard to prevent null dereference (always required)
+ if (other.init_expr != nullptr)
+ init_expr = other.init_expr->clone_expr ();
+ if (other.type != nullptr)
+ type = other.type->clone_type ();
+ }
+
+ // Overloaded assignment operator to clone
+ LetStmt &operator= (LetStmt const &other)
+ {
+ outer_attrs = other.outer_attrs;
+ locus = other.locus;
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.variables_pattern != nullptr)
+ variables_pattern = other.variables_pattern->clone_pattern ();
+ else
+ variables_pattern = nullptr;
+
+ // guard to prevent null dereference (always required)
+ if (other.init_expr != nullptr)
+ init_expr = other.init_expr->clone_expr ();
+ else
+ init_expr = nullptr;
+ if (other.type != nullptr)
+ type = other.type->clone_type ();
+ else
+ type = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ LetStmt (LetStmt &&other) = default;
+ LetStmt &operator= (LetStmt &&other) = default;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if pattern is null, so base stripping on that.
+ void mark_for_strip () override { variables_pattern = nullptr; }
+ bool is_marked_for_strip () const override
+ {
+ return variables_pattern == nullptr;
+ }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_init_expr ()
+ {
+ rust_assert (has_init_expr ());
+ return init_expr;
+ }
+
+ std::unique_ptr<Pattern> &get_pattern ()
+ {
+ rust_assert (variables_pattern != nullptr);
+ return variables_pattern;
+ }
+
+ std::unique_ptr<Type> &get_type ()
+ {
+ rust_assert (has_type ());
+ return type;
+ }
+
+ bool is_item () const override final { return false; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ LetStmt *clone_stmt_impl () const override { return new LetStmt (*this); }
+};
+
+/* Abstract base class for expression statements (statements containing an
+ * expression) */
+class ExprStmt : public Stmt
+{
+public:
+ enum ExprStmtType
+ {
+ WITH_BLOCK,
+ WITHOUT_BLOCK
+ };
+
+protected:
+ Location locus;
+
+public:
+ Location get_locus () const override final { return locus; }
+
+ bool is_item () const override final { return false; }
+
+ virtual ExprStmtType get_type () const = 0;
+
+protected:
+ ExprStmt (Location locus) : locus (locus) {}
+};
+
+/* Statement containing an expression without a block (or, due to technical
+ * difficulties, can only be guaranteed to hold an expression). */
+class ExprStmtWithoutBlock : public ExprStmt
+{
+ // TODO: ensure that this works
+ std::unique_ptr<ExprWithoutBlock> expr;
+ /* HACK: cannot ensure type safety of ExprWithoutBlock due to Pratt parsing,
+ * so have to store more general type of Expr. FIXME: fix this issue somehow
+ * or redesign AST. */
+ // std::unique_ptr<Expr> expr;
+
+public:
+ std::string as_string () const override;
+
+ ExprStmtWithoutBlock (std::unique_ptr<ExprWithoutBlock> expr, Location locus)
+ : ExprStmt (locus), expr (std::move (expr->to_stmt ()))
+ {}
+
+ /*ExprStmtWithoutBlock (std::unique_ptr<Expr> expr, Location locus)
+ : ExprStmt (locus), expr (std::move (expr))
+ {}*/
+
+ // Copy constructor with clone
+ ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other) : ExprStmt (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_expr_without_block ();
+ }
+ /*ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other)
+ : ExprStmt (other), expr (other.expr->clone_expr ())
+ {}*/
+
+ // Overloaded assignment operator to clone
+ ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock const &other)
+ {
+ ExprStmt::operator= (other);
+ // expr = other.expr->clone_expr ();
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_expr_without_block ();
+ else
+ expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ ExprStmtWithoutBlock (ExprStmtWithoutBlock &&other) = default;
+ ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if expr is null, so base stripping on that.
+ void mark_for_strip () override { expr = nullptr; }
+ bool is_marked_for_strip () const override { return expr == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<ExprWithoutBlock> &get_expr ()
+ {
+ rust_assert (expr != nullptr);
+ return expr;
+ }
+
+ ExprStmtType get_type () const override
+ {
+ return ExprStmtType::WITHOUT_BLOCK;
+ };
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ExprStmtWithoutBlock *clone_stmt_impl () const override
+ {
+ return new ExprStmtWithoutBlock (*this);
+ }
+};
+
+// Statement containing an expression with a block
+class ExprStmtWithBlock : public ExprStmt
+{
+ std::unique_ptr<ExprWithBlock> expr;
+ bool semicolon_followed;
+
+public:
+ std::string as_string () const override;
+
+ std::vector<LetStmt *> locals;
+
+ ExprStmtWithBlock (std::unique_ptr<ExprWithBlock> expr, Location locus,
+ bool semicolon_followed)
+ : ExprStmt (locus), expr (std::move (expr)),
+ semicolon_followed (semicolon_followed)
+ {}
+
+ // Copy constructor with clone
+ ExprStmtWithBlock (ExprStmtWithBlock const &other) : ExprStmt (other)
+ {
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_expr_with_block ();
+ }
+
+ // Overloaded assignment operator to clone
+ ExprStmtWithBlock &operator= (ExprStmtWithBlock const &other)
+ {
+ ExprStmt::operator= (other);
+
+ // guard to prevent null dereference (only required if error state)
+ if (other.expr != nullptr)
+ expr = other.expr->clone_expr_with_block ();
+ else
+ expr = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ ExprStmtWithBlock (ExprStmtWithBlock &&other) = default;
+ ExprStmtWithBlock &operator= (ExprStmtWithBlock &&other) = default;
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // Invalid if expr is null, so base stripping on that.
+ void mark_for_strip () override { expr = nullptr; }
+ bool is_marked_for_strip () const override { return expr == nullptr; }
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<ExprWithBlock> &get_expr ()
+ {
+ rust_assert (expr != nullptr);
+ return expr;
+ }
+
+ bool is_semicolon_followed () const { return semicolon_followed; }
+
+ ExprStmtType get_type () const override { return ExprStmtType::WITH_BLOCK; };
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ExprStmtWithBlock *clone_stmt_impl () const override
+ {
+ return new ExprStmtWithBlock (*this);
+ }
+};
+
+} // namespace AST
+} // namespace Rust
+
+#endif
--- /dev/null
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_AST_TYPE_H
+#define RUST_AST_TYPE_H
+
+#include "rust-ast.h"
+#include "rust-path.h"
+
+namespace Rust {
+namespace AST {
+// definitions moved to rust-ast.h
+class TypeParamBound;
+class Lifetime;
+
+// A trait bound
+class TraitBound : public TypeParamBound
+{
+ bool in_parens;
+ bool opening_question_mark;
+
+ // bool has_for_lifetimes;
+ // LifetimeParams for_lifetimes;
+ std::vector<LifetimeParam> for_lifetimes; // inlined LifetimeParams
+
+ TypePath type_path;
+
+ Location locus;
+
+public:
+ // Returns whether trait bound has "for" lifetimes
+ bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+ TraitBound (TypePath type_path, Location locus, bool in_parens = false,
+ bool opening_question_mark = false,
+ std::vector<LifetimeParam> for_lifetimes
+ = std::vector<LifetimeParam> ())
+ : TypeParamBound (Analysis::Mappings::get ()->get_next_node_id ()),
+ in_parens (in_parens), opening_question_mark (opening_question_mark),
+ for_lifetimes (std::move (for_lifetimes)),
+ type_path (std::move (type_path)), locus (locus)
+ {}
+
+ TraitBound (NodeId id, TypePath type_path, Location locus,
+ bool in_parens = false, bool opening_question_mark = false,
+ std::vector<LifetimeParam> for_lifetimes
+ = std::vector<LifetimeParam> ())
+ : TypeParamBound (id), in_parens (in_parens),
+ opening_question_mark (opening_question_mark),
+ for_lifetimes (std::move (for_lifetimes)),
+ type_path (std::move (type_path)), locus (locus)
+ {}
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: this mutable getter seems kinda dodgy
+ TypePath &get_type_path () { return type_path; }
+ const TypePath &get_type_path () const { return type_path; }
+
+ bool is_in_parens () const { return in_parens; }
+ bool has_opening_question_mark () const { return opening_question_mark; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TraitBound *clone_type_param_bound_impl () const override
+ {
+ return new TraitBound (node_id, type_path, locus, in_parens,
+ opening_question_mark, for_lifetimes);
+ }
+};
+
+// definition moved to rust-ast.h
+class TypeNoBounds;
+
+// An impl trait? Poor reference material here.
+class ImplTraitType : public Type
+{
+ // TypeParamBounds type_param_bounds;
+ // inlined form
+ std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds;
+
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ImplTraitType *clone_type_impl () const override
+ {
+ return new ImplTraitType (*this);
+ }
+
+public:
+ ImplTraitType (
+ std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds,
+ Location locus)
+ : type_param_bounds (std::move (type_param_bounds)), locus (locus)
+ {}
+
+ // copy constructor with vector clone
+ ImplTraitType (ImplTraitType const &other) : locus (other.locus)
+ {
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+ for (const auto &e : other.type_param_bounds)
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+
+ // overloaded assignment operator to clone
+ ImplTraitType &operator= (ImplTraitType const &other)
+ {
+ locus = other.locus;
+
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+ for (const auto &e : other.type_param_bounds)
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+
+ return *this;
+ }
+
+ // move constructors
+ ImplTraitType (ImplTraitType &&other) = default;
+ ImplTraitType &operator= (ImplTraitType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: mutable getter seems kinda dodgy
+ std::vector<std::unique_ptr<TypeParamBound> > &get_type_param_bounds ()
+ {
+ return type_param_bounds;
+ }
+ const std::vector<std::unique_ptr<TypeParamBound> > &
+ get_type_param_bounds () const
+ {
+ return type_param_bounds;
+ }
+};
+
+// An opaque value of another type that implements a set of traits
+class TraitObjectType : public Type
+{
+ bool has_dyn;
+ std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds;
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TraitObjectType *clone_type_impl () const override
+ {
+ return new TraitObjectType (*this);
+ }
+
+public:
+ TraitObjectType (
+ std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds,
+ Location locus, bool is_dyn_dispatch)
+ : has_dyn (is_dyn_dispatch),
+ type_param_bounds (std::move (type_param_bounds)), locus (locus)
+ {}
+
+ // copy constructor with vector clone
+ TraitObjectType (TraitObjectType const &other)
+ : has_dyn (other.has_dyn), locus (other.locus)
+ {
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+ for (const auto &e : other.type_param_bounds)
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+ }
+
+ // overloaded assignment operator to clone
+ TraitObjectType &operator= (TraitObjectType const &other)
+ {
+ has_dyn = other.has_dyn;
+ locus = other.locus;
+ type_param_bounds.reserve (other.type_param_bounds.size ());
+ for (const auto &e : other.type_param_bounds)
+ type_param_bounds.push_back (e->clone_type_param_bound ());
+
+ return *this;
+ }
+
+ // move constructors
+ TraitObjectType (TraitObjectType &&other) = default;
+ TraitObjectType &operator= (TraitObjectType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ bool is_dyn () const { return has_dyn; }
+
+ // TODO: mutable getter seems kinda dodgy
+ std::vector<std::unique_ptr<TypeParamBound> > &get_type_param_bounds ()
+ {
+ return type_param_bounds;
+ }
+ const std::vector<std::unique_ptr<TypeParamBound> > &
+ get_type_param_bounds () const
+ {
+ return type_param_bounds;
+ }
+};
+
+// A type with parentheses around it, used to avoid ambiguity.
+class ParenthesisedType : public TypeNoBounds
+{
+ std::unique_ptr<Type> type_in_parens;
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ParenthesisedType *clone_type_no_bounds_impl () const override
+ {
+ return new ParenthesisedType (*this);
+ }
+
+public:
+ // Constructor uses Type pointer for polymorphism
+ ParenthesisedType (std::unique_ptr<Type> type_inside_parens, Location locus)
+ : type_in_parens (std::move (type_inside_parens)), locus (locus)
+ {}
+
+ /* Copy constructor uses custom deep copy method for type to preserve
+ * polymorphism */
+ ParenthesisedType (ParenthesisedType const &other)
+ : type_in_parens (other.type_in_parens->clone_type ()), locus (other.locus)
+ {}
+
+ // overload assignment operator to use custom clone method
+ ParenthesisedType &operator= (ParenthesisedType const &other)
+ {
+ type_in_parens = other.type_in_parens->clone_type ();
+ locus = other.locus;
+ return *this;
+ }
+
+ // default move semantics
+ ParenthesisedType (ParenthesisedType &&other) = default;
+ ParenthesisedType &operator= (ParenthesisedType &&other) = default;
+
+ std::string as_string () const override
+ {
+ return "(" + type_in_parens->as_string () + ")";
+ }
+
+ // Creates a trait bound (clone of this one's trait bound) - HACK
+ TraitBound *to_trait_bound (bool) const override
+ {
+ /* NOTE: obviously it is unknown whether the internal type is a trait bound
+ * due to polymorphism, so just let the internal type handle it. As
+ * parenthesised type, it must be in parentheses. */
+ return type_in_parens->to_trait_bound (true);
+ }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: would a "vis_type" be better?
+ std::unique_ptr<Type> &get_type_in_parens ()
+ {
+ rust_assert (type_in_parens != nullptr);
+ return type_in_parens;
+ }
+};
+
+// Impl trait with a single bound? Poor reference material here.
+class ImplTraitTypeOneBound : public TypeNoBounds
+{
+ TraitBound trait_bound;
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ImplTraitTypeOneBound *clone_type_no_bounds_impl () const override
+ {
+ return new ImplTraitTypeOneBound (*this);
+ }
+
+public:
+ ImplTraitTypeOneBound (TraitBound trait_bound, Location locus)
+ : trait_bound (std::move (trait_bound)), locus (locus)
+ {}
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: would a "vis_type" be better?
+ TraitBound &get_trait_bound ()
+ {
+ // TODO: check to ensure invariants are met?
+ return trait_bound;
+ }
+};
+
+/* A trait object with a single trait bound. The "trait bound" is really just
+ * the trait. Basically like using an interface as a type in an OOP language. */
+class TraitObjectTypeOneBound : public TypeNoBounds
+{
+ bool has_dyn;
+ TraitBound trait_bound;
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TraitObjectTypeOneBound *clone_type_no_bounds_impl () const override
+ {
+ return new TraitObjectTypeOneBound (*this);
+ }
+
+public:
+ TraitObjectTypeOneBound (TraitBound trait_bound, Location locus,
+ bool is_dyn_dispatch = false)
+ : has_dyn (is_dyn_dispatch), trait_bound (std::move (trait_bound)),
+ locus (locus)
+ {}
+
+ std::string as_string () const override;
+
+ // Creates a trait bound (clone of this one's trait bound) - HACK
+ TraitBound *to_trait_bound (bool) const override
+ {
+ /* NOTE: this assumes there is no dynamic dispatch specified- if there was,
+ * this cloning would not be required as parsing is unambiguous. */
+ return new TraitBound (trait_bound);
+ }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: would a "vis_type" be better?
+ TraitBound &get_trait_bound ()
+ {
+ // TODO: check to ensure invariants are met?
+ return trait_bound;
+ }
+
+ bool is_dyn () const { return has_dyn; }
+};
+
+class TypePath; // definition moved to "rust-path.h"
+
+/* A type consisting of the "product" of others (the tuple's elements) in a
+ * specific order */
+class TupleType : public TypeNoBounds
+{
+ std::vector<std::unique_ptr<Type> > elems;
+ Location locus;
+
+public:
+ // Returns whether the tuple type is the unit type, i.e. has no elements.
+ bool is_unit_type () const { return elems.empty (); }
+
+ TupleType (std::vector<std::unique_ptr<Type> > elems, Location locus)
+ : elems (std::move (elems)), locus (locus)
+ {}
+
+ // copy constructor with vector clone
+ TupleType (TupleType const &other) : locus (other.locus)
+ {
+ elems.reserve (other.elems.size ());
+ for (const auto &e : other.elems)
+ elems.push_back (e->clone_type ());
+ }
+
+ // overloaded assignment operator to clone
+ TupleType &operator= (TupleType const &other)
+ {
+ locus = other.locus;
+
+ elems.reserve (other.elems.size ());
+ for (const auto &e : other.elems)
+ elems.push_back (e->clone_type ());
+
+ return *this;
+ }
+
+ // move constructors
+ TupleType (TupleType &&other) = default;
+ TupleType &operator= (TupleType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: mutable getter seems kinda dodgy
+ std::vector<std::unique_ptr<Type> > &get_elems () { return elems; }
+ const std::vector<std::unique_ptr<Type> > &get_elems () const
+ {
+ return elems;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ TupleType *clone_type_no_bounds_impl () const override
+ {
+ return new TupleType (*this);
+ }
+};
+
+/* A type with no values, representing the result of computations that never
+ * complete. Expressions of NeverType can be coerced into any other types.
+ * Represented as "!". */
+class NeverType : public TypeNoBounds
+{
+ Location locus;
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ NeverType *clone_type_no_bounds_impl () const override
+ {
+ return new NeverType (*this);
+ }
+
+public:
+ NeverType (Location locus) : locus (locus) {}
+
+ std::string as_string () const override { return "! (never type)"; }
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+};
+
+// A type consisting of a pointer without safety or liveness guarantees
+class RawPointerType : public TypeNoBounds
+{
+public:
+ enum PointerType
+ {
+ MUT,
+ CONST
+ };
+
+private:
+ PointerType pointer_type;
+ std::unique_ptr<TypeNoBounds> type;
+ Location locus;
+
+public:
+ // Returns whether the pointer is mutable or constant.
+ PointerType get_pointer_type () const { return pointer_type; }
+
+ // Constructor requires pointer for polymorphism reasons
+ RawPointerType (PointerType pointer_type,
+ std::unique_ptr<TypeNoBounds> type_no_bounds, Location locus)
+ : pointer_type (pointer_type), type (std::move (type_no_bounds)),
+ locus (locus)
+ {}
+
+ // Copy constructor calls custom polymorphic clone function
+ RawPointerType (RawPointerType const &other)
+ : pointer_type (other.pointer_type),
+ type (other.type->clone_type_no_bounds ()), locus (other.locus)
+ {}
+
+ // overload assignment operator to use custom clone method
+ RawPointerType &operator= (RawPointerType const &other)
+ {
+ pointer_type = other.pointer_type;
+ type = other.type->clone_type_no_bounds ();
+ locus = other.locus;
+ return *this;
+ }
+
+ // default move semantics
+ RawPointerType (RawPointerType &&other) = default;
+ RawPointerType &operator= (RawPointerType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: would a "vis_type" be better?
+ std::unique_ptr<TypeNoBounds> &get_type_pointed_to ()
+ {
+ rust_assert (type != nullptr);
+ return type;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ RawPointerType *clone_type_no_bounds_impl () const override
+ {
+ return new RawPointerType (*this);
+ }
+};
+
+// A type pointing to memory owned by another value
+class ReferenceType : public TypeNoBounds
+{
+ // bool has_lifetime; // TODO: handle in lifetime or something?
+ Lifetime lifetime;
+
+ bool has_mut;
+ std::unique_ptr<TypeNoBounds> type;
+ Location locus;
+
+public:
+ // Returns whether the reference is mutable or immutable.
+ bool is_mut () const { return has_mut; }
+
+ // Returns whether the reference has a lifetime.
+ bool has_lifetime () const { return !lifetime.is_error (); }
+
+ // Constructor
+ ReferenceType (bool is_mut, std::unique_ptr<TypeNoBounds> type_no_bounds,
+ Location locus, Lifetime lifetime = Lifetime::error ())
+ : lifetime (std::move (lifetime)), has_mut (is_mut),
+ type (std::move (type_no_bounds)), locus (locus)
+ {}
+
+ // Copy constructor with custom clone method
+ ReferenceType (ReferenceType const &other)
+ : lifetime (other.lifetime), has_mut (other.has_mut),
+ type (other.type->clone_type_no_bounds ()), locus (other.locus)
+ {}
+
+ // Operator overload assignment operator to custom clone the unique pointer
+ ReferenceType &operator= (ReferenceType const &other)
+ {
+ lifetime = other.lifetime;
+ has_mut = other.has_mut;
+ type = other.type->clone_type_no_bounds ();
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ ReferenceType (ReferenceType &&other) = default;
+ ReferenceType &operator= (ReferenceType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: would a "vis_type" be better?
+ std::unique_ptr<TypeNoBounds> &get_type_referenced ()
+ {
+ rust_assert (type != nullptr);
+ return type;
+ }
+
+ bool get_has_mut () const { return has_mut; }
+
+ Lifetime &get_lifetime () { return lifetime; }
+
+ std::unique_ptr<TypeNoBounds> &get_base_type () { return type; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ReferenceType *clone_type_no_bounds_impl () const override
+ {
+ return new ReferenceType (*this);
+ }
+};
+
+// A fixed-size sequence of elements of a specified type
+class ArrayType : public TypeNoBounds
+{
+ std::unique_ptr<Type> elem_type;
+ std::unique_ptr<Expr> size;
+ Location locus;
+
+public:
+ // Constructor requires pointers for polymorphism
+ ArrayType (std::unique_ptr<Type> type, std::unique_ptr<Expr> array_size,
+ Location locus)
+ : elem_type (std::move (type)), size (std::move (array_size)), locus (locus)
+ {}
+
+ // Copy constructor requires deep copies of both unique pointers
+ ArrayType (ArrayType const &other)
+ : elem_type (other.elem_type->clone_type ()),
+ size (other.size->clone_expr ()), locus (other.locus)
+ {}
+
+ // Overload assignment operator to deep copy pointers
+ ArrayType &operator= (ArrayType const &other)
+ {
+ elem_type = other.elem_type->clone_type ();
+ size = other.size->clone_expr ();
+ locus = other.locus;
+ return *this;
+ }
+
+ // move constructors
+ ArrayType (ArrayType &&other) = default;
+ ArrayType &operator= (ArrayType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: would a "vis_type" be better?
+ std::unique_ptr<Type> &get_elem_type ()
+ {
+ rust_assert (elem_type != nullptr);
+ return elem_type;
+ }
+
+ // TODO: would a "vis_expr" be better?
+ std::unique_ptr<Expr> &get_size_expr ()
+ {
+ rust_assert (size != nullptr);
+ return size;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ ArrayType *clone_type_no_bounds_impl () const override
+ {
+ return new ArrayType (*this);
+ }
+};
+
+/* A dynamically-sized type representing a "view" into a sequence of elements of
+ * a type */
+class SliceType : public TypeNoBounds
+{
+ std::unique_ptr<Type> elem_type;
+ Location locus;
+
+public:
+ // Constructor requires pointer for polymorphism
+ SliceType (std::unique_ptr<Type> type, Location locus)
+ : elem_type (std::move (type)), locus (locus)
+ {}
+
+ // Copy constructor requires deep copy of Type smart pointer
+ SliceType (SliceType const &other)
+ : elem_type (other.elem_type->clone_type ()), locus (other.locus)
+ {}
+
+ // Overload assignment operator to deep copy
+ SliceType &operator= (SliceType const &other)
+ {
+ elem_type = other.elem_type->clone_type ();
+ locus = other.locus;
+
+ return *this;
+ }
+
+ // move constructors
+ SliceType (SliceType &&other) = default;
+ SliceType &operator= (SliceType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: would a "vis_type" be better?
+ std::unique_ptr<Type> &get_elem_type ()
+ {
+ rust_assert (elem_type != nullptr);
+ return elem_type;
+ }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ SliceType *clone_type_no_bounds_impl () const override
+ {
+ return new SliceType (*this);
+ }
+};
+
+/* Type used in generic arguments to explicitly request type inference (wildcard
+ * pattern) */
+class InferredType : public TypeNoBounds
+{
+ Location locus;
+
+ // e.g. Vec<_> = whatever
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ InferredType *clone_type_no_bounds_impl () const override
+ {
+ return new InferredType (*this);
+ }
+
+public:
+ InferredType (Location locus) : locus (locus) {}
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+};
+
+class QualifiedPathInType; // definition moved to "rust-path.h"
+
+// A possibly named param used in a BaseFunctionType
+struct MaybeNamedParam
+{
+public:
+ enum ParamKind
+ {
+ UNNAMED,
+ IDENTIFIER,
+ WILDCARD
+ };
+
+private:
+ std::vector<Attribute> outer_attrs;
+
+ std::unique_ptr<Type> param_type;
+
+ ParamKind param_kind;
+ Identifier name; // technically, can be an identifier or '_'
+
+ Location locus;
+
+public:
+ MaybeNamedParam (Identifier name, ParamKind param_kind,
+ std::unique_ptr<Type> param_type,
+ std::vector<Attribute> outer_attrs, Location locus)
+ : outer_attrs (std::move (outer_attrs)),
+ param_type (std::move (param_type)), param_kind (param_kind),
+ name (std::move (name)), locus (locus)
+ {}
+
+ // Copy constructor with clone
+ MaybeNamedParam (MaybeNamedParam const &other)
+ : outer_attrs (other.outer_attrs), param_kind (other.param_kind),
+ name (other.name), locus (other.locus)
+ {
+ // guard to prevent null dereference
+ if (other.param_type != nullptr)
+ param_type = other.param_type->clone_type ();
+ }
+
+ ~MaybeNamedParam () = default;
+
+ // Overloaded assignment operator with clone
+ MaybeNamedParam &operator= (MaybeNamedParam const &other)
+ {
+ outer_attrs = other.outer_attrs;
+ name = other.name;
+ param_kind = other.param_kind;
+ locus = other.locus;
+
+ // guard to prevent null dereference
+ if (other.param_type != nullptr)
+ param_type = other.param_type->clone_type ();
+ else
+ param_type = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ MaybeNamedParam (MaybeNamedParam &&other) = default;
+ MaybeNamedParam &operator= (MaybeNamedParam &&other) = default;
+
+ std::string as_string () const;
+
+ // Returns whether the param is in an error state.
+ bool is_error () const { return param_type == nullptr; }
+
+ // Creates an error state param.
+ static MaybeNamedParam create_error ()
+ {
+ return MaybeNamedParam ("", UNNAMED, nullptr, {}, Location ());
+ }
+
+ Location get_locus () const { return locus; }
+
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+ const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+ // TODO: would a "vis_type" be better?
+ std::unique_ptr<Type> &get_type ()
+ {
+ rust_assert (param_type != nullptr);
+ return param_type;
+ }
+
+ ParamKind get_param_kind () const { return param_kind; }
+
+ Identifier get_name () const { return name; }
+};
+
+/* A function pointer type - can be created via coercion from function items and
+ * non- capturing closures. */
+class BareFunctionType : public TypeNoBounds
+{
+ // bool has_for_lifetimes;
+ // ForLifetimes for_lifetimes;
+ std::vector<LifetimeParam> for_lifetimes; // inlined version
+
+ FunctionQualifiers function_qualifiers;
+ std::vector<MaybeNamedParam> params;
+ bool is_variadic;
+ std::vector<Attribute> variadic_attrs;
+
+ // bool has_return_type;
+ // BareFunctionReturnType return_type;
+ std::unique_ptr<TypeNoBounds> return_type; // inlined version
+
+ Location locus;
+
+public:
+ // Whether a return type is defined with the function.
+ bool has_return_type () const { return return_type != nullptr; }
+
+ // Whether the function has ForLifetimes.
+ bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+ BareFunctionType (std::vector<LifetimeParam> lifetime_params,
+ FunctionQualifiers qualifiers,
+ std::vector<MaybeNamedParam> named_params, bool is_variadic,
+ std::vector<Attribute> variadic_attrs,
+ std::unique_ptr<TypeNoBounds> type, Location locus)
+ : for_lifetimes (std::move (lifetime_params)),
+ function_qualifiers (std::move (qualifiers)),
+ params (std::move (named_params)), is_variadic (is_variadic),
+ variadic_attrs (std::move (variadic_attrs)),
+ return_type (std::move (type)), locus (locus)
+ {
+ if (!variadic_attrs.empty ())
+ is_variadic = true;
+ }
+
+ // Copy constructor with clone
+ BareFunctionType (BareFunctionType const &other)
+ : for_lifetimes (other.for_lifetimes),
+ function_qualifiers (other.function_qualifiers), params (other.params),
+ is_variadic (other.is_variadic), variadic_attrs (other.variadic_attrs),
+ locus (other.locus)
+ {
+ // guard to prevent null dereference
+ if (other.return_type != nullptr)
+ return_type = other.return_type->clone_type_no_bounds ();
+ }
+
+ // Overload assignment operator to deep copy
+ BareFunctionType &operator= (BareFunctionType const &other)
+ {
+ for_lifetimes = other.for_lifetimes;
+ function_qualifiers = other.function_qualifiers;
+ params = other.params;
+ is_variadic = other.is_variadic;
+ variadic_attrs = other.variadic_attrs;
+ locus = other.locus;
+
+ // guard to prevent null dereference
+ if (other.return_type != nullptr)
+ return_type = other.return_type->clone_type_no_bounds ();
+ else
+ return_type = nullptr;
+
+ return *this;
+ }
+
+ // move constructors
+ BareFunctionType (BareFunctionType &&other) = default;
+ BareFunctionType &operator= (BareFunctionType &&other) = default;
+
+ std::string as_string () const override;
+
+ Location get_locus () const override final { return locus; }
+
+ void accept_vis (ASTVisitor &vis) override;
+
+ // TODO: this mutable getter seems kinda dodgy
+ std::vector<MaybeNamedParam> &get_function_params () { return params; }
+ const std::vector<MaybeNamedParam> &get_function_params () const
+ {
+ return params;
+ }
+
+ // TODO: would a "vis_type" be better?
+ std::unique_ptr<TypeNoBounds> &get_return_type ()
+ {
+ rust_assert (has_return_type ());
+ return return_type;
+ }
+
+ FunctionQualifiers get_function_qualifiers () { return function_qualifiers; }
+
+protected:
+ /* Use covariance to implement clone function as returning this object rather
+ * than base */
+ BareFunctionType *clone_type_no_bounds_impl () const override
+ {
+ return new BareFunctionType (*this);
+ }
+};
+
+// Forward decl - defined in rust-macro.h
+class MacroInvocation;
+
+/* TODO: possible types
+ * struct type?
+ * "enum" (tagged union) type?
+ * C-like union type?
+ * function item type?
+ * closure expression types?
+ * primitive types (bool, int, float, char, str (the slice))
+ * Although supposedly TypePaths are used to reference these types (including
+ * primitives) */
+
+/* FIXME: Incomplete spec references:
+ * anonymous type parameters, aka "impl Trait in argument position" - impl then
+ * trait bounds abstract return types, aka "impl Trait in return position" -
+ * impl then trait bounds */
+} // namespace AST
+} // namespace Rust
+
+#endif