Feature Coverage

Every formalang IR construct maps to a compile phase. The tables below record exactly what's lowered today and how. Inside a module, every feature is supported; the Boundary Policy restrictions apply only to types appearing in pub signatures.

IrExpr variants

VariantPhaseNotes
Literal1aNumeric, boolean literals; string literals in 2
Reference (dotted path)1aResolved to local, global, or function reference
LetRef1aLocal-variable read
SelfFieldRef1bself.field inside methods
FieldAccess1bobj.field
BinaryOp (numeric / boolean / comparison)1aDirect Wasm instructions
BinaryOp::Add on String2String concatenation runtime helper
BinaryOp::Range1cLowers to {start, end} pair in linear memory
BinaryOp::Eq/Ne on String2String equality runtime helper
UnaryOp1aNeg, Not
If1aMaps to Wasm if/else
Block1aSequence of statements + result expression
For (over Array)1cloop + br_if with index counter
For (over Range)1c (I32 / I64) + post-Phase-4 housekeeping (F32 / F64)Same lowering for all four numeric primitives. Float ranges advance by 1.0 per iteration; the output buffer is sized to ceil(end - start) so fractional gaps don't overrun.
Match1bbr_table on enum tag, payload extraction by offset
FunctionCall (direct)1aWasm call instruction
MethodCall (Static dispatch)1bResolved to direct call at compile time
MethodCall (Virtual dispatch)3Vtable lookup + call_indirect
StructInst1bBump-allocate, write fields, return pointer
EnumInst1bAllocate tag + payload, return pointer
Array (literal)1cAllocate {ptr, len, cap} header + element buffer
Tuple (literal)1bTreated as anonymous struct
DictLiteral2Sorted-pairs array v1
DictAccess2Lookup runtime helper
ClosureEliminated upstream by closure-conversion IrPass
ClosureRef { funcref, env_struct }1bSynthesised by closure conversion; lowered as funcref index + env pointer

ResolvedType variants

TypePhaseNotes
Primitive(I32/I64/F32/F64)1aNative Wasm valtypes
Primitive(Boolean)1aLowered as i32 (0 or 1)
Primitive(Never)1aZero-sized; functions returning Never emit unreachable
Primitive(String)2Linear-memory {ptr, len}
Primitive(Path)2Same layout as String; identity preserved internally
Primitive(Regex)2Same layout as String; identity preserved internally
Struct1bHeap-allocated record
Enum1bTag (i32) + padded payload
Tuple1bSame layout as anonymous Struct
Array<T>1c{ptr, len, cap}
Range<T>1c{start, end} over numeric T
Optional<T>2Tag + payload, or null-pointer trick for reference types
Dictionary<K, V>2Sorted-pairs array v1
Closure { param_tys, return_ty }1bFuncref index + env pointer; intramodule only
External { module_path, name, … }5+Upstream-blocked. compile_to_ir_with_resolver returns one IrModule and discards imported-module IRs; backend has nothing to resolve External against.
Generic { base, args }Eliminated by upstream MonomorphisePass
TypeParamPre-flight rejection
TraitBanned as a value at semantic time upstream
ErrorPre-flight rejection (frontend invariant violation)

ParamConvention variants

ConventionPhaseLowering
Let (default)1aPass by value (or by pointer for aggregates)
Mut1bPass pointer into caller's frame; callee mutates in place
Sink1bMove semantics: caller relinquishes the buffer; callee owns it

DispatchKind variants

DispatchPhaseLowering
Static { impl_id }1bDirect call to a known function index
Virtual { trait_id, method_name }3Per-trait vtable in linear memory; call_indirect

Patterns

formalang's IR flattens patterns to variant-name + simple bindings (no nested patterns, guards, or-patterns, or range patterns at the IR level). The wasm lowering handles this directly via br_table on the variant tag plus offset-based payload extraction. BindingPattern destructuring in let bindings is also flattened upstream into simple Let nodes.

Operators

BinaryOp: Add, Sub, Mul, Div, Mod, Lt, Gt, Le, Ge, Eq, Ne, And, Or, Range. UnaryOp: Neg, Not.

Operator lowering is type-dispatchedBinaryOp::Add on I32 lowers to a single Wasm instruction, but on String it calls the __str_concat runtime helper. The dispatch table lives in src/lower/binary_op.rs.

Phase milestones

The phases referenced above correspond to the project's milestone tests. Each milestone hand-builds an IrModule exercising the phase's features and runs it under wasmtime end-to-end:

PhaseMilestone testWhat it proves
1atests/backend_smoke.rsRecursive fibonacci runs
1btests/milestone_1b.rsCounter struct + Action enum + methods (mut self, Match)
1ctests/sieve.rsSieve of Eratosthenes returns list<bool>
2tests/milestone_2.rsgreet(role: String) -> String exercising Dictionary<String,String>, I32? Some-wrap, string concatenation
3tests/milestone_3.rsTrait Greet dispatches across two impls
4tests/milestone_4.rsHost-provided host_double extern called from call_host

The full phase-by-phase history is in CHANGELOG.md.