Something went wrong. Try again.
The Monad language. Dependent types, functional programming compiled with LLVM. Hobby project. monad-lang.org
dependent-types language compiler programming-language functional-programming
Something went wrong. Try again.
6.7 kB · 142 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143/// Regression tests for a family of related codegen gaps found while/// chasing `bootstrap compile cli/src/main.mo monad`'s self-compile past the/// string phi-merge fix: values that reach a `phi` (an `if`/`match`/// merge point) or a bare `ret` need the SAME materialization call/// arguments already get (`materialize_void`/`materialize_native_bool_/// arg`, `lang/codegen/emit.mo`) -- neither `build_db_if_blocks`//// `build_merge_result` (if/else merges), `build_match_case_block`/// (match-arm merges), nor `compile_db_def_ir_body` (a def's own bare/// return value) called them until this session's fixes.////// Also exercises the adjacent `I64.beq` native-dispatch fix:/// `lookup_native`'s table hardcoded `"I64_eq"`, an identifier that/// doesn't exist in real source (`I64.eq` is "unknown variable" --/// confirmed) -- the real, only I64 equality function throughout the/// whole corpus is `I64.beq` (`init/number.mo`'s `BEq I64` instance),/// which never matched the table at all and silently miscompiled to a/// "return Unit" stub, both as a bare call and as an `if`'s own/// condition.open IO {println}use lang::codegen::test::e2e_harness {compile_source_run_expect}/// `I64.beq` used directly as an `if`'s own condition and as an/// ordinary comparison -- exercises `lookup_native`'s fixed dispatch/// table entry (was `"I64_eq"`, now `"I64_beq"`). Uses a `do`-block/// `let`/`return` rather than a bare `IO.io (if ...)` body -- the latter/// hits a SEPARATE, already-filed, pre-existing bug (`implementations//// 2026-08-29-io-wrap-dropped-after-branching-argument.md`), unrelated/// to what this test guards.#[test]def test_i64_beq_native_dispatch : IO Bool := let source := r#"def main (args : List String) : IO I64 := do { let r := if I64.beq 5 5 then 1 else 0; return r}"# in compile_source_run_expect source "test_i64_beq_native_dispatch" 1/// An outer `if` whose `else` branch is ITSELF a nested `if` -- the/// nested if's own compiled body ends in ITS OWN branch instruction/// (not a `jump` to the outer merge block), so the outer merge's `phi`/// must have only ONE incoming edge (from `then`), not two. Before the/// fix, `build_merge_result` always built a 2-entry phi regardless,/// producing a phi edge from a non-predecessor block -- `llc`: "PHINode/// should have one entry for each predecessor of its parent basic/// block!" / "Instruction does not dominate all uses!". Mirrors the/// exact shape that blocked `lang/parser/position.mo`'s/// `line_col_scan_direct` during the self-compile.#[test]def test_nested_if_in_else_branch_phi_arity : IO Bool := let source := r#"def classify (n : I64) (m : I64) : I64 := if I64.beq n 0 then 100 else if I64.gt m 0 then 1 else 2def main (args : List String) : IO I64 := do { let a := classify 0 5; let b := classify 3 5; let c := classify 3 (0 - 5); return (I64.add a (I64.add b c))}"# in compile_source_run_expect source "test_nested_if_in_else_branch_phi_arity" 103/// A def whose whole body is a BARE native comparison (no wrapping/// `if`/call) -- `compile_db_def_ir_body`'s own `ret val_r` used to pass/// a raw, unboxed `i1` straight through when `val_r` came from a native/// comparison with no materialization step, matching the exact shape of/// `lang/parser/position.mo`'s own `is_all_ascii_direct`/`String.is_/// empty`-style helpers (a comparison as a function's entire/// implementation). `llc`: "'%tN' defined with type 'i1' but expected/// 'i64'" at the `ret` itself.#[test]def test_def_body_bare_native_comparison_return : IO Bool := let source := r#"def is_five (n : I64) : Bool := I64.beq n 5def main (args : List String) : IO I64 := do { let r := if is_five 5 then 1 else 0; return r}"# in compile_source_run_expect source "test_def_body_bare_native_comparison_return" 1/// A `let`'s own BODY/continuation (not its bound argument -- that site/// was already covered by `implementations/2026-08-29-let-bound-native-/// bool-boxing.md`) reaching `void_val` with no materialization step:/// `try_compile_let_beta_db` passes `body`'s own compiled value straight/// into `compose_seq`, which splices it into the ALREADY-COMPILED `arg`/// expression's own dangling `ret` -- if `body` is void-shaped (a struct/// literal that doesn't desugar to `Term.con`, `implementations//// 2026-08-29-struct-literal-not-desugared-in-branch-position.md`, is/// the real-world trigger; `lang/parser/position.mo`'s `combine_line_/// col_scan` is the exact shape: `let trailing := (if ...) in { struct/// literal } `), the spliced `ret` ends up `ret void` where `i64` is/// expected. This test's own `main` never reads back `p`'s fields, so it/// only exercises whether `make_pair` compiles+links+runs without/// crashing (this fix's actual scope) -- the struct's own field/// correctness is the SEPARATE, still-open bug tracked in that doc.#[test]def test_let_body_void_struct_literal : IO Bool := let source := r#"struct Pair { a : I64, b : I64,}def make_pair (n : I64) : Pair := let x := if I64.gt n 0 then 1 else 2 in { a := x, b := n }def main (args : List String) : IO I64 := do { let p := make_pair 5; return 42}"# in compile_source_run_expect source "test_let_body_void_struct_literal" 42/// The hole `materialize_terminal_ret`'s own doc comment recorded and/// declined to fix blind: a body shaped `let x := <branching> in I64.beq/// ...`. The field access is the branching subterm, so the comparison's/// `icmp` is spliced into a block that already ends in `ret` -- the one/// `i1` position the TERM-shape gate could not see, because a `let`-shaped/// body is an application and not a comparison. Forge hit it/// (`plans/implementations/2026-10-05-raw-i1-return-after-a-branching-/// subterm.md`) and `llc` rejected the function outright:/// `'%tN' defined with type 'i1' but expected 'i64'`.////// Pre-fix this row fails at COMPILE time, not on its value -- which is/// why the expected number is just the comparison's own answer (1 for/// true, through `I64.add`), with nothing clever riding on it.#[test]def test_native_bool_ret_after_a_branching_subterm : IO Bool := let source := r#"struct Inner { items : List I64,}def icount (xs : List I64) : I64 := match xs { empty => 0, cons _ rest => I64.add 1 (icount rest), _ => 0}def seed : Inner := { items := [1, 2] : Inner }def two_items : Bool := let i : Inner := seed in I64.beq (icount i.items) 2def main (args : List String) : IO I64 := do { return (if two_items then 1 else 0)}"# in compile_source_run_expect source "test_native_bool_ret_after_a_branching_subterm" 1