Section 21A: LLVM Backend
Current Test Results
| Test Suite | Passed | Failed | Skipped | LCFail | Total |
|---|---|---|---|---|---|
| Ori spec (evaluator) | 3035 | 0 | 42 | - | 3077 |
| Ori spec (LLVM backend) | 1082 | 1 | 9 | 1985 | 3077 |
| Rust unit tests (LLVM) | 527 | 0 | 15 | - | 542 |
Import Resolution (Unified Pipeline)
The JIT test runner resolves imports via oric::imports::resolve_imports() and compiles
imported function bodies directly into the JIT module. This uses the same declare_all /
define_all path as main module functions, so most new codegen features automatically
work for imported functions too.
Features that need import-aware changes
When implementing these features, ensure they also work across module boundaries:
-
Generic monomorphization:
declare_allskips generics (sig.is_generic()). To compileassert_eq<T: Eq>(actual: int, expected: int), the monomorphization pass must collect concrete instantiation sites from the calling module and generate specialized versions of imported generic functions. This is the single largest gap —assert_eqis used in 2,472 test call sites. -
Impl blocks from imported modules: Currently only top-level functions are compiled from imports. If an imported module has
impl Type { @method ... }, those methods needcompile_impls()processing with the imported module’s impl_sigs. -
Type declarations from imported modules:
register_user_types()only processes the main module’s types. Imported struct/sum type definitions need registration so the LLVM type resolver can compute their layouts. -
Prelude functions: Currently skipped in JIT mode because some prelude functions (e.g.,
comparereturningOrdering) use types the codegen can’t handle yet. Once sum type codegen (21.2) works, prelude compilation should be re-enabled.
21.1 LLVM Setup & Infrastructure
-
Setup: LLVM development environment
- Docker container with LLVM 21 and development headers
- Add
inkwellcrate tocompiler/ori_llvm/Cargo.toml - Verify LLVM bindings compile and link correctly
- Create
compiler/ori_llvm/src/module structure
-
Implement: LLVM context and module initialization
- Rust Tests:
context.rs— SimpleCx, CodegenCx, TypeCache - Create LLVM context, module, and builder abstractions
- Rust Tests:
-
Implement: Basic target configuration
- Support native target detection (JIT)
- Configure data layout and target features (AOT)
21.2 Type Lowering
-
Implement: Primitive type mapping
- Rust Tests:
types.rs,context.rs— type mapping - Map Ori primitives (int → i64, float → f64, bool → i1, char → i32, byte → i8)
- Map strings to
{ i64 len, ptr data }struct - Handle function types
- Rust Tests:
-
Implement: Option/Result types
- Map Option/Result to
{ i8 tag, i64 payload }tagged unions - Tag values: None=0, Some=1; Err=0, Ok=1
- Proper payload handling for non-primitive types
- Map Option/Result to
-
Implement: Collection type representations in
ori_llvm/src/codegen/— list/map/set LLVM struct layouts- Map lists to
{ i64 len, i64 cap, ptr data }struct - Map maps to appropriate hash table representation
- Map sets to appropriate hash set representation
- Map lists to
-
Implement: Duration & Size types
- Map Duration to i64 (nanoseconds)
- Map Size to i64 (bytes)
- Duration arithmetic operations (add, sub, mul, div, mod)
- Size arithmetic operations (add, sub, mul, div, mod)
- Size cross-unit normalization —
512kb + 512kb == 1mbequality fails in AOT (test_op_size_arithmetic) - Mixed-type arithmetic (Duration * int, int * Duration, Duration / Duration → int)
- Size unary negation compile error
- Overflow panic semantics for Duration
- Division by zero panics
- Duration methods:
.nanoseconds(),.microseconds(),.milliseconds(),.seconds(),.minutes(),.hours() - Size methods:
.bytes(),.kilobytes(),.megabytes(),.gigabytes(),.terabytes() - Factory functions:
Duration.from_nanoseconds(ns:),Size.from_bytes(b:)
-
Implement: Newtype codegen in
ori_llvm/src/codegen/— transparent wrapper representation- Newtype type representation (same as wrapped type)
- Constructor codegen:
TypeName(value) -
.innerfield access - Proper type distinction for trait dispatch
-
Implement: Sum type codegen (beyond Option/Result)
- User-defined sum type representation (tagged union)
- Variant constructor codegen
- Tag-based dispatch in match
- Multi-field variant payloads
- Import note: Once sum types work, re-enable prelude function compilation in
JIT mode (
runner.rs). Currently skipped becausecompare() -> Orderingfails.
-
Implement: Fixed-capacity lists
[T, max N]- Inline allocation strategy
- Compile-time capacity tracking
-
.capacity(),.is_full(),.remaining()methods -
.push()with panic on full -
.try_push()→bool -
.push_or_drop(),.push_or_oldest() -
.to_dynamic()conversion -
.to_fixed<$N>()and.try_to_fixed<$N>()
-
Implement: Channel type codegen —
Producer<T>/Consumer<T>LLVM representations and runtime calls-
Producer<T>andConsumer<T>type representation -
CloneableProducer<T>andCloneableConsumer<T> -
Sendabletrait constraint checking - Channel buffer management
-
21.3 Expression Codegen
-
Implement: Core expression codegen — literals, binary/unary ops, calls, field access in
ori_llvm/src/codegen/- Rust Tests:
tests/arithmetic_tests.rs,tests/operator_tests.rs - Literals (int, float, bool, string, char, byte)
- Binary ops (add, sub, mul, div, mod, comparisons, logical)
- Unary ops (neg, not)
- Function calls and method dispatch
- Field access and basic indexing
- Rust Tests:
-
Implement: Range expressions with step
-
start..end by stepcodegen -
start..=end by stepinclusive ranges - Negative step support (descending:
10..0 by -1) - Infinite ranges:
0..,0.. by step - Zero-step panic
- Empty range detection (mismatched direction)
-
-
Implement: Spread operator
...- List spread:
[...a, x, ...b] - Map spread:
{...a, key: v, ...b} - Struct spread:
Point { ...orig, x: 10 } - Later-wins merge semantics
- List spread:
-
Implement: Coalesce operator
??-
option ?? defaultcodegen -
result ?? defaultcodegen - Short-circuit evaluation
- Type inference for coalesce chains
-
-
Implement: Floor division operator
div- Integer floor division codegen
- Distinct from
/(truncating division)
-
Implement: Bitwise operator codegen —
~/&/|/^/<</>>for int and byte types-
~(BitNot) codegen -
&,|,^for int and byte types -
<<,>>shift operators - Shift overflow panics (count < 0 or count >= bit width)
-
-
Implement: Type conversion codegen —
as/as?/Intotrait dispatch in LLVM IR-
expr as Typeinfallible conversion codegen -
expr as? Typefallible conversion →Option<T> - Standard conversions: int→float, str→Error, Set
→[T] - Into trait method dispatch
-
-
Implement: Assignment to complex targets
- Field assignment:
point.x = 10 - Index assignment:
list[0] = value - Nested assignments:
a.b.c = value
- Field assignment:
-
Implement: String operation codegen — indexing, interpolation, escapes, methods via
ori_rtcalls- String indexing with Unicode handling (
str[i]→ single-codepointstr) - String interpolation:
`Hello {name}` - All escape sequences:
\n,\t,\r,\0,\\,\" -
str.concat()return type tracking — result not tracked as str, downstream comparison uses icmp (test_str_concat_basic) -
str.to_str()identity return type tracking — same icmp issue (test_str_to_str_identity) - String ordering (
<,>,<=,>=) — uses icmp instead of compare runtime call (test_str_compare_less) - String methods not in builtin table:
contains,starts_with,ends_with,trim,to_uppercase,to_lowercase,replace,split,repeat,chars(test_str_containset al.) - Struct update (
...spread) with string fields — GEP on non-pointer value causes segfault (test_struct_update_with_string)
- String indexing with Unicode handling (
21.4 Operator Trait Dispatch
-
Implement: User-defined impl blocks and associated functions
- Register user-defined struct types with LLVM
- Support user-defined
impl Type { ... }blocks - Generate method dispatch for user-defined methods
- Support associated functions (methods without
selfparameter) - Enable
Type.method()syntax for user-defined types - Import note: When impl blocks work, also compile imported modules’ impl blocks
via
compile_impls(). Currently only top-level functions are compiled from imports. - Import note:
register_user_types()must also process imported modules’ type declarations so the LLVM type resolver can compute their layouts. - Tests:
tests/spec/types/associated_functions.ori(9 tests passing)
-
Implement: User-defined operator dispatch
- Rust Tests:
tests/operator_trait_tests.rs - Detect when operand is a user-defined type (struct)
- Generate method calls to trait methods instead of direct LLVM ops
- Arithmetic operators →
.add(),.subtract(),.multiply(),.divide(),.floor_divide(),.remainder() - Unary operators →
.negate(),.not(),.bit_not() - Bitwise operators →
.bit_and(),.bit_or(),.bit_xor(),.shift_left(),.shift_right() - Comparison operators →
.equals(),.compare() - Tuple
==equality — compiles but returns wrong result; comparison codegen incorrect (test_tuple_equality,test_tuple_equality_triple) - Handle generic operator traits with type parameters (e.g.,
Mul<int>) - Files:
ori_llvm/src/operators.rs— add trait dispatch logic - Tests:
tests/spec/traits/operators/user_defined.ori(currently skipped)
- Rust Tests:
-
Implement: Overflow and panic semantics
- Integer overflow detection and panic
- Division by zero panic for integers
- Shift count validation (negative or >= bit width → panic)
- Float special cases: Inf, NaN handling
- NaN comparison semantics (NaN > all values)
21.5 Control Flow
-
Implement: Basic control flow
- Rust Tests:
tests/control_flow_tests.rs,tests/advanced_control_flow_tests.rs - Basic block creation and linking
- Phi nodes for SSA form
- Branch and conditional instructions
- Basic break/continue in loops
- Rust Tests:
-
Implement: Labeled loops
-
loop:namesyntax support -
for:namesyntax support -
break:nametargeting specific loop -
continue:nametargeting specific loop -
continue:name valuein yield context - No label shadowing enforcement
-
-
Implement: Break with values
-
break valuecodegen - Phi node setup in loop exit block
- Type resolution for loop expressions with break values
-
break:name valuefor labeled loops
-
-
Implement: For-yield expressions
-
for x in items yield expr→ list collection -
for x in items if guard yield exprwith filtering - Nested for-yield:
for x in xs for y in ys yield (x, y) -
continue valuesubstitution in yield -
break valuefinal element addition -
{K: V}collection from(K, V)tuples - Bool comparison stored in struct field during for-yield always produces false (
test_depth_combined_struct_iter_match) - Mutation of outer variables inside match arms within for-do body — ArcIrEmitter: variable not yet defined (
test_depth_combined_match_closure_result)
-
-
Implement: Try expression and error propagation
-
?operator on Result: unwrap or early return -
?operator on Option: unwrap or early return None - Proper error propagation up call stack
- Result accumulation in try blocks
-
-
Implement: Catch pattern
-
catch(expr:)→Result<T, str>codegen - Panic recovery mechanism
- Error message capturing
-
21.6 Pattern Matching
-
Implement: Basic patterns
- Literal patterns
- Binding patterns
- Wildcard patterns
_ - Basic struct destructuring
- Basic tuple destructuring
-
Implement: Advanced patterns
- Range patterns:
1..10,'a'..'z' - Or-patterns:
A | B | C - At-patterns:
x @ Some(_) - Guard patterns:
x if x > 0 - Struct destructuring with field renaming:
{ x: px, y: py } - List patterns with rest:
[first, ..rest],[..init, last] - Nested patterns
- Range patterns:
-
Implement: Match expression improvements
- Exhaustiveness verification in codegen
- Never variant handling
- Pattern matrix optimization
21.7 Function Sequences & Expressions
-
Implement: Generic function monomorphization
- Collect monomorphization sites (call sites with concrete type args)
- Clone generic function bodies with type substitution
- Re-type-check monomorphized bodies to get concrete expr_types
- Compile specialized instances through normal declare/define path
- Import note: Must collect call sites from the calling module and generate
specialized versions of imported generic functions (e.g.,
assert_eq<int>). Currentlydeclare_allskips all generics — this is the single biggest gap for LLVM test coverage (affects 2,472+assert_eqcall sites). - Implementation plan: Monomorphization (4 sections: type checker, ARC lowering, LLVM pipeline, verification)
- Architecture document:
docs/ori_lang/v2026/design/monomorphization-architecture.md
-
Implement: Basic function codegen
- Rust Tests:
tests/function_tests.rs,tests/function_call_tests.rs - Function signatures and calling conventions
- Local variables
- Return statements
- Rust Tests:
-
Implement: Block expressions and control flow (
{ },try,match)-
{ let x = a \n result }sequential binding via block expressions -
pre(cond) post(r -> cond)function-level contract declarations -
try { let x = f()? \n Ok(x) }error propagation -
match v { P -> e, _ -> d }pattern matching
-
-
Fix: Block scope variable isolation
- Inner block
let xleaks to outer scope — shadow bindings overwrite outer variables instead of creating block-local scope (test_scope_shadow_in_nested_block,test_scope_shadow_in_nested_block_2) -
let xin for-do body overwrites outerx(test_scope_for_loop_shadow)
- Inner block
-
Implement: Recurse pattern
-
recurse(condition:, base:, step:)basic recursion -
memo:memoization support -
parallel:parallel recursion flag - Proper state threading
-
-
Implement: With pattern (RAII)
-
with(acquire:, use:, release:)codegen - Guaranteed release even on panic
- Resource binding in
use:block - Note: Evaluator has a loud stub in
can_eval.rs— replace stub with real impl when ready
-
21.8 Concurrency Patterns
Related Proposals:
-
proposals/approved/parallel-execution-guarantees-proposal.md -
proposals/approved/timeout-spawn-patterns-proposal.md -
proposals/approved/nursery-cancellation-proposal.md -
proposals/approved/sendable-channels-proposal.md -
proposals/approved/cache-pattern-proposal.md -
proposals/approved/task-async-context-proposal.md -
Implement: Parallel pattern
-
parallel(tasks:, max_concurrent:, timeout:)→[Result] - Task spawning using OS threads or async runtime
- Result collection preserving task order
- Timeout handling with cancellation propagation
-
uses Suspendcapability requirement -
Sendablebound on task return types - Rust Tests:
ori_llvm/src/concurrency/parallel_tests.rs - Evaluator: Replace loud stub in
can_eval.rs:eval_can_function_expwith real parallel eval
-
-
Implement: Spawn pattern
-
spawn(tasks:, max_concurrent:)→void - Fire-and-forget semantics (errors logged, not propagated)
- Background execution with structured lifetime
- Tasks must complete before parent scope exits
- Rust Tests:
ori_llvm/src/concurrency/spawn_tests.rs - Evaluator: Replace loud stub in
can_eval.rs:eval_can_function_expwith real spawn eval
-
-
Implement: Timeout pattern
-
timeout(op:, after:)→Result<T, TimeoutError> - Operation cancellation on timeout via
is_cancelled()checks - Cleanup: resources released, destructors run
- Cooperative cancellation model (no preemption)
- Rust Tests:
ori_llvm/src/concurrency/timeout_tests.rs - Evaluator: Replace loud stub in
can_eval.rs:eval_can_function_expwith real timeout eval
-
-
Implement: Cache pattern
-
cache(key:, op:, ttl:)codegen - Cache key hashing via
Hashabletrait - TTL management with Duration type
- Thread-safe cache access
-
uses Cachecapability requirement - Rust Tests:
ori_llvm/src/concurrency/cache_tests.rs - Evaluator: Replace loud stub in
can_eval.rs:eval_can_function_expwith real cache eval
-
-
Implement: Nursery pattern
-
nursery(body:, on_error:, timeout:)codegen -
NurseryErrorMode.CancelRemaining: cancel siblings on first failure -
NurseryErrorMode.CollectAll: run all, collect all errors -
NurseryErrorMode.FailFast: return first error immediately - Structured concurrency: all children complete before nursery returns
- Cancellation propagation via
CancellationError - Rust Tests:
ori_llvm/src/concurrency/nursery_tests.rs
-
-
Implement: Channel operations
-
channel<T>(buffer:)→(Producer, Consumer)creation -
T: Sendablebound enforced at compile time -
channel_in,channel_out,channel_allselection patterns - Blocking send/receive operations
- Bounded buffer with backpressure
-
CloneableProducer<T>,CloneableConsumer<T>variants - Rust Tests:
ori_llvm/src/concurrency/channel_tests.rs
-
-
Implement: Sendable trait enforcement
- Check
Sendablebound for all cross-task data - Compile error for non-Sendable types in parallel contexts
- Interior mutability rules per
sendable-interior-mutability-proposal.md
- Check
21.9 Capabilities & With Pattern
Related Proposals:
-
proposals/approved/capability-composition-proposal.md -
proposals/approved/with-pattern-proposal.md -
proposals/approved/default-impl-proposal.md -
proposals/approved/default-impl-resolution-proposal.md -
proposals/approved/intrinsics-capability-proposal.md -
Implement: Capability tracking
-
uses Capabilitydeclaration in function signatures - Capability propagation through call graph
- Compile error if capability not available at call site
- Transitive capability requirements
- Rust Tests:
ori_llvm/src/capabilities/tracking_tests.rs
-
-
Implement: Capability provision
-
with Cap = impl in exprcodegen - Multiple capabilities:
with A = a, B = b in expr - Provider vtable generation
- Implicit capability parameter threading
- Scope-based resolution (innermost
withwins) - Rust Tests:
ori_llvm/src/capabilities/provision_tests.rs
-
-
Implement: Default implementations
-
def implvtable generation at module level - Resolution order: with scope > imported def impl > local def impl
- Override with explicit
withpattern -
without defimport to exclude default impl - One def impl per trait per module
- Rust Tests:
ori_llvm/src/capabilities/default_impl_tests.rs
-
-
Implement: Standard capabilities
-
Print(default provided, writes to stdout) -
Http(network requests) -
FileSystem(file I/O) -
Clock(time queries) -
Random(random number generation) -
Crypto(cryptographic operations) -
Cache(memoization and caching) -
Logger(structured logging) -
Env(environment variables) -
Intrinsics(SIMD, bit ops per intrinsics-capability-proposal) -
Suspend(async/concurrency marker) -
FFI(foreign function interface) - Rust Tests:
ori_llvm/src/capabilities/standard_tests.rs
-
21.10 Collections & Iterators
Related Proposals:
-
proposals/approved/iterator-traits-proposal.md -
proposals/approved/iterator-performance-semantics-proposal.md -
proposals/approved/computed-map-keys-proposal.md -
proposals/approved/fixed-capacity-list-proposal.md -
Implement: List operations
-
.push(element:)- append to end, grow if needed (test_coll_list_push) -
.pop()→Option<T>- remove and return last element (test_coll_list_pop) -
.first()→Option<T>- return first element (test_coll_list_first) -
.last()→Option<T>- return last element (test_coll_list_last) -
.insert(at:, element:)- insert at index, shift elements -
.remove(at:)→T- remove at index, shift elements -
.get(index:)→Option<T>- safe indexed access -
list[index]→T- direct access (panics if out of bounds) (test_coll_list_index) -
list[# - 1]- length-relative indexing -
.reverse()- return reversed list (test_coll_list_reverse) -
.contains(element:)→bool(test_coll_list_contains) -
.concat(other:)— produces wrong result (test_aot_list_concat) - Capacity management: grow by 2x when full
- List iteration:
for x in list do ... - List-of-tuples iteration — heap corruption when iterating
[(1, 10), (2, 20)](test_tuple_in_loop) - Rust Tests:
ori_llvm/src/collections/list_tests.rs
-
-
Implement: Map operations
- Map literal:
{key: value},{"string": value} - Computed keys:
{[expr]: value}where expr is evaluated -
.get(key:)→Option<V>- lookup by key (test_coll_map_get) -
map[key]→Option<V>- subscript access -
.insert(key:, value:)- insert or update (test_coll_map_insert) -
.remove(key:)→Option<V>- remove and return (test_coll_map_remove) -
.is_empty()→bool(test_aot_map_is_empty) -
.contains_key(key:)→bool(test_coll_map_contains_key) -
.keys()→[K]- return list of keys (test_coll_map_keys) -
.values()→[V]- return list of values (test_coll_map_values) - Map iteration: yields
(K, V)tuples - Spread in literals:
{...base, key: value} - Rust Tests:
ori_llvm/src/collections/map_tests.rs
- Map literal:
-
Implement: Set operations [partial]
-
Set<T>type representation:{i64 len, i64 cap, ptr data}(same as list — sorted flat array, not hash set) - Set creation in AOT:
__collect_setimplemented — intercepts inemit_apply, callsori_iter_collect_setruntime (2026-03-01) -
.len()→emit_set_length()— extract field 0 -
.is_empty()→emit_set_is_empty()—len == 0 -
.contains(element:)→emit_set_contains()viaori_set_containsruntime -
.insert(element:)→emit_set_insert()viaori_set_insertruntime (returns new set via sret) -
.remove(element:)→emit_set_remove()viaori_set_removeruntime (returns new set via sret) -
.union(other:)→emit_set_union()viaori_set_unionruntime (sret) -
.intersection(other:)→emit_set_intersection()viaori_set_intersectionruntime (sret) -
.difference(other:)→emit_set_difference()viaori_set_differenceruntime (sret) -
.to_list()→emit_set_to_list()viaori_set_to_listruntime (sret) - Set iteration: yields
Telements (set.iter()codegen) - Runtime functions: 7 functions in
ori_rt/src/lib.rs+ declarations inruntime_decl/mod.rs - AOT Tests:
ori_llvm/tests/aot/sets.rs— 10 tests all passing (2026-03-01)
-
-
Implement: Iterator trait codegen
-
Iteratortrait:type Item; @next (self) -> (Option<Self.Item>, Self) -
DoubleEndedIteratortrait:@next_back (self) -> (Option<Self.Item>, Self) - Fused iterator semantics:
NonestaysNone -
.map(transform:)- lazy transformation -
.filter(predicate:)- lazy filtering -
.fold(initial:, op:)- eager reduction -
.collect()into target collection viaCollecttrait -
.enumerate()- yield(index, item)tuples -
.zip(other:)- pair with another iterator -
.chain(other:)- concatenate iterators -
.take(count:),.skip(count:)- limit iteration -
.cycle()- repeat infinitely - Copy elision: avoid intermediate allocations
- Rust Tests:
ori_llvm/src/collections/iterator_tests.rs
-
-
Implement: DoubleEndedIterator methods
-
.rev()- reverse iteration order -
.last()- get last element -
.rfind(predicate:)- find from end -
.rfold(initial:, op:)- fold from end - Rust Tests:
ori_llvm/src/collections/double_ended_tests.rs
-
-
Implement: Infinite iterators
-
repeat(value:)→ infinite iterator yielding value -
(0..).iter()→ infinite range from 0 -
(0.. by -1).iter()→ infinite descending (requires bound) - Must use
.take(count:)before.collect() - Rust Tests:
ori_llvm/src/collections/infinite_tests.rs
-
21.11 Lambda & Closure Support
-
Implement: Basic lambdas
- Simple lambda syntax:
x -> x + 1 - Multi-param lambdas:
(a, b) -> a + b - No-param lambdas:
() -> 42
- Simple lambda syntax:
-
Implement: Advanced lambda features
- Typed lambdas:
(x: int) -> int = x * 2 - Proper capture-by-value semantics
- Nested lambdas
- Lambda in lambda captures
- Default parameters in lambdas
- Typed lambdas:
-
Implement: Function references
- Function reference as value
- Higher-order function codegen
- Passing functions to other functions
- Named function → closure coercion (named fns are
i64function pointers, closure params expect{ ptr, ptr }struct) —test_hof_apply_identity,test_hof_apply_named_function,test_hof_named_fn_as_arg
-
Fix: Closure ABI issues
-
(int) -> boolas function parameter — bool return ABI unresolved in closure-typed params (test_hof_bool_lambda) - Zero-arg closure capturing 3+ strings causes heap corruption —
malloc(): corrupted top size(test_depth_closure_capturing_multiple_strings) -
.map(r -> r.score)closure accessing struct field produces invalid LLVM IR (test_stress_combined_struct_closure_iteration)
-
21.12 Built-in Functions
-
Implement: Basic built-ins
-
print(msg:) -
panic(msg:)→Never - Basic assertions
-
-
Implement: Comparison built-ins
-
compare(left:, right:)→Ordering -
min(left:, right:)with Comparable -
max(left:, right:)with Comparable
-
-
Implement: Collection built-ins
-
len(collection:)for all collection types -
is_empty(collection:) -
repeat(value:)→ infinite iterator -
hash_combine(seed:, value:)→ int
-
-
Implement: Developer built-ins
-
todo()/todo(reason:)→Never -
unreachable()/unreachable(reason:)→Never -
dbg(value:)/dbg(value:, label:)→T -
drop_early(value:) -
is_cancelled()→bool
-
-
Implement: Assertion built-ins
-
assert_some(option:),assert_none(option:) -
assert_ok(result:),assert_err(result:) -
assert_panics(f:) -
assert_panics_with(f:, msg:)
-
-
Implement: Option/Result methods
-
.map(transform:),.and_then(transform:) -
.unwrap_or(default:),.filter(predicate:) -
.ok_or(error:),.ok(),.err() -
.context(msg:)for Result -
.trace(),.trace_entries(),.has_trace()
-
-
Fix: Conversion method return type tracking
-
int.f()result type not tracked as float —icmpused instead offcmpfor comparison (test_conv_int_f_shorthand) -
int.byte()— byte type not fully supported in AOT pipeline (test_conv_int_to_byte_truncates) -
int.byte().to_int()— type tracking lost through conversion chain (test_conv_int_to_byte,test_conv_int_to_byte_max,test_conv_int_to_byte_to_int)
-
21.13 FFI Support
Related Proposals:
-
proposals/approved/platform-ffi-proposal.md -
Implement: C FFI
-
extern "c" from "lib" { ... }declaration codegen - C type bindings:
c_char,c_short,c_int,c_long,c_longlong - C type bindings:
c_float,c_double,c_size - Function name mapping with
as "native_name" -
CPtropaque pointer type (size_t sized) -
Option<CPtr>for nullable pointers (None = null) - C variadic functions:
extern "c" { @printf (fmt: CPtr, ...) -> c_int }- Parse variadic
...in extern function declarations - LLVM codegen: emit variadic function type (
fn_type(..., true)) - Argument promotion rules:
float→double,i8/i16→i32(C ABI) - Variadic calls only allowed for extern declarations (not user-defined)
- Parse variadic
- Library linking:
-l<lib>flag generation - Rust Tests:
ori_llvm/src/ffi/c_ffi_tests.rs
-
-
Implement: Unsafe expressions
-
unsafe { ... }block codegen (same as inner expr, marker only) -
uses FFIcapability requirement at call sites - Pointer operations:
ptr_read<T>(ptr:),ptr_write<T>(ptr:, value:) - Pointer arithmetic (future)
- Rust Tests:
ori_llvm/src/ffi/unsafe_tests.rs
-
-
Implement: JavaScript FFI (WASM target)
-
extern "js" { ... }declaration codegen -
extern "js" from "./file.js"imports with path resolution -
JsValuehandle type (index into JS heap slab) -
JsPromise<T>async handling - Implicit promise resolution at
letbinding sites - String marshalling: Ori str ↔ JS TextEncoder/TextDecoder
- Rust Tests:
ori_llvm/src/ffi/js_ffi_tests.rs
-
-
Implement: Memory layout control
-
#repr("c")struct attribute - C-compatible struct layout (field order, padding, alignment)
- Callback support: Ori functions as C function pointers
- Rust Tests:
ori_llvm/src/ffi/layout_tests.rs
-
21.14 Conditional Compilation
Related Proposals:
-
proposals/approved/conditional-compilation-proposal.md -
Implement: Target conditionals
-
#target(os: "linux")- compile only if target OS matches -
#target(arch: "x86_64")- compile only if arch matches -
#target(family: "unix")- compile only if family matches -
any_os: ["linux", "macos"]- compile if any match -
not_os: "windows"- compile if OS doesn’t match - File-level:
#!target(...)at top of file - Non-matching branches not emitted to object file
- Rust Tests:
ori_llvm/src/conditional/target_tests.rs
-
-
Implement: Config conditionals
-
#cfg(debug)- compile only in debug builds -
#cfg(release)- compile only in release builds -
#cfg(feature: "name")- compile if feature enabled -
any_feature: ["a", "b"]- compile if any feature enabled -
not_feature: "x"- compile if feature not enabled -
not_debug- compile only in release - Rust Tests:
ori_llvm/src/conditional/config_tests.rs
-
-
Implement: Compile-time constants
-
$target_os→"linux"|"macos"|"windows"| … -
$target_arch→"x86_64"|"aarch64"|"wasm32"| … -
$target_family→"unix"|"windows"|"wasm" -
$debug→truein debug builds,falseotherwise -
$release→truein release builds,falseotherwise - False branch not type-checked (dead code elimination)
- Rust Tests:
ori_llvm/src/conditional/const_tests.rs
-
-
Implement: Compile errors
-
compile_error("msg")emits error at compile time - Used with conditionals for unsupported configurations
- Rust Tests:
ori_llvm/src/conditional/error_tests.rs
-
21.15 Memory Management (ARC)
Related Proposals:
-
proposals/approved/drop-trait-proposal.md -
proposals/approved/memory-model-edge-cases-proposal.md -
proposals/approved/clone-trait-proposal.md -
Spec: Type classification in
15-memory-model.mdupdated to type-containment model (matchesori_arc) -
Implement: Reference counting
- Heap allocation with atomic refcount header:
{ refcount: AtomicU64, data: T } -
fetch_add(1, Acquire)on clone/share -
fetch_sub(1, Release)on drop - Free when refcount reaches zero (after acquire fence)
- Stack-allocated values: no refcount (moved or copied)
- Rust Tests:
ori_llvm/src/arc/refcount_tests.rs
- Heap allocation with atomic refcount header:
-
Spec: Drop trait in
spec/08-types.md§ Drop Trait DONE- Trait definition, execution timing, LIFO order
- Constraints (no async, must return void, panic during unwind = abort)
- drop_early built-in function
-
Implement: Drop trait codegen
- Detect types implementing
Droptrait - Generate destructor call when refcount reaches zero
- Destructor runs before memory reclamation
-
@drop (self) -> voidsignature - Drop cannot declare
uses Suspend(compile error) - Rust Tests:
ori_llvm/src/arc/drop_tests.rs
- Detect types implementing
-
Implement: Destruction ordering
- Local bindings: reverse declaration order
- Struct fields: reverse declaration order
- List elements: back-to-front (last element first)
- Tuple elements: right-to-left
(a, b, c)→ c, b, a - Map entries: unspecified order (no guarantees)
- Rust Tests:
ori_llvm/src/arc/order_tests.rs
-
Implement: Panic during destruction
- Single panic in destructor: propagate normally
- Other destructors still run after first panic
- Double panic (destructor panics during unwind): immediate abort
- Abort message: “panic during panic - aborting”
- Rust Tests:
ori_llvm/src/arc/panic_tests.rs
-
Implement: Early drop
-
drop_early(value:)built-in - Immediately decrements refcount and runs destructor if zero
- Value cannot be used after
drop_early - Compile error if value used after drop
- Rust Tests:
ori_llvm/src/arc/early_drop_tests.rs
-
-
Implement: Panic during destruction
- Rust Tests:
tests/destructor_panic_tests.rs - Single panic in destructor: propagate normally
- Other destructors still run after panic
- Double panic (destructor panics during unwind): abort
- Rust Tests:
-
Implement: Async destructor restriction
- Compile error if Drop.drop declares
uses Suspend - Error code and message for async destructor attempt
- Compile error if Drop.drop declares
-
Implement: Last-reference optimization (in-place mutation when RC=1)
- Detect
isUnique(ref)at runtime (refcount == 1) - When unique, reuse allocation for same-shape value instead of alloc+free
- Applies to struct update, map insert/remove, list push/pop
- Reference: Perceus paper §3.3 “Reuse Analysis”, Koka
CheckFBIP.hs - Rust Tests:
ori_llvm/tests/aot/arc.rs— last-reference reuse tests
- Detect
-
Implement: Reset/reuse optimization (constructing same-shape value after match)
- After pattern match deconstructs a value, reuse its allocation for newly constructed value of same type
- Emit
reset/reuseinstructions in canonical IR (Lean 4 / Koka pattern) - Only applies when matched value has RC=1 (exclusive ownership)
- Reference: Lean 4
ExpandResetReuse.lean, Perceus paper §3.4 - Rust Tests:
ori_llvm/tests/aot/arc.rs— reset/reuse tests
21.16 Optimization Passes
21.16.1 Representation Optimization
Proposal: proposals/approved/representation-optimization-proposal.md
Formalize the compiler’s freedom to optimize machine representations while preserving semantic equivalence. Tier 1 and most Tier 2 optimizations are already implemented.
- Tier 1: Type-Intrinsic Narrowing —
type_info/mod.rs-
bool→i1,byte→i8,char→i32,Ordering→i8 -
void→ zero-sized ori64(0) - Range inclusive flag →
i1
-
- Tier 2a: Enum Discriminant Narrowing —
type_info/mod.rs- All enum tags use
i8 - Dynamic tag width for >256 variants (i16/i32)
- All enum tags use
- Tier 2b: All-Unit Enum Elimination —
type_info/mod.rs- Enums with no payload variants omit payload array
- Tier 2c: Sum Type Payload Sharing —
lower_error_handling.rs- Result uses max(Ok, Err) payload slot
- Alloca+store+load coercion pattern with zero-initialization
- Tier 2d: ARC Elision —
type_info/mod.rs- Transitive triviality classification with cycle detection
- Two-level check: per-type conservative + transitive walk
- Tier 2e: Newtype Erasure — already specified in Types § Newtype
- Tier 2f: Struct Field Reordering — not implemented
- Alignment-aware field ordering to reduce padding
- Respect
#reprattribute constraints - Maintain declaration-order mapping for derived traits
- Tier 3: LLVM-Delegated Value Range Narrowing — future
- Emit
nsw/nuwflags on overflow-checked arithmetic - Emit
rangemetadata on loads with known value ranges - Emit
nonnull/dereferenceableattributes on pointers
- Emit
- Known Gaps:
- ABI size computation ignores struct field padding (
abi/mod.rs) -
TypeInfo::alignment()returns hardcoded values, not representation-aware
- ABI size computation ignores struct field padding (
21.16.2 LLVM Optimization Pipeline
-
Implement: Optimization pipeline
- Configure standard optimization levels (O0, O1, O2, O3)
- Enable inlining, dead code elimination, constant folding
- Loop optimizations
- Tail call optimization
-
Implement: Debug info generation
- Source location tracking
- Variable debug info
- Type debug info
- DWARF/CodeView emission
21.17 Runtime Support
-
Implement: Basic runtime functions
- Rust Tests:
tests/runtime_tests.rs -
ori_print,ori_print_int,ori_print_float,ori_print_bool -
ori_panic,ori_panic_cstr -
ori_assert,ori_assert_eq_int,ori_assert_eq_bool,ori_assert_eq_str -
ori_str_concat,ori_str_eq,ori_str_ne -
ori_str_from_int,ori_str_from_bool,ori_str_from_float -
ori_list_new,ori_list_free,ori_list_len -
ori_compare_int,ori_min_int,ori_max_int
- Rust Tests:
-
Implement: Extended runtime
- Map runtime functions
- Set runtime functions
- Channel runtime functions
- Duration/Size runtime functions
- Stack trace capture functions
21.18 Architecture (Reference)
The LLVM backend follows Rust’s rustc_codegen_llvm patterns:
Context Hierarchy
// Simple context - just LLVM types
pub struct SimpleCx<'ll> {
pub llcx: &'ll Context,
pub llmod: Module<'ll>,
pub ptr_type: PointerType<'ll>,
pub isize_ty: IntType<'ll>,
}
// Full context - adds Ori-specific state
pub struct CodegenCx<'ll, 'tcx> {
pub scx: SimpleCx<'ll>,
pub interner: &'tcx StringInterner,
pub instances: RefCell<HashMap<Name, FunctionValue<'ll>>>,
pub type_cache: RefCell<TypeCache<'ll>>,
}
Builder Pattern
pub struct Builder<'a, 'll, 'tcx> {
llbuilder: &'a inkwell::builder::Builder<'ll>,
cx: &'a CodegenCx<'ll, 'tcx>,
}
Directory Structure
ori_llvm/src/
├── lib.rs # Crate root, re-exports
├── context.rs # SimpleCx, CodegenCx, TypeCache
├── builder.rs # Builder + expression compilation
├── types.rs # Type mapping helpers
├── declare.rs # Function declaration
├── traits.rs # BackendTypes, BuilderMethods traits
├── module.rs # ModuleCompiler (two-phase codegen)
├── runtime.rs # Runtime FFI functions
├── evaluator.rs # JIT evaluator (OwnedLLVMEvaluator)
├── operators.rs # Binary/unary operator codegen
├── control_flow.rs # if/else, loops, break/continue
├── matching.rs # Pattern matching codegen
├── collections/ # Collection codegen (tuples, structs, lists)
│ ├── mod.rs
│ ├── tuples.rs
│ ├── structs.rs
│ ├── lists.rs
│ └── option_result.rs
├── functions/ # Function codegen
│ ├── mod.rs
│ ├── body.rs # Function body compilation
│ ├── calls.rs # Function call codegen
│ ├── lambdas.rs # Lambda/closure codegen
│ ├── builtins.rs # Built-in function codegen
│ ├── sequences.rs # FunctionSeq (run, try, match)
│ └── expressions.rs # FunctionExp (recurse, print, panic)
└── tests/ # Unit tests (206 tests)
├── mod.rs
├── arithmetic_tests.rs
├── collection_tests.rs
├── control_flow_tests.rs
├── function_tests.rs
├── matching_tests.rs
└── ...
21.19 Section Completion Checklist
Infrastructure:
- JIT compilation working
- All current Ori tests pass via LLVM (1082/3077 passing, 1985 LCFail)
- All Rust unit tests pass (527/527, 15 skipped)
- Architecture follows Rust patterns
- Unified import pipeline (imports resolved once, compiled into JIT module)
- Generic monomorphization (biggest remaining gap for test coverage)
- AOT compilation (see Section 21B)
Type System:
- Primitive types
- Option/Result (basic)
- Lists (basic)
- Duration/Size types
- Newtypes
- Sum types (general)
- Fixed-capacity lists
- Channels
- Maps (full support)
- Sets
Expressions:
- Basic expressions
- Binary/unary operators (primitives)
- Range with step (
by) - Spread operator (
...) - Coalesce operator (
??) - Floor division (
div) - Type conversions (
as,as?) - Complex assignments
Control Flow:
- Basic if/else, loops
- Labeled loops
- Break with values
- For-yield expressions
- Try/catch patterns
Traits & Dispatch:
- Associated functions
- Operator trait dispatch
- Iterator trait methods
- Comparison trait methods
Concurrency:
- Parallel pattern
- Spawn pattern
- Channels
- Nursery pattern
Capabilities:
- Capability tracking
- With pattern provision
- Default implementations
FFI:
- C FFI
- JavaScript FFI
- Unsafe blocks
Memory:
- ARC implementation
- Drop trait
- Destruction ordering
Optimization:
- Optimization passes
- Debug info generation
- 80+% test coverage (currently ~68%)
Verified AOT Gaps (from Section 11.1 verification, updated 2026-02-23):
Resolved:
- Derive Eq struct codegen:
emit_comparison_via_traitnow dispatches to derivedeqmethod for non-primitive types instead of falling through to scalaricmp - Derive Comparable struct codegen:
emit_ordering_comparisonnow dispatches to derivedcomparemethod and checks Ordering result (0=Less, 1=Equal, 2=Greater) for</>/<=/>= - ARC enum basic drop and string-payload drop:
test_arc_enum_basic_dropandtest_arc_enum_with_string_payloadun-ignored and passing
Open:
- List mutation methods not in AOT builtin table:
.push(),.first(),.last(),.is_empty(),.concat() - Map methods not in AOT builtin table:
.is_empty(),.get(),.insert(),.remove(),.keys(),.values() -
list[index]subscript not resolved in AOT - Closure-returning-closure type inference:
let f = (n: int) -> (int) -> int = { (x: int) -> int = ... }infers()return instead of(int) -> int - Enum variant constructors not declared as LLVM functions
- Generic monomorphization not in ARC pipeline
-
catch(expr:)not yet lowered through ARC pipeline - String interpolation produces wrong result
Cross-references to existing items:
- Generic monomorphization → § 21.7 (CRITICAL — blocks 2,472+ test call sites)
- Enum variant constructors → § 21.2 (blocks enums, recursive types)
catch(expr:)→ § 21.5 (blocks panic recovery in AOT)- String interpolation → § 21.3
New AOT tests added (2026-02-23, 13 tests, all passing): derive_eq_struct (un-ignored), derive_eq_struct_not_equal, derive_eq_struct_with_strings, derive_comparable_struct (4 comparisons: <, >, <=, >=), panic_basic, option_unwrap_some, result_unwrap_ok, struct_with_list_and_string, nested_struct_with_strings, for_yield_with_filter, for_yield_transform
New AOT test files added (2026-02-23, Section 11.1 verification — 5 files, 117 tests total):
error_handling.rs— 21 tests (21 pass, 0 ignore): Result/Option basics,?operator, chaining, deep chainshigher_order.rs— 27 tests (23 pass, 4 ignore): HOF apply, composition, closures with captures, nested closures, foldtuples.rs— 29 tests (23 pass, 6 ignore): construction, destructuring, field access, as param/return, nested, strings, closuresstructs.rs— 29 tests (28 pass, 1 ignore): construction, string fields, update syntax, nested, as param/return, closures, derived Eqrecursion.rs— 22 tests (22 pass, 0 ignore): factorial, fibonacci, tail-recursive, mutual, with Result/Option/match, depth tests
Total AOT test counts (2026-02-23): 850 passed, 0 failed, 70 ignored
Exit Criteria: Feature parity with evaluator for all language constructs
Running Tests
# Build Docker container (first time only)
./docker/llvm/build.sh
# Run all Ori tests via LLVM
./docker/llvm/run.sh ori test
# Run spec tests only
./docker/llvm/run.sh ori test tests/spec
# Run Rust unit tests
./docker/llvm/run.sh cargo test -p ori_llvm --lib
# Run with debug output
ORI_DEBUG_LLVM=1 ./docker/llvm/run.sh ori test tests/spec/types/primitives.ori