feat: contextually type empty mutable slice literals

This commit is contained in:
2026-10-25 01:09:27 +02:00
parent 3a90a90428
commit ecb7b2fc64
3 changed files with 142 additions and 10 deletions
+96
View File
@@ -1828,6 +1828,102 @@ immutable_pointer_and_slice_bindings_preserve_mutable_pointees :: proc(t: ^testi
testing.expect_value(t, len(diagnostics.items), 0)
}
@(test)
fresh_empty_literals_initialize_mutable_slices_and_grow :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-contextual-empty-slices"
main_path := "/tmp/brolang-test-contextual-empty-slices/main.bro"
output := "/tmp/brolang-test-contextual-empty-slices-output"
text := `mem :: import "@std/mem"
Item :: struct { value i32 }
Views :: struct { integers []mut i32, records []mut Item }
integers func(values []mut i32) usize { return values.len }
records func(values []mut Item) usize { return values.len }
generic func($T type, values []mut T, _ T) usize { return values.len }
main func() i32 {
numbers []mut i32 := &[]
items []mut Item := &[]
views Views := {integers = &[], records = &[]}
if numbers.len != 0 or items.len != 0 { return 1 }
if integers(&[]) != 0 or records(&[]) != 0 { return 2 }
if views.integers.len != 0 or views.records.len != 0 { return 3 }
integer i32 := 7
item Item := {value = 9}
if generic(&[], integer) != 0 or generic(&[], item) != 0 { return 7 }
numbers = mem.realloc(mem.c_allocator, numbers, 2) catch |_| { return 4 }
defer mem.free(mem.c_allocator, numbers)
items = mem.realloc(mem.c_allocator, items, 1) catch |_| { return 5 }
defer mem.free(mem.c_allocator, items)
numbers[0] = 17
numbers[1] = 25
items[0] = {value = numbers[0] + numbers[1]}
if numbers.len != 2 or items.len != 1 or items[0].value != 42 { return 6 }
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)
empty_literal_mutability_does_not_weaken_readonly_or_sentinel_rules :: proc(t: ^testing.T) {
cases := [?]string {
`main func() void {
values [0]i32 := []
view []mut i32 := &values
_ = view
}
`,
`main func() void {
view []mut i32 := &[1]
_ = view
}
`,
`main func() void {
readonly []i32 := &[]
view []mut i32 := readonly
_ = view
}
`,
`main func() void {
view [;0]i32 := &[]
_ = view
}
`,
`main func() void {
view [;0]mut i32 := &[]
_ = view
}
`,
}
for text in cases {
source_file := source.Source{path="invalid_empty_slice.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
stream := lexer.lex(&source_file, &diagnostics, &symbols)
defer delete(stream.items)
ast_module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&ast_module)
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
found_conversion := false
for diagnostic in diagnostics.items {
found_conversion = found_conversion ||
strings.contains(diagnostic.message, "cannot implicitly convert")
}
testing.expect(t, found_conversion, text)
}
}
@(test)
many_item_pointer_slices_compile_and_run :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-pointer-slices"