[rust] ForStatement support

Implements support for `ForStatement` from swc -> estree -> hir, flushing out 
more of the HIR representation and porting pieces from HIRBuilder as necessary.
This commit is contained in:
Joe Savona
2023-07-08 22:59:54 +09:00
parent 58a8d0bdc7
commit 8176f25719
9 changed files with 393 additions and 76 deletions
+105 -13
View File
@@ -1,14 +1,15 @@
use bumpalo::collections::{CollectIn, String};
use estree::{
AssignmentTarget, ExpressionLike, FunctionDeclaration, IfStatement, Literal, LiteralValue,
Pattern, Statement, VariableDeclarationKind,
AssignmentTarget, BinaryExpression, ExpressionLike, ForInit, ForStatement, FunctionDeclaration,
IfStatement, Literal, LiteralValue, Pattern, Statement, VariableDeclarationKind,
};
use hir::{
ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionKind,
InstructionValue, LValue, LoadGlobal, LoadLocal, Place, PrimitiveValue, TerminalValue,
ArrayElement, BlockKind, BranchTerminal, Environment, ForTerminal, Function, GotoKind,
Identifier, InstructionKind, InstructionValue, LValue, LoadGlobal, LoadLocal, Place,
PrimitiveValue, TerminalValue,
};
use crate::builder::{Binding, Builder};
use crate::builder::{Binding, Builder, LoopScope};
/// Converts a React function in ESTree format into HIR. Returns the HIR
/// if it was constructed sucessfully, otherwise a list of diagnostics
@@ -35,7 +36,7 @@ pub fn build<'a>(
}),
);
builder.terminate(
TerminalValue::ReturnTerminal(hir::ReturnTerminal {
TerminalValue::Return(hir::ReturnTerminal {
value: implicit_return_value,
}),
hir::BlockKind::Block,
@@ -55,7 +56,7 @@ fn lower_statement<'a>(
env: &'a Environment<'a>,
builder: &mut Builder<'a>,
stmt: Statement,
_label: Option<String<'a>>,
label: Option<String<'a>>,
) -> Result<(), Diagnostic> {
match stmt {
Statement::BlockStatement(stmt) => {
@@ -66,7 +67,7 @@ fn lower_statement<'a>(
Statement::BreakStatement(stmt) => {
let block = builder.resolve_break(stmt.label.as_ref())?;
builder.terminate(
TerminalValue::GotoTerminal(hir::GotoTerminal {
TerminalValue::Goto(hir::GotoTerminal {
block,
kind: GotoKind::Break,
}),
@@ -76,7 +77,7 @@ fn lower_statement<'a>(
Statement::ContinueStatement(stmt) => {
let block = builder.resolve_continue(stmt.label.as_ref())?;
builder.terminate(
TerminalValue::GotoTerminal(hir::GotoTerminal {
TerminalValue::Goto(hir::GotoTerminal {
block,
kind: GotoKind::Continue,
}),
@@ -95,7 +96,7 @@ fn lower_statement<'a>(
),
};
builder.terminate(
TerminalValue::ReturnTerminal(hir::ReturnTerminal { value }),
TerminalValue::Return(hir::ReturnTerminal { value }),
BlockKind::Block,
);
}
@@ -158,7 +159,7 @@ fn lower_statement<'a>(
let consequent_block = builder.enter(BlockKind::Block, |builder| {
lower_statement(env, builder, consequent, None).unwrap();
TerminalValue::GotoTerminal(hir::GotoTerminal {
TerminalValue::Goto(hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
})
@@ -168,14 +169,14 @@ fn lower_statement<'a>(
if let Some(alternate) = alternate {
lower_statement(env, builder, alternate, None).unwrap();
}
TerminalValue::GotoTerminal(hir::GotoTerminal {
TerminalValue::Goto(hir::GotoTerminal {
block: fallthrough_block.id,
kind: GotoKind::Break,
})
});
let test = lower_expression_to_temporary(env, builder, test);
let terminal = TerminalValue::IfTerminal(hir::IfTerminal {
let terminal = TerminalValue::If(hir::IfTerminal {
test,
consequent: consequent_block,
alternate: alternate_block,
@@ -183,6 +184,80 @@ fn lower_statement<'a>(
});
builder.terminate_with_fallthrough(terminal, fallthrough_block);
}
Statement::ForStatement(stmt) => {
let ForStatement {
init,
test,
update,
body,
..
} = *stmt;
// Block for the loop's test condition
let test_block = builder.reserve(BlockKind::Loop);
// Block for code following the loop
let fallthrough_block = builder.reserve(BlockKind::Block);
let init_block = builder.enter(BlockKind::Loop, |builder| {
if let Some(ForInit::VariableDeclaration(decl)) = init {
lower_statement(env, builder, Statement::VariableDeclaration(decl), None)
.unwrap();
TerminalValue::Goto(hir::GotoTerminal {
block: test_block.id,
kind: GotoKind::Break,
})
} else {
panic!("Expected for statement to have a variable declaration initializer")
}
});
let update_block = update.map(|update| {
builder.enter(BlockKind::Loop, |builder| {
lower_expression_to_temporary(env, builder, update);
TerminalValue::Goto(hir::GotoTerminal {
block: test_block.id,
kind: GotoKind::Break,
})
})
});
let body_block = builder.enter(BlockKind::Block, |builder| {
let loop_ = LoopScope {
label,
continue_block: update_block.unwrap_or(test_block.id),
break_block: fallthrough_block.id,
};
builder.enter_loop(loop_, |builder| {
lower_statement(env, builder, body, None).unwrap();
TerminalValue::Goto(hir::GotoTerminal {
block: update_block.unwrap_or(test_block.id),
kind: GotoKind::Continue,
})
})
});
let terminal = TerminalValue::For(ForTerminal {
body: body_block,
init: init_block,
test: test_block.id,
fallthrough: fallthrough_block.id,
update: update_block,
});
builder.terminate_with_fallthrough(terminal, test_block);
if let Some(test) = test {
let test_value = lower_expression_to_temporary(env, builder, test);
let terminal = TerminalValue::Branch(BranchTerminal {
test: test_value,
consequent: body_block,
alternate: fallthrough_block.id,
});
builder.terminate_with_fallthrough(terminal, fallthrough_block);
} else {
panic!("Expected for statement to have a tesst block");
}
}
_ => todo!("Lower {stmt:#?}"),
}
Ok(())
@@ -240,6 +315,7 @@ fn lower_expression<'a>(
.collect_in(env.allocator);
InstructionValue::Array(hir::Array { elements })
}
ExpressionLike::AssignmentExpression(expr) => match expr.operator {
estree::AssignmentOperator::Equals => {
let right = lower_expression_to_temporary(env, builder, expr.right);
@@ -248,6 +324,22 @@ fn lower_expression<'a>(
_ => todo!("lower assignment expr {:#?}", expr),
},
ExpressionLike::BinaryExpression(expr) => {
let BinaryExpression {
left,
operator,
right,
..
} = *expr;
let left = lower_expression_to_temporary(env, builder, left);
let right = lower_expression_to_temporary(env, builder, right);
InstructionValue::Binary(hir::Binary {
left,
operator,
right,
})
}
// Cases that cannot appear in expression position but which are included in ExpressionLike
// to make serialization easier
ExpressionLike::SpreadElement(_) => {
+113 -20
View File
@@ -1,4 +1,4 @@
use bumpalo::collections::Vec;
use bumpalo::collections::{String, Vec};
use std::{cell::RefCell, collections::HashSet, rc::Rc};
use hir::{
@@ -27,6 +27,8 @@ pub(crate) struct Builder<'a> {
wip: WipBlock<'a>,
id_gen: InstructionIdGenerator,
scopes: Vec<'a, ControlFlowScope<'a>>,
}
pub(crate) struct WipBlock<'a> {
@@ -35,6 +37,48 @@ pub(crate) struct WipBlock<'a> {
pub instructions: Vec<'a, Instruction<'a>>,
}
pub(crate) enum Binding<'a> {
Local(Identifier<'a>),
Module(Identifier<'a>),
Global,
}
#[derive(Clone, PartialEq, Eq, Debug)]
enum ControlFlowScope<'a> {
Loop(LoopScope<'a>),
// Switch(SwitchScope<'a>),
Label(LabelScope<'a>),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct LoopScope<'a> {
pub label: Option<String<'a>>,
pub continue_block: BlockId,
pub break_block: BlockId,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct LabelScope<'a> {
pub label: String<'a>,
pub block: BlockId,
}
impl<'a> ControlFlowScope<'a> {
fn label(&self) -> Option<&String<'a>> {
match self {
Self::Loop(scope) => scope.label.as_ref(),
Self::Label(scope) => Some(&scope.label),
}
}
fn break_block(&self) -> BlockId {
match self {
Self::Loop(scope) => scope.break_block,
Self::Label(scope) => scope.block,
}
}
}
impl<'a> Builder<'a> {
pub(crate) fn new(environment: &'a Environment<'a>) -> Self {
let entry = environment.next_block_id();
@@ -49,6 +93,7 @@ impl<'a> Builder<'a> {
entry,
wip: current,
id_gen: InstructionIdGenerator::new(),
scopes: Vec::new_in(&environment.allocator),
}
}
@@ -155,6 +200,17 @@ impl<'a> Builder<'a> {
);
}
pub(crate) fn enter_loop<F>(&mut self, scope: LoopScope<'a>, f: F) -> TerminalValue<'a>
where
F: FnOnce(&mut Self) -> TerminalValue<'a>,
{
self.scopes.push(ControlFlowScope::Loop(scope.clone()));
let terminal = f(self);
let last = self.scopes.pop().unwrap();
assert_eq!(last, ControlFlowScope::Loop(scope));
terminal
}
/// Returns a new temporary identifier
pub(crate) fn make_temporary(&self) -> hir::Identifier<'a> {
hir::Identifier {
@@ -173,9 +229,21 @@ impl<'a> Builder<'a> {
/// provided but cannot be resolved.
pub(crate) fn resolve_break(
&self,
_label: Option<&estree::Identifier>,
label: Option<&estree::Identifier>,
) -> Result<BlockId, Diagnostic> {
todo!()
for scope in self.scopes.iter().rev() {
match (label, scope.label()) {
// If this is an unlabeled break, return the most recent break target
(None, _) => return Ok(scope.break_block()),
// If the break is labeled and matches the current scope, return its break target
(Some(label), Some(scope_label)) if &label.name == scope_label => {
return Ok(scope.break_block());
}
// Otherwise keep searching
_ => continue,
}
}
Err(())
}
/// Resolves the target for the given continue label (if present), or returns the default
@@ -183,9 +251,36 @@ impl<'a> Builder<'a> {
/// provided but cannot be resolved.
pub(crate) fn resolve_continue(
&self,
_label: Option<&estree::Identifier>,
label: Option<&estree::Identifier>,
) -> Result<BlockId, Diagnostic> {
todo!()
for scope in self.scopes.iter().rev() {
match scope {
ControlFlowScope::Loop(scope) => {
match (label, &scope.label) {
// If this is an unlabeled continue, return the first matching loop
(None, _) => return Ok(scope.continue_block),
// If the continue is labeled and matches the current scope, return its continue target
(Some(label), Some(scope_label))
if label.name.as_str() == scope_label.as_str() =>
{
return Ok(scope.continue_block);
}
// Otherwise keep searching
_ => continue,
}
}
_ => {
match (label, scope.label()) {
(Some(label), Some(scope_label)) if label.name.as_str() == scope_label => {
// Error, the continue referred to a label that is not a loop
return Err(());
}
_ => continue,
}
}
}
}
Err(())
}
pub(crate) fn resolve_binding(
@@ -206,12 +301,6 @@ impl<'a> Builder<'a> {
}
}
pub(crate) enum Binding<'a> {
Local(Identifier<'a>),
Module(Identifier<'a>),
Global,
}
/// Modifies the HIR to put the blocks in reverse postorder, with predecessors before
/// successors (except for the case of loops)
fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) {
@@ -230,20 +319,24 @@ fn reverse_postorder_blocks<'a>(hir: &mut HIR<'a>) {
let block = hir.block(block_id);
let terminal = &block.terminal;
match &terminal.value {
TerminalValue::IfTerminal(terminal) => {
TerminalValue::Branch(terminal) => {
visit(terminal.alternate, hir, visited, postorder);
visit(terminal.consequent, hir, visited, postorder);
}
TerminalValue::ForTerminal(terminal) => {
TerminalValue::If(terminal) => {
visit(terminal.alternate, hir, visited, postorder);
visit(terminal.consequent, hir, visited, postorder);
}
TerminalValue::For(terminal) => {
visit(terminal.init, hir, visited, postorder);
}
TerminalValue::DoWhileTerminal(terminal) => {
TerminalValue::DoWhile(terminal) => {
visit(terminal.body, hir, visited, postorder);
}
TerminalValue::GotoTerminal(terminal) => {
TerminalValue::Goto(terminal) => {
visit(terminal.block, hir, visited, postorder);
}
TerminalValue::ReturnTerminal(..) => { /* no-op */ }
TerminalValue::Return(..) => { /* no-op */ }
}
postorder.push(block_id);
}
@@ -263,7 +356,7 @@ fn remove_unreachable_for_updates<'a>(hir: &mut HIR<'a>) {
let block_ids: HashSet<BlockId> = hir.blocks.keys().cloned().collect();
for block in hir.blocks.values_mut() {
if let TerminalValue::ForTerminal(terminal) = &mut block.terminal.value {
if let TerminalValue::For(terminal) = &mut block.terminal.value {
if let Some(update) = terminal.update {
if !block_ids.contains(&update) {
terminal.update = None;
@@ -297,9 +390,9 @@ fn remove_unreachable_do_while_statements<'a>(hir: &mut HIR<'a>) {
let block_ids: HashSet<BlockId> = hir.blocks.keys().cloned().collect();
for block in hir.blocks.values_mut() {
if let TerminalValue::DoWhileTerminal(terminal) = &mut block.terminal.value {
if let TerminalValue::DoWhile(terminal) = &mut block.terminal.value {
if !block_ids.contains(&terminal.test) {
block.terminal.value = TerminalValue::GotoTerminal(hir::GotoTerminal {
block.terminal.value = TerminalValue::Goto(hir::GotoTerminal {
block: terminal.body,
kind: GotoKind::Break,
});
@@ -353,7 +446,7 @@ fn mark_predecessors<'a>(hir: &mut HIR<'a>) {
fn invariant<F>(cond: bool, f: F) -> Result<(), Diagnostic>
where
F: FnOnce() -> String,
F: FnOnce() -> std::string::String,
{
if !cond {
let msg = f();
+34 -13
View File
@@ -6,8 +6,8 @@ use swc_core::common::errors::Handler;
use swc_core::common::source_map::Pos;
use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS};
use swc_core::ecma::ast::{
AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, MemberExpr, ModuleItem, Pat,
PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr,
AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Ident, Lit, MemberExpr, MemberProp,
ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind, VarDeclOrExpr,
};
use swc_core::ecma::parser::Syntax;
use swc_core::ecma::transforms::base::resolver;
@@ -366,6 +366,9 @@ fn convert_expression(cx: &Context, expr: &Expr) -> estree::ExpressionLike {
range: convert_span(&expr.span),
}))
}
Expr::Member(expr) => {
estree::ExpressionLike::MemberExpression(Box::new(convert_member_expression(cx, expr)))
}
_ => todo!("translate expression {:#?}", expr),
}
}
@@ -376,22 +379,40 @@ fn convert_assignment_target(cx: &Context, target: &PatOrExpr) -> estree::Assign
estree::AssignmentTarget::Pattern(Box::new(convert_pattern(cx, target)))
}
PatOrExpr::Expr(target) => {
if let Expr::Member(target) = target.as_ref() {
estree::AssignmentTarget::MemberExpression(Box::new(convert_member_expression(
cx, target,
)))
} else {
panic!(
"Invalid input, expected either a pattern or member expression, got {:#?}",
target
)
match target.as_ref() {
Expr::Member(target) => estree::AssignmentTarget::MemberExpression(Box::new(
convert_member_expression(cx, target),
)),
Expr::Ident(target) => estree::AssignmentTarget::Pattern(Box::new(
estree::Pattern::Identifier(Box::new(convert_identifier(cx, target))),
)),
_ => {
panic!("Expected assignment target to be member expression or identifier, got {:#?}", target)
}
}
}
}
}
fn convert_member_expression(_cx: &Context, _expr: &MemberExpr) -> estree::MemberExpression {
todo!("convert member expression")
fn convert_member_expression(cx: &Context, expr: &MemberExpr) -> estree::MemberExpression {
let (is_computed, property) = match &expr.prop {
MemberProp::Ident(prop) => (
false,
estree::ExpressionLike::Identifier(Box::new(convert_identifier(cx, prop))),
),
MemberProp::Computed(prop) => (true, convert_expression(cx, &prop.expr)),
_ => {
panic!("PrivateName member expression properties are not supported")
}
};
estree::MemberExpression {
object: convert_expression(cx, &expr.obj),
property,
is_computed,
is_optional: false,
loc: None,
range: convert_span(&expr.span),
}
}
fn convert_unary_operator(op: UnaryOp) -> estree::UnaryOperator {
+12 -1
View File
@@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
use static_assertions::assert_eq_size;
use std::num::NonZeroU32;
use std::{fmt::Display, num::NonZeroU32};
#[derive(Serialize, Deserialize, Debug)]
pub struct SourceLocation {
@@ -689,6 +689,17 @@ pub enum BinaryOperator {
Instanceof,
}
impl Display for BinaryOperator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
Self::Plus => "+",
Self::LessThan => "<",
_ => todo!("display for operator: {:#?}", self),
};
f.write_str(name)
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CallExpression {
#[serde(rename = "optional")]
@@ -0,0 +1,7 @@
function foo() {
let x = 0;
for (let i = 0; i < 10; i = i + 1) {
x = x + i;
}
return x;
}
@@ -0,0 +1,46 @@
---
source: crates/fixtures/tests/fixtures_test.rs
expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
input_file: crates/fixtures/tests/fixtures/for-statement.js
---
Input:
function foo() {
let x = 0;
for (let i = 0; i < 10; i = i + 1) {
x = x + i;
}
return x;
}
Output:
entry bb0
bb0
[0] unknown $0 = 0
[1] unknown $2 = StoreLocal Let unknown x$1 = unknown $0
[2] For init=bb3 test=bb1 update=bb4 body=bb5 fallthrough=bb2
bb3
[3] unknown $3 = 0
[4] unknown $5 = StoreLocal Let unknown i$4 = unknown $3
[5] Goto bb1
bb1
[6] unknown $14 = LoadLocal unknown i$4
[7] unknown $15 = 10
[8] unknown $16 = Binary unknown $14 < unknown $15
[9] Branch unknown $16 consequent=bb5 alternate=bb2
bb5
[10] unknown $10 = LoadLocal unknown x$1
[11] unknown $11 = LoadLocal unknown i$4
[12] unknown $12 = Binary unknown $10 + unknown $11
[13] unknown $13 = StoreLocal Reassign unknown x$1 = unknown $12
[14] Goto bb4
bb4
[15] unknown $6 = LoadLocal unknown i$4
[16] unknown $7 = 1
[17] unknown $8 = Binary unknown $6 + unknown $7
[18] unknown $9 = StoreLocal Reassign unknown i$4 = unknown $8
[19] Goto bb1
bb2
[20] unknown $17 = LoadLocal unknown x$1
[21] Return unknown $17
@@ -1,6 +1,7 @@
use std::{cell::RefCell, fmt::Display, rc::Rc};
use bumpalo::collections::{String, Vec};
use estree::BinaryOperator;
use crate::{IdentifierId, InstructionId, ScopeId, Type};
@@ -15,7 +16,7 @@ pub struct Instruction<'a> {
pub enum InstructionValue<'a> {
Array(Array<'a>),
// Await(Await<'a>),
// Binary(Binary<'a>),
Binary(Binary<'a>),
// Call(Call<'a>),
// ComputedDelete(ComputedDelete<'a>),
// ComputedLoad(ComputedLoad<'a>),
@@ -59,6 +60,13 @@ pub enum ArrayElement<'a> {
Spread(Place<'a>),
}
#[derive(Debug)]
pub struct Binary<'a> {
pub left: Place<'a>,
pub operator: BinaryOperator,
pub right: Place<'a>,
}
#[derive(Debug)]
pub struct Primitive<'a> {
pub value: PrimitiveValue<'a>,
+32 -3
View File
@@ -90,6 +90,12 @@ impl<'a> Print for InstructionValue<'a> {
write!(out, "DeclareLocal ")?;
value.lvalue.print(out)?;
}
InstructionValue::Binary(value) => {
write!(out, "Binary ")?;
value.left.print(out)?;
write!(out, " {} ", value.operator)?;
value.right.print(out)?;
}
_ => write!(out, "{:?}", self)?,
}
Ok(())
@@ -146,14 +152,14 @@ impl<'a> Print for Terminal<'a> {
impl<'a> Print for TerminalValue<'a> {
fn print(&self, out: &mut impl Write) -> Result {
match self {
TerminalValue::ReturnTerminal(terminal) => {
TerminalValue::Return(terminal) => {
write!(out, "Return ")?;
terminal.value.print(out)?;
}
TerminalValue::GotoTerminal(terminal) => {
TerminalValue::Goto(terminal) => {
write!(out, "Goto {}", terminal.block)?;
}
TerminalValue::IfTerminal(terminal) => {
TerminalValue::If(terminal) => {
write!(out, "If ")?;
terminal.test.print(out)?;
write!(
@@ -167,6 +173,29 @@ impl<'a> Print for TerminalValue<'a> {
}
)?;
}
TerminalValue::Branch(terminal) => {
write!(out, "Branch ")?;
terminal.test.print(out)?;
write!(
out,
" consequent={} alternate={}",
terminal.consequent, terminal.alternate,
)?;
}
TerminalValue::For(terminal) => {
write!(
out,
"For init={} test={} update={} body={} fallthrough={}",
terminal.init,
terminal.test,
match terminal.update {
Some(fallthrough) => format!("{fallthrough}"),
None => "<none>".to_string(),
},
terminal.body,
terminal.fallthrough,
)?;
}
_ => write!(out, "{:?}", self)?,
}
Ok(())
+35 -25
View File
@@ -10,22 +10,22 @@ pub struct Terminal<'a> {
#[derive(Debug)]
pub enum TerminalValue<'a> {
// BranchTerminal(BranchTerminal),
DoWhileTerminal(DoWhileTerminal),
// ForOfTerminal(ForOfTerminal),
ForTerminal(ForTerminal),
GotoTerminal(GotoTerminal),
IfTerminal(IfTerminal<'a>),
// LabelTerminal(LabelTerminal),
// LogicalTerminal(LogicalTerminal),
// OptionalTerminal(OptionalTerminal),
ReturnTerminal(ReturnTerminal<'a>),
// SequenceTerminal(SequenceTerminal),
// SwitchTerminal(SwitchTerminal),
// TernaryTerminal(TernaryTerminal),
// ThrowTerminal(ThrowTerminal),
// UnsupportedTerminal(UnsupportedTerminal),
// WhileTerminal(WhileTerminal),
Branch(BranchTerminal<'a>),
DoWhile(DoWhileTerminal),
// ForOf(ForOfTerminal),
For(ForTerminal),
Goto(GotoTerminal),
If(IfTerminal<'a>),
// Label(LabelTerminal),
// Logical(LogicalTerminal),
// Optional(OptionalTerminal),
Return(ReturnTerminal<'a>),
// Sequence(SequenceTerminal),
// Switch(SwitchTerminal),
// Ternary(TernaryTerminal),
// Throw(ThrowTerminal),
// Unsupported(UnsupportedTerminal),
// While(WhileTerminal),
}
impl<'a> TerminalValue<'a> {
@@ -34,43 +34,53 @@ impl<'a> TerminalValue<'a> {
F: Fn(BlockId) -> Option<BlockId>,
{
match self {
Self::IfTerminal(terminal) => {
Self::If(terminal) => {
terminal.fallthrough = match terminal.fallthrough {
Some(fallthrough) => f(fallthrough),
_ => None,
}
}
Self::DoWhileTerminal(DoWhileTerminal { fallthrough, .. })
| Self::ForTerminal(ForTerminal { fallthrough, .. }) => {
Self::DoWhile(DoWhileTerminal { fallthrough, .. })
| Self::For(ForTerminal { fallthrough, .. }) => {
// statically detect if fallthrough is changed to Option so
// that we can update to map the fallthrough w f()
let _: BlockId = *fallthrough;
}
Self::GotoTerminal(_) | Self::ReturnTerminal(_) => {}
Self::Branch(_) | Self::Goto(_) | Self::Return(_) => {}
}
}
pub fn successors(&self) -> Vec<BlockId> {
match self {
Self::IfTerminal(terminal) => {
Self::If(terminal) => {
vec![terminal.consequent, terminal.alternate]
}
Self::ForTerminal(terminal) => {
Self::Branch(terminal) => {
vec![terminal.consequent, terminal.alternate]
}
Self::For(terminal) => {
vec![terminal.init]
}
Self::DoWhileTerminal(terminal) => {
Self::DoWhile(terminal) => {
vec![terminal.body]
}
Self::GotoTerminal(terminal) => {
Self::Goto(terminal) => {
vec![terminal.block]
}
Self::ReturnTerminal(_) => {
Self::Return(_) => {
vec![]
}
}
}
}
#[derive(Debug)]
pub struct BranchTerminal<'a> {
pub test: Place<'a>,
pub consequent: BlockId,
pub alternate: BlockId,
}
#[derive(Debug)]
pub struct GotoTerminal {
pub block: BlockId,