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.
3.4 kB · 72 lines
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172/// Regression test for `validate_no_unwired_natives`'s fail-fast check/// (`lang/codegen/emit.mo`): a reachable bodyless `#[native X]` def whose/// X is wired nowhere must produce a `Result.err` naming the native/// target and enclosing def, not a silently-miscompiled "return Unit"/// stub that only surfaces as a runtime SIGSEGV in the resulting binary.////// This exact bug class cost two multi-hour runtime-debugging sessions/// before the check existed: `String.length` ("printed a garbage heap/// address instead of `3`") and, much worse, the self-compiled v25/// binary's immediate `monad_get_tag` SIGSEGV inside/// `List_reverse_append` -- `String_to_list` had silently stubbed to/// Unit, so `String.ends_with`/`String.reverse` pattern-matched a Unit/// object and walked a wild pointer out of `monad_get_field`. Both were/// structurally valid IR: `llc`'s verifier passes them, only running the/// binary finds them. See `validate_no_unwired_natives`'s own doc/// comment for the full story.////// The test exercises the mechanism directly with a program-DECLARED/// native that by construction has no runtime implementation, rather/// than pinning any specific init/std native (which could later get/// wired for real and silently flip this test to the wrong reason).////// The unwired native MUST be reachable from `main` -- the check runs on/// the REACHABLE decls only (see `validate_no_unwired_natives`'s own doc/// comment), so an unreferenced `use_it` would be filtered out by/// `filter_reachable_decls` before the check ever saw it, silently/// passing for the wrong reason.use std::process {exec_cmd, process_id}use lang::module {LoadedModules, load_file_modules}use lang::codegen::emit {compile_loaded_modules_to_ir}#[test]def test_unwired_native_fails_fast : IO Bool := do { let output_dir := "/tmp/monad_e2e_" ++ I64.to_string process_id; let src_path := output_dir ++ "/unwired_native.mo"; let source := r#"#[native definitely_not_a_real_native]def use_it (x : I64) : I64def main (args : List String) : IO I64 := do { let _ := use_it 42; return 0}"#; exec_cmd "mkdir" ["-p", output_dir]; IO.write_file (Path.path src_path) source; let loaded_result <- load_file_modules src_path false; match loaded_result { Result.err e => do { IO.println ("test_unwired_native_fails_fast: failed to load: " ++ e); return false }, Result.ok loaded => do { let mod_result <- compile_loaded_modules_to_ir loaded false; exec_cmd "rm" ["-f", src_path]; match mod_result { Result.err msg => do { let names_native := String.contains msg "definitely_not_a_real_native"; let names_def := String.contains msg "use_it"; if names_native && names_def then return true else do { IO.println ("test_unwired_native_fails_fast: error message missing native/def name: " ++ msg); return false } }, Result.ok _ => do { IO.println "test_unwired_native_fails_fast: expected Result.err (native has no runtime implementation), got Result.ok"; return false }, } }, }}