import "@std/mem" import "@std/arraylist" import "@std/testing" import "@source/diagnostics" import "@source/strpool" import "@source/lexer" import "@source/parser" import "@source/ast" import "@source/ir" import "@source/interpreter" sema :: import "@source/sema" typecheck :: import "@source/sema/check" @hide:file CompileError :: alias mem.AllocError | diagnostics.CapacityError | strpool.InternError | lexer.SourceError # every consumer test executes only after all source and frontend storage is gone. @hide:file compile_owned proc(text []u8, diagnostic_store @mut diagnostics.Store) ir.Module ! CompileError { source :: try mem.alloc(u8, mem.c_allocator, text.len) defer mem.free(mem.c_allocator, source) for (text) |byte, i| source[i] = byte strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, diagnostic_store) defer sema.deinit(&resolution) checked := try typecheck.check(mem.c_allocator, &tree, &resolution, diagnostic_store) defer typecheck.deinit(&checked) return try lower(mem.c_allocator, &tree, &resolution, &checked, diagnostic_store) } @hide:file expect_trap proc( outcome interpreter.Outcome, diagnostic_store @diagnostics.Store, code diagnostics.Code, ) void ! testing.Error { if (outcome.trap) |id| { if (diagnostics.get(diagnostic_store, id)) |diagnostic| { try testing.expect_equal(code, diagnostic.code) return } } return .expectation_failed } executes_calls_locals_arithmetic_and_nested_argument_slices test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `add proc(left int, right int) int { ` result :: left + right ` return result `} `double proc(value int) int { ` return value * 2 `} `main proc() int { ` a :: add(double(4), add(5, 6)) ` b :: add(10, 13) ` return a + b `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.int, outcome.value.type) try testing.expect_equal(i64(42), outcome.value.data.integer) } owns_decoded_strings_after_source_and_frontend_are_freed test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `text :: "a\\\"\n\r\t\0\x41\x7a" `identity proc(value string) string { ` return value `} `main proc() string { ` return identity(text) `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.string, outcome.value.type) try testing.expect(mem.eql(ir.string_bytes(&module, outcome.value.data.string), "a\\\"\n\r\t\x00Az")) } resolves_module_local_string_ids_after_frontend_cleanup test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned( "first :: \"not the result\"\nidentity proc(value string) string { return value }\nmain proc() string { return identity(\"second\\n\\x00\") }\n", &diagnostic_store, ) defer ir.deinit(&module) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal([]u8, "second\n\x00", ir.string_bytes(&module, outcome.value.data.string)) } honors_float_context_and_float_division test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() float { ` left :: 3 ` return -(left / 2) + 2.0 `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.float, outcome.value.type) try testing.expect_equal(f64(0.5), outcome.value.data.real) } executes_forward_lazy_globals_and_function_aliases test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `forward :: later + 1 `alias :: add `later :: 18 `add proc(left int, right int) int { ` local_alias :: alias ` return left + right `} `main proc() int { ` invoke :: alias ` return invoke(forward, 23) `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) again :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(again.trap == null) try testing.expect_equal(i64(42), again.value.data.integer) } supports_bare_void_value_returns_and_fallthrough test { sources [3][]u8 :: [ "main proc() void {\n return\n}\n", "noop proc() void {}\nmain proc() void {\n return noop()\n}\n", "noop proc() void {}\nmain proc() void {\n noop()\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.void, outcome.value.type) } } preserves_demanded_diagnostic_identity_and_source_location test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `bad proc() int { ` return unknown `} `main proc() int { ` before :: 1 + 2 ` return bad() `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) diagnostic :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? try testing.expect_equal(diagnostics.Code.unknown_name, diagnostic.code) try testing.expect(mem.eql(source[usize(diagnostic.span.start)..usize(diagnostic.span.end)], "unknown")) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } skips_unreachable_and_unused_invalid_code test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `bad proc() int { ` return unknown `} `unused :: "\q" `main proc() int { ` return 42 ` 9223372036854775807 + 1 ` missing ` huge :: 9223372036854775808 `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect(diagnostics.count(&diagnostic_store) > 0) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } traps_local_forward_loads_and_global_initialization_cycles test { sources [3][]u8 :: [ "main proc() int {\n before :: after\n after :: 42\n return before\n}\n", "left int :: right\nright int :: left\nmain proc() int {\n return left\n}\n", "self int :: self\nmain proc() int {\n return self\n}\n", ] for (sources) |source, i| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) code diagnostics.Code :: if (i == 0) .read_before_initialization else .initialization_cycle try expect_trap(outcome, &diagnostic_store, code) } } handles_integer_boundaries_without_host_overflow test { sources [3][]u8 :: [ "main proc() int {\n return 9223372036854775807\n}\n", "main proc() int {\n return -9223372036854775808\n}\n", "main proc() int {\n return -0009223372036854775808\n}\n", ] expected [3]i64 :: [maxval!(i64), minval!(i64), minval!(i64)] for (sources) |source, i| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(expected[i], outcome.value.data.integer) } } traps_invalid_literals_escapes_and_checked_integer_arithmetic test { sources [8][]u8 :: [ "main proc() int {\n return 9223372036854775808\n}\n", "main proc() int {\n return -9223372036854775809\n}\n", "main proc() int {\n return 9223372036854775807 + 1\n}\n", "main proc() int {\n return -(-9223372036854775808)\n}\n", "main proc() int {\n return -9223372036854775808 - 1\n}\n", "main proc() int {\n return 9223372036854775807 * 2\n}\n", "main proc() string {\n return \"\\q\"\n}\n", "main proc() string {\n return \"\\x0g\"\n}\n", ] for (sources) |source, i| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) code := diagnostics.Code.integer_overflow if (i < 2) code = .literal_out_of_range if (i >= 6) code = .invalid_escape try expect_trap(outcome, &diagnostic_store, code) } } validates_main_and_preserves_first_declaration test { sources [4][]u8 :: [ "other proc() void {}\n", "main :: 42\n", "main proc(value int) int {\n return value\n}\n", "main proc() int {\n return 42\n}\nmain proc() int {\n return 7\n}\n", ] for (sources) |source, i| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) if (i == 3) { try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } else { code diagnostics.Code :: if (i == 0) .missing_entry else .invalid_entry try expect_trap(outcome, &diagnostic_store, code) try testing.expect_equal(module.entry_diagnostic, outcome.trap?) } } } invalid_signatures_trap_without_poisoning_unrelated_main test { sources [2][]u8 :: [ "broken proc(value void) void {}\nmain proc() int {\n return 42\n}\n", "main proc() Unknown {\n return 42\n}\n", ] for (sources) |source, i| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect(diagnostics.count(&diagnostic_store) > 0) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) if (i == 0) { try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } else { try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) try expect_trap(outcome, &diagnostic_store, .unknown_name) } } } parses_float_context_without_integer_range_limits_and_rejects_float_overflow test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned( "main proc() float {\n return 18446744073709551616\n}\n", &diagnostic_store, ) defer ir.deinit(&module) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.float, outcome.value.type) try testing.expect_equal(f64(18446744073709551616.0), outcome.value.data.real) source := arraylist.init(u8, mem.c_allocator) defer arraylist.deinit(&source) for ("main proc() float {\n return ") |byte| try arraylist.append(&source, byte) for (usize(0)..usize(400)) |_| try arraylist.append(&source, '9') for (".0\n}\n") |byte| try arraylist.append(&source, byte) oversized := try compile_owned(source.items, &diagnostic_store) defer ir.deinit(&oversized) trapped :: try interpreter.run(mem.c_allocator, &oversized, &diagnostic_store) try expect_trap(trapped, &diagnostic_store, .literal_out_of_range) } accepts_representable_subnormals_and_rejects_underflow_to_zero test { zeros [2]usize :: [307, 400] for (zeros) |count| { source := arraylist.init(u8, mem.c_allocator) defer arraylist.deinit(&source) for ("main proc() float {\n return 0.") |byte| try arraylist.append(&source, byte) for (usize(0)..count) |_| try arraylist.append(&source, '0') for ("1\n}\n") |byte| try arraylist.append(&source, byte) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source.items, &diagnostic_store) defer ir.deinit(&module) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) if (count == 307) { try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.float, outcome.value.type) try testing.expect(outcome.value.data.real > 0 and outcome.value.data.real < 1) } else { try expect_trap(outcome, &diagnostic_store, .literal_out_of_range) } } } @hide:file AllocationState :: struct { remaining usize live usize = 0 } @hide:file limited_alloc proc(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8 { if (context) |userdata| { state @mut AllocationState :: ptrcast!(userdata) if (state.remaining == 0) return null state.remaining -= 1 memory :: mem.raw_alloc(mem.c_allocator, size, alignment) if (memory) |_| state.live += 1 return memory } unreachable } @hide:file limited_realloc proc(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { if (context) |userdata| { state @mut AllocationState :: ptrcast!(userdata) if (state.remaining == 0) return null state.remaining -= 1 resized :: mem.raw_realloc(mem.c_allocator, memory, old_size, new_size, alignment) if (resized) |_| { if (memory) |_| {} else state.live += 1 } return resized } unreachable } @hide:file limited_free proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void { if (context) |userdata| { state @mut AllocationState :: ptrcast!(userdata) if (memory) |_| state.live -= 1 mem.raw_free(mem.c_allocator, memory, size, alignment) return } unreachable } @hide:file limited_vtable :: mem.AllocatorVTable{ alloc = limited_alloc, realloc = limited_realloc, free = limited_free } releases_temporaries_and_partially_owned_modules_on_allocation_failure test { strpool.strings = strpool.init(mem.c_allocator) defer strpool.deinit(&strpool.strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `text :: "owned\nstring" `factor float :: 3.5 `enabled :: true or false and false `compose proc(left bool, right bool) bool { ` return left and right `} `broken proc() bool { ` return compose(true and absent, false or true) `} `identity proc(value string) string { ` if false { ` return value ` } ` local :: value ` return local `} `main proc() string { ` computed float :: factor * 2 ` remaining := 2 ` while compose(remaining > 0 and enabled, compose(!false, compose(true or false, compose(true, compose(true, true))))) { ` pass :: identity(text) ` identity(pass) ` remaining = remaining - 1 ` inner := 2 ` while inner > 0 { ` inner = inner - 1 ` if (inner > 0) continue else break ` } ` if (remaining > 0) continue else break ` } ` if computed > 0 and (enabled or false) { ` saved :: identity(text) ` if computed < 10 { ` return saved ` } ` } else { ` fallback :: "fallback" ` identity(fallback) ` } ` return identity(text) `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &diagnostic_store) defer sema.deinit(&resolution) checked := try typecheck.check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer typecheck.deinit(&checked) succeeded := false failures := usize(0) for (usize(0)..usize(512)) |limit| { state := AllocationState{ remaining = limit } allocator :: mem.Allocator{ context = &state, vtable = &limited_vtable } module := lower(allocator, &tree, &resolution, &checked, &diagnostic_store) catch |error| { try testing.expect(error == .out_of_memory) failures += 1 try testing.expect_equal(usize(0), state.live) continue } errdefer ir.deinit(&module) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect(mem.eql(outcome.value.data.string, "owned\nstring")) ir.deinit(&module) try testing.expect_equal(usize(0), state.live) succeeded = true break } try testing.expect(succeeded) try testing.expect(failures > 0) } recursive_factorial_and_conditional_returns_execute_after_frontend_cleanup test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `factorial proc(n int) int { ` if n <= 1 { ` return 1 ` } else { ` return n * factorial(n - 1) ` } `} `main proc() int { ` return factorial(5) `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(120), outcome.value.data.integer) } nested_branches_joins_calls_and_lazy_globals_preserve_execution_position test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `seed :: classify(2) `enabled bool :: true `identity proc(value int) int { return value } `classify proc(n int) int { ` if n < 0 { ` return 3 ` } else if n == 0 { ` return 5 ` } else { ` if n > 1 { ` local :: identity(7) ` identity(local) ` } else { ` local :: identity(9) ` identity(local) ` } ` return identity(11) ` } `} `choose proc(flag bool) int { ` if flag { ` local :: identity(seed) ` identity(local) ` } else { ` local :: identity(13) ` identity(local) ` } ` return identity(seed) + classify(0) + classify(-1) `} `main proc() int { ` return choose(enabled) + choose(false) + classify(1) - 7 `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } bool_storage_results_and_numeric_comparisons_execute test { sources [16][]u8 :: [ "main proc() bool { return 2 == 2 }\n", "main proc() bool { return 2 != 3 }\n", "main proc() bool { return -2 < 1 }\n", "main proc() bool { return 2 <= 2 }\n", "main proc() bool { return 3 > 2 }\n", "main proc() bool { return 2 >= 2 }\n", "main proc() bool { return 2 == 2.0 }\n", "main proc() bool { return 2 != 3.0 }\n", "main proc() bool { return 2 < 2.5 }\n", "main proc() bool { return 2 <= 2.0 }\n", "main proc() bool { return 3 > 2.5 }\n", "main proc() bool { return 2 >= 2.0 }\n", "main proc() bool { return true == true }\n", "main proc() bool { return false != true }\n", "flag bool :: false\nidentity proc(value bool) bool { return value }\nmain proc() bool {\n local bool :: identity(flag)\n return identity(local == false)\n}\n", "main proc() bool {\n local :: 2\n return local < 2.5\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.bool, outcome.value.type) try testing.expect(outcome.value.data.boolean) } } conditional_errors_trap_only_when_demanded_and_preserve_original_identity test { sources [7][]u8 :: [ "main proc() int {\n if true {\n return 42\n } else {\n return unknown\n }\n}\n", "main proc() int {\n if false {\n return 42\n } else {\n return unknown\n }\n}\n", "main proc() int {\n if true {\n return 42\n }\n}\n", "main proc() int {\n if false {\n return 42\n }\n}\n", "main proc() void {\n if false {\n unknown\n }\n}\n", "main proc() int {\n if 1 {\n return 42\n } else {\n return 0\n }\n}\n", "main proc() int {\n if unknown {\n return 42\n }\n}\n", ] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) original :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) if (index == 0 or index == 2 or index == 4) { try testing.expect(outcome.trap == null) if (index == 4) try testing.expect_equal(ir.Type.void, outcome.value.type) else try testing.expect_equal(i64(42), outcome.value.data.integer) } else { try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) code := diagnostics.Code.unknown_name if (index == 3) code = .missing_return if (index == 5) code = .condition_not_bool try testing.expect_equal(code, original.code) if (index == 5) try testing.expect(mem.eql(source[usize(original.span.start)..usize(original.span.end)], "1")) } try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } missing_return_does_not_replace_reached_arm_error test { sources [3][]u8 :: [ "main proc() int {\n if true {\n return unknown\n }\n}\n", "main proc() int {\n if false {\n return unknown\n }\n}\n", "main proc() int {\n if false {\n return unknown\n }\n return 42\n}\n", ] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) if (index == 2) { try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } else { code diagnostics.Code :: if (index == 0) .unknown_name else .missing_return try expect_trap(outcome, &diagnostic_store, code) for (0..diagnostics.count(&diagnostic_store)) |i| { diagnostic :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(i))? if (diagnostic.code == code) try testing.expect_equal(diagnostics.DiagnosticId(i), outcome.trap?) } } } } nested_locals_are_predeclared_before_any_arm_executes test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() int { ` if true { ` if true { ` before :: later ` later :: 42 ` return before ` } ` } ` return 0 `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try expect_trap(outcome, &diagnostic_store, .read_before_initialization) } iterative_factorial_executes_from_owned_ir_after_frontend_cleanup test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `factorial proc(n int) int { ` remaining := n ` result := 1 ` while remaining > 1 { ` result = result * remaining ` remaining = remaining - 1 ` } ` return result `} `main proc() int { ` return factorial(5) `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.int, outcome.value.type) try testing.expect_equal(i64(120), outcome.value.data.integer) } zero_nested_early_return_and_predicate_call_loops_execute test { sources [6][]u8 :: [ "main proc() int {\n n := 0\n while n > 0 {\n n = n - 1\n }\n return 42 + n\n}\n", "main proc() int {\n outer := 3\n total := 0\n while outer > 0 {\n inner := 2\n while inner > 0 {\n total = total + 7\n inner = inner - 1\n }\n outer = outer - 1\n }\n return total\n}\n", "main proc() int {\n n := 2\n while n > 0 {\n if n == 1 { return 42 }\n n = n - 1\n }\n return 0\n}\n", "enabled :: positive(1)\npositive proc(n int) bool { return n > 0 }\nmain proc() int {\n n := 3\n total := 0\n while positive(n) {\n if enabled { total = total + 14 }\n n = n - 1\n }\n return total\n}\n", "main proc() int {\n n := 0\n while (n < 42) n = n + 1\n return n\n}\n", "main proc() int {\n while true { return 42 }\n return 0\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.int, outcome.value.type) try testing.expect_equal(i64(42), outcome.value.data.integer) } } float_bool_string_and_compatible_function_updates_execute test { sources [4][]u8 :: [ "main proc() float {\n value float := 0\n value = 3 / 2\n return value\n}\n", "main proc() bool {\n flag := false\n flag = true\n return flag\n}\n", "main proc() string {\n text := \"before\"\n text = \"owned\\nupdated\"\n return text\n}\n", "first proc(value int) int { return value + 1 }\nsecond proc(renamed int) int { return renamed + 2 }\nmain proc() int {\n invoke := first\n invoke = second\n return invoke(40)\n}\n", ] expected_types [4]ir.Type :: [.float, .bool, .string, .int] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(expected_types[index], outcome.value.type) match index { 0: try testing.expect_equal(f64(1.5), outcome.value.data.real) 1: try testing.expect(outcome.value.data.boolean) 2: try testing.expect(mem.eql(ir.string_bytes(&module, outcome.value.data.string), "owned\nupdated")) 3: try testing.expect_equal(i64(42), outcome.value.data.integer) else: unreachable } } } forward_assignments_check_initialization_after_evaluating_the_rhs test { sources [2][]u8 :: [ "main proc() int {\n later = 42\n later := 0\n return later\n}\n", "main proc() int {\n later = later + 1\n later := 0\n return later\n}\n", ] codes [2]diagnostics.Code :: [.write_before_initialization, .read_before_initialization] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try expect_trap(outcome, &diagnostic_store, codes[index]) diagnostic :: diagnostics.get(&diagnostic_store, outcome.trap?)? try testing.expect(mem.eql(source[usize(diagnostic.span.start)..usize(diagnostic.span.end)], "later")) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } loop_and_reentered_arm_locals_are_uninitialized_before_later_declarations test { sources [11][]u8 :: [ "main proc() int {\n n := 2\n while n > 0 {\n if n == 1 { return later }\n later :: 42\n n = n - 1\n }\n return 0\n}\n", "main proc() int {\n n := 2\n while n > 0 {\n if n == 1 {\n later = 42\n return later\n }\n later := 0\n n = n - 1\n }\n return 0\n}\n", "main proc() int {\n n := 2\n while n > 0 {\n if true {\n if n == 1 { return later }\n later :: 42\n }\n n = n - 1\n }\n return 0\n}\n", "main proc() int {\n n := 2\n while n > 0 {\n if true {\n if n == 1 {\n later = 42\n return later\n }\n later := 0\n }\n n = n - 1\n }\n return 0\n}\n", "main proc() int {\n n := 2\n while n > 0 {\n if false {} else {\n if n == 1 { return later }\n later :: 42\n }\n n = n - 1\n }\n return 0\n}\n", "main proc() int {\n n := 3\n while n > 0 {\n n = n - 1\n if n == 0 { return later }\n if (n == 1) continue\n later :: 42\n continue\n }\n return 0\n}\n", "main proc() int {\n n := 3\n while n > 0 {\n n = n - 1\n if n == 0 { later = 42 }\n if (n == 1) continue\n later := 0\n continue\n }\n return 0\n}\n", "main proc() int {\n n := 3\n while n > 0 {\n n = n - 1\n if true {\n if n == 0 { return later }\n if (n == 1) continue\n later :: 42\n continue\n }\n }\n return 0\n}\n", "main proc() int {\n n := 3\n while n > 0 {\n n = n - 1\n if true {\n if n == 0 { later = 42 }\n if (n == 1) continue\n later := 0\n continue\n }\n }\n return 0\n}\n", "main proc() int {\n n := 3\n while n > 0 {\n n = n - 1\n if false {} else {\n if n == 0 { return later }\n if (n == 1) continue\n later :: 42\n continue\n }\n }\n return 0\n}\n", "main proc() int {\n n := 3\n while n > 0 {\n n = n - 1\n if false {} else {\n if n == 0 { later = 42 }\n if (n == 1) continue\n later := 0\n continue\n }\n }\n return 0\n}\n", ] codes [11]diagnostics.Code :: [ .read_before_initialization, .write_before_initialization, .read_before_initialization, .write_before_initialization, .read_before_initialization, .read_before_initialization, .write_before_initialization, .read_before_initialization, .write_before_initialization, .read_before_initialization, .write_before_initialization, ] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try expect_trap(outcome, &diagnostic_store, codes[index]) diagnostic :: diagnostics.get(&diagnostic_store, outcome.trap?)? try testing.expect(mem.eql(source[usize(diagnostic.span.start)..usize(diagnostic.span.end)], "later")) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } invalid_assignments_and_loop_conditions_preserve_original_diagnostics test { sources [9][]u8 :: [ "main proc() int {\n value :: 0\n value = 1\n return 42\n}\n", "update proc(value int) int {\n value = 1\n return value\n}\nmain proc() int { return update(0) }\n", "value := 0\nmain proc() int {\n value = 1\n return value\n}\n", "main proc() int {\n unknown = 1\n return 42\n}\n", "main proc() int {\n value := 0\n value = unknown\n return value\n}\n", "main proc() int {\n value := 0\n value = 1.5\n return value\n}\n", "first proc(value int) int { return value }\nsecond proc(value bool) int { return 42 }\nmain proc() int {\n invoke := first\n invoke = second\n return 42\n}\n", "main proc() int {\n while 1 { return 42 }\n return 0\n}\n", "main proc() int {\n while unknown { return 42 }\n return 0\n}\n", ] codes [9]diagnostics.Code :: [.invalid_assignment_target, .invalid_assignment_target, .invalid_assignment_target, .unknown_name, .unknown_name, .type_mismatch, .type_mismatch, .condition_not_bool, .unknown_name] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) original :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? try testing.expect_equal(codes[index], original.code) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } zero_iteration_body_errors_are_checked_but_not_executed test { sources [5][]u8 :: [ "main proc() int {\n while false {\n unknown = 1\n }\n return 42\n}\n", "main proc() int {\n while false {\n value :: 0\n value = 1\n }\n return 42\n}\n", "main proc() int {\n while false {\n value := 0\n value = true\n }\n return 42\n}\n", "main proc() int {\n while false {\n return unknown\n }\n return 42\n}\n", "main proc() int {\n while false {\n while unknown {}\n }\n return 42\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect(diagnostics.count(&diagnostic_store) > 0) count :: diagnostics.count(&diagnostic_store) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) try testing.expect_equal(count, diagnostics.count(&diagnostic_store)) } } loop_returns_do_not_guarantee_return_and_assignment_arithmetic_still_checks_overflow test { sources [5][]u8 :: [ "main proc() int {\n while true { return 42 }\n}\n", "main proc() int {\n while false { return 42 }\n}\n", "main proc() int {\n value := 9223372036854775807\n while true {\n value = value + 1\n return value\n }\n return 0\n}\n", "main proc() int {\n while true { break }\n}\n", "main proc() int {\n while false { continue }\n}\n", ] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) if (index != 2) { try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) diagnostic :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? try testing.expect_equal(diagnostics.Code.missing_return, diagnostic.code) } else try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) if (index == 0) { try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } else { code diagnostics.Code :: if (index == 2) .integer_overflow else .missing_return try expect_trap(outcome, &diagnostic_store, code) if (index != 2) try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) } } } bounded_pure_loops_do_not_allocate_per_iteration_and_release_runtime_storage test { sources [2][]u8 :: [ "main proc() int {\n n := 0\n while (n < 1 and !(n == 1)) or false {\n local :: n\n n = local + 1\n if (n == 1) break\n continue\n later :: 0\n }\n return n\n}\n", "main proc() int {\n n := 0\n while (n < 10000 and !(n == 10000)) or false {\n local :: n\n n = local + 1\n if (n == 10000) break\n continue\n later :: 0\n }\n return n\n}\n", ] expected [2]i64 :: [1, 10000] allocations := usize(0) for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) state := AllocationState{ remaining = 64 } allocator :: mem.Allocator{ context = &state, vtable = &limited_vtable } outcome :: try interpreter.run(allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(expected[index], outcome.value.data.integer) try testing.expect_equal(usize(0), state.live) if (index == 0) allocations = usize(64) - state.remaining else try testing.expect_equal(allocations, usize(64) - state.remaining) } } boolean_truth_tables_nesting_negation_and_precedence_execute test { sources [18][]u8 :: [ "main proc() bool { return false and false }\n", "main proc() bool { return false and true }\n", "main proc() bool { return true and false }\n", "main proc() bool { return true and true }\n", "main proc() bool { return false or false }\n", "main proc() bool { return false or true }\n", "main proc() bool { return true or false }\n", "main proc() bool { return true or true }\n", "main proc() bool { return !false }\n", "main proc() bool { return !true }\n", "main proc() bool { return !!true }\n", "main proc() bool { return !!!true }\n", "main proc() bool { return true or false and false }\n", "main proc() bool { return (true or false) and false }\n", "main proc() bool { return !(1 + 2 < 3) and 4 == 4 or false }\n", "main proc() bool { return false or (true and (false or !false)) }\n", "main proc() bool { return true and false or true and true }\n", "main proc() bool { return !(-9223372036854775808 < 0) }\n", ] expected [18]bool :: [ false, false, false, true, false, true, true, true, true, false, true, false, true, false, true, true, true, false, ] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.bool, outcome.value.type) try testing.expect_equal(expected[index], outcome.value.data.boolean) } } branchful_calls_preserve_earlier_temps_nested_arguments_and_function_callees test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `integer proc(value int) int { return value } `boolean proc(value bool) bool { return value } `score proc(first int, left bool, right bool, last int) int { ` if left and right { return first + last } ` return 0 `} `invoke :: score `main proc() int { ` local :: invoke ` return local(integer(20), boolean(false or true), boolean(true and (false or true)), local(integer(0), false or boolean(true), true and !false, local(integer(5), true or false, true and true, integer(17)))) `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } short_circuit_source_errors_remain_checked_but_skipped_in_every_consumer test { sources [10][]u8 :: [ "main proc() bool { return false and absent or true }\n", "main proc() bool { return true or 1 }\n", "main proc() bool { return !(false and absent) }\n", "main proc() bool { return (false and absent) == false }\n", "flag :: true or absent\nmain proc() bool { return flag }\n", "main proc() bool {\n flag :: true or absent\n return flag\n}\n", "main proc() bool {\n flag := false\n flag = true or absent\n return flag\n}\n", "identity proc(value bool) bool { return value }\nmain proc() bool { return identity(true or absent) }\n", "main proc() bool {\n if true or absent { return true }\n return false\n}\n", "main proc() bool {\n true or absent\n while false and absent {}\n return true\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) count :: diagnostics.count(&diagnostic_store) try testing.expect(count > 0) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(ir.Type.bool, outcome.value.type) try testing.expect(outcome.value.data.boolean) try testing.expect_equal(count, diagnostics.count(&diagnostic_store)) } } demanded_boolean_source_errors_keep_the_original_diagnostic_and_operand_span test { sources [8][]u8 :: [ "main proc() bool { return true and absent }\n", "main proc() bool { return false or absent }\n", "main proc() bool { return true and 1 }\n", "main proc() bool { return false or 1 }\n", "main proc() bool { return !1 }\n", "main proc() bool { return !absent }\n", "main proc() bool { return absent and false }\n", "main proc() bool { return absent or true }\n", ] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) original :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? code diagnostics.Code :: if (index >= 2 and index <= 4) .invalid_operand else .unknown_name operand []u8 :: if (index >= 2 and index <= 4) "1" else "absent" try testing.expect_equal(code, original.code) try testing.expect(mem.eql(source[usize(original.span.start)..usize(original.span.end)], operand)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } branchful_call_arguments_evaluate_left_to_right_before_later_errors test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `both proc(left bool, right bool) bool { return left and right } `main proc() bool { ` return both(false or (true and first_absent), true and second_absent) `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) original :: diagnostics.get(&diagnostic_store, outcome.trap?)? try testing.expect_equal(diagnostics.Code.unknown_name, original.code) try testing.expect(mem.eql(source[usize(original.span.start)..usize(original.span.end)], "first_absent")) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) } short_circuit_protects_uninitialized_reads_global_cycles_and_overflow test { sources [6][]u8 :: [ "main proc() bool {\n before :: false and later\n later :: true\n return !before\n}\n", "main proc() bool {\n before :: true or later\n later :: false\n return before\n}\n", "flag bool :: true or flag\nmain proc() bool { return flag }\n", "left bool :: false and right\nright bool :: left\nmain proc() bool { return !left }\n", "main proc() bool { return !(false and 9223372036854775807 + 1 == 0) }\n", "main proc() bool { return true or -(-9223372036854775808) == 0 }\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect(outcome.value.data.boolean) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } } demanded_boolean_runtime_errors_keep_the_instruction_span test { sources [4][]u8 :: [ "main proc() bool {\n before :: true and later\n later :: true\n return before\n}\n", "flag bool :: false or flag\nmain proc() bool { return flag }\n", "main proc() bool { return true and 9223372036854775807 + 1 == 0 }\n", "main proc() bool {\n minimum :: -9223372036854775808\n return false or -minimum == 0\n}\n", ] codes [4]diagnostics.Code :: [.read_before_initialization, .initialization_cycle, .integer_overflow, .integer_overflow] spans [4][]u8 :: ["later", "flag", "9223372036854775807 + 1", "-minimum"] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try expect_trap(outcome, &diagnostic_store, codes[index]) diagnostic :: diagnostics.get(&diagnostic_store, outcome.trap?)? try testing.expect(mem.eql(source[usize(diagnostic.span.start)..usize(diagnostic.span.end)], spans[index])) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } composed_loop_guards_recompute_and_overwrite_synthetic_results test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `enabled :: false or true and !false `positive proc(n int) bool { return n > 0 } `main proc() int { ` n := 4 ` total := 0 ` while (enabled and positive(n)) or n > 4 { ` if (n == 4 or n == 2) and !(n == 3) { ` total = total + 21 ` } ` n = n - 1 ` } ` return total + n `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } lazy_branchful_global_initializers_and_calls_release_every_runtime_allocation test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `first :: true and later `later :: false or !false `both proc(left bool, right bool) bool { return left and right } `main proc() bool { ` n := 3 ` while both(first and first, both(false or later, !false and later)) and n > 0 { ` n = n - 1 ` if (n > 0) continue else break ` } ` return both(first and first, both(false or later, !false and later)) `} module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) failures := usize(0) succeeded := false for (usize(0)..usize(64)) |limit| { state := AllocationState{ remaining = limit } allocator :: mem.Allocator{ context = &state, vtable = &limited_vtable } outcome :: interpreter.run(allocator, &module, &diagnostic_store) catch |error| { try testing.expect(error == .out_of_memory) failures += 1 try testing.expect_equal(usize(0), state.live) continue } try testing.expect(outcome.trap == null) try testing.expect(outcome.value.data.boolean) try testing.expect_equal(usize(0), state.live) succeeded = true break } try testing.expect(succeeded) try testing.expect(failures > 0) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } skipped_rhs_does_not_hide_a_demanded_outer_type_mismatch test { sources [3][]u8 :: [ "main proc() int { return true or absent }\n", "main proc() void {\n number := 0\n number = true or absent\n}\n", "integer proc(value int) int { return value }\nmain proc() int { return integer(true or absent) }\n", ] codes [3]diagnostics.Code :: [.return_type_mismatch, .type_mismatch, .type_mismatch] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) count :: diagnostics.count(&diagnostic_store) try testing.expect_equal(2, count) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try expect_trap(outcome, &diagnostic_store, codes[index]) try testing.expect_equal(count, diagnostics.count(&diagnostic_store)) } } break_and_continue_skip_iterations_exit_early_and_restore_outer_targets test { sources [3][]u8 :: [ "main proc() int {\n n := 0\n total := 0\n while n < 10 {\n n = n + 1\n if (n == 2 or n == 4) continue\n if (n == 6) break\n total = total + n\n }\n return total + 33\n}\n", "main proc() int {\n outer := 3\n total := 0\n while outer > 0 {\n outer = outer - 1\n inner := 3\n while inner > 0 {\n inner = inner - 1\n if (inner == 1) continue\n if (inner == 0) break\n total = total + 7\n }\n if (outer > 0) continue else break\n }\n return total + 21\n}\n", "main proc() int {\n n := 0\n while n < 5 {\n n = n + 1\n if (n < 3) continue else break\n n = n + 100\n }\n return n + 39\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } } continue_restarts_composed_calling_guards_after_branchful_body_calls test { guards [3][]u8 :: [ "(positive(3 - n) and !(n == 2)) or n == 2", "!(n >= 3) and (positive(3 - n) or false)", "positive(3 - n) and ((n < 3 or false) and !false)", ] for (guards) |guard| { source := arraylist.init(u8, mem.c_allocator) defer arraylist.deinit(&source) for ("positive proc(n int) bool { return n > 0 }\nadvance proc(flag bool, n int) int {\n if flag { return n + 1 }\n return n + 1\n}\nmain proc() int {\n n := 0\n while ") |byte| try arraylist.append(&source, byte) for (guard) |byte| try arraylist.append(&source, byte) # bound a stale guard too, so incorrect continue lowering fails instead of hanging. for (" {\n n = advance(n == 0 or n == 1, n)\n if (n > 5) break\n continue\n n = n + 100\n }\n return n + 39\n}\n") |byte| try arraylist.append(&source, byte) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source.items, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } } reached_outside_controls_preserve_diagnostic_identity_and_keyword_span test { sources [4][]u8 :: [ "main proc() int {\n break\n return 42\n}\n", "main proc() int {\n continue\n return 42\n}\n", "bad proc() void {\n break\n}\nmain proc() int {\n while true { bad()\n break }\n return 42\n}\n", "main proc() int {\n while true { break }\n continue\n return 42\n}\n", ] codes [4]diagnostics.Code :: [.break_outside_loop, .continue_outside_loop, .break_outside_loop, .continue_outside_loop] keywords [4][]u8 :: ["break", "continue", "break", "continue"] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) original :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? try testing.expect_equal(codes[index], original.code) try testing.expect(mem.eql(source[usize(original.span.start)..usize(original.span.end)], keywords[index])) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } untaken_outside_controls_remain_checked_without_trapping test { sources [5][]u8 :: [ "main proc() int {\n if (false) break\n return 42\n}\n", "main proc() int {\n if true { return 42 } else continue\n}\n", "main proc() int {\n return 42\n break\n}\n", "main proc() int {\n return 42\n continue\n}\n", "bad proc() void {\n continue\n}\nmain proc() int {\n while false { bad() }\n return 42\n}\n", ] codes [5]diagnostics.Code :: [.break_outside_loop, .continue_outside_loop, .break_outside_loop, .continue_outside_loop, .continue_outside_loop] for (sources) |source, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) original :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? try testing.expect_equal(codes[index], original.code) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } source_errors_after_loop_transfers_stay_checked_but_unreached test { sources [4][]u8 :: [ "main proc() int {\n n := 2\n while n > 0 {\n n = n - 1\n continue\n missing\n }\n return 42\n}\n", "main proc() int {\n while true {\n break\n missing\n }\n return 42\n}\n", "main proc() int {\n n := 2\n while n > 0 {\n n = n - 1\n if (n > 0) continue else break\n missing\n }\n return 42\n}\n", "main proc() int {\n while true {\n return 42\n break\n continue\n missing\n }\n return 0\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) original :: diagnostics.get(&diagnostic_store, diagnostics.DiagnosticId(0))? try testing.expect_equal(diagnostics.Code.unknown_name, original.code) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } } traps_before_loop_controls_keep_the_original_error test { sources [2][]u8 :: [ "main proc() int {\n while true {\n missing\n break\n }\n return 42\n}\n", "main proc() int {\n while true {\n missing\n continue\n }\n return 42\n}\n", ] for (sources) |source| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module := try compile_owned(source, &diagnostic_store) defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) outcome :: try interpreter.run(mem.c_allocator, &module, &diagnostic_store) try expect_trap(outcome, &diagnostic_store, .unknown_name) try testing.expect_equal(diagnostics.DiagnosticId(0), outcome.trap?) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } }