[rust] IfStatement support
Lowering for IfStatements, which includes porting some of the HIRBuilder helpers like enter() and reserve().
Joe Savona committed
Jul 6, 2023 at 09:24 UTC
357e9de19b891d4424a6d2888c8a849d7e2c3b92
5 files changed
+148
-4
compiler/forget/crates/build-hir/src/build.rs
+41
-2
@@ -1,7 +1,7 @@
1
use bumpalo::collections::{CollectIn, String};
2
use estree::{
3
- AssignmentTarget, ExpressionLike, FunctionDeclaration, Literal, LiteralValue, Pattern,
4
- Statement, VariableDeclarationKind,
3
+ AssignmentTarget, ExpressionLike, FunctionDeclaration, IfStatement, Literal, LiteralValue,
4
+ Pattern, Statement, VariableDeclarationKind,
5
};
6
use hir::{
7
ArrayElement, BlockKind, Environment, Function, GotoKind, Identifier, InstructionKind,
@@ -144,6 +144,45 @@ fn lower_statement<'a>(
144
}
145
}
146
}
147
+ Statement::IfStatement(stmt) => {
148
+ // block for what follows the if statement, though this may
149
+ // not be reachable
150
+ let fallthrough_block = builder.reserve(BlockKind::Block);
151
+
152
+ let IfStatement {
153
+ test,
154
+ consequent,
155
+ alternate,
156
+ ..
157
+ } = *stmt;
158
+
159
+ let consequent_block = builder.enter(BlockKind::Block, |builder| {
160
+ lower_statement(env, builder, consequent, None).unwrap();
161
+ TerminalValue::GotoTerminal(hir::GotoTerminal {
162
+ block: fallthrough_block.id,
163
+ kind: GotoKind::Break,
164
+ })
165
+ });
166
+
167
+ let alternate_block = builder.enter(BlockKind::Block, |builder| {
168
+ if let Some(alternate) = alternate {
169
+ lower_statement(env, builder, alternate, None).unwrap();
170
+ }
171
+ TerminalValue::GotoTerminal(hir::GotoTerminal {
172
+ block: fallthrough_block.id,
173
+ kind: GotoKind::Break,
174
+ })
175
+ });
176
+
177
+ let test = lower_expression_to_temporary(env, builder, test);
178
+ let terminal = TerminalValue::IfTerminal(hir::IfTerminal {
179
+ test,
180
+ consequent: consequent_block,
181
+ alternate: alternate_block,
182
+ fallthrough: Some(fallthrough_block.id),
183
+ });
184
+ builder.terminate_with_fallthrough(terminal, fallthrough_block);
185
+ }
186
_ => todo!("Lower {stmt:#?}"),
187
}
188
Ok(())
compiler/forget/crates/build-hir/src/builder.rs
+49
-1
@@ -96,7 +96,15 @@ impl<'a> Builder<'a> {
96
kind: next_kind,
97
instructions: Vec::new_in(&self.environment.allocator),
98
};
99
- let prev_wip = std::mem::replace(&mut self.wip, next_wip);
99
+ self.terminate_with_fallthrough(terminal, next_wip)
100
+ }
101
+
102
+ pub(crate) fn terminate_with_fallthrough(
103
+ &mut self,
104
+ terminal: TerminalValue<'a>,
105
+ fallthrough: WipBlock<'a>,
106
+ ) {
107
+ let prev_wip = std::mem::replace(&mut self.wip, fallthrough);
108
self.completed.insert(
109
prev_wip.id,
110
BasicBlock {
@@ -112,6 +120,46 @@ impl<'a> Builder<'a> {
120
);
121
}
122
123
+ pub(crate) fn reserve(&mut self, kind: BlockKind) -> WipBlock<'a> {
124
+ WipBlock {
125
+ id: self.environment.next_block_id(),
126
+ kind,
127
+ instructions: Vec::new_in(&self.environment.allocator),
128
+ }
129
+ }
130
+
131
+ pub(crate) fn enter<F>(&mut self, kind: BlockKind, f: F) -> BlockId
132
+ where
133
+ F: FnOnce(&mut Self) -> TerminalValue<'a>,
134
+ {
135
+ let wip = self.reserve(kind);
136
+ let id = wip.id;
137
+ self.enter_reserved(wip, f);
138
+ id
139
+ }
140
+
141
+ fn enter_reserved<F>(&mut self, wip: WipBlock<'a>, f: F)
142
+ where
143
+ F: FnOnce(&mut Self) -> TerminalValue<'a>,
144
+ {
145
+ let current = std::mem::replace(&mut self.wip, wip);
146
+ let terminal = f(self);
147
+ let completed = std::mem::replace(&mut self.wip, current);
148
+ self.completed.insert(
149
+ completed.id,
150
+ BasicBlock {
151
+ id: completed.id,
152
+ kind: completed.kind,
153
+ instructions: completed.instructions,
154
+ terminal: Terminal {
155
+ id: self.id_gen.next(),
156
+ value: terminal,
157
+ },
158
+ predecessors: Default::default(),
159
+ },
160
+ );
161
+ }
162
+
163
/// Returns a new temporary identifier
164
pub(crate) fn make_temporary(&self) -> hir::Identifier<'a> {
165
hir::Identifier {
compiler/forget/crates/fixtures/tests/fixtures/if-statement.js
new
+8
@@ -0,0 +1,8 @@
1
+function foo(a, b, c, d) {
2
+ if (a) {
3
+ return b;
4
+ } else {
5
+ c;
6
+ }
7
+ d;
8
+}
compiler/forget/crates/fixtures/tests/snapshots/fixtures_test__fixtures@if-statement.js.snap
new
+32
@@ -0,0 +1,32 @@
1
+---
2
+source: crates/fixtures/tests/fixtures_test.rs
3
+expression: "format!(\"Input:\\n{input}\\n\\nOutput:\\n{output}\")"
4
+input_file: crates/fixtures/tests/fixtures/if-statement.js
5
+---
6
+Input:
7
+function foo(a, b, c, d) {
8
+ if (a) {
9
+ return b;
10
+ } else {
11
+ c;
12
+ }
13
+ d;
14
+}
15
+
16
+
17
+Output:
18
+entry bb0
19
+bb0
20
+ [0] unknown $5 = LoadLocal unknown a$4
21
+ [1] If unknown $5 consequent=bb2 alternate=bb4 fallthrough=bb1
22
+bb2
23
+ [2] unknown $1 = LoadLocal unknown b$0
24
+ [3] Return unknown $1
25
+bb4
26
+ [4] unknown $3 = LoadLocal unknown c$2
27
+ [5] Goto bb1
28
+bb1
29
+ [6] unknown $7 = LoadLocal unknown d$6
30
+ [7] unknown $8 = <undefined>
31
+ [8] Return unknown $8
32
+
compiler/forget/crates/hir/src/print.rs
+18
-1
@@ -1,7 +1,7 @@
1
use std::fmt::{Result, Write};
2
3
use crate::{
4
- ArrayElement, BasicBlock, Function, Instruction, InstructionValue, LValue, Place,
4
+ terminal, ArrayElement, BasicBlock, Function, Instruction, InstructionValue, LValue, Place,
5
PrimitiveValue, Terminal, TerminalValue,
6
};
7
@@ -150,6 +150,23 @@ impl<'a> Print for TerminalValue<'a> {
150
write!(out, "Return ")?;
151
terminal.value.print(out)?;
152
}
153
+ TerminalValue::GotoTerminal(terminal) => {
154
+ write!(out, "Goto {}", terminal.block)?;
155
+ }
156
+ TerminalValue::IfTerminal(terminal) => {
157
+ write!(out, "If ")?;
158
+ terminal.test.print(out)?;
159
+ write!(
160
+ out,
161
+ " consequent={} alternate={} fallthrough={}",
162
+ terminal.consequent,
163
+ terminal.alternate,
164
+ match terminal.fallthrough {
165
+ Some(fallthrough) => format!("{fallthrough}"),
166
+ None => "<none>".to_string(),
167
+ }
168
+ )?;
169
+ }
170
_ => write!(out, "{:?}", self)?,
171
}
172
Ok(())