pub enum Stmt {
Show 26 variants
ConstInt(Temp, i64),
ConstFloat(Temp, f64),
ConstBool(Temp, bool),
ConstStr(Temp, String),
ConstUnit(Temp),
Copy {
dst: Temp,
src: Temp,
},
Bin {
dst: Temp,
op: BinOp,
class: OperandClass,
lhs: Temp,
rhs: Temp,
},
Neg {
dst: Temp,
class: OperandClass,
src: Temp,
},
Not {
dst: Temp,
src: Temp,
},
BitNot {
dst: Temp,
src: Temp,
},
Call {
dst: Option<Temp>,
callee: String,
args: Vec<Temp>,
},
CallIndirect {
dst: Option<Temp>,
callee: Temp,
params: Vec<MirTy>,
ret: MirTy,
args: Vec<Temp>,
},
Await {
dst: Option<Temp>,
future: Temp,
},
CallRuntime {
dst: Option<Temp>,
func: RtFunc,
args: Vec<Temp>,
},
MakeClosure {
dst: Temp,
code: String,
captures: Vec<(Temp, MirTy)>,
},
MakeSum {
dst: Temp,
tag: u32,
fields: Vec<(Temp, MirTy)>,
},
SumTag {
dst: Temp,
sum: Temp,
},
SumField {
dst: Temp,
sum: Temp,
index: u32,
ty: MirTy,
},
MakeRecord {
dst: Temp,
fields: Vec<(Temp, MirTy)>,
},
RecordField {
dst: Temp,
record: Temp,
index: u32,
ty: MirTy,
},
SetField {
record: Temp,
index: u32,
value: Temp,
ty: MirTy,
},
MakeArray {
dst: Temp,
elems: Vec<(Temp, MirTy)>,
},
ArrayAlloc {
dst: Temp,
len: Temp,
},
ArrayLen {
dst: Temp,
arr: Temp,
},
ArrayGet {
dst: Temp,
arr: Temp,
index: Temp,
ty: MirTy,
},
ArraySet {
arr: Temp,
index: Temp,
value: Temp,
ty: MirTy,
},
}Expand description
A single 3-address statement.
Variants§
ConstInt(Temp, i64)
ConstFloat(Temp, f64)
ConstBool(Temp, bool)
ConstStr(Temp, String)
Interned string literal; codegen emits it as module data.
ConstUnit(Temp)
Copy
Bin
Neg
Not
Boolean not.
BitNot
Bitwise not (I64).
Call
Direct call to another Nova function by mangled name.
CallIndirect
Indirect call through a function value (fat pointer). callee is a
{ code_ptr, env_ptr } block; codegen loads both and calls
code_ptr(env_ptr, args...). params are the real parameter
classes (excluding the leading env).
Await
A suspend point — future.await — before the async state-machine
transform has turned it into control flow.
No statement reaching a codegen backend is this one. It is a marker
that async_lower’s block split consumes, replacing it with the
sequence that actually performs an await: poll future through its own
fat pointer, return POLL_PENDING if the answer is pending, and
otherwise copy the awaited value out of the inner state object’s output
slot into dst’s slot. Every part of that sequence addresses the state
object of the function containing the await, which does not exist until
the transform builds it — so the suspend cannot be lowered into control
flow where it is parsed without inventing that layout twice. Both
backends reject this variant rather than emitting anything for it.
future holds a { poll_code, state } two-word future value, the same
fat-pointer shape MakeClosure builds. dst is None exactly when the
awaited output is unit, matching Call’s convention for a call that
produces no value.
CallRuntime
Call into the Nova runtime.
MakeClosure
Build a function value: a 2-word fat pointer { code_ptr, env_ptr }.
code is the mangled callee whose address is code_ptr; captures
are stored into a freshly allocated environment (empty → null env).
MakeSum
Allocate and initialize a sum value: { tag, fields... }.
SumTag
Load the tag of a sum value.
SumField
Load payload field index of a sum value.
MakeRecord
Allocate and initialize a record value { fields... } (no tag).
RecordField
Load field index of a record value.
SetField
Store field index of a record. Mirrors RecordField’s 8 * index
offset, so reads and writes stay in the same layout.
MakeArray
Allocate and initialize an array { len, elems... }.
ArrayAlloc
Allocate an array of len elements: 8 + 8*len zeroed bytes with len
stored at offset 0. Elements are left zeroed — the lowering fills them
with its own loop (see lower_array_repeat), so neither backend needs a
loop emitter.
len must be in 0..=MAX_ARRAY_LEN; the lowering guarantees that by
emitting guards that abort first. Both bounds are memory-safety
requirements, because both backends compute the size as 8*len + 8 with
wrapping arithmetic:
- A small negative
lenis harmless on its own —gc::allocclamps every request withsize.max(8), so8 + 8*(-1) = 0still yields a block the 8-byte length store fits in — but storing a negative length would leave an array whose every access fails, and a large-magnitude negativelenoverflows the8 * lenmultiplication into a wild size. - A
lenaboveMAX_ARRAY_LENwraps8*len + 8back to a negative size (atlen = 2^60it is exactlyi64::MIN + 8), which the samesize.max(8)clamp turns into an 8-byte block. The huge length is then stored into that block’s header and the fill loop — which carries no bounds check by design — writes far past its end.
Aborting up front reports the mistake where it was made instead.
ArrayLen
Load the length (element count) of an array.
ArrayGet
Load arr[index] (dynamic index; caller has bounds-checked).
ArraySet
Store arr[index] = value (dynamic index; caller has bounds-checked).