Files
honey/source/compilation/compilation.test.hon
T

949 lines
42 KiB
Plaintext

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)
}
}