mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-27 16:50:10 +01:00
This switches `DCHECK` and `FATAL` as well. The goal is to reduce the code size impact of these assertions so that we can keep more of them enabled. Currently, the largest cost I see from `CHECK` is not the actual check or the cold code itself, but actually the failure to inline trivial functions due to the presence of the cold code. This means that our goal isn't to reduce apparent code size in the final binary but the LLVM IR cost assessed for these routines in the inliner, which closely correlates with code size but is a bit different. As discussed in #4283, experimentation shows that a single function call with a minimal number of arguments is the lowest cost model for these. This is easily achieved with a format-string API that internally uses `llvm::formatv`. This PR is essentially the `CHECK` version of #4283. However, the check macros are substantially harder to make work with both format strings and streaming because they also take a condition. Also, unexpectedly, I was very successful at devising a regular expression based automated rewrite from the streaming to the format string form with only low 10s of manual fixes. This includes compacting strings broken up across lines, etc. Given how well that went, I've prepared this PR which just directly switches to the format string API and migrate everything to use it. One nice side-effect is that the format string approach ends up greatly simplifying the implementation here as well. This is ... *shockingly* effective. Parsing speeds up by more than 3% with just this change. And checking speeds up by **8%** with this change alone: ``` BM_CompileAPIFileDenseDecls<Phase::Parse>/256 86.3µs ± 1% 82.9µs ± 1% -3.94% (p=0.000 n=17+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/1024 431µs ± 1% 415µs ± 1% -3.76% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/4096 1.77ms ± 1% 1.71ms ± 1% -3.18% (p=0.000 n=18+19) BM_CompileAPIFileDenseDecls<Phase::Parse>/16384 7.44ms ± 1% 7.17ms ± 2% -3.56% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/65536 30.7ms ± 1% 29.7ms ± 1% -3.15% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Parse>/262144 131ms ± 1% 127ms ± 1% -2.81% (p=0.000 n=18+18) BM_CompileAPIFileDenseDecls<Phase::Check>/256 878µs ± 2% 800µs ± 1% -8.91% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/1024 1.88ms ± 2% 1.72ms ± 1% -8.56% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/4096 5.78ms ± 2% 5.28ms ± 1% -8.70% (p=0.000 n=20+18) BM_CompileAPIFileDenseDecls<Phase::Check>/16384 21.9ms ± 1% 20.1ms ± 1% -8.02% (p=0.000 n=18+20) BM_CompileAPIFileDenseDecls<Phase::Check>/65536 90.4ms ± 2% 83.1ms ± 1% -8.04% (p=0.000 n=19+20) BM_CompileAPIFileDenseDecls<Phase::Check>/262144 381ms ± 2% 352ms ± 1% -7.79% (p=0.000 n=19+19) ``` --------- Co-authored-by: Richard Smith <richard@metafoo.co.uk> Co-authored-by: josh11b <15258583+josh11b@users.noreply.github.com>
588 lines
19 KiB
C++
588 lines
19 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_EXPLORER_AST_STATEMENT_H_
|
|
#define CARBON_EXPLORER_AST_STATEMENT_H_
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "common/ostream.h"
|
|
#include "explorer/ast/ast_node.h"
|
|
#include "explorer/ast/clone_context.h"
|
|
#include "explorer/ast/expression.h"
|
|
#include "explorer/ast/expression_category.h"
|
|
#include "explorer/ast/pattern.h"
|
|
#include "explorer/ast/return_term.h"
|
|
#include "explorer/ast/value_node.h"
|
|
#include "explorer/base/arena.h"
|
|
#include "explorer/base/source_location.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
|
|
namespace Carbon {
|
|
|
|
class CallableDeclaration;
|
|
|
|
class Statement : public AstNode {
|
|
public:
|
|
~Statement() override = 0;
|
|
|
|
void Print(llvm::raw_ostream& out) const override { PrintIndent(0, out); }
|
|
void PrintID(llvm::raw_ostream& out) const override;
|
|
|
|
void PrintIndent(int indent_num_spaces, llvm::raw_ostream& out) const;
|
|
|
|
static auto classof(const AstNode* node) {
|
|
return InheritsFromStatement(node->kind());
|
|
}
|
|
|
|
// Returns the enumerator corresponding to the most-derived type of this
|
|
// object.
|
|
auto kind() const -> StatementKind {
|
|
return static_cast<StatementKind>(root_kind());
|
|
}
|
|
|
|
protected:
|
|
explicit Statement(AstNodeKind kind, SourceLocation source_loc)
|
|
: AstNode(kind, source_loc) {}
|
|
|
|
explicit Statement(CloneContext& context, const Statement& other)
|
|
: AstNode(context, other) {}
|
|
};
|
|
|
|
class Block : public Statement {
|
|
public:
|
|
Block(SourceLocation source_loc, std::vector<Nonnull<Statement*>> statements)
|
|
: Statement(AstNodeKind::Block, source_loc),
|
|
statements_(std::move(statements)) {}
|
|
|
|
explicit Block(CloneContext& context, const Block& other)
|
|
: Statement(context, other),
|
|
statements_(context.Clone(other.statements_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromBlock(node->kind());
|
|
}
|
|
|
|
auto statements() const -> llvm::ArrayRef<Nonnull<const Statement*>> {
|
|
return statements_;
|
|
}
|
|
auto statements() -> llvm::MutableArrayRef<Nonnull<Statement*>> {
|
|
return statements_;
|
|
}
|
|
|
|
private:
|
|
std::vector<Nonnull<Statement*>> statements_;
|
|
};
|
|
|
|
class ExpressionStatement : public Statement {
|
|
public:
|
|
ExpressionStatement(SourceLocation source_loc,
|
|
Nonnull<Expression*> expression)
|
|
: Statement(AstNodeKind::ExpressionStatement, source_loc),
|
|
expression_(expression) {}
|
|
|
|
explicit ExpressionStatement(CloneContext& context,
|
|
const ExpressionStatement& other)
|
|
: Statement(context, other),
|
|
expression_(context.Clone(other.expression_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromExpressionStatement(node->kind());
|
|
}
|
|
|
|
auto expression() const -> const Expression& { return *expression_; }
|
|
auto expression() -> Expression& { return *expression_; }
|
|
|
|
private:
|
|
Nonnull<Expression*> expression_;
|
|
};
|
|
|
|
enum class AssignOperator {
|
|
Plain,
|
|
Add,
|
|
Div,
|
|
Mul,
|
|
Mod,
|
|
Sub,
|
|
And,
|
|
Or,
|
|
Xor,
|
|
ShiftLeft,
|
|
ShiftRight,
|
|
};
|
|
|
|
// Returns the spelling of this assignment operator token.
|
|
auto AssignOperatorToString(AssignOperator op) -> std::string_view;
|
|
|
|
class Assign : public Statement {
|
|
public:
|
|
Assign(SourceLocation source_loc, Nonnull<Expression*> lhs, AssignOperator op,
|
|
Nonnull<Expression*> rhs)
|
|
: Statement(AstNodeKind::Assign, source_loc),
|
|
lhs_(lhs),
|
|
rhs_(rhs),
|
|
op_(op) {}
|
|
|
|
explicit Assign(CloneContext& context, const Assign& other)
|
|
: Statement(context, other),
|
|
lhs_(context.Clone(other.lhs_)),
|
|
rhs_(context.Clone(other.rhs_)),
|
|
op_(other.op_),
|
|
rewritten_form_(context.Clone(other.rewritten_form_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromAssign(node->kind());
|
|
}
|
|
|
|
auto lhs() const -> const Expression& { return *lhs_; }
|
|
auto lhs() -> Expression& { return *lhs_; }
|
|
auto rhs() const -> const Expression& { return *rhs_; }
|
|
auto rhs() -> Expression& { return *rhs_; }
|
|
|
|
auto op() const -> AssignOperator { return op_; }
|
|
|
|
// Can only be called by type-checking, if a conversion was required.
|
|
void set_rhs(Nonnull<Expression*> rhs) { rhs_ = rhs; }
|
|
|
|
// Set the rewritten form of this statement. Can only be called during type
|
|
// checking.
|
|
auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
|
|
CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
|
|
rewritten_form_ = rewritten_form;
|
|
}
|
|
|
|
// Get the rewritten form of this statement. A rewritten form is used when
|
|
// the statement is rewritten as a function call on an interface. A
|
|
// rewritten form is not used when providing built-in operator semantics for
|
|
// a plain assignment.
|
|
auto rewritten_form() const -> std::optional<Nonnull<const Expression*>> {
|
|
return rewritten_form_;
|
|
}
|
|
|
|
private:
|
|
Nonnull<Expression*> lhs_;
|
|
Nonnull<Expression*> rhs_;
|
|
AssignOperator op_;
|
|
std::optional<Nonnull<const Expression*>> rewritten_form_;
|
|
};
|
|
|
|
class IncrementDecrement : public Statement {
|
|
public:
|
|
IncrementDecrement(SourceLocation source_loc, Nonnull<Expression*> argument,
|
|
bool is_increment)
|
|
: Statement(AstNodeKind::IncrementDecrement, source_loc),
|
|
argument_(argument),
|
|
is_increment_(is_increment) {}
|
|
|
|
explicit IncrementDecrement(CloneContext& context,
|
|
const IncrementDecrement& other)
|
|
: Statement(context, other),
|
|
argument_(context.Clone(other.argument_)),
|
|
is_increment_(other.is_increment_),
|
|
rewritten_form_(context.Clone(other.rewritten_form_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromIncrementDecrement(node->kind());
|
|
}
|
|
|
|
auto argument() const -> const Expression& { return *argument_; }
|
|
auto argument() -> Expression& { return *argument_; }
|
|
|
|
auto is_increment() const -> bool { return is_increment_; }
|
|
|
|
// Set the rewritten form of this statement. Can only be called during type
|
|
// checking.
|
|
auto set_rewritten_form(Nonnull<const Expression*> rewritten_form) -> void {
|
|
CARBON_CHECK(!rewritten_form_.has_value(), "rewritten form set twice");
|
|
rewritten_form_ = rewritten_form;
|
|
}
|
|
|
|
// Get the rewritten form of this statement.
|
|
auto rewritten_form() const -> std::optional<Nonnull<const Expression*>> {
|
|
return rewritten_form_;
|
|
}
|
|
|
|
private:
|
|
Nonnull<Expression*> argument_;
|
|
bool is_increment_;
|
|
std::optional<Nonnull<const Expression*>> rewritten_form_;
|
|
};
|
|
|
|
class VariableDefinition : public Statement {
|
|
public:
|
|
enum DefinitionType {
|
|
Var,
|
|
Returned,
|
|
};
|
|
|
|
VariableDefinition(SourceLocation source_loc, Nonnull<Pattern*> pattern,
|
|
std::optional<Nonnull<Expression*>> init,
|
|
ExpressionCategory expression_category,
|
|
DefinitionType def_type)
|
|
: Statement(AstNodeKind::VariableDefinition, source_loc),
|
|
pattern_(pattern),
|
|
init_(init),
|
|
expression_category_(expression_category),
|
|
def_type_(def_type) {}
|
|
|
|
explicit VariableDefinition(CloneContext& context,
|
|
const VariableDefinition& other)
|
|
: Statement(context, other),
|
|
pattern_(context.Clone(other.pattern_)),
|
|
init_(context.Clone(other.init_)),
|
|
expression_category_(other.expression_category_),
|
|
def_type_(other.def_type_) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromVariableDefinition(node->kind());
|
|
}
|
|
|
|
auto pattern() const -> const Pattern& { return *pattern_; }
|
|
auto pattern() -> Pattern& { return *pattern_; }
|
|
|
|
auto init() const -> const Expression& {
|
|
CARBON_CHECK(has_init());
|
|
return **init_;
|
|
}
|
|
auto init() -> Expression& {
|
|
CARBON_CHECK(has_init());
|
|
return **init_;
|
|
}
|
|
|
|
auto has_init() const -> bool { return init_.has_value(); }
|
|
|
|
// Can only be called by type-checking, if a conversion was required.
|
|
void set_init(Nonnull<Expression*> init) {
|
|
CARBON_CHECK(has_init(), "should not add a new initializer");
|
|
init_ = init;
|
|
}
|
|
|
|
auto expression_category() const -> ExpressionCategory {
|
|
return expression_category_;
|
|
}
|
|
|
|
auto is_returned() const -> bool { return def_type_ == Returned; }
|
|
|
|
private:
|
|
Nonnull<Pattern*> pattern_;
|
|
std::optional<Nonnull<Expression*>> init_;
|
|
ExpressionCategory expression_category_;
|
|
const DefinitionType def_type_;
|
|
};
|
|
|
|
class If : public Statement {
|
|
public:
|
|
If(SourceLocation source_loc, Nonnull<Expression*> condition,
|
|
Nonnull<Block*> then_block, std::optional<Nonnull<Block*>> else_block)
|
|
: Statement(AstNodeKind::If, source_loc),
|
|
condition_(condition),
|
|
then_block_(then_block),
|
|
else_block_(else_block) {}
|
|
|
|
explicit If(CloneContext& context, const If& other)
|
|
: Statement(context, other),
|
|
condition_(context.Clone(other.condition_)),
|
|
then_block_(context.Clone(other.then_block_)),
|
|
else_block_(context.Clone(other.else_block_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromIf(node->kind());
|
|
}
|
|
|
|
auto condition() const -> const Expression& { return *condition_; }
|
|
auto condition() -> Expression& { return *condition_; }
|
|
auto then_block() const -> const Block& { return *then_block_; }
|
|
auto then_block() -> Block& { return *then_block_; }
|
|
auto else_block() const -> std::optional<Nonnull<const Block*>> {
|
|
return else_block_;
|
|
}
|
|
auto else_block() -> std::optional<Nonnull<Block*>> { return else_block_; }
|
|
|
|
// Can only be called by type-checking, if a conversion was required.
|
|
void set_condition(Nonnull<Expression*> condition) { condition_ = condition; }
|
|
|
|
private:
|
|
Nonnull<Expression*> condition_;
|
|
Nonnull<Block*> then_block_;
|
|
std::optional<Nonnull<Block*>> else_block_;
|
|
};
|
|
|
|
class Return : public Statement {
|
|
public:
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromReturn(node->kind());
|
|
}
|
|
|
|
// The AST node representing the function body this statement returns from.
|
|
// Can only be called after ResolveControlFlow has visited this node.
|
|
//
|
|
// Note that this function does not represent an edge in the tree
|
|
// structure of the AST: the return value is not a child of this node,
|
|
// but an ancestor.
|
|
auto function() const -> const CallableDeclaration& { return **function_; }
|
|
auto function() -> CallableDeclaration& { return **function_; }
|
|
|
|
// Can only be called once, by ResolveControlFlow.
|
|
void set_function(Nonnull<CallableDeclaration*> function) {
|
|
CARBON_CHECK(!function_.has_value());
|
|
function_ = function;
|
|
}
|
|
|
|
protected:
|
|
Return(AstNodeKind node_kind, SourceLocation source_loc)
|
|
: Statement(node_kind, source_loc) {}
|
|
|
|
explicit Return(CloneContext& context, const Return& other);
|
|
|
|
private:
|
|
std::optional<Nonnull<CallableDeclaration*>> function_;
|
|
};
|
|
|
|
class ReturnVar : public Return {
|
|
public:
|
|
explicit ReturnVar(SourceLocation source_loc)
|
|
: Return(AstNodeKind::ReturnVar, source_loc) {}
|
|
|
|
explicit ReturnVar(CloneContext& context, const ReturnVar& other)
|
|
: Return(context, other), value_node_(context.Clone(other.value_node_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromReturnVar(node->kind());
|
|
}
|
|
|
|
// Returns the value node of the BindingPattern of the returned var
|
|
// definition. Cannot be called before name resolution.
|
|
auto value_node() const -> const ValueNodeView& { return *value_node_; }
|
|
|
|
// Can only be called once, by ResolveNames.
|
|
void set_value_node(ValueNodeView value_node) {
|
|
CARBON_CHECK(!value_node_.has_value());
|
|
value_node_ = value_node;
|
|
}
|
|
|
|
private:
|
|
// The value node of the BindingPattern of the returned var definition.
|
|
std::optional<ValueNodeView> value_node_;
|
|
};
|
|
|
|
class ReturnExpression : public Return {
|
|
public:
|
|
ReturnExpression(Nonnull<Arena*> arena, SourceLocation source_loc)
|
|
: ReturnExpression(source_loc, arena->New<TupleLiteral>(source_loc),
|
|
true) {}
|
|
ReturnExpression(SourceLocation source_loc, Nonnull<Expression*> expression,
|
|
bool is_omitted_expression)
|
|
: Return(AstNodeKind::ReturnExpression, source_loc),
|
|
expression_(expression),
|
|
is_omitted_expression_(is_omitted_expression) {}
|
|
|
|
explicit ReturnExpression(CloneContext& context,
|
|
const ReturnExpression& other)
|
|
: Return(context, other),
|
|
expression_(context.Clone(other.expression_)),
|
|
is_omitted_expression_(other.is_omitted_expression_) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromReturnExpression(node->kind());
|
|
}
|
|
|
|
auto expression() const -> const Expression& { return *expression_; }
|
|
auto expression() -> Expression& { return *expression_; }
|
|
auto is_omitted_expression() const -> bool { return is_omitted_expression_; }
|
|
|
|
// Can only be called by type-checking, if a conversion was required.
|
|
void set_expression(Nonnull<Expression*> expression) {
|
|
expression_ = expression;
|
|
}
|
|
|
|
private:
|
|
Nonnull<Expression*> expression_;
|
|
bool is_omitted_expression_;
|
|
};
|
|
|
|
class While : public Statement {
|
|
public:
|
|
While(SourceLocation source_loc, Nonnull<Expression*> condition,
|
|
Nonnull<Block*> body)
|
|
: Statement(AstNodeKind::While, source_loc),
|
|
condition_(condition),
|
|
body_(body) {}
|
|
|
|
explicit While(CloneContext& context, const While& other)
|
|
: Statement(context, other),
|
|
condition_(context.Clone(other.condition_)),
|
|
body_(context.Clone(other.body_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromWhile(node->kind());
|
|
}
|
|
|
|
auto condition() const -> const Expression& { return *condition_; }
|
|
auto condition() -> Expression& { return *condition_; }
|
|
auto body() const -> const Block& { return *body_; }
|
|
auto body() -> Block& { return *body_; }
|
|
|
|
// Can only be called by type-checking, if a conversion was required.
|
|
void set_condition(Nonnull<Expression*> condition) { condition_ = condition; }
|
|
|
|
private:
|
|
Nonnull<Expression*> condition_;
|
|
Nonnull<Block*> body_;
|
|
};
|
|
|
|
class For : public Statement {
|
|
public:
|
|
For(SourceLocation source_loc, Nonnull<BindingPattern*> variable_declaration,
|
|
Nonnull<Expression*> loop_target, Nonnull<Block*> body)
|
|
: Statement(AstNodeKind::For, source_loc),
|
|
variable_declaration_(variable_declaration),
|
|
loop_target_(loop_target),
|
|
body_(body) {}
|
|
|
|
explicit For(CloneContext& context, const For& other)
|
|
: Statement(context, other),
|
|
variable_declaration_(context.Clone(other.variable_declaration_)),
|
|
loop_target_(context.Clone(other.loop_target_)),
|
|
body_(context.Clone(other.body_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromFor(node->kind());
|
|
}
|
|
|
|
auto variable_declaration() const -> const BindingPattern& {
|
|
return *variable_declaration_;
|
|
}
|
|
auto variable_declaration() -> BindingPattern& {
|
|
return *variable_declaration_;
|
|
}
|
|
|
|
auto loop_target() const -> const Expression& { return *loop_target_; }
|
|
auto loop_target() -> Expression& { return *loop_target_; }
|
|
|
|
auto body() const -> const Block& { return *body_; }
|
|
auto body() -> Block& { return *body_; }
|
|
|
|
private:
|
|
Nonnull<BindingPattern*> variable_declaration_;
|
|
Nonnull<Expression*> loop_target_;
|
|
Nonnull<Block*> body_;
|
|
};
|
|
|
|
class Break : public Statement {
|
|
public:
|
|
explicit Break(SourceLocation source_loc)
|
|
: Statement(AstNodeKind::Break, source_loc) {}
|
|
|
|
explicit Break(CloneContext& context, const Break& other)
|
|
: Statement(context, other), loop_(context.Clone(other.loop_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromBreak(node->kind());
|
|
}
|
|
|
|
// The AST node representing the loop this statement breaks out of.
|
|
// Can only be called after ResolveControlFlow has visited this node.
|
|
//
|
|
// Note that this function does not represent an edge in the tree
|
|
// structure of the AST: the return value is not a child of this node,
|
|
// but an ancestor.
|
|
auto loop() const -> const Statement& { return **loop_; }
|
|
|
|
// Can only be called once, by ResolveControlFlow.
|
|
void set_loop(Nonnull<const Statement*> loop) {
|
|
CARBON_CHECK(!loop_.has_value());
|
|
loop_ = loop;
|
|
}
|
|
|
|
private:
|
|
std::optional<Nonnull<const Statement*>> loop_;
|
|
};
|
|
|
|
class Continue : public Statement {
|
|
public:
|
|
explicit Continue(SourceLocation source_loc)
|
|
: Statement(AstNodeKind::Continue, source_loc) {}
|
|
|
|
explicit Continue(CloneContext& context, const Continue& other)
|
|
: Statement(context, other), loop_(context.Clone(other.loop_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromContinue(node->kind());
|
|
}
|
|
|
|
// The AST node representing the loop this statement continues.
|
|
// Can only be called after ResolveControlFlow has visited this node.
|
|
//
|
|
// Note that this function does not represent an edge in the tree
|
|
// structure of the AST: the return value is not a child of this node,
|
|
// but an ancestor.
|
|
auto loop() const -> const Statement& { return **loop_; }
|
|
|
|
// Can only be called once, by ResolveControlFlow.
|
|
void set_loop(Nonnull<const Statement*> loop) {
|
|
CARBON_CHECK(!loop_.has_value());
|
|
loop_ = loop;
|
|
}
|
|
|
|
private:
|
|
std::optional<Nonnull<const Statement*>> loop_;
|
|
};
|
|
|
|
class Match : public Statement {
|
|
public:
|
|
class Clause {
|
|
public:
|
|
explicit Clause(Nonnull<Pattern*> pattern, Nonnull<Statement*> statement)
|
|
: pattern_(pattern), statement_(statement) {}
|
|
|
|
explicit Clause(CloneContext& context, const Clause& other)
|
|
: pattern_(context.Clone(other.pattern_)),
|
|
statement_(context.Clone(other.statement_)) {}
|
|
|
|
auto pattern() const -> const Pattern& { return *pattern_; }
|
|
auto pattern() -> Pattern& { return *pattern_; }
|
|
auto statement() const -> const Statement& { return *statement_; }
|
|
auto statement() -> Statement& { return *statement_; }
|
|
|
|
private:
|
|
Nonnull<Pattern*> pattern_;
|
|
Nonnull<Statement*> statement_;
|
|
};
|
|
|
|
Match(SourceLocation source_loc, Nonnull<Expression*> expression,
|
|
std::vector<Clause> clauses)
|
|
: Statement(AstNodeKind::Match, source_loc),
|
|
expression_(expression),
|
|
clauses_(std::move(clauses)) {}
|
|
|
|
explicit Match(CloneContext& context, const Match& other)
|
|
: Statement(context, other),
|
|
expression_(context.Clone(other.expression_)),
|
|
clauses_(context.Clone(other.clauses_)) {}
|
|
|
|
static auto classof(const AstNode* node) -> bool {
|
|
return InheritsFromMatch(node->kind());
|
|
}
|
|
|
|
auto expression() const -> const Expression& { return *expression_; }
|
|
auto expression() -> Expression& { return *expression_; }
|
|
auto clauses() const -> llvm::ArrayRef<Clause> { return clauses_; }
|
|
auto clauses() -> llvm::MutableArrayRef<Clause> { return clauses_; }
|
|
|
|
// Can only be called by type-checking, if a conversion was required.
|
|
void set_expression(Nonnull<Expression*> expression) {
|
|
expression_ = expression;
|
|
}
|
|
|
|
private:
|
|
Nonnull<Expression*> expression_;
|
|
std::vector<Clause> clauses_;
|
|
};
|
|
|
|
} // namespace Carbon
|
|
|
|
#endif // CARBON_EXPLORER_AST_STATEMENT_H_
|