import "@std/mem" import "@std/arraylist" import "@std/testing" import "@source/ast" import "@source/diagnostics" import "@source/ir" import "@source/interpreter" @hide:file owned_text proc(text []u8) arraylist.ArrayList(u8) ! mem.AllocError { bytes := arraylist.init(u8, mem.c_allocator) errdefer arraylist.deinit(&bytes) try arraylist.reserve(&bytes, text.len) for (text) |byte| try arraylist.append(&bytes, byte) return bytes } keeps_package_declarations_distinct_and_ir_independent test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) first := try owned_text("value :: 21\nmain proc() int { return value * 2 }") defer arraylist.deinit(&first) second := try owned_text("value :: 3\nmain proc() int { return value * 2 }") defer arraylist.deinit(&second) sibling := try owned_text("helper proc() int { return 7 }") defer arraylist.deinit(&sibling) path [9]mut u8 := undefined memcopy!(path[..], "first.hon") session := init(mem.c_allocator) # the IR must outlive the compilation, including source buffers and interned names. modules [2]mut ir.Module := undefined { defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) left :: try add_source(&session, root, path[..], &first, &diagnostic_store) right :: try add_source(&session, dependency, "second.hon", &second, &diagnostic_store) third :: try add_source(&session, root, "sibling.hon", &sibling, &diagnostic_store) path[0] = 'X' try testing.expect_equal(0, first.capacity) try testing.expect_equal(0, first.items.len) try testing.expect(mem.eql(source(&session, left).path, "first.hon")) try testing.expect_equal(root, source(&session, left).package) try testing.expect_equal(dependency, source(&session, right).package) try testing.expect_equal(root, source(&session, third).package) try testing.expect_equal(left, session.packages.items[usize(root)].sources.items[0]) try testing.expect_equal(third, session.packages.items[usize(root)].sources.items[1]) a :: declaration(&session, left, 0) b :: declaration(&session, right, 0) try testing.expect(a.source != b.source) tree :: ast.view(&session.builder, source(&session, left).root) try testing.expect_equal(ast.name_id(&tree, a.node), ast.name_id(&tree, b.node)) try testing.expect(mem.eql(ast.source_text(&tree, ast.statement_decl(&tree, a.node).span), "value :: 21")) try testing.expect(mem.eql(ast.source_text(&tree, ast.statement_decl(&tree, b.node).span), "value :: 3")) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, dependency, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) left_result := try interpreter.run(mem.c_allocator, &modules[0], &diagnostic_store) defer interpreter.deinit(&left_result) right_result := try interpreter.run(mem.c_allocator, &modules[1], &diagnostic_store) defer interpreter.deinit(&right_result) try testing.expect(left_result.trap == null and right_result.trap == null) try testing.expect_equal(i64(42), left_result.value.data.integer) try testing.expect_equal(i64(6), right_result.value.data.integer) } recompiles_specialized_calls_without_borrowing_previous_ir test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) bytes := try owned_text( `identity proc($T type, value T) T { ` if (T == i16) return value else return value.missing `} `Box proc(T type) type { return struct { value T } } `make_box proc($T type) Box(T) { return { value = 40 } } `count proc() usize { ` result := 0 ` while (result < 2) result = result + 1 ` return result `} `Fixed proc(N usize) type { ` Element := i8 ` length usize := 0 ` while (length < N) { ` match length { 0: Element = i16, else: Element = i16 } ` length = length + 1 ` } ` return match length { ` 2: struct { ` values [match length { 2: length, else: usize(1) }](match true { ` true: Element, ` false: i8(128), ` }) ` bias i16 = match true { true: 0, false: i8(128) } ` }, ` else: i8(128), ` } `} `data proc() [2]i16 { return [40, 2] } `Tag :: enum(i16) { empty = -7, payload = 90 } `Choice :: union(Tag) { payload struct { values [2]i16, text string }, empty void } `Error :: union(enum) { failed struct { code i16, text string } } `Packet :: struct { choices [2]Choice, results [2](i16 ! Error) } `packet_data proc() Packet { ` return { ` choices = [.payload { values = [40, 2], text = "ok" }, .empty], ` results = [40, .failed { code = 2, text = "owned" }], ` } `} `Token proc($value Packet) type { return struct {} } `Captured proc(choice Choice) type { ` match choice { ` .payload |detail|: return struct { ` values [match detail.text.len { 2: usize(2), else: usize(0) }]i16 ` bias i16 = match choice { .payload |selected|: selected.values[1], .empty: 0 } ` }, ` .empty: return bool, ` } `} `Optional proc() type { ` pending ?usize := 2 ` Result := i8 ` while pending |length| { ` pending = null ` Result = [length]i16 ` } ` return Result `} `Updated proc() type { ` config struct { counts [2]mut usize, bias i16 } := { counts = [0, 0], bias = 0 } ` original :: config ` for (usize(0)..2) |index| { ` field!(config, "counts")[index] = index + 1 ` field!(config, "bias") = field!(config, "bias") + 21 ` } ` return struct { ` values [config.counts[1]]i16 ` bias i16 = config.bias + i16(original.counts[1]) ` } `} `Collected proc() type { ` counts [2]mut usize := [1, 1] ` length usize := 0 ` for counts |amount, index| { ` counts[1] = 2 ` length = length + amount ` if (index == 0) continue ` } ` Element := i8 ` for "ok" |byte| { if (byte == u8(107)) Element = i16 } ` return [length]Element `} `TypeArray proc() type { ` types [2]mut [1]mut type := [[i8], [bool]] ` original :: types ` for types |row, index| { ` copied := row ` copied[0] = u8 ` if (row[0] == u8) return bool ` if (index == 0) { (types[1])[0] = i16 } ` if (index == 1 and row[0] != i16) return bool ` } ` if (original[1][0] != bool) return bool ` inferred := [[i8], [bool]] ` inferred = [[i16], [u8]] ` empty [0]type :: [] ` for (empty) |_| return bool ` return struct { values [types.len]types[1][0], bias inferred[0][0] = 2 } `} `TypeRows proc() [2]mut [1]mut type { ` rows [2]mut [1]mut type := [[i8], [i16]] ` (rows[0])[0] = i16 ` return rows `} `copy_type_rows proc(rows [2]mut [1]mut type) [2]mut [1]mut type { ` copied := rows ` (copied[0])[0] = u8 ` if (rows[0][0] != i16) return [[bool], [bool]] ` return copied `} `BoundaryType proc(rows [2]mut [1]mut type) type { ` original :: rows ` changed :: copy_type_rows(rows) ` if (changed[0][0] != u8 or rows[0][0] != i16 or original[0][0] != i16) return bool ` return struct { values [rows.len]rows[0][0], bias rows[1][0] = 2 } `} `read_type_rows proc($rows [2]mut [1]mut type, value rows[0][0]) rows[1][0] { ` return value + rows[1][0](rows.len) `} `UninitializedRows proc() [2]mut type { ` types [2]mut type := undefined ` for (usize(0)..types.len) |index| { ` if (index == 0) { types[index] = struct { value i16 } } ` else { types[index] = types[0] } ` } ` return types `} `UninitializedType proc(types [2]mut type) type { ` return struct { first types[0], second types[1] } `} `UninitializedValues proc() type { ` rows [2]mut [2]mut usize := undefined ` (rows[0])[0] = 1 ` copied := rows ` rows[0] = [3, 4] ` (copied[0])[1] = 2 ` copied[1] = [1, 1] ` for (usize(0)..rows[1].len) |index| { (rows[1])[index] = index + 1 } ` return struct { ` values [copied[0][1]]i16 ` bias i16 = i16(copied[1][0] + rows[1][0]) ` } `} `TypeRecord proc() type { ` Config :: struct { child type, counts [2]mut usize, bias i16 = 2 } ` config Config := { child = i8, counts = [0, 0] } ` original :: config ` config.child = i16 ` for config.counts |_, index| { config.counts[index] = index + 1 } ` if (original.child != i8 or original.counts[1] != 0) return bool ` return struct { values [config.counts[1]]config.child, bias i16 = config.bias } `} `BoundaryConfig :: struct { child type, counts [2]mut usize, bias i16 = 2 } `record_config proc() BoundaryConfig { return { child = i16, counts = [1, 2] } } `change_record_config proc(input BoundaryConfig) BoundaryConfig { ` copied := input ` copied.child = u8 ` copied.counts[1] = 3 ` return copied `} `RecordBoundary proc(input BoundaryConfig) type { ` changed :: change_record_config(input) ` if (input.child != i16 or input.counts[1] != 2) return bool ` if (changed.child != u8 or changed.counts[1] != 3) return bool ` return struct { values [input.counts[1]]input.child, bias i16 = input.bias } `} `RecordAlias :: alias RecordBoundary(record_config()) `read_record_config proc($input BoundaryConfig, value input.child) input.child { ` return value + input.bias `} `record_pair proc() struct { type, usize } { return {i16, 2} } `RecordPairElement proc(input struct { type, usize }) type { return field!(input, "0") } `read_record_pair proc($input struct { type, usize }, value RecordPairElement(input)) RecordPairElement(input) { ` return value + i16(field!(input, "1")) `} `read_selected proc($choice Choice, value i16) i16 { ` match choice { ` .payload |detail|: { ` copied :: detail ` if (copied.text.len != 2) return -1 ` return match detail.values[1] { ` 2: match true { true: value + detail.values[1], false: value.missing }, ` else: value.missing, ` } ` } ` .empty: return value.missing, ` } `} `main proc() i16 { ` if $(i16 != i16) { return $(i8(128)) } ` values :: $(data()) ` box Box(i16) :: make_box() ` inferred i16 :: identity(40) ` if (inferred != box.value) return 0 ` fixed Fixed(count()) :: { values = [box.value, 2] } ` same Fixed(2) :: fixed ` packet :: $(packet_data()) ` if (Token(packet_data()) != Token(packet)) return 0 ` captured Captured(packet.choices[0]) :: { values = [19, 21] } ` if (captured.values[0] + captured.values[1] + captured.bias != 42) return 0 ` optional Optional() :: captured.values ` if (optional[0] + optional[1] != 40) return 0 ` updated Updated() :: { values = optional } ` if (updated.bias != 42 or updated.values[1] != 21) return 0 ` collected Collected() :: [19, 21, 2] ` same_collected Collected() :: collected ` total i16 := 0 ` for same_collected |item| { total = total + item } ` if (total != 42) return 0 ` type_array TypeArray() :: { values = [19, 21] } ` same_type_array TypeArray() :: type_array ` if (same_type_array.values[0] + same_type_array.values[1] + same_type_array.bias != 42) return 0 ` boundary BoundaryType(TypeRows()) :: { values = [19, 21] } ` same_boundary BoundaryType(TypeRows()) :: boundary ` if (same_boundary.values[0] + same_boundary.values[1] + field!(same_boundary, "bias") != 42) return 0 ` if (copy_type_rows(TypeRows())[0][0] != u8 or copy_type_rows(TypeRows())[1][0] != i16) return 0 ` if (TypeRows()[0][0] != i16) return 0 ` if (read_type_rows(TypeRows(), 40) != 42 or read_type_rows(TypeRows(), 40) != 42) return 0 ` initialized UninitializedType(UninitializedRows()) :: { ` first = { value = 19 }, second = { value = 21 }, ` } ` same_initialized UninitializedType(UninitializedRows()) :: initialized ` copied_initialized := same_initialized.first ` initialized_values UninitializedValues() :: { values = [19, 21] } ` same_initialized_values UninitializedValues() :: initialized_values ` if (same_initialized_values.values[0] + same_initialized_values.values[1] + same_initialized_values.bias != 42) return 0 ` type_record TypeRecord() :: { values = [19, 21] } ` same_type_record TypeRecord() :: type_record ` if (same_type_record.values[0] + same_type_record.values[1] + same_type_record.bias != 42) return 0 ` record_boundary RecordAlias :: { values = [19, 21] } ` same_record_boundary RecordBoundary({ child = i16, counts = [1, 2] }) :: record_boundary ` if (same_record_boundary.values[0] + same_record_boundary.values[1] + same_record_boundary.bias != 42) return 0 ` if (read_record_config(record_config(), 40) != 42) return 0 ` if (read_record_pair(record_pair(), 40) != 42 or read_record_pair(record_pair(), 40) != 42) return 0 ` copied_initialized = same_initialized.second ` payload :: packet.choices[0].payload ` if (payload.values[0] != 40 or payload.values[1] != 2 or payload.text.len != 2) return 0 ` match packet.choices[1] { .empty: {}, .payload: return 0 } ` if ((packet.results[0] catch 0) != 40) return 0 ` error_code :: packet.results[1] catch |err| match err { ` .failed |detail|: detail.code + i16(detail.text.len), ` } ` if (error_code != 7) return 0 ` return read_selected(packet.choices[0], identity(initialized.first.value) + copied_initialized.value + same.values[$(usize(1))] + same.bias - 2) `} , ) defer arraylist.deinit(&bytes) _ = try add_source(&session, root, "generic.hon", &bytes, &diagnostic_store) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for (0..modules.len) |index| { outcome := try interpreter.run(mem.c_allocator, &modules[index], &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } } recompiles_imported_inline_for_without_retaining_expansions test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) root_bytes := try owned_text( `dep :: import "../dependency" `touch proc(value i16) i16 { return value } `main proc() i16 { ` total i16 := 0 ` inline for dep.values() |value, index| { ` at usize :: index ` total = total + touch(value) + i16(at) ` } ` inline for dep.types() |T| { ` if (T == i16) { ` value T :: total ` total = touch(value) ` } else { ` value T :: true ` if (!value) return 0 ` } ` } ` inline for dep.metadata() |item, index| { ` if (index == 0) { ` if (item != i16) return 0 ` } else if (index == 1) { ` exact i8 :: item ` if (exact != 42) return 0 ` } else if (item.len != 2) return 0 ` } ` inline for (i8(0)..i8(2)) |outer| { ` inline for [i8(0), i8(1)] |inner| { ` for (0..usize(2)) |index| { ` if (index == 0) continue ` total = total + i16(outer + inner) ` } ` } ` } ` return total `} , ) defer arraylist.deinit(&root_bytes) dependency_bytes := try owned_text( `values proc() [2]i16 { return [17, 20] } `types proc() [2]type { return [i16, bool] } `metadata proc() struct { type, i8, string } { return {i16, 42, "ok"} } , ) defer arraylist.deinit(&dependency_bytes) root_source :: try add_source(&session, root, "app/main.hon", &root_bytes, &diagnostic_store) _ = try add_source(&session, dependency, "dependency/values.hon", &dependency_bytes, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = dependency, }) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for (0..modules.len) |index| { outcome := try interpreter.run(mem.c_allocator, &modules[index], &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } recompiles_imported_inline_match_arms_without_retaining_expansions test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) root_bytes := try owned_text( `dep :: import "../dependency" `known proc($tag dep.Mode) i16 { ` if (tag == .answer) return 0 else return false `} `enum_score proc(input dep.Mode) i16 { ` return match input { .skip: 0, inline |tag|: known(tag) } `} `main proc() i16 { ` values :: dep.values() ` if (enum_score(.skip) + enum_score(dep.mode()) != 0) return -1 ` if (dep.error_score(.Failure{code = 42}) + dep.error_score(.missing) != 42) return -2 ` return ( ` dep.read(dep.Choice, values[0]) + dep.read(dep.Choice, values[1]) ` + dep.read(dep.Choice, values[2]) + dep.read(dep.Choice, values[3]) ` ) `} , ) defer arraylist.deinit(&root_bytes) dependency_bytes := try owned_text( `Mode :: enum(i16) { skip = -7, answer = 42 } `Tag :: enum(i16) { number = -7, record = 3, text = 42, empty = 90 } `Pair :: struct { values [2]i16 } `Choice :: union(Tag) { text string, empty void, number i8, record Pair } `Missing :: enum { missing } `Failure :: struct { code i16 } `Errors :: alias (Missing | Failure | Missing) `fail proc(input Errors) i16 ! Errors { return input } `error_score proc(input Errors) i16 { ` return fail(input) catch |err| match err { ` inline |payload, tag|: match tag { .Failure: payload.code, else: 0 }, ` } `} `mode proc() Mode { return .answer } `values proc() [4]Choice { ` return [.number{19}, .record{values = [9, 11]}, .text{"ok"}, .empty] `} `narrow proc(value i8) i16 { return i16(value) } `pair proc(value Pair) i16 { return value.values[0] + value.values[1] } `read proc($T type, input T) i16 { ` match input { ` inline |payload, tag|: { ` match tag { ` inline |selected|: { ` if (selected == .number) { return narrow(payload) } ` else if (selected == .record) { return pair(payload) } ` else if (selected == .text) { return i16(payload.len) } ` else { ` payload ` return 1 ` } ` }, ` } ` }, ` } `} , ) defer arraylist.deinit(&dependency_bytes) root_source :: try add_source(&session, root, "app/main.hon", &root_bytes, &diagnostic_store) _ = try add_source(&session, dependency, "dependency/values.hon", &dependency_bytes, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = dependency, }) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for (0..modules.len) |index| { outcome := try interpreter.run(mem.c_allocator, &modules[index], &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } recompiles_generated_records_without_borrowing_previous_ir test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) bytes := try owned_text( `Child proc(value i16) type { ` return struct_type!(.auto, ["value"], [i16], [value]) `} `Parent proc(value i16) type { ` ChildType :: Child(value) ` child ChildType :: {} ` return struct_type!( ` .auto, ` ["child", "bias", "text"], ` [ChildType, i16, string], ` [child, 2, "owned"], ` ) `} `Captured proc(value ?i16) type { ` return if value |payload| struct { value i16 = payload } else bool `} `selected proc($value ?i16) i16 { ` return if $(value) |payload| payload else 7 `} `main proc() i16 { ` parent Parent(40) :: {} ` captured Captured(40) :: {} ` absent Captured(null) :: false ` if (absent or captured.value != selected(40) or selected(null) != 7) return 0 ` same Parent(40) :: parent ` child Child(40) :: same.child ` if (Child(40) == Child(41) or Parent(40) == Parent(41)) return 0 ` other Parent(41) :: {} ` if (other.child.value != 41 or same.text.len != 5) return 0 ` return child.value + same.bias `} , ) defer arraylist.deinit(&bytes) _ = try add_source(&session, root, "generated.hon", &bytes, &diagnostic_store) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for (0..modules.len) |index| { outcome := try interpreter.run(mem.c_allocator, &modules[index], &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } recompiles_imported_typeinfo_without_borrowing_metadata test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) metadata :: try add_package(&session) root_bytes := try owned_text( `meta :: import "@std/meta" `Id :: distinct i16 `Keys :: enum { left, right } `Source :: struct { value Id } `describe proc($T type) meta.TypeInfo { return typeinfo!(T) } `identity proc($T type, value T) T { return value } `Storage proc() type { ` match describe(Keys) { ` .enum |info|: { ` names [info.fields.len]mut []u8 := undefined ` types [info.fields.len]mut type := undefined ` defaults [info.fields.len]mut ?Id := undefined ` inline for info.fields |field, index| { ` if (field.index != index or field.type != Keys) compile_error!("invalid enum field") ` names[index] = field.name ` types[index] = Id ` defaults[index] = Id(40) ` } ` return struct_type!(.auto, names, types, defaults) ` } ` else: compile_error!("expected enum") ` } `} `main proc() i16 { ` match describe([2]Id) { ` .array |info|: { ` if (info.child != Id or info.len != 2) return 0 ` } ` else: return 0 ` } ` match describe(Id) { ` .distinct |backing|: if (backing != i16) return 0 ` else: return 0 ` } ` match describe(Source) { ` .record |info|: { ` if (info.fields.len != 1 or info.is_tuple or info.layout != .auto) return 0 ` inline for info.fields |field, index| { ` if (field.type != Id or field.index != index or field.name.len != 5) return 0 ` } ` } ` else: return 0 ` } ` storage Storage() :: {} ` left Id :: identity(Id, storage.left) ` right Id :: identity(Id, storage.right) ` return i16(left) + i16(right) - 38 `} , ) defer arraylist.deinit(&root_bytes) metadata_bytes := try owned_text( `Layout :: enum { auto, c } `ArrayInfo :: struct { child type, len usize } `FieldInfo :: struct { name []u8, type type, index usize } `RecordInfo :: struct { name ?[]u8, fields []FieldInfo, is_tuple bool, layout Layout } `EnumInfo :: struct { fields []FieldInfo } `TypeInfo :: union(enum) { ` invalid void ` void void ` noreturn void ` anyopaque void ` bool void ` integer void ` float void ` array ArrayInfo ` pointer void ` slice void ` range void ` optional void ` function void ` enum EnumInfo ` record RecordInfo ` union void ` fallible void ` distinct type `} , ) defer arraylist.deinit(&metadata_bytes) root_source :: try add_source(&session, root, "app/main.hon", &root_bytes, &diagnostic_store) _ = try add_source(&session, metadata, "std/meta/meta.hon", &metadata_bytes, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = metadata, }) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for (0..modules.len) |index| { outcome := try interpreter.run(mem.c_allocator, &modules[index], &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } dependency_traps_keep_original_spans_and_remain_demand_driven test { mains [2][]u8 :: [ `dep :: import "../dependency" `main proc() int { return dep.fail(false) } ` , `dep :: import "../dependency" `main proc() int { return dep.fail(true) } ` , ] prefix :: ` `fail proc(take bool) int { ` if take { ` for (mains) |main, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) module ir.Module := undefined dependency_source SourceId := undefined original diagnostics.DiagnosticId := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) root_bytes := try owned_text(main) defer arraylist.deinit(&root_bytes) dependency_bytes := try owned_text( ` `fail proc(take bool) int { ` if take { ` return missing ` } ` return 42 `} ` , ) defer arraylist.deinit(&dependency_bytes) root_source :: try add_source(&session, root, "app/main.hon", &root_bytes, &diagnostic_store) dependency_source = try add_source(&session, dependency, "dependency/fail.hon", &dependency_bytes, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = dependency, }) module = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&module) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) original = diagnostics.DiagnosticId(0) diagnostic :: diagnostics.get(&diagnostic_store, original)? try testing.expect_equal(diagnostics.Code.unknown_name, diagnostic.code) try testing.expect_equal(dependency_source, diagnostic.span.source) start :: u32(prefix.len + " return ".len) try testing.expect_equal(start, diagnostic.span.start) try testing.expect_equal(start + 7, diagnostic.span.end) outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) if (index == 0) { try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } else { try testing.expect_equal(original, outcome.trap?) } } } file_local_declarations_are_isolated_from_siblings_and_import_selection test { callers [4][]u8 :: [ "dep :: import \"../dependency\"\nmain proc() int { return dep.answer() }\n", "dep :: import \"../dependency\"\nmain proc() int { return dep.sibling() }\n", "dep :: import \"../dependency\"\nmain proc() int { return dep.leak() }\n", "dep :: import \"../dependency\"\nmain proc() int { return dep.helper() }\n", ] for callers |caller, index| { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) root_bytes := try owned_text(caller) defer arraylist.deinit(&root_bytes) first_bytes := try owned_text( `@hide:file `helper proc() int { return 42 } `@hide:file secret :: 7 `answer proc() int { return helper() } , ) defer arraylist.deinit(&first_bytes) second_bytes := try owned_text( `@hide:file `helper proc() int { return 99 } `sibling proc() int { return helper() } `leak proc() int { return secret } , ) defer arraylist.deinit(&second_bytes) root_source :: try add_source(&session, root, "app/main.hon", &root_bytes, &diagnostic_store) _ = try add_source(&session, dependency, "dependency/first.hon", &first_bytes, &diagnostic_store) _ = try add_source(&session, dependency, "dependency/second.hon", &second_bytes, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = dependency, }) module := try compile(&session, root, &diagnostic_store) defer ir.deinit(&module) outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store) defer interpreter.deinit(&outcome) if (index < 2) { try testing.expect(outcome.trap == null) expected :: if (index == 0) i64(42) else i64(99) try testing.expect_equal(expected, outcome.value.data.integer) } else { diagnostic :: diagnostics.get(&diagnostic_store, outcome.trap?)? try testing.expect_equal(diagnostics.Code.unknown_name, diagnostic.code) } } } yielding_blocks_recompile_owned_constants_and_generated_types test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) bytes := try owned_text( `Choose proc(wide bool) type { ` return { Selected := i8 ` if (wide) Selected = i16 ` yield Selected } `} `Box proc(wide bool) type { ` return struct { value Choose(wide) = { yield 42 } } `} `compute proc() i16 { ` return { value i16 := 40 ` defer value = 0 ` yield value + 2 } `} `main proc() i16 { ` value Box(true) :: {} ` constant :: $(compute()) ` return { yield if (constant == value.value) constant else -1 } `} , ) defer arraylist.deinit(&bytes) _ = try add_source(&session, root, "yielding.hon", &bytes, &diagnostic_store) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for modules |module| { outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } } compound_updates_and_type_composition_recompile_without_retaining_rewrites test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) bytes := try owned_text( `A :: enum { first } `B :: enum { second } `Errors :: A | B `Box proc() type { ` counts [1]mut usize := [1] ` counts[0] <<= 1 ` return struct { values [counts[0]]i16 } `} `compute proc() u8 { ` value u8 := 21 ` value <<= 1 ` return value `} `main proc() i16 { ` box Box() :: { values = [40, 2] } ` answer i16 := i16($(compute())) ` answer xor= box.values[0] + box.values[1] ` return answer + 42 `} , ) defer arraylist.deinit(&bytes) _ = try add_source(&session, root, "operators.hon", &bytes, &diagnostic_store) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for modules |module| { outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } } computed_callbacks_recompile_without_retaining_interpreter_function_ids test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) bytes := try owned_text( `lib :: import "../library" `increment proc(value i16) i16 { return value + 100 } `main proc() i16 { ` callback :: $(lib.choose()) ` return callback(41) `} , ) defer arraylist.deinit(&bytes) root_source :: try add_source(&session, root, "/callbacks/app/main.hon", &bytes, &diagnostic_store) imported := try owned_text( `Callback :: alias @proc(value i16) i16 `bias i16 :: 1 `@hide:file `increment proc(value i16) i16 { return value + bias } `choose proc() Callback { return increment } , ) defer arraylist.deinit(&imported) _ = try add_source(&session, dependency, "/callbacks/library/callback.hon", &imported, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = dependency, }) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for modules |module| { outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } } computed_byte_slices_recompile_without_borrowing_evaluation_storage test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) bytes := try owned_text( `lib :: import "../library" `main proc() i16 { ` packet :: $(lib.make()) ` if (packet.bytes.len != 4 or packet.suffix.len != 3) return -1 ` if (packet.bytes[0] != 255 or packet.bytes[1] != 0) return -2 ` if (packet.bytes[2] != 128 or packet.bytes[3] != 42) return -3 ` if (packet.suffix[0] != 65 or packet.suffix[1] != 0) return -4 ` if (packet.suffix[2] != 66 or packet.suffix.ptr[3] != 0) return -5 ` if (packet.marked.len != 2 or packet.marked.ptr[2] != 255) return -6 ` if (packet.marked[0] != 40 or packet.marked[1] != 2) return -7 ` return 42 `} , ) defer arraylist.deinit(&bytes) root_source :: try add_source(&session, root, "/byte_slices/app/main.hon", &bytes, &diagnostic_store) imported := try owned_text( `Packet :: struct { bytes []u8, suffix [;0]u8, marked [;255]u8 } `make proc() Packet { ` bytes [4]mut u8 := undefined ` bytes[0] = 255 ` bytes[1] = 0 ` bytes[2] = 128 ` bytes[3] = 42 ` marked [3]mut u8 := [40, 2, 255] ` return Packet { bytes = bytes[..], suffix = "xA\0B"[1..;0], marked = marked[..2;255] } `} , ) defer arraylist.deinit(&imported) _ = try add_source(&session, dependency, "/byte_slices/library/bytes.hon", &imported, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = dependency, }) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for modules |module| { outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } } computed_untagged_unions_recompile_without_borrowing_storage_bytes test { diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) modules [2]mut ir.Module := undefined { session := init(mem.c_allocator) defer deinit(&session) root :: try add_package(&session) dependency :: try add_package(&session) bytes := try owned_text( `lib :: import "../library" `main proc() u64 { ` value := $(lib.make()) ` saved :: value ` value.small = 0 ` value.words[1] = 99 ` if (saved.small != 9 or saved.bytes[1] != 255 or saved.bytes[7] != 255) return 0 ` if (saved.bytes[17] != 255 or saved.bytes[23] != 255) return 0 ` generated lib.Generated() :: {} ` if (generated.value.words[1] != 40 or generated.value.small != 9) return 0 ` return saved.words[1] + saved.words[3] `} , ) defer arraylist.deinit(&bytes) root_source :: try add_source(&session, root, "/union_bytes/app/main.hon", &bytes, &diagnostic_store) imported := try owned_text( `Pair :: struct { small u8, large u64 } `Storage :: union { small u8, rows [2]Pair, words [4]mut u64, bytes [32]u8 } `make proc() Storage { ` value := Storage { words = [maxval!(u64), 40, maxval!(u64), 2] } ` value.small = 9 ` return value `} `Generated proc() type { ` return struct_type!(.auto, ["value"], [Storage], [make()]) `} , ) defer arraylist.deinit(&imported) _ = try add_source(&session, dependency, "/union_bytes/library/storage.hon", &imported, &diagnostic_store) try arraylist.append(&session.imports, Import{ node = declaration(&session, root_source, 0).node, target = dependency, }) modules[0] = try compile(&session, root, &diagnostic_store) errdefer ir.deinit(&modules[0]) modules[1] = try compile(&session, root, &diagnostic_store) } defer ir.deinit(&modules[0]) defer ir.deinit(&modules[1]) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) for modules |module| { outcome := try interpreter.run(mem.c_allocator, &module, &diagnostic_store) defer interpreter.deinit(&outcome) try testing.expect(outcome.trap == null) try testing.expect_equal(i64(42), outcome.value.data.integer) } }