aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorSimplyTheOther <simplytheother@gmail.com>2020-11-12 22:22:41 +0800
committerSimplyTheOther <simplytheother@gmail.com>2020-12-08 21:10:28 +0800
commitf5ae2781823b3a2bcded1ac3446de83cc52403a9 (patch)
treee90b9876a23cd40e0da140aa62fb220ef22dfd58 /gcc
parent37bbf2b8cbd4578f0fbe7cbbd573493481f62a45 (diff)
downloadgcc-f5ae2781823b3a2bcded1ac3446de83cc52403a9.zip
gcc-f5ae2781823b3a2bcded1ac3446de83cc52403a9.tar.gz
gcc-f5ae2781823b3a2bcded1ac3446de83cc52403a9.tar.bz2
Added more expression stripping
Fixed compile errors and started on enum expr stripping
Diffstat (limited to 'gcc')
-rw-r--r--gcc/rust/analysis/rust-type-resolution.cc14
-rw-r--r--gcc/rust/analysis/rust-type-resolution.h4
-rw-r--r--gcc/rust/ast/rust-expr.h84
-rw-r--r--gcc/rust/ast/rust-stmt.h4
-rw-r--r--gcc/rust/backend/rust-compile.cc18
-rw-r--r--gcc/rust/expand/rust-macro-expand.cc200
6 files changed, 264 insertions, 60 deletions
diff --git a/gcc/rust/analysis/rust-type-resolution.cc b/gcc/rust/analysis/rust-type-resolution.cc
index 87cf828..fcf475e 100644
--- a/gcc/rust/analysis/rust-type-resolution.cc
+++ b/gcc/rust/analysis/rust-type-resolution.cc
@@ -427,15 +427,15 @@ TypeResolution::visit (AST::StructExprFieldIdentifier &field)
void
TypeResolution::visit (AST::StructExprFieldIdentifierValue &field)
{
- identifierBuffer = &field.field_name;
- field.value->accept_vis (*this);
+ identifierBuffer = std::unique_ptr<std::string> (new std::string (field.get_field_name ()));
+ field.get_value ()->accept_vis (*this);
}
void
TypeResolution::visit (AST::StructExprFieldIndexValue &field)
{
- tupleIndexBuffer = &field.index;
- field.value->accept_vis (*this);
+ tupleIndexBuffer = std::unique_ptr<int> (new int (field.get_index ()));
+ field.get_value ()->accept_vis (*this);
}
void
@@ -448,7 +448,7 @@ TypeResolution::visit (AST::StructExprStructFields &expr)
return;
}
- for (auto &field : expr.fields)
+ for (auto &field : expr.get_fields ())
{
identifierBuffer = NULL;
tupleIndexBuffer = NULL;
@@ -1069,14 +1069,14 @@ TypeResolution::visit (AST::LetStmt &stmt)
void
TypeResolution::visit (AST::ExprStmtWithoutBlock &stmt)
{
- stmt.expr->accept_vis (*this);
+ stmt.get_expr ()->accept_vis (*this);
}
void
TypeResolution::visit (AST::ExprStmtWithBlock &stmt)
{
scope.Push ();
- stmt.expr->accept_vis (*this);
+ stmt.get_expr ()->accept_vis (*this);
auto localMap = scope.PeekLocals ();
for (auto &[_, value] : localMap)
{
diff --git a/gcc/rust/analysis/rust-type-resolution.h b/gcc/rust/analysis/rust-type-resolution.h
index af4594e..0c6413b 100644
--- a/gcc/rust/analysis/rust-type-resolution.h
+++ b/gcc/rust/analysis/rust-type-resolution.h
@@ -301,8 +301,8 @@ private:
bool isTypeInScope (AST::Type *type, Location locus);
TypeScoping scope;
- std::string *identifierBuffer;
- int *tupleIndexBuffer;
+ std::unique_ptr<std::string> identifierBuffer;
+ std::unique_ptr<int> tupleIndexBuffer;
};
} // namespace Analysis
diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h
index a92a8ad..1cf8f9c 100644
--- a/gcc/rust/ast/rust-expr.h
+++ b/gcc/rust/ast/rust-expr.h
@@ -1218,7 +1218,8 @@ class TupleExpr : public ExprWithoutBlock
public:
std::string as_string () const override;
- std::vector<Attribute> get_inner_attrs () const { return inner_attrs; }
+ const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
TupleExpr (std::vector<std::unique_ptr<Expr> > tuple_elements,
std::vector<Attribute> inner_attribs,
@@ -1269,6 +1270,10 @@ public:
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; }
+
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
@@ -1340,6 +1345,12 @@ public:
void mark_for_strip () override { tuple_expr = nullptr; }
bool is_marked_for_strip () const override { return tuple_expr == nullptr; }
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_tuple_expr () {
+ rust_assert (tuple_expr != nullptr);
+ return tuple_expr;
+ }
+
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
@@ -1382,7 +1393,8 @@ class StructExprStruct : public StructExpr
public:
std::string as_string () const override;
- std::vector<Attribute> get_inner_attrs () const { return inner_attrs; }
+ 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,
@@ -1410,9 +1422,10 @@ protected:
* struct */
struct StructBase
{
-public:
+private:
std::unique_ptr<Expr> base_struct;
+public:
// TODO: should this store location data?
StructBase (std::unique_ptr<Expr> base_struct_ptr)
: base_struct (std::move (base_struct_ptr))
@@ -1453,6 +1466,12 @@ public:
bool is_invalid () const { return base_struct == nullptr; }
std::string as_string () const;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_base_struct () {
+ rust_assert (base_struct != nullptr);
+ return base_struct;
+ }
};
/* Base AST node for a single struct expression field (in struct instance
@@ -1480,11 +1499,10 @@ protected:
// Identifier-only variant of StructExprField AST node
class StructExprFieldIdentifier : public StructExprField
{
-public:
Identifier field_name;
// TODO: should this store location data?
-
+public:
StructExprFieldIdentifier (Identifier field_identifier)
: field_name (std::move (field_identifier))
{}
@@ -1506,7 +1524,6 @@ protected:
* abstract */
class StructExprFieldWithVal : public StructExprField
{
-public:
std::unique_ptr<Expr> value;
protected:
@@ -1533,16 +1550,21 @@ protected:
public:
std::string as_string () const override;
+
+ // TODO: is this better? Or is a "vis_block" better?
+ std::unique_ptr<Expr> &get_value () {
+ rust_assert (value != nullptr);
+ return value;
+ }
};
// Identifier and value variant of StructExprField AST node
class StructExprFieldIdentifierValue : public StructExprFieldWithVal
{
-public:
Identifier field_name;
// TODO: should this store location data?
-
+public:
StructExprFieldIdentifierValue (Identifier field_identifier,
std::unique_ptr<Expr> field_value)
: StructExprFieldWithVal (std::move (field_value)),
@@ -1553,6 +1575,8 @@ public:
void accept_vis (ASTVisitor &vis) override;
+ std::string get_field_name () const { return field_name; }
+
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
@@ -1565,11 +1589,10 @@ protected:
// Tuple index and value variant of StructExprField AST node
class StructExprFieldIndexValue : public StructExprFieldWithVal
{
-public:
TupleIndex index;
// TODO: should this store location data?
-
+public:
StructExprFieldIndexValue (TupleIndex tuple_index,
std::unique_ptr<Expr> field_value)
: StructExprFieldWithVal (std::move (field_value)), index (tuple_index)
@@ -1579,6 +1602,8 @@ public:
void accept_vis (ASTVisitor &vis) override;
+ TupleIndex get_index () const { return index; }
+
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
@@ -1591,25 +1616,17 @@ protected:
// AST node of a struct creator with fields
class StructExprStructFields : public StructExprStruct
{
-public:
// 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 (); }
- /*inline std::vector<std::unique_ptr<StructExprField>> get_fields()
- const { return fields;
- }*/
-
- /*inline StructBase get_struct_base() const {
- return has_struct_base ? struct_base : StructBase::error();
- }*/
-
// Constructor for StructExprStructFields when no struct base is used
StructExprStructFields (
PathInExpression struct_path,
@@ -1650,6 +1667,13 @@ public:
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 */
@@ -1660,6 +1684,8 @@ protected:
};
// 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;
@@ -1667,10 +1693,6 @@ class StructExprStructBase : public StructExprStruct
public:
std::string as_string () const override;
- /*inline StructBase get_struct_base() const {
- return struct_base;
- }*/
-
StructExprStructBase (PathInExpression struct_path, StructBase base_struct,
std::vector<Attribute> inner_attribs,
std::vector<Attribute> outer_attribs, Location locus)
@@ -1681,6 +1703,9 @@ public:
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 */
@@ -1702,10 +1727,7 @@ public:
std::string as_string () const override;
const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
-
- /*inline std::vector<std::unique_ptr<Expr>> get_exprs() const {
- return exprs;
- }*/
+ std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
StructExprTuple (PathInExpression struct_path,
std::vector<std::unique_ptr<Expr> > tuple_exprs,
@@ -1748,6 +1770,9 @@ public:
void accept_vis (ASTVisitor &vis) override;
+ const std::vector<std::unique_ptr<Expr> > &get_elems () const { return exprs; }
+ std::vector<std::unique_ptr<Expr> > &get_elems () { return exprs; }
+
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
@@ -1766,7 +1791,6 @@ public:
std::string as_string () const override
{
return get_struct_name ().as_string ();
- // return struct_name.as_string();
}
StructExprUnit (PathInExpression struct_path,
@@ -1990,6 +2014,10 @@ public:
void accept_vis (ASTVisitor &vis) override;
+ // TODO: this mutable getter seems really dodgy. Think up better way.
+ std::vector<std::unique_ptr<EnumExprField> > &get_fields () { return fields; }
+ const std::vector<std::unique_ptr<EnumExprField> > &get_fields () const { return fields; }
+
protected:
/* Use covariance to implement clone function as returning this object rather
* than base */
diff --git a/gcc/rust/ast/rust-stmt.h b/gcc/rust/ast/rust-stmt.h
index f7c47d7..0bb1021 100644
--- a/gcc/rust/ast/rust-stmt.h
+++ b/gcc/rust/ast/rust-stmt.h
@@ -160,7 +160,6 @@ protected:
* difficulties, can only be guaranteed to hold an expression). */
class ExprStmtWithoutBlock : public ExprStmt
{
-public:
// TODO: ensure that this works
std::unique_ptr<ExprWithoutBlock> expr;
/* HACK: cannot ensure type safety of ExprWithoutBlock due to Pratt parsing,
@@ -168,6 +167,7 @@ public:
* or redesign AST. */
// std::unique_ptr<Expr> expr;
+public:
std::string as_string () const override;
ExprStmtWithoutBlock (std::unique_ptr<ExprWithoutBlock> expr, Location locus)
@@ -232,9 +232,9 @@ protected:
// Statement containing an expression with a block
class ExprStmtWithBlock : public ExprStmt
{
-public:
std::unique_ptr<ExprWithBlock> expr;
+public:
std::string as_string () const override;
std::vector<LetStmt *> locals;
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
index 0589347..ca310ca 100644
--- a/gcc/rust/backend/rust-compile.cc
+++ b/gcc/rust/backend/rust-compile.cc
@@ -538,7 +538,7 @@ Compilation::visit (AST::StructExprFieldIdentifierValue &field)
bool found = false;
for (auto &df : decl->get_fields ())
{
- if (field.field_name.compare (df.field_name) == 0)
+ if (field.get_field_name ().compare (df.field_name) == 0)
{
found = true;
break;
@@ -546,16 +546,16 @@ Compilation::visit (AST::StructExprFieldIdentifierValue &field)
}
if (!found)
{
- rust_fatal_error (field.value->get_locus_slow (),
+ rust_fatal_error (field.get_value ()->get_locus_slow (),
"failed to lookup field index");
return;
}
Bexpression *value = NULL;
- VISIT_POP (field.value->get_locus_slow (), field.value.get (), value, exprs);
+ VISIT_POP (field.get_value ()->get_locus_slow (), field.get_value ().get (), value, exprs);
if (value == NULL)
{
- rust_fatal_error (field.value->get_locus_slow (),
+ rust_fatal_error (field.get_value ()->get_locus_slow (),
"failed to compile value to struct");
return;
}
@@ -566,10 +566,10 @@ void
Compilation::visit (AST::StructExprFieldIndexValue &field)
{
Bexpression *value = NULL;
- VISIT_POP (field.value->get_locus_slow (), field.value.get (), value, exprs);
+ VISIT_POP (field.get_value ()->get_locus_slow (), field.get_value ().get (), value, exprs);
if (value == NULL)
{
- rust_fatal_error (field.value->get_locus_slow (),
+ rust_fatal_error (field.get_value ()->get_locus_slow (),
"failed to compile value to struct");
return;
}
@@ -598,7 +598,7 @@ Compilation::visit (AST::StructExprStructFields &expr)
// FIXME type resolution pass should ensures these are in correct order
// and have defaults if required
- for (auto &field : expr.fields)
+ for (auto &field : expr.get_fields ())
{
Bexpression *value = NULL;
VISIT_POP (expr.get_locus (), field, value, exprs);
@@ -1332,7 +1332,7 @@ Compilation::visit (AST::LetStmt &stmt)
void
Compilation::visit (AST::ExprStmtWithoutBlock &stmt)
{
- stmt.expr->accept_vis (*this);
+ stmt.get_expr ()->accept_vis (*this);
}
void
@@ -1348,7 +1348,7 @@ Compilation::visit (AST::ExprStmtWithBlock &stmt)
start_location, end_location);
scope.PushBlock (code_block);
- stmt.expr->accept_vis (*this);
+ stmt.get_expr ()->accept_vis (*this);
// get trailing if required
for (auto &s : stmts)
diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
index 164b5a5..65918db 100644
--- a/gcc/rust/expand/rust-macro-expand.cc
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -345,20 +345,196 @@ namespace Rust {
expr.get_array_expr()->accept_vis(*this);
expr.get_index_expr()->accept_vis(*this);
}
- void visit(AST::TupleExpr& expr) override {}
- void visit(AST::TupleIndexExpr& expr) override {}
- void visit(AST::StructExprStruct& expr) override {}
- void visit(AST::StructExprFieldIdentifier& field) override {}
- void visit(AST::StructExprFieldIdentifierValue& field) override {}
- void visit(AST::StructExprFieldIndexValue& field) override {}
- void visit(AST::StructExprStructFields& expr) override {}
- void visit(AST::StructExprStructBase& expr) override {}
- void visit(AST::StructExprTuple& expr) override {}
- void visit(AST::StructExprUnit& expr) override {}
- void visit(AST::EnumExprFieldIdentifier& field) override {}
+ void visit(AST::TupleExpr& expr) override {
+ /* according to spec, outer attributes are allowed on "elements of
+ * tuple expressions" */
+
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* strip test based on inner attrs - spec says these are inner
+ * attributes, not outer attributes of inner expr */
+ expander.expand_cfg_attrs(expr.get_inner_attrs());
+ if (expander.fails_cfg(expr.get_inner_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* apparently outer attributes are allowed in "elements of tuple
+ * expressions" according to spec */
+ auto& values = expr.get_tuple_elems();
+ for (int i = 0; i < values.size();) {
+ auto& value = values[i];
+
+ // mark for stripping if required
+ value->accept_vis(*this);
+
+ if (value->is_marked_for_strip())
+ values.erase(values.begin() + i);
+ else
+ i++;
+ }
+ }
+ void visit(AST::TupleIndexExpr& expr) override {
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* wouldn't strip this directly (as outer attrs should be
+ * associated with this level), but any sub-expressions would be
+ * stripped. Thus, no need to erase when strip check called. */
+ expr.get_tuple_expr()->accept_vis(*this);
+ }
+ void visit(AST::StructExprStruct& expr) override {
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* strip test based on inner attrs - spec says these are inner
+ * attributes, not outer attributes of inner expr */
+ expander.expand_cfg_attrs(expr.get_inner_attrs());
+ if (expander.fails_cfg(expr.get_inner_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+ }
+ void visit(AST::StructExprFieldIdentifier& field) override {
+ // as no attrs (at moment, at least), no stripping possible
+ }
+ void visit(AST::StructExprFieldIdentifierValue& field) override {
+ /* as no attrs possible (at moment, at least), only sub-expression
+ * stripping is possible */
+ field.get_value()->accept_vis(*this);
+ }
+ void visit(AST::StructExprFieldIndexValue& field) override {
+ /* as no attrs possible (at moment, at least), only sub-expression
+ * stripping is possible */
+ field.get_value()->accept_vis(*this);
+ }
+ void visit(AST::StructExprStructFields& expr) override {
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* strip test based on inner attrs - spec says these are inner
+ * attributes, not outer attributes of inner expr */
+ expander.expand_cfg_attrs(expr.get_inner_attrs());
+ if (expander.fails_cfg(expr.get_inner_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* spec does not specify whether expressions are allowed to be
+ * stripped at top level of struct fields, but I wouldn't think
+ * that they would be, so operating under the assumption that only
+ * sub-expressions can be stripped. */
+ for (auto& field : expr.get_fields()) {
+ field->accept_vis(*this);
+ // shouldn't strip in this
+ }
+
+ /* struct base presumably can't be stripped, as the '..' is before
+ * the expression. as such, can only strip sub-expressions. */
+ if (expr.has_struct_base())
+ expr.get_struct_base().get_base_struct()->accept_vis(*this);
+ }
+ void visit(AST::StructExprStructBase& expr) override {
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* strip test based on inner attrs - spec says these are inner
+ * attributes, not outer attributes of inner expr */
+ expander.expand_cfg_attrs(expr.get_inner_attrs());
+ if (expander.fails_cfg(expr.get_inner_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* struct base presumably can't be stripped, as the '..' is before
+ * the expression. as such, can only strip sub-expressions. */
+ rust_assert(!expr.get_struct_base().is_invalid());
+ expr.get_struct_base().get_base_struct()->accept_vis(*this);
+ }
+ void visit(AST::StructExprTuple& expr) override {
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* strip test based on inner attrs - spec says these are inner
+ * attributes, not outer attributes of inner expr */
+ expander.expand_cfg_attrs(expr.get_inner_attrs());
+ if (expander.fails_cfg(expr.get_inner_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ /* spec says outer attributes are specifically allowed for elements
+ * of tuple-style struct expressions, so full stripping possible */
+ auto& values = expr.get_elems();
+ for (int i = 0; i < values.size();) {
+ auto& value = values[i];
+
+ // mark for stripping if required
+ value->accept_vis(*this);
+
+ if (value->is_marked_for_strip())
+ values.erase(values.begin() + i);
+ else
+ i++;
+ }
+ }
+ void visit(AST::StructExprUnit& expr) override {
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+ }
+ void visit(AST::EnumExprFieldIdentifier& field) override {
+
+ }
void visit(AST::EnumExprFieldIdentifierValue& field) override {}
void visit(AST::EnumExprFieldIndexValue& field) override {}
- void visit(AST::EnumExprStruct& expr) override {}
+ void visit(AST::EnumExprStruct& expr) override {
+ // initial strip test based on outer attrs
+ expander.expand_cfg_attrs(expr.get_outer_attrs());
+ if (expander.fails_cfg(expr.get_outer_attrs())) {
+ expr.mark_for_strip();
+ return;
+ }
+
+ // supposedly spec doesn't allow inner attributes in enum exprs
+
+ /* spec does not specify whether expressions are allowed to be
+ * stripped at top level of expression fields, but I wouldn't think
+ * that they would be, so operating under the assumption that only
+ * sub-expressions can be stripped. */
+ for (auto& field : expr.get_fields()) {
+ field->accept_vis(*this);
+ // shouldn't strip in this
+ }
+ }
void visit(AST::EnumExprTuple& expr) override {}
void visit(AST::EnumExprFieldless& expr) override {}
void visit(AST::CallExpr& expr) override {}