blob: 4830ae0d2d3843764341079493247f43646c763a [file] [log] [blame]
#ifndef RUST_AST_EXPR_H
#define RUST_AST_EXPR_H
#include "rust-ast.h"
#include "rust-path.h"
#include "rust-macro.h"
#include "rust-operators.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)? */
// 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_t locus;
NodeId node_id;
public:
std::string as_string () const;
LoopLabel (Lifetime loop_label, location_t locus = UNDEF_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_t get_locus () const { return locus; }
Lifetime &get_lifetime () { return label; }
NodeId get_node_id () const { return node_id; }
};
// 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_t 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_t 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_t 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_t get_locus () const override final { return locus; }
bool is_literal () const override final { return true; }
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 () override { 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);
}
};
// Like an AttrInputLiteral, but stores a MacroInvocation
class AttrInputMacro : public AttrInput
{
std::unique_ptr<MacroInvocation> macro;
public:
AttrInputMacro (std::unique_ptr<MacroInvocation> macro)
: macro (std::move (macro))
{}
AttrInputMacro (const AttrInputMacro &oth);
AttrInputMacro (AttrInputMacro &&oth) : macro (std::move (oth.macro)) {}
void operator= (const AttrInputMacro &oth);
void operator= (AttrInputMacro &&oth) { macro = std::move (oth.macro); }
std::string as_string () const override;
void accept_vis (ASTVisitor &vis) override;
// assuming this can't be a cfg predicate
bool check_cfg_predicate (const Session &) const override { return false; }
// assuming this is like AttrInputLiteral
bool is_meta_item () const override { return false; }
std::unique_ptr<MacroInvocation> &get_macro () { return macro; }
AttrInputType get_attr_input_type () const final override
{
return AttrInput::AttrInputType::MACRO;
}
protected:
AttrInputMacro *clone_attr_input_impl () const override
{
return new AttrInputMacro (*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 (); }
location_t get_locus () const override { return lit_expr.get_locus (); }
LiteralExpr get_literal () const { return lit_expr; }
LiteralExpr &get_literal () { return lit_expr; }
void accept_vis (ASTVisitor &vis) override;
bool check_cfg_predicate (const Session &session) const override;
MetaItemInner::Kind get_kind () override
{
return MetaItemInner::Kind::LitExpr;
}
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))
{}
SimplePath get_path () const { return path; }
SimplePath &get_path () { return path; }
LiteralExpr get_literal () const { return lit; }
LiteralExpr &get_literal () { return lit; }
std::string as_string () const override
{
return path.as_string () + " = " + lit.as_string ();
}
MetaItem::ItemKind get_item_kind () const override
{
return MetaItem::ItemKind::PathLit;
}
// There are two Locations in MetaItemPathLit (path and lit_expr),
// we have no idea use which of them, just simply return UNKNOWN_LOCATION
// now.
// Maybe we will figure out when we really need the location in the future.
location_t get_locus () const override { return UNKNOWN_LOCATION; }
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_t 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_t 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_t 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 () override { 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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 () override { 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_t 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_t 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_t locus;
public:
ArrayElemsValues (std::vector<std::unique_ptr<Expr> > elems, location_t 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_t locus;
public:
// Constructor requires pointers for polymorphism
ArrayElemsCopied (std::unique_ptr<Expr> copied_elem,
std::unique_ptr<Expr> copy_amount, location_t 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_t 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 () override { 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_t 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_t 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_t 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_t 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_t 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 () override { 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_t 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 () override { 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_t 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_t 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_t 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_t 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_t 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 () override { 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 () override { 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_t 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_t locus)
: StructExpr (std::move (struct_path), std::move (outer_attribs)),
inner_attrs (std::move (inner_attribs)), locus (locus)
{}
location_t 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_t locus;
public:
StructBase (std::unique_ptr<Expr> base_struct_ptr, location_t 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, UNDEF_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_t 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_t locus;
public:
StructExprFieldIdentifier (Identifier field_identifier, location_t locus)
: StructExprField (), field_name (std::move (field_identifier)),
locus (locus)
{}
std::string as_string () const override { return field_name.as_string (); }
location_t 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_t locus;
public:
StructExprFieldIdentifierValue (Identifier field_identifier,
std::unique_ptr<Expr> field_value,
location_t 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.as_string (); }
location_t 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_t locus;
public:
StructExprFieldIndexValue (TupleIndex tuple_index,
std::unique_ptr<Expr> field_value,
location_t 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_t 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_t 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_t 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_t 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_t 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_t 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 () override { 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_t 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_t 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_t 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 () override { 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_t 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_t 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_t 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 () override { 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;
std::unique_ptr<Type> type;
location_t 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_t 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, UNDEF_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; }
std::unique_ptr<Pattern> &get_pattern ()
{
rust_assert (pattern != nullptr);
return pattern;
}
std::unique_ptr<Type> &get_type ()
{
rust_assert (has_type_given ());
return type;
}
location_t get_locus () const { return locus; }
};
// 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_t locus;
protected:
ClosureExpr (std::vector<ClosureParam> closure_params, bool has_move,
std::vector<Attribute> outer_attribs, location_t 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_t 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 () override { return outer_attrs; }
void set_outer_attrs (std::vector<Attribute> new_attrs) override
{
outer_attrs = std::move (new_attrs);
}
bool get_has_move () const { return has_move; }
};
// 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_t 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;
}
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;
LoopLabel label;
location_t start_locus;
location_t 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, LoopLabel label,
location_t start_locus, location_t end_locus)
: outer_attrs (std::move (outer_attribs)),
inner_attrs (std::move (inner_attribs)),
statements (std::move (block_statements)), expr (std::move (block_expr)),
label (std::move (label)), 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), label (other.label),
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_t get_locus () const override final { return start_locus; }
location_t get_start_locus () const { return start_locus; }
location_t 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;
}
std::unique_ptr<Expr> take_tail_expr ()
{
rust_assert (has_tail_expr ());
return std::move (expr);
}
void set_tail_expr (std::unique_ptr<Expr> expr)
{
this->expr = std::move (expr);
}
// Removes the tail expression from the block.
void strip_tail_expr () { expr = nullptr; }
// Normalizes a trailing statement without a semicolon to a tail expression.
void normalize_tail_expr ();
void try_convert_last_stmt ();
const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
void set_outer_attrs (std::vector<Attribute> new_attrs) override
{
outer_attrs = std::move (new_attrs);
}
bool has_label () { return !label.is_error (); }
LoopLabel &get_label () { return label; }
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_t 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_t 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_t locus)
: outer_attrs (std::move (outer_attribs)), label (std::move (label)),
locus (locus)
{}
location_t 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 () override { 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;
LoopLabel label;
std::unique_ptr<Expr> break_expr;
location_t 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 (LoopLabel break_label, std::unique_ptr<Expr> expr_in_break,
std::vector<Attribute> outer_attribs, location_t 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_t 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 () override { return outer_attrs; }
void set_outer_attrs (std::vector<Attribute> new_attrs) override
{
outer_attrs = std::move (new_attrs);
}
LoopLabel &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_t locus;
protected:
// outer attributes not allowed before range expressions
RangeExpr (location_t locus) : locus (locus) {}
public:
location_t get_locus () const override final { return locus; }
std::vector<Attribute> &get_outer_attrs () override final
{
// RangeExpr cannot have any outer attributes
rust_assert (false);
}
// 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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 () override { 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_t locus;
public:
std::string as_string () const override;
UnsafeBlockExpr (std::unique_ptr<BlockExpr> block_expr,
std::vector<Attribute> outer_attribs, location_t 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_t 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 () override { 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);
}
};
// 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_t locus;
protected:
// Constructor for BaseLoopExpr
BaseLoopExpr (std::unique_ptr<BlockExpr> loop_block, location_t 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_t 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 () override { 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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 () override { 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<ExprWithBlock> else_block;
public:
std::string as_string () const override;
IfExprConseqElse (std::unique_ptr<Expr> condition,
std::unique_ptr<BlockExpr> if_block,
std::unique_ptr<ExprWithBlock> else_block,
std::vector<Attribute> outer_attrs, location_t 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_expr_with_block ())
{}
// 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_expr_with_block ();
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<ExprWithBlock> &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);
}
};
// 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_t 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_t 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_t 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 () override { 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);
}
};
/* AST node representing "if let" expression with an "else" expression at the
* end */
class IfLetExprConseqElse : public IfLetExpr
{
std::unique_ptr<ExprWithBlock> 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<ExprWithBlock> else_block,
std::vector<Attribute> outer_attrs, location_t 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_expr_with_block ())
{}
// 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_expr_with_block ();
// 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<ExprWithBlock> &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);
}
};
// 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_t 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_t 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_t locus = UNDEF_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_t 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_t 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_t 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_t 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 () override { 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_t locus;
public:
// TODO: ensure outer attributes are actually allowed
AwaitExpr (std::unique_ptr<Expr> awaited_expr,
std::vector<Attribute> outer_attrs, location_t 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_t 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 () override { 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_t locus;
public:
AsyncBlockExpr (std::unique_ptr<BlockExpr> block_expr, bool has_move,
std::vector<Attribute> outer_attrs, location_t 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;
bool get_has_move () { return has_move; }
location_t 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 () override { 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);
}
};
// Inline-assembly specific options
enum class InlineAsmOptions
{
PURE = 1 << 0,
NOMEM = 1 << 1,
READONLY = 1 << 2,
PRESERVES_FLAGS = 1 << 3,
NORETURN = 1 << 4,
NOSTACK = 1 << 5,
ATT_SYNTAX = 1 << 6,
RAW = 1 << 7,
MAY_UNWIND = 1 << 8,
};
struct AnonConst
{
NodeId id;
std::unique_ptr<Expr> value;
};
struct InlineAsmRegOrRegClass
{
enum Type
{
Reg,
RegClass,
};
struct Reg
{
std::string Symbol;
};
struct RegClass
{
std::string Symbol;
};
Identifier name;
location_t locus;
};
struct InlineAsmOperand
{
enum RegisterType
{
In,
Out,
InOut,
SplitInOut,
Const,
Sym,
};
struct In
{
InlineAsmRegOrRegClass reg;
std::unique_ptr<Expr> expr;
};
struct Out
{
InlineAsmRegOrRegClass reg;
bool late;
std::unique_ptr<Expr> expr; // can be null
};
struct InOut
{
InlineAsmRegOrRegClass reg;
bool late;
std::unique_ptr<Expr> expr; // this can't be null
};
struct SplitInOut
{
InlineAsmRegOrRegClass reg;
bool late;
std::unique_ptr<Expr> in_expr;
std::unique_ptr<Expr> out_expr; // could be null
};
struct Const
{
AnonConst anon_const;
};
struct Sym
{
std::unique_ptr<Expr> sym;
};
location_t locus;
};
struct InlineAsmPlaceHolder
{
size_t operand_idx;
char modifier; // can be null
location_t locus;
};
struct InlineAsmTemplatePiece
{
bool is_placeholder;
union
{
std::string string;
InlineAsmPlaceHolder placeholder;
};
};
struct TupleClobber
{
// as gccrs still doesn't contain a symbol class I have put them as strings
std::string symbol;
location_t loc;
};
struct TupleTemplateStr
{
// as gccrs still doesn't contain a symbol class I have put them as strings
std::string symbol;
std::string optional_symbol;
location_t loc;
};
// Inline Assembly Node
class InlineAsm : public ExprWithoutBlock
{
public:
std::vector<InlineAsmTemplatePiece> template_;
std::vector<TupleTemplateStr> template_strs;
std::vector<InlineAsmOperand> operands;
TupleClobber clobber_abi;
InlineAsmOptions options;
std::vector<location_t> line_spans;
};
} // namespace AST
} // namespace Rust
#endif