array reflection

This commit is contained in:
2026-07-22 10:12:12 +02:00
parent 0d04925b3a
commit 07e89e23e1
12 changed files with 675 additions and 68 deletions
+219 -3
View File
@@ -4990,6 +4990,10 @@ missing func() i32 {
if true {
}
}
escape_slice func() []i32 {
values [2]mut i32 = [1, 2]
return values[..]
}
GLOBAL :: 1
main func() void {
runtime i32 = 1
@@ -5000,6 +5004,30 @@ main func() void {
yield callback()
}
_ = $&GLOBAL
_ = ${
values [2]mut i32 = [1, 2]
pointer *mut i32 :: (&values).ptr
yield pointer
}
_ = ${
values [2]mut i32 = [1, 2]
yield values[1..].ptr
}
_ = ${
values [2]mut i32 = [1, 2]
yield &values[0]
}
_ = ${
values [2]mut i32 = [1, 2]
view []mut i32 = values[..]
yield view
}
_ = ${
values [2]mut i32 = [1, 2]
view []i32 = values[1..]
yield view
}
_ = $escape_slice()
_ = $spin()
_ = $missing()
_ = ${
@@ -5021,7 +5049,9 @@ main func() void {
found_runtime := false
runtime_only_count := 0
found_pointer := false
pointer_errors := 0
slice_errors := 0
found_expired := false
found_quota := false
found_missing := false
found_yield := false
@@ -5029,14 +5059,18 @@ main func() void {
message := diagnostic.message
found_runtime = found_runtime || strings.contains(message, "unresolved comptime value 'runtime'")
runtime_only_count += 1 if strings.contains(message, "runtime-only") else 0
found_pointer = found_pointer || strings.contains(message, "comptime storage pointers and slices cannot materialize as runtime memory")
pointer_errors += 1 if strings.contains(message, "only immutable pointers to whole comptime arrays can materialize as runtime memory") else 0
slice_errors += 1 if strings.contains(message, "only immutable full-array comptime slices can materialize as runtime memory") else 0
found_expired = found_expired || strings.contains(message, "expired storage")
found_quota = found_quota || strings.contains(message, "comptime evaluation exceeded the step quota")
found_missing = found_missing || strings.contains(message, "did not return a value")
found_yield = found_yield || strings.contains(message, "comptime block must yield a value")
}
testing.expect(t, found_runtime)
testing.expect(t, runtime_only_count >= 2)
testing.expect(t, found_pointer)
testing.expect(t, pointer_errors >= 4)
testing.expect(t, slice_errors >= 2)
testing.expect(t, found_expired)
testing.expect(t, found_quota)
testing.expect(t, found_missing)
testing.expect(t, found_yield)
@@ -14706,6 +14740,188 @@ std_meta_tests_compile_and_run :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 0)
}
@(test)
array_reflection_reports_child_and_logical_length :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-array-reflection"
main_path := "/tmp/brolang-test-array-reflection/main.bro"
output := "/tmp/brolang-test-array-reflection-output"
text := `meta :: import "@std/meta"
Alias :: alias [3]u16
matches func($Array, $Child type, $len usize) bool {
match typeinfo!(Array) {
.array |info|: return info.child == Child and info.len == len
else: return false
}
}
main func() i32 {
if !$(matches([4]i32, i32, 4)) { return 1 }
if !$(matches([0]bool, bool, 0)) { return 2 }
if !$(matches(Alias, u16, 3)) { return 3 }
if !$(matches([2]mut i64, i64, 2)) { return 4 }
if !$(matches([2;0]u8, u8, 2)) { return 5 }
return 0
}
`
_ = os2.remove_all(directory)
defer _ = os2.remove_all(directory)
defer _ = os.remove(output)
testing.expect(t, os.make_directory(directory) == nil)
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
testing.expect_value(t, compiler_core.compile_package(
directory, output, nil, target.DEFAULT, cimport.Options{}, ".",
), 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
static_string_map_infers_array_size_and_preserves_promoted_backing :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-static-string-map"
main_path := "/tmp/brolang-test-static-string-map/main.bro"
output := "/tmp/brolang-test-static-string-map-output"
text := `std :: import "@std"
static_string_map :: import "@std/static_string_map"
TokenKind :: enum {
keyword_if
keyword_else
keyword_for
keyword_return
keyword_while
}
keywords std.StaticStringMap(TokenKind) = static_string_map.init([
{"if", .keyword_if},
{"else", .keyword_else},
{"for", .keyword_for},
{"return", .keyword_return},
])
fallback :: static_string_map.init(TokenKind, [
{"while", .keyword_while},
])
empty std.StaticStringMap(TokenKind) = static_string_map.init([])
numbers []i32 = ${
values [3]mut i32 = [7, 8, 9]
yield values[..]
}
main func() i32 {
if keywords.keys.len != 4 or keywords.values.len != 4 or keywords.len_indexes.len != 7 { return 1 }
if keywords.min_len != 2 or keywords.max_len != 6 { return 17 }
if empty.keys.len != 0 or empty.values.len != 0 or empty.len_indexes.len != 0 { return 18 }
if numbers.len != 3 or numbers[0] != 7 or numbers[2] != 9 { return 19 }
if static_string_map.get(&keywords, "if") |value| {
if value != TokenKind.keyword_if { return 2 }
} else { return 3 }
if static_string_map.get(&keywords, "else") |value| {
if value != TokenKind.keyword_else { return 4 }
} else { return 5 }
if static_string_map.get(&keywords, "for") |value| {
if value != TokenKind.keyword_for { return 6 }
} else { return 7 }
if static_string_map.get(&keywords, "return") |value| {
if value != TokenKind.keyword_return { return 8 }
} else { return 9 }
if static_string_map.get(&keywords, "no") |_| { return 10 }
if static_string_map.get(&keywords, "four") |_| { return 11 }
if static_string_map.get(&keywords, "x") |_| { return 12 }
if static_string_map.get(&keywords, "longer-than-any-key") |_| { return 13 }
if static_string_map.get(&empty, "if") |_| { return 14 }
if static_string_map.get(&fallback, "while") |value| {
if value != TokenKind.keyword_while { return 15 }
} else { return 16 }
return 0
}
`
_ = os2.remove_all(directory)
defer _ = os2.remove_all(directory)
defer _ = os.remove(output)
testing.expect(t, os.make_directory(directory) == nil)
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
testing.expect_value(t, compiler_core.compile_package(
directory, output, nil, target.DEFAULT, cimport.Options{}, ".",
), 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
static_string_map_reports_duplicate_and_malformed_entries :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-static-string-map-errors"
main_path := "/tmp/brolang-test-static-string-map-errors/main.bro"
_ = os2.remove_all(directory)
defer _ = os2.remove_all(directory)
testing.expect(t, os.make_directory(directory) == nil)
duplicate_text := `std :: import "@std"
static_string_map :: import "@std/static_string_map"
TokenKind :: enum { keyword_if, keyword_else }
bad std.StaticStringMap(TokenKind) = static_string_map.init([
{"if", .keyword_if},
{"if", .keyword_else},
])
main func() void {}
`
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)duplicate_text))
{
sources := source.init_store()
defer source.destroy_store(&sources)
diagnostics := source.init_store_diagnostics(&sources)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
module, loaded := loader.load(directory, &sources, &diagnostics, &symbols, project_root_path=".")
defer ast.destroy_module(&module)
hir_module := checker.check(&module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
testing.expect(t, loaded)
found := false
for diagnostic in diagnostics.items {
found = found || strings.contains(diagnostic.message, "duplicate static string map key")
}
testing.expect(t, found)
}
malformed_text := `std :: import "@std"
static_string_map :: import "@std/static_string_map"
TokenKind :: enum { keyword_if }
bad std.StaticStringMap(TokenKind) = static_string_map.init([
{123, .keyword_if},
])
bad_value std.StaticStringMap(TokenKind) = static_string_map.init([
{"if", "bad"},
])
main func() void {}
`
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)malformed_text))
{
sources := source.init_store()
defer source.destroy_store(&sources)
diagnostics := source.init_store_diagnostics(&sources)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
module, loaded := loader.load(directory, &sources, &diagnostics, &symbols, project_root_path=".")
defer ast.destroy_module(&module)
hir_module := checker.check(&module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
testing.expect(t, loaded)
found_key := false
found_value := false
for diagnostic in diagnostics.items {
found_key = found_key || strings.contains(diagnostic.message, "cannot implicitly convert i8 to []u8 at comptime")
found_value = found_value ||
strings.contains(diagnostic.message, "cannot implicitly convert") &&
strings.contains(diagnostic.message, "to TokenKind at comptime")
}
testing.expect(t, found_key)
testing.expect(t, found_value)
}
}
@(test)
noreturn_functions_function_pointers_and_peer_types_compile_and_run :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-noreturn"