mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 12:00:11 -05:00
Previously we aborted when querying the location on a MetaItemPathExpr, the location should start on the path and continue over the expr but we do not support that kind of location range yet. gcc/rust/ChangeLog: * ast/rust-expr.h: Use path locus. gcc/testsuite/ChangeLog: * rust/compile/issue-4301.rs: New test. Signed-off-by: Pierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
5926 lines
166 KiB
C++
5926 lines
166 KiB
C++
#ifndef RUST_AST_EXPR_H
|
|
#define RUST_AST_EXPR_H
|
|
|
|
#include "optional.h"
|
|
#include "rust-ast.h"
|
|
#include "rust-common.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 Visitable*/
|
|
{
|
|
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.
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Literal; }
|
|
|
|
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)) {}
|
|
|
|
AttrInputMacro &operator= (const AttrInputMacro &oth);
|
|
|
|
AttrInputMacro &operator= (AttrInputMacro &&oth)
|
|
{
|
|
macro = std::move (oth.macro);
|
|
return *this;
|
|
}
|
|
|
|
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 = expr" form
|
|
class MetaItemPathExpr : public MetaItem
|
|
{
|
|
SimplePath path;
|
|
std::unique_ptr<Expr> expr;
|
|
|
|
public:
|
|
MetaItemPathExpr (SimplePath path, std::unique_ptr<Expr> expr)
|
|
: path (std::move (path)), expr (std::move (expr))
|
|
{}
|
|
|
|
MetaItemPathExpr (const MetaItemPathExpr &other)
|
|
: MetaItem (other), path (other.path), expr (other.expr->clone_expr ())
|
|
{}
|
|
|
|
MetaItemPathExpr (MetaItemPathExpr &&) = default;
|
|
|
|
MetaItemPathExpr &operator= (MetaItemPathExpr &&) = default;
|
|
|
|
MetaItemPathExpr operator= (const MetaItemPathExpr &other)
|
|
{
|
|
MetaItem::operator= (other);
|
|
path = other.path;
|
|
expr = other.expr->clone_expr ();
|
|
return *this;
|
|
}
|
|
|
|
SimplePath get_path () const { return path; }
|
|
|
|
SimplePath &get_path () { return path; }
|
|
|
|
Expr &get_expr () { return *expr; }
|
|
|
|
std::unique_ptr<Expr> &get_expr_ptr () { return expr; }
|
|
|
|
std::string as_string () const override
|
|
{
|
|
return path.as_string () + " = " + expr->as_string ();
|
|
}
|
|
|
|
MetaItem::ItemKind get_item_kind () const override
|
|
{
|
|
return MetaItem::ItemKind::PathExpr;
|
|
}
|
|
|
|
location_t get_locus () const override { return path.get_locus (); }
|
|
|
|
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
|
|
MetaItemPathExpr *clone_meta_item_inner_impl () const override
|
|
{
|
|
return new MetaItemPathExpr (*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?
|
|
private:
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Operator; }
|
|
};
|
|
|
|
/* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be
|
|
* overloaded. */
|
|
class BorrowExpr : public OperatorExpr
|
|
{
|
|
Mutability mutability;
|
|
bool raw_borrow;
|
|
bool double_borrow;
|
|
|
|
public:
|
|
std::string as_string () const override;
|
|
|
|
BorrowExpr (std::unique_ptr<Expr> borrow_lvalue, Mutability mutability,
|
|
bool raw_borrow, bool is_double_borrow,
|
|
std::vector<Attribute> outer_attribs, location_t locus)
|
|
: OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs),
|
|
locus),
|
|
mutability (mutability), raw_borrow (raw_borrow),
|
|
double_borrow (is_double_borrow)
|
|
{}
|
|
|
|
void accept_vis (ASTVisitor &vis) override;
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_borrowed_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_borrowed_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
bool has_borrow_expr () const { return main_or_left_expr != nullptr; }
|
|
|
|
bool get_is_mut () const { return mutability == Mutability::Mut; }
|
|
|
|
Mutability get_mutability () const { return mutability; }
|
|
|
|
bool get_is_double_borrow () const { return double_borrow; }
|
|
bool is_raw_borrow () const { return raw_borrow; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::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?
|
|
Expr &get_dereferenced_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_dereferenced_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Dereference; }
|
|
|
|
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?
|
|
Expr &get_propagating_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_propagating_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override
|
|
{
|
|
return Expr::Kind::ErrorPropagation;
|
|
}
|
|
|
|
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?
|
|
Expr &get_negated_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_negated_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Negation; }
|
|
|
|
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?
|
|
Expr &get_left_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_left_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_right_expr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return *right_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_right_expr_ptr ()
|
|
{
|
|
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); }
|
|
|
|
Expr::Kind get_expr_kind () const override
|
|
{
|
|
return Expr::Kind::ArithmeticOrLogical;
|
|
}
|
|
|
|
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?
|
|
Expr &get_left_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_left_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_right_expr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return *right_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_right_expr_ptr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return right_expr;
|
|
}
|
|
|
|
ExprType get_kind () { return expr_type; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Comparison; }
|
|
|
|
/* 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?
|
|
Expr &get_left_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_left_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_right_expr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return *right_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_right_expr_ptr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return right_expr;
|
|
}
|
|
|
|
ExprType get_kind () { return expr_type; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::LazyBoolean; }
|
|
|
|
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?
|
|
Expr &get_casted_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_casted_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
TypeNoBounds &get_type_to_cast_to ()
|
|
{
|
|
rust_assert (type_to_convert_to != nullptr);
|
|
return *type_to_convert_to;
|
|
}
|
|
|
|
std::unique_ptr<TypeNoBounds> &get_type_to_cast_to_ptr ()
|
|
{
|
|
rust_assert (type_to_convert_to != nullptr);
|
|
return type_to_convert_to;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::TypeCast; }
|
|
|
|
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?
|
|
Expr &get_left_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_left_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_right_expr_ptr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return right_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_right_expr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return *right_expr;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Assignment; }
|
|
|
|
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?
|
|
Expr &get_left_expr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return *main_or_left_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_left_expr_ptr ()
|
|
{
|
|
rust_assert (main_or_left_expr != nullptr);
|
|
return main_or_left_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_right_expr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return *right_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_right_expr_ptr ()
|
|
{
|
|
rust_assert (right_expr != nullptr);
|
|
return right_expr;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override
|
|
{
|
|
return Expr::Kind::CompoundAssignment;
|
|
}
|
|
|
|
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?
|
|
Expr &get_expr_in_parens ()
|
|
{
|
|
rust_assert (expr_in_parens != nullptr);
|
|
return *expr_in_parens;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_expr_in_parens_ptr ()
|
|
{
|
|
rust_assert (expr_in_parens != nullptr);
|
|
return expr_in_parens;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Grouped; }
|
|
|
|
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;
|
|
|
|
location_t get_locus () const { return locus; }
|
|
|
|
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;
|
|
|
|
// TODO: This should be replaced by a ConstExpr
|
|
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;
|
|
|
|
location_t get_locus () const { return locus; }
|
|
|
|
void accept_vis (ASTVisitor &vis) override;
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_elem_to_copy ()
|
|
{
|
|
rust_assert (elem_to_copy != nullptr);
|
|
return *elem_to_copy;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_elem_to_copy_ptr ()
|
|
{
|
|
rust_assert (elem_to_copy != nullptr);
|
|
return elem_to_copy;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_num_copies ()
|
|
{
|
|
rust_assert (num_copies != nullptr);
|
|
return *num_copies;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_num_copies_ptr ()
|
|
{
|
|
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;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Array; }
|
|
|
|
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?
|
|
Expr &get_array_expr ()
|
|
{
|
|
rust_assert (array_expr != nullptr);
|
|
return *array_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_array_expr_ptr ()
|
|
{
|
|
rust_assert (array_expr != nullptr);
|
|
return array_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_index_expr ()
|
|
{
|
|
rust_assert (index_expr != nullptr);
|
|
return *index_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_index_expr_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::ArrayIndex; }
|
|
|
|
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; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Tuple; }
|
|
|
|
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;
|
|
bool to_strip;
|
|
|
|
// 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),
|
|
to_strip (false)
|
|
{}
|
|
|
|
// 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),
|
|
to_strip (other.to_strip)
|
|
{
|
|
// 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;
|
|
to_strip = other.to_strip;
|
|
|
|
// 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 { to_strip = true; }
|
|
bool is_marked_for_strip () const override { return to_strip; }
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_tuple_expr ()
|
|
{
|
|
rust_assert (tuple_expr != nullptr);
|
|
return *tuple_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_tuple_expr_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::TupleIndex; }
|
|
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Struct; }
|
|
};
|
|
|
|
// 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;
|
|
|
|
location_t get_locus () const { return locus; }
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_base_struct ()
|
|
{
|
|
rust_assert (base_struct != nullptr);
|
|
return *base_struct;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_base_struct_ptr ()
|
|
{
|
|
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; }
|
|
|
|
const std::vector<AST::Attribute> &get_outer_attrs () const
|
|
{
|
|
return outer_attrs;
|
|
}
|
|
|
|
std::vector<AST::Attribute> &get_outer_attrs () { return outer_attrs; }
|
|
|
|
protected:
|
|
// pure virtual clone implementation
|
|
virtual StructExprField *clone_struct_expr_field_impl () const = 0;
|
|
|
|
StructExprField () : node_id (Analysis::Mappings::get ().get_next_node_id ())
|
|
{}
|
|
|
|
StructExprField (AST::AttrVec outer_attrs)
|
|
: outer_attrs (std::move (outer_attrs)),
|
|
node_id (Analysis::Mappings::get ().get_next_node_id ())
|
|
{}
|
|
|
|
AST::AttrVec outer_attrs;
|
|
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,
|
|
AST::AttrVec outer_attrs, location_t locus)
|
|
: StructExprField (std::move (outer_attrs)),
|
|
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,
|
|
AST::AttrVec outer_attrs)
|
|
: StructExprField (std::move (outer_attrs)), value (std::move (field_value))
|
|
{}
|
|
|
|
// Copy constructor requires clone
|
|
StructExprFieldWithVal (StructExprFieldWithVal const &other)
|
|
: StructExprField (other.get_outer_attrs ()),
|
|
value (other.value->clone_expr ())
|
|
{}
|
|
|
|
// Overload assignment operator to clone unique_ptr
|
|
StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other)
|
|
{
|
|
value = other.value->clone_expr ();
|
|
outer_attrs = other.get_outer_attrs ();
|
|
|
|
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?
|
|
Expr &get_value ()
|
|
{
|
|
rust_assert (value != nullptr);
|
|
return *value;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_value_ptr ()
|
|
{
|
|
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,
|
|
AST::AttrVec outer_attrs, location_t locus)
|
|
: StructExprFieldWithVal (std::move (field_value), std::move (outer_attrs)),
|
|
field_name (std::move (field_identifier)), locus (locus)
|
|
{}
|
|
|
|
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,
|
|
AST::AttrVec outer_attrs, location_t locus)
|
|
: StructExprFieldWithVal (std::move (field_value), std::move (outer_attrs)),
|
|
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?
|
|
Expr &get_function_expr ()
|
|
{
|
|
rust_assert (function != nullptr);
|
|
return *function;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_function_expr_ptr () { 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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Call; }
|
|
|
|
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?
|
|
Expr &get_receiver_expr ()
|
|
{
|
|
rust_assert (receiver != nullptr);
|
|
return *receiver;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_receiver_expr_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::MethodCall; }
|
|
|
|
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?
|
|
Expr &get_receiver_expr ()
|
|
{
|
|
rust_assert (receiver != nullptr);
|
|
return *receiver;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_receiver_expr_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::FieldAccess; }
|
|
|
|
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; }
|
|
|
|
Pattern &get_pattern ()
|
|
{
|
|
rust_assert (pattern != nullptr);
|
|
return *pattern;
|
|
}
|
|
|
|
std::unique_ptr<Pattern> &get_pattern_ptr ()
|
|
{
|
|
rust_assert (pattern != nullptr);
|
|
return pattern;
|
|
}
|
|
|
|
Type &get_type ()
|
|
{
|
|
rust_assert (has_type_given ());
|
|
return *type;
|
|
}
|
|
|
|
std::unique_ptr<Type> &get_type_ptr ()
|
|
{
|
|
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; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Closure; }
|
|
|
|
virtual Expr &get_definition_expr () = 0;
|
|
virtual std::unique_ptr<Expr> &get_definition_expr_ptr () = 0;
|
|
};
|
|
|
|
// 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;
|
|
}
|
|
|
|
Expr &get_definition_expr () override
|
|
{
|
|
rust_assert (closure_inner != nullptr);
|
|
return *closure_inner;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_definition_expr_ptr () override
|
|
{
|
|
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;
|
|
tl::optional<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,
|
|
tl::optional<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?
|
|
Expr &get_tail_expr ()
|
|
{
|
|
rust_assert (has_tail_expr ());
|
|
return *expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_tail_expr_ptr ()
|
|
{
|
|
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.has_value (); }
|
|
LoopLabel &get_label () { return label.value (); }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Block; }
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
class AnonConst : public ExprWithBlock
|
|
{
|
|
public:
|
|
enum class Kind
|
|
{
|
|
Explicit,
|
|
DeferredInference,
|
|
};
|
|
|
|
AnonConst (std::unique_ptr<Expr> &&expr, location_t locus = UNKNOWN_LOCATION)
|
|
: ExprWithBlock (), locus (locus), kind (Kind::Explicit),
|
|
expr (std::move (expr))
|
|
{
|
|
rust_assert (this->expr.value ());
|
|
}
|
|
|
|
AnonConst (location_t locus = UNKNOWN_LOCATION)
|
|
: ExprWithBlock (), locus (locus), kind (Kind::DeferredInference),
|
|
expr (tl::nullopt)
|
|
{}
|
|
|
|
AnonConst (const AnonConst &other)
|
|
{
|
|
node_id = other.node_id;
|
|
locus = other.locus;
|
|
kind = other.kind;
|
|
|
|
if (other.expr)
|
|
expr = other.expr.value ()->clone_expr ();
|
|
}
|
|
|
|
AnonConst operator= (const AnonConst &other)
|
|
{
|
|
node_id = other.node_id;
|
|
locus = other.locus;
|
|
kind = other.kind;
|
|
|
|
if (other.expr)
|
|
expr = other.expr.value ()->clone_expr ();
|
|
|
|
return *this;
|
|
}
|
|
|
|
std::string as_string () const override;
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::ConstExpr; }
|
|
|
|
location_t get_locus () const override { return locus; }
|
|
|
|
Expr &get_inner_expr ()
|
|
{
|
|
rust_assert (expr.has_value ());
|
|
return *expr.value ();
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_inner_expr_ptr ()
|
|
{
|
|
rust_assert (expr.has_value ());
|
|
return expr.value ();
|
|
}
|
|
|
|
NodeId get_node_id () const override { return node_id; }
|
|
|
|
/* FIXME: AnonConst are always "internal" and should not have outer attributes
|
|
* - is that true? Or should we instead call
|
|
* expr->get_outer_attrs()/expr->set_outer_attrs() */
|
|
|
|
std::vector<Attribute> &get_outer_attrs () override
|
|
{
|
|
static auto attrs = std::vector<Attribute> ();
|
|
return attrs;
|
|
}
|
|
|
|
void set_outer_attrs (std::vector<Attribute>) override {}
|
|
|
|
/* FIXME: Likewise for mark_for_strip() ? */
|
|
void mark_for_strip () override {}
|
|
bool is_marked_for_strip () const override { return false; }
|
|
|
|
void accept_vis (ASTVisitor &vis) override;
|
|
|
|
bool is_deferred () const { return kind == Kind::DeferredInference; }
|
|
|
|
private:
|
|
location_t locus;
|
|
Kind kind;
|
|
tl::optional<std::unique_ptr<Expr>> expr;
|
|
|
|
AnonConst *clone_expr_with_block_impl () const override
|
|
{
|
|
return new AnonConst (*this);
|
|
}
|
|
};
|
|
|
|
class ConstBlock : public ExprWithBlock
|
|
{
|
|
public:
|
|
ConstBlock (AnonConst &&expr, location_t locus = UNKNOWN_LOCATION,
|
|
std::vector<Attribute> &&outer_attrs = {})
|
|
: ExprWithBlock (), expr (std::move (expr)),
|
|
outer_attrs (std::move (outer_attrs)), locus (locus)
|
|
{}
|
|
|
|
ConstBlock (const ConstBlock &other)
|
|
: ExprWithBlock (other), expr (other.expr), outer_attrs (other.outer_attrs),
|
|
locus (other.locus)
|
|
{}
|
|
|
|
ConstBlock operator= (const ConstBlock &other)
|
|
{
|
|
expr = other.expr;
|
|
node_id = other.node_id;
|
|
outer_attrs = other.outer_attrs;
|
|
locus = other.locus;
|
|
|
|
return *this;
|
|
}
|
|
|
|
std::string as_string () const override;
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::ConstBlock; }
|
|
|
|
AnonConst &get_const_expr () { return expr; }
|
|
|
|
void accept_vis (ASTVisitor &vis) override;
|
|
|
|
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);
|
|
}
|
|
|
|
location_t get_locus () const override { return locus; }
|
|
|
|
bool is_marked_for_strip () const override { return marked_for_strip; }
|
|
void mark_for_strip () override { marked_for_strip = true; }
|
|
|
|
private:
|
|
AnonConst expr;
|
|
|
|
std::vector<Attribute> outer_attrs;
|
|
location_t locus;
|
|
bool marked_for_strip = false;
|
|
|
|
ConstBlock *clone_expr_with_block_impl () const override
|
|
{
|
|
return new ConstBlock (*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<Expr> 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_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_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?
|
|
Expr &get_definition_expr () override
|
|
{
|
|
rust_assert (expr != nullptr);
|
|
return *expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_definition_expr_ptr () override
|
|
{
|
|
rust_assert (expr != nullptr);
|
|
|
|
return expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Type &get_return_type ()
|
|
{
|
|
rust_assert (return_type != nullptr);
|
|
return *return_type;
|
|
}
|
|
|
|
std::unique_ptr<Type> &get_return_type_ptr ()
|
|
{
|
|
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;
|
|
tl::optional<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.has_value (); }
|
|
|
|
// Constructor for a ContinueExpr with a label.
|
|
ContinueExpr (tl::optional<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_unchecked () { return label.value (); }
|
|
const Lifetime &get_label_unchecked () const { return label.value (); }
|
|
|
|
tl::optional<Lifetime> &get_label () { return label; }
|
|
const tl::optional<Lifetime> &get_label () const { return label; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Continue; }
|
|
|
|
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;
|
|
tl::optional<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.has_value (); }
|
|
|
|
/* 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 (tl::optional<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?
|
|
Expr &get_break_expr ()
|
|
{
|
|
rust_assert (has_break_expr ());
|
|
return *break_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_break_expr_ptr ()
|
|
{
|
|
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_unchecked () { return label.value (); }
|
|
const LoopLabel &get_label_unchecked () const { return label.value (); }
|
|
|
|
tl::optional<LoopLabel> &get_label () { return label; }
|
|
const tl::optional<LoopLabel> &get_label () const { return label; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Break; }
|
|
|
|
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;
|
|
|
|
// Some visitors still check for attributes on RangeExprs, and they will need
|
|
// to be supported in the future - so keep that for now
|
|
std::vector<Attribute> empty_attributes = {};
|
|
|
|
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
|
|
{
|
|
return empty_attributes;
|
|
}
|
|
|
|
// should never be called - error if called
|
|
void set_outer_attrs (std::vector<Attribute> /* new_attrs */) override {}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Range; }
|
|
};
|
|
|
|
// 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?
|
|
Expr &get_from_expr ()
|
|
{
|
|
rust_assert (from != nullptr);
|
|
return *from;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_to_expr ()
|
|
{
|
|
rust_assert (to != nullptr);
|
|
return *to;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_from_expr_ptr ()
|
|
{
|
|
rust_assert (from != nullptr);
|
|
return from;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_to_expr_ptr ()
|
|
{
|
|
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?
|
|
Expr &get_from_expr ()
|
|
{
|
|
rust_assert (from != nullptr);
|
|
return *from;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_from_expr_ptr ()
|
|
{
|
|
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?
|
|
Expr &get_to_expr ()
|
|
{
|
|
rust_assert (to != nullptr);
|
|
return *to;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_to_expr_ptr ()
|
|
{
|
|
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?
|
|
Expr &get_from_expr ()
|
|
{
|
|
rust_assert (from != nullptr);
|
|
return *from;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Expr &get_to_expr ()
|
|
{
|
|
rust_assert (to != nullptr);
|
|
return *to;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_from_expr_ptr ()
|
|
{
|
|
rust_assert (from != nullptr);
|
|
return from;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_to_expr_ptr ()
|
|
{
|
|
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?
|
|
Expr &get_to_expr ()
|
|
{
|
|
rust_assert (to != nullptr);
|
|
return *to;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_to_expr_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
};
|
|
|
|
class BoxExpr : public ExprWithoutBlock
|
|
{
|
|
std::unique_ptr<Expr> expr;
|
|
std::vector<Attribute> outer_attrs;
|
|
location_t locus;
|
|
|
|
public:
|
|
BoxExpr (std::unique_ptr<Expr> expr, std::vector<Attribute> outer_attrs,
|
|
location_t locus)
|
|
: expr (std::move (expr)), outer_attrs (outer_attrs), locus (locus)
|
|
{}
|
|
|
|
// Copy constructor with clone
|
|
BoxExpr (BoxExpr const &other)
|
|
: ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
|
|
locus (other.locus)
|
|
{
|
|
// guard to protect from null pointer dereference
|
|
if (other.expr != nullptr)
|
|
expr = other.expr->clone_expr ();
|
|
}
|
|
|
|
BoxExpr &operator= (BoxExpr const &other)
|
|
{
|
|
ExprWithoutBlock::operator= (other);
|
|
locus = other.locus;
|
|
outer_attrs = other.outer_attrs;
|
|
|
|
// guard to protect from null pointer dereference
|
|
if (other.expr != nullptr)
|
|
expr = other.expr->clone_expr ();
|
|
else
|
|
expr = nullptr;
|
|
|
|
return *this;
|
|
}
|
|
|
|
// move constructors
|
|
BoxExpr (BoxExpr &&other) = default;
|
|
BoxExpr &operator= (BoxExpr &&other) = default;
|
|
|
|
location_t get_locus () const override final { return locus; }
|
|
|
|
void accept_vis (ASTVisitor &vis) override;
|
|
|
|
void mark_for_strip () override { expr = nullptr; }
|
|
bool is_marked_for_strip () const override { return 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);
|
|
}
|
|
|
|
std::string as_string () const override;
|
|
|
|
Expr &get_boxed_expr ()
|
|
{
|
|
rust_assert (expr != nullptr);
|
|
return *expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_boxed_expr_ptr ()
|
|
{
|
|
rust_assert (expr != nullptr);
|
|
return expr;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Box; }
|
|
|
|
protected:
|
|
/* Use covariance to implement clone function as returning this object rather
|
|
* than base */
|
|
BoxExpr *clone_expr_without_block_impl () const override
|
|
{
|
|
return new BoxExpr (*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?
|
|
Expr &get_returned_expr ()
|
|
{
|
|
rust_assert (return_expr != nullptr);
|
|
return *return_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_returned_expr_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Return; }
|
|
|
|
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);
|
|
}
|
|
};
|
|
|
|
// Try expression AST node representation
|
|
class TryExpr : public ExprWithBlock
|
|
{
|
|
std::vector<Attribute> outer_attrs;
|
|
std::unique_ptr<BlockExpr> block_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;
|
|
|
|
// Constructor for ReturnExpr.
|
|
TryExpr (std::unique_ptr<BlockExpr> block_expr,
|
|
std::vector<Attribute> outer_attribs, location_t locus)
|
|
: outer_attrs (std::move (outer_attribs)),
|
|
block_expr (std::move (block_expr)), locus (locus)
|
|
{
|
|
rust_assert (this->block_expr);
|
|
}
|
|
|
|
// Copy constructor with clone
|
|
TryExpr (TryExpr const &other)
|
|
: ExprWithBlock (other), outer_attrs (other.outer_attrs),
|
|
block_expr (other.block_expr->clone_block_expr ()), locus (other.locus),
|
|
marked_for_strip (other.marked_for_strip)
|
|
{}
|
|
|
|
// Overloaded assignment operator to clone return_expr pointer
|
|
TryExpr &operator= (TryExpr const &other)
|
|
{
|
|
ExprWithBlock::operator= (other);
|
|
locus = other.locus;
|
|
marked_for_strip = other.marked_for_strip;
|
|
outer_attrs = other.outer_attrs;
|
|
|
|
block_expr = other.block_expr->clone_block_expr ();
|
|
|
|
return *this;
|
|
}
|
|
|
|
// move constructors
|
|
TryExpr (TryExpr &&other) = default;
|
|
TryExpr &operator= (TryExpr &&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?
|
|
BlockExpr &get_block_expr () { return *block_expr; }
|
|
std::unique_ptr<BlockExpr> &get_block_expr_ptr () { 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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Try; }
|
|
|
|
protected:
|
|
/* Use covariance to implement clone function as returning this object rather
|
|
* than base */
|
|
TryExpr *clone_expr_with_block_impl () const override
|
|
{
|
|
return new TryExpr (*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?
|
|
BlockExpr &get_block_expr ()
|
|
{
|
|
rust_assert (expr != nullptr);
|
|
return *expr;
|
|
}
|
|
|
|
std::unique_ptr<BlockExpr> &get_block_expr_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::UnsafeBlock; }
|
|
|
|
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;
|
|
tl::optional<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,
|
|
tl::optional<LoopLabel> loop_label = tl::nullopt,
|
|
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.has_value (); }
|
|
|
|
LoopLabel &get_loop_label () { return loop_label.value (); }
|
|
const LoopLabel &get_loop_label () const { return loop_label.value (); }
|
|
|
|
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?
|
|
BlockExpr &get_loop_block ()
|
|
{
|
|
rust_assert (loop_block != nullptr);
|
|
return *loop_block;
|
|
}
|
|
|
|
std::unique_ptr<BlockExpr> &get_loop_block_ptr ()
|
|
{
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Loop; }
|
|
|
|
enum class Kind
|
|
{
|
|
Loop,
|
|
While,
|
|
WhileLet,
|
|
For
|
|
};
|
|
|
|
virtual Kind get_loop_kind () const = 0;
|
|
};
|
|
|
|
// '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,
|
|
tl::optional<LoopLabel> loop_label = tl::nullopt,
|
|
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;
|
|
|
|
BaseLoopExpr::Kind get_loop_kind () const override
|
|
{
|
|
return BaseLoopExpr::Kind::Loop;
|
|
}
|
|
|
|
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,
|
|
tl::optional<LoopLabel> loop_label = tl::nullopt,
|
|
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?
|
|
Expr &get_predicate_expr ()
|
|
{
|
|
rust_assert (condition != nullptr);
|
|
return *condition;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_predicate_expr_ptr ()
|
|
{
|
|
rust_assert (condition != nullptr);
|
|
return condition;
|
|
}
|
|
|
|
BaseLoopExpr::Kind get_loop_kind () const override
|
|
{
|
|
return BaseLoopExpr::Kind::While;
|
|
}
|
|
|
|
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,
|
|
tl::optional<LoopLabel> loop_label = tl::nullopt,
|
|
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?
|
|
Expr &get_scrutinee_expr ()
|
|
{
|
|
rust_assert (scrutinee != nullptr);
|
|
return *scrutinee;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
|
|
{
|
|
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;
|
|
}
|
|
|
|
BaseLoopExpr::Kind get_loop_kind () const override
|
|
{
|
|
return BaseLoopExpr::Kind::WhileLet;
|
|
}
|
|
|
|
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,
|
|
tl::optional<LoopLabel> loop_label = tl::nullopt,
|
|
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?
|
|
Expr &get_iterator_expr ()
|
|
{
|
|
rust_assert (iterator_expr != nullptr);
|
|
return *iterator_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_iterator_expr_ptr ()
|
|
{
|
|
rust_assert (iterator_expr != nullptr);
|
|
return iterator_expr;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
Pattern &get_pattern ()
|
|
{
|
|
rust_assert (pattern != nullptr);
|
|
return *pattern;
|
|
}
|
|
|
|
std::unique_ptr<Pattern> &get_pattern_ptr ()
|
|
{
|
|
rust_assert (pattern != nullptr);
|
|
return pattern;
|
|
}
|
|
|
|
BaseLoopExpr::Kind get_loop_kind () const override
|
|
{
|
|
return BaseLoopExpr::Kind::For;
|
|
}
|
|
|
|
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?
|
|
Expr &get_condition_expr ()
|
|
{
|
|
rust_assert (condition != nullptr);
|
|
return *condition;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_condition_expr_ptr ()
|
|
{
|
|
rust_assert (condition != nullptr);
|
|
return condition;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
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; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::If; }
|
|
|
|
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?
|
|
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?
|
|
Expr &get_value_expr ()
|
|
{
|
|
rust_assert (value != nullptr);
|
|
return *value;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_value_expr_ptr ()
|
|
{
|
|
rust_assert (value != nullptr);
|
|
return value;
|
|
}
|
|
|
|
// TODO: is this better? Or is a "vis_block" better?
|
|
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; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::IfLet; }
|
|
|
|
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?
|
|
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?
|
|
Expr &get_guard_expr ()
|
|
{
|
|
rust_assert (has_match_arm_guard ());
|
|
return *guard_expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_guard_expr_ptr ()
|
|
{
|
|
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?
|
|
Expr &get_expr ()
|
|
{
|
|
rust_assert (expr != nullptr);
|
|
return *expr;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_expr_ptr ()
|
|
{
|
|
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?
|
|
Expr &get_scrutinee_expr ()
|
|
{
|
|
rust_assert (branch_value != nullptr);
|
|
return *branch_value;
|
|
}
|
|
|
|
std::unique_ptr<Expr> &get_scrutinee_expr_ptr ()
|
|
{
|
|
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; }
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Match; }
|
|
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::Await; }
|
|
|
|
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);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::AsyncBlock; }
|
|
|
|
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 InlineAsmOption
|
|
{
|
|
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 InlineAsmRegOrRegClass
|
|
{
|
|
enum Type
|
|
{
|
|
Reg,
|
|
RegClass,
|
|
};
|
|
|
|
struct Reg
|
|
{
|
|
std::string Symbol;
|
|
};
|
|
|
|
struct RegClass
|
|
{
|
|
std::string Symbol;
|
|
};
|
|
|
|
Type type;
|
|
struct Reg reg;
|
|
struct RegClass reg_class;
|
|
|
|
Identifier name;
|
|
location_t locus;
|
|
};
|
|
|
|
struct LlvmOperand
|
|
{
|
|
std::string constraint;
|
|
std::unique_ptr<Expr> expr;
|
|
|
|
LlvmOperand (std::string constraint, std::unique_ptr<Expr> &&expr)
|
|
: constraint (constraint), expr (std::move (expr))
|
|
{}
|
|
|
|
LlvmOperand (const LlvmOperand &other)
|
|
: constraint (other.constraint), expr (other.expr->clone_expr ())
|
|
{}
|
|
LlvmOperand &operator= (const LlvmOperand &other)
|
|
{
|
|
constraint = other.constraint;
|
|
expr = other.expr->clone_expr ();
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
class InlineAsmOperand
|
|
{
|
|
public:
|
|
enum class RegisterType
|
|
{
|
|
In,
|
|
Out,
|
|
InOut,
|
|
SplitInOut,
|
|
Const,
|
|
Sym,
|
|
Label,
|
|
};
|
|
|
|
class Register
|
|
{
|
|
public:
|
|
Register () {}
|
|
virtual ~Register () = default;
|
|
|
|
std::unique_ptr<Register> clone () const
|
|
{
|
|
return std::unique_ptr<Register> (clone_impl ());
|
|
}
|
|
|
|
protected:
|
|
virtual Register *clone_impl () const = 0;
|
|
};
|
|
|
|
class In : public Register
|
|
{
|
|
public:
|
|
tl::optional<InlineAsmRegOrRegClass> reg;
|
|
std::unique_ptr<Expr> expr;
|
|
|
|
In (tl::optional<struct InlineAsmRegOrRegClass> ®,
|
|
std::unique_ptr<Expr> expr)
|
|
: reg (reg), expr (std::move (expr))
|
|
{
|
|
rust_assert (this->expr != nullptr);
|
|
}
|
|
|
|
In (const In &other)
|
|
{
|
|
reg = other.reg;
|
|
|
|
expr = other.expr->clone_expr ();
|
|
}
|
|
|
|
In operator= (const In &other)
|
|
{
|
|
reg = other.reg;
|
|
expr = other.expr->clone_expr ();
|
|
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
In *clone_impl () const { return new In (*this); }
|
|
};
|
|
|
|
class Out : public Register
|
|
{
|
|
public:
|
|
tl::optional<InlineAsmRegOrRegClass> reg;
|
|
bool late;
|
|
std::unique_ptr<Expr> expr; // can be null
|
|
|
|
Out (tl::optional<struct InlineAsmRegOrRegClass> ®, bool late,
|
|
std::unique_ptr<Expr> expr)
|
|
: reg (reg), late (late), expr (std::move (expr))
|
|
{
|
|
rust_assert (this->expr != nullptr);
|
|
}
|
|
|
|
Out (const Out &other)
|
|
{
|
|
reg = other.reg;
|
|
late = other.late;
|
|
expr = other.expr->clone_expr ();
|
|
}
|
|
|
|
Out operator= (const Out &other)
|
|
{
|
|
reg = other.reg;
|
|
late = other.late;
|
|
expr = other.expr->clone_expr ();
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
Out *clone_impl () const { return new Out (*this); }
|
|
};
|
|
|
|
class InOut : public Register
|
|
{
|
|
public:
|
|
tl::optional<InlineAsmRegOrRegClass> reg;
|
|
bool late;
|
|
std::unique_ptr<Expr> expr; // this can't be null
|
|
|
|
InOut (tl::optional<struct InlineAsmRegOrRegClass> ®, bool late,
|
|
std::unique_ptr<Expr> expr)
|
|
: reg (reg), late (late), expr (std::move (expr))
|
|
{
|
|
rust_assert (this->expr != nullptr);
|
|
}
|
|
|
|
InOut (const InOut &other)
|
|
{
|
|
reg = other.reg;
|
|
late = other.late;
|
|
expr = other.expr->clone_expr ();
|
|
}
|
|
|
|
InOut operator= (const InOut &other)
|
|
{
|
|
reg = other.reg;
|
|
late = other.late;
|
|
expr = other.expr->clone_expr ();
|
|
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
InOut *clone_impl () const { return new InOut (*this); }
|
|
};
|
|
|
|
class SplitInOut : public Register
|
|
{
|
|
public:
|
|
tl::optional<InlineAsmRegOrRegClass> reg;
|
|
bool late;
|
|
std::unique_ptr<Expr> in_expr;
|
|
std::unique_ptr<Expr> out_expr; // could be null
|
|
|
|
SplitInOut (tl::optional<struct InlineAsmRegOrRegClass> ®, bool late,
|
|
std::unique_ptr<Expr> in_expr, std::unique_ptr<Expr> out_expr)
|
|
: reg (reg), late (late), in_expr (std::move (in_expr)),
|
|
out_expr (std::move (out_expr))
|
|
{
|
|
rust_assert (this->in_expr != nullptr);
|
|
rust_assert (this->out_expr != nullptr);
|
|
}
|
|
|
|
SplitInOut (const SplitInOut &other)
|
|
{
|
|
reg = other.reg;
|
|
late = other.late;
|
|
in_expr = other.in_expr->clone_expr ();
|
|
out_expr = other.out_expr->clone_expr ();
|
|
}
|
|
|
|
SplitInOut operator= (const SplitInOut &other)
|
|
{
|
|
reg = other.reg;
|
|
late = other.late;
|
|
in_expr = other.in_expr->clone_expr ();
|
|
out_expr = other.out_expr->clone_expr ();
|
|
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
SplitInOut *clone_impl () const { return new SplitInOut (*this); }
|
|
};
|
|
|
|
class Const : public Register
|
|
{
|
|
public:
|
|
AnonConst anon_const;
|
|
|
|
private:
|
|
Const *clone_impl () const { return new Const (*this); }
|
|
};
|
|
|
|
class Sym : public Register
|
|
{
|
|
public:
|
|
std::unique_ptr<Expr> expr;
|
|
|
|
Sym (std::unique_ptr<Expr> expr) : expr (std::move (expr))
|
|
{
|
|
rust_assert (this->expr != nullptr);
|
|
}
|
|
Sym (const Sym &other)
|
|
{
|
|
expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
|
|
}
|
|
|
|
Sym operator= (const Sym &other)
|
|
{
|
|
expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
Sym *clone_impl () const { return new Sym (*this); }
|
|
};
|
|
|
|
class Label : public Register
|
|
{
|
|
public:
|
|
std::string label_name;
|
|
std::unique_ptr<Expr> expr;
|
|
|
|
Label (tl::optional<std::string> label_name, std::unique_ptr<Expr> expr)
|
|
: expr (std::move (expr))
|
|
{
|
|
rust_assert (this->expr != nullptr);
|
|
if (label_name.has_value ())
|
|
this->label_name = label_name.value ();
|
|
}
|
|
Label (const Label &other)
|
|
{
|
|
expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
|
|
}
|
|
|
|
Label operator= (const Label &other)
|
|
{
|
|
expr = std::unique_ptr<Expr> (other.expr->clone_expr ());
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
Label *clone_impl () const { return new Label (*this); }
|
|
};
|
|
|
|
InlineAsmOperand (const InlineAsmOperand &other)
|
|
: register_type (other.register_type), locus (other.locus),
|
|
reg (other.reg->clone ())
|
|
{}
|
|
|
|
InlineAsmOperand (const In ®, location_t locus)
|
|
: register_type (RegisterType::In), locus (locus), reg (new In (reg))
|
|
{}
|
|
InlineAsmOperand (const Out ®, location_t locus)
|
|
: register_type (RegisterType::Out), locus (locus), reg (new Out (reg))
|
|
{}
|
|
InlineAsmOperand (const InOut ®, location_t locus)
|
|
: register_type (RegisterType::InOut), locus (locus), reg (new InOut (reg))
|
|
{}
|
|
InlineAsmOperand (const SplitInOut ®, location_t locus)
|
|
: register_type (RegisterType::SplitInOut), locus (locus),
|
|
reg (new SplitInOut (reg))
|
|
{}
|
|
InlineAsmOperand (const Const ®, location_t locus)
|
|
: register_type (RegisterType::Const), locus (locus), reg (new Const (reg))
|
|
{}
|
|
InlineAsmOperand (const Sym ®, location_t locus)
|
|
: register_type (RegisterType::Sym), locus (locus), reg (new Sym (reg))
|
|
{}
|
|
InlineAsmOperand (const Label ®, location_t locus)
|
|
: register_type (RegisterType::Label), locus (locus), reg (new Label (reg))
|
|
{}
|
|
|
|
location_t get_locus () const { return locus; }
|
|
RegisterType get_register_type () const { return register_type; }
|
|
|
|
// Potentially fail immediately if you don't use get_register_type() to
|
|
// inspect the RegisterType first before calling the following functions Check
|
|
// first
|
|
In &get_in ()
|
|
{
|
|
rust_assert (register_type == RegisterType::In);
|
|
return static_cast<In &> (*reg);
|
|
}
|
|
const In &get_in () const
|
|
{
|
|
rust_assert (register_type == RegisterType::In);
|
|
return static_cast<const In &> (*reg);
|
|
}
|
|
|
|
Out &get_out ()
|
|
{
|
|
rust_assert (register_type == RegisterType::Out);
|
|
return static_cast<Out &> (*reg);
|
|
}
|
|
const Out &get_out () const
|
|
{
|
|
rust_assert (register_type == RegisterType::Out);
|
|
return static_cast<const Out &> (*reg);
|
|
}
|
|
|
|
InOut &get_in_out ()
|
|
{
|
|
rust_assert (register_type == RegisterType::InOut);
|
|
return static_cast<InOut &> (*reg);
|
|
}
|
|
const InOut &get_in_out () const
|
|
{
|
|
rust_assert (register_type == RegisterType::InOut);
|
|
return static_cast<const InOut &> (*reg);
|
|
}
|
|
|
|
SplitInOut &get_split_in_out ()
|
|
{
|
|
rust_assert (register_type == RegisterType::SplitInOut);
|
|
return static_cast<SplitInOut &> (*reg);
|
|
}
|
|
const SplitInOut &get_split_in_out () const
|
|
{
|
|
rust_assert (register_type == RegisterType::SplitInOut);
|
|
return static_cast<const SplitInOut &> (*reg);
|
|
}
|
|
|
|
Const &get_const ()
|
|
{
|
|
rust_assert (register_type == RegisterType::Const);
|
|
return static_cast<Const &> (*reg);
|
|
}
|
|
const Const &get_const () const
|
|
{
|
|
rust_assert (register_type == RegisterType::Const);
|
|
return static_cast<Const &> (*reg);
|
|
}
|
|
|
|
Sym &get_sym ()
|
|
{
|
|
rust_assert (register_type == RegisterType::Sym);
|
|
return static_cast<Sym &> (*reg);
|
|
}
|
|
const Sym &get_sym () const
|
|
{
|
|
rust_assert (register_type == RegisterType::Sym);
|
|
return static_cast<const Sym &> (*reg);
|
|
}
|
|
|
|
Label &get_label ()
|
|
{
|
|
rust_assert (register_type == RegisterType::Label);
|
|
return static_cast<Label &> (*reg);
|
|
}
|
|
const Label &get_label () const
|
|
{
|
|
rust_assert (register_type == RegisterType::Label);
|
|
return static_cast<const Label &> (*reg);
|
|
}
|
|
|
|
private:
|
|
RegisterType register_type;
|
|
|
|
location_t locus;
|
|
std::unique_ptr<Register> reg;
|
|
};
|
|
|
|
struct InlineAsmPlaceHolder
|
|
{
|
|
size_t operand_idx;
|
|
char modifier; // can be null
|
|
location_t locus;
|
|
};
|
|
|
|
struct InlineAsmTemplatePiece
|
|
{
|
|
bool is_placeholder;
|
|
std::string string;
|
|
InlineAsmPlaceHolder placeholder;
|
|
};
|
|
|
|
struct TupleClobber
|
|
{
|
|
TupleClobber (std::string symbol, location_t loc) : symbol (symbol), loc (loc)
|
|
{}
|
|
// 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
|
|
location_t loc;
|
|
std::string symbol;
|
|
|
|
location_t get_locus () { return loc; }
|
|
TupleTemplateStr (location_t loc, const std::string &symbol)
|
|
: loc (loc), symbol (symbol)
|
|
{}
|
|
};
|
|
|
|
// Inline Assembly Node
|
|
class InlineAsm : public ExprWithoutBlock
|
|
{
|
|
public:
|
|
enum class Option
|
|
{
|
|
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,
|
|
};
|
|
|
|
private:
|
|
location_t locus;
|
|
// TODO: Not sure how outer_attrs plays with InlineAsm, I put it here in order
|
|
// to override, very hacky.
|
|
std::vector<Attribute> outer_attrs;
|
|
|
|
public:
|
|
// https://github.com/rust-lang/rust/blob/55cac26a9ef17da1c9c77c0816e88e178b7cc5dd/compiler/rustc_builtin_macros/src/asm.rs#L56C1-L64C7
|
|
// let mut args = AsmArgs {
|
|
// templates: vec![first_template],
|
|
// operands: vec![],
|
|
// named_args: Default::default(),
|
|
// reg_args: Default::default(),
|
|
// clobber_abis: Vec::new(),
|
|
// options: ast::InlineAsmOptions::empty(),
|
|
// options_spans: vec![],
|
|
// };
|
|
std::vector<InlineAsmTemplatePiece> template_;
|
|
std::vector<TupleTemplateStr> template_strs;
|
|
std::vector<InlineAsmOperand> operands;
|
|
std::map<std::string, int> named_args;
|
|
std::set<int> reg_args;
|
|
std::vector<TupleClobber> clobber_abi;
|
|
std::set<InlineAsm::Option> options;
|
|
|
|
std::vector<location_t> line_spans;
|
|
|
|
bool is_global_asm;
|
|
|
|
InlineAsm (location_t locus, bool is_global_asm)
|
|
: locus (locus), is_global_asm (is_global_asm)
|
|
{}
|
|
|
|
void accept_vis (ASTVisitor &vis) override;
|
|
std::string as_string () const override { return "InlineAsm AST Node"; }
|
|
|
|
location_t get_locus () const override { return locus; }
|
|
|
|
void mark_for_strip () override {}
|
|
|
|
bool is_marked_for_strip () const override { return false; }
|
|
|
|
std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
|
|
|
|
void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
|
|
|
|
std::vector<InlineAsmTemplatePiece> get_template_ () { return template_; }
|
|
|
|
std::vector<TupleTemplateStr> get_template_strs () { return template_strs; }
|
|
|
|
std::vector<InlineAsmOperand> get_operands () { return operands; }
|
|
|
|
std::vector<TupleClobber> get_clobber_abi () { return clobber_abi; }
|
|
|
|
std::set<InlineAsm::Option> get_options () { return options; }
|
|
|
|
InlineAsm *clone_expr_without_block_impl () const override
|
|
{
|
|
return new InlineAsm (*this);
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override { return Expr::Kind::InlineAsm; }
|
|
|
|
static std::string option_to_string (Option option)
|
|
{
|
|
switch (option)
|
|
{
|
|
case Option::PURE:
|
|
return "pure";
|
|
case Option::NOMEM:
|
|
return "nomem";
|
|
case Option::READONLY:
|
|
return "readonly";
|
|
case Option::PRESERVES_FLAGS:
|
|
return "preserves_flags";
|
|
case Option::NORETURN:
|
|
return "noreturn";
|
|
case Option::NOSTACK:
|
|
return "nostack";
|
|
case Option::ATT_SYNTAX:
|
|
return "att_syntax";
|
|
case Option::RAW:
|
|
return "raw";
|
|
case Option::MAY_UNWIND:
|
|
return "may_unwind";
|
|
default:
|
|
rust_unreachable ();
|
|
}
|
|
}
|
|
};
|
|
|
|
class LlvmInlineAsm : public ExprWithoutBlock
|
|
{
|
|
// llvm_asm!("" : : "r"(&mut dummy) : "memory" : "volatile");
|
|
// Asm, Outputs, Inputs, Clobbers, Options,
|
|
|
|
public:
|
|
enum class Dialect
|
|
{
|
|
Att,
|
|
Intel,
|
|
};
|
|
|
|
private:
|
|
location_t locus;
|
|
std::vector<Attribute> outer_attrs;
|
|
std::vector<LlvmOperand> inputs;
|
|
std::vector<LlvmOperand> outputs;
|
|
std::vector<TupleTemplateStr> templates;
|
|
std::vector<TupleClobber> clobbers;
|
|
bool volatility;
|
|
bool align_stack;
|
|
Dialect dialect;
|
|
|
|
public:
|
|
LlvmInlineAsm (location_t locus) : locus (locus) {}
|
|
|
|
Dialect get_dialect () { return dialect; }
|
|
|
|
location_t get_locus () const override { return locus; }
|
|
|
|
void mark_for_strip () override {}
|
|
|
|
bool is_marked_for_strip () const override { return false; }
|
|
|
|
std::vector<Attribute> &get_outer_attrs () override { return outer_attrs; }
|
|
|
|
void accept_vis (ASTVisitor &vis) override;
|
|
|
|
std::string as_string () const override { return "InlineAsm AST Node"; }
|
|
|
|
void set_outer_attrs (std::vector<Attribute> v) override { outer_attrs = v; }
|
|
|
|
LlvmInlineAsm *clone_expr_without_block_impl () const override
|
|
{
|
|
return new LlvmInlineAsm (*this);
|
|
}
|
|
|
|
std::vector<TupleTemplateStr> &get_templates () { return templates; }
|
|
const std::vector<TupleTemplateStr> &get_templates () const
|
|
{
|
|
return templates;
|
|
}
|
|
|
|
Expr::Kind get_expr_kind () const override
|
|
{
|
|
return Expr::Kind::LlvmInlineAsm;
|
|
}
|
|
|
|
void set_align_stack (bool align_stack) { this->align_stack = align_stack; }
|
|
bool is_stack_aligned () { return align_stack; }
|
|
|
|
void set_volatile (bool volatility) { this->volatility = volatility; }
|
|
bool is_volatile () { return volatility; }
|
|
|
|
void set_dialect (Dialect dialect) { this->dialect = dialect; }
|
|
|
|
void set_inputs (std::vector<LlvmOperand> operands) { inputs = operands; }
|
|
void set_outputs (std::vector<LlvmOperand> operands) { outputs = operands; }
|
|
|
|
std::vector<LlvmOperand> &get_inputs () { return inputs; }
|
|
const std::vector<LlvmOperand> &get_inputs () const { return inputs; }
|
|
std::vector<LlvmOperand> &get_outputs () { return outputs; }
|
|
const std::vector<LlvmOperand> &get_outputs () const { return outputs; }
|
|
|
|
std::vector<TupleClobber> &get_clobbers () { return clobbers; }
|
|
const std::vector<TupleClobber> &get_clobbers () const { return clobbers; }
|
|
};
|
|
|
|
} // namespace AST
|
|
} // namespace Rust
|
|
|
|
#endif
|