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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139use lang::types { Def, Identifier, TypeConstraint, app, binder_named, empty_attrs, i64, lam, lit, named, npath, num, package_private, sort_n, var,}use llvm::ir {emit_module}use lib::codegen::emit {check_contains, compile_db_decls_ir}open Term {lam, lit}open Literal {num}open Identifier {id}open DebugName {named}open NumSuffix {i64}open Param {mk}open Def {mk}#[test]def test_e2e_simple_literal : Bool := let id := Identifier.id "myfunc" in let def_ := Def.mk (NamePath.npath (List.cons id empty_ids)) (sort_n 1) (Term.lit (Literal.num 42 NumSuffix.i64)) empty_cons empty_attrs Visibility.package_private List.empty in let mod_ := compile_db_decls_ir (List.cons def_ empty_defs) in let text := emit_module mod_ in check_contains text "myfunc"#[test]def test_e2e_function_with_param : Bool := let id := Identifier.id "add5" in let body := Term.lit (Literal.num 99 NumSuffix.i64) in let term_ := Term.lam (binder_named (Identifier.id "x")) (sort_n 1) body in let def_ := Def.mk (NamePath.npath (List.cons id empty_ids)) (sort_n 1) term_ empty_cons empty_attrs Visibility.package_private List.empty in let mod_ := compile_db_decls_ir (List.cons def_ empty_defs) in let text := emit_module mod_ in check_contains text "add5"#[test]def test_e2e_module_structure : Bool := let mod_ := compile_db_decls_ir empty_defs in let text := emit_module mod_ in if check_contains text "; ModuleID" then check_contains text "Type Definitions" else false#[test]def test_e2e_runtime_decls_present : Bool := let mod_ := compile_db_decls_ir empty_defs in let text := emit_module mod_ in check_contains text "monad_alloc"/// Every `define` carries LLVM's default `ccc`. Asserted as an absence/// because the regression this guards is an annotation creeping BACK:/// aarch64, darwin and riscv64 all reject the `cc 9` that used to be/// here. The positive conjunct is load-bearing -- a bare negation would/// pass on empty output.#[test]def test_e2e_no_calling_convention : Bool := let id := Identifier.id "f" in let def_ := Def.mk (NamePath.npath (List.cons id empty_ids)) (sort_n 1) (Term.lit (Literal.num 1 NumSuffix.i64)) empty_cons empty_attrs Visibility.package_private List.empty in let mod_ := compile_db_decls_ir (List.cons def_ empty_defs) in let text := emit_module mod_ in Bool.not (check_contains text " cc ") && check_contains text "define i64 @"/// Regression test for the `IO`-typed `main` garbage-exit-code fix/// (`lang/codegen/emit.mo`'s `unwrap_io_return_blocks`//// `emit_type_head_is_io`): a `main` declared `IO I64` must have its/// generated LLVM body call the runtime's `monad_get_field` to unwrap/// the boxed `IO.mk (RawIO.io _)` payload before returning — confirmed by checking/// the emitted IR text directly (same `check_contains`-on-IR-text/// technique this whole file already uses, e.g./// `test_e2e_no_calling_convention`'s `define` check).#[test]def test_e2e_io_main_unwraps_before_return : Bool := let main_id := Identifier.id "main" in let io_type_id := DebugName.named (Identifier.id "IO") in let io_typ := Term.app (Term.var sentinel_idx io_type_id) (sort_n 1) in let def_ := Def.mk (NamePath.npath (List.cons main_id empty_ids)) io_typ (Term.lit (Literal.num 5 NumSuffix.i64)) empty_cons empty_attrs Visibility.package_private List.empty in let mod_ := compile_db_decls_ir (List.cons def_ empty_defs) in let text := emit_module mod_ in // `monad_get_field` is always DECLARED at the top of every module // (`runtime_declarations`) regardless of whether anything calls it // -- checking for the bare name would pass even without the fix. // The actual CALL renders as `call i64 @monad_get_field(...)` // (`LLVMValue.call`'s own text form, `llvm/src/ir.mo`), which // only appears if `main`'s own body was actually rewritten. check_contains text "call i64 @monad_get_field"/// The negative case: an ordinary `I64`-typed `main` must NOT get the/// unwrap treatment (it already returns a raw `i64`, unwrapping it/// would corrupt a genuine numeric exit code) — guards against/// `emit_type_head_is_io`/`ends_with_main` over-triggering.#[test]def test_e2e_non_io_main_does_not_unwrap : Bool := let main_id := Identifier.id "main" in let def_ := Def.mk (NamePath.npath (List.cons main_id empty_ids)) (sort_n 1) (Term.lit (Literal.num 5 NumSuffix.i64)) empty_cons empty_attrs Visibility.package_private List.empty in let mod_ := compile_db_decls_ir (List.cons def_ empty_defs) in let text := emit_module mod_ in Bool.not (check_contains text "call i64 @monad_get_field")#[partial]def sentinel_idx : I64 := 0 - 1#[partial]def empty_defs : List Def := List.empty#[partial]def empty_ids : List Identifier := List.empty#[partial]def empty_cons : List TypeConstraint := List.empty