allocation related primitives

This commit is contained in:
2026-07-08 20:29:25 +02:00
parent 5e18df9bc1
commit 2cda024614
11 changed files with 617 additions and 98 deletions
+165 -10
View File
@@ -1294,6 +1294,160 @@ immutable_pointer_and_slice_bindings_preserve_mutable_pointees :: proc(t: ^testi
testing.expect_value(t, len(diagnostics.items), 0)
}
@(test)
many_item_pointer_slices_compile_and_run :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-pointer-slices"
main_path := "/tmp/brolang-test-pointer-slices/main.bro"
output := "/tmp/brolang-test-pointer-slices-output"
text := `main func() i32 {
values [4]mut i32 = [3, 4, 5, 6]
pointer *mut i32 :: (&values).ptr
const_pointer *i32 :: pointer
all []mut i32 :: pointer[..4]
middle []mut i32 :: pointer[1..3]
readonly []i32 :: const_pointer[..2]
if (all.len != 4) return 1
if (middle.len != 2) return 2
all[0] = 10
if (readonly[0] != 10) return 3
if (middle.ptr[0] != 4) return 4
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))
status := compiler_core.compile_package(directory, output)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
many_item_pointer_slices_require_end_bound :: proc(t: ^testing.T) {
text := `main func() void {
values [2]i32 = [1, 2]
pointer *i32 :: (&values).ptr
_ = pointer[..]
_ = pointer[1..]
}
`
source_file := source.Source{path="test.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)
end_bound_errors := 0
for diagnostic in diagnostics.items {
if strings.contains(diagnostic.message, "many-item pointer slicing requires an explicit end bound") {
end_bound_errors += 1
}
}
testing.expect_value(t, end_bound_errors, 2)
}
@(test)
layout_builtins_compile_and_run :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-layout-builtins"
main_path := "/tmp/brolang-test-layout-builtins/main.bro"
output := "/tmp/brolang-test-layout-builtins-output"
text := `Point :: struct {
x i32
y u8
}
Opaque :: opaque
Color :: enum {
red
blue
}
UserID :: distinct u32
SIZE_GLOBAL :: size_of(i32)
needs_usize func(value usize) usize {
return value
}
buffer func($T type) [size_of(T)]u8 {
data [size_of(T)]u8 = undefined
return data
}
main func() i32 {
bytes [_]u8 :: buffer(i32)
if (needs_usize(SIZE_GLOBAL) != 4) return 1
if (bytes.len != 4) return 2
if (size_of([3]u8) != 3) return 3
if (size_of([]u8) != 16) return 4
if (align_of([]u8) != 8) return 5
if (size_of(*anyopaque) != 8) return 6
if (size_of(?*i32) != 8) return 7
if (size_of(*Opaque) != 8) return 8
if (size_of(Color) != 2) return 9
if (align_of(Color) != 2) return 10
if (size_of(Point) != 8) return 11
if (align_of(Point) != 4) return 12
if (size_of(UserID) != 4) return 13
if (align_of(UserID) != 4) return 14
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))
status := compiler_core.compile_package(directory, output)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
layout_builtins_reject_unsized_targets :: proc(t: ^testing.T) {
text := `Opaque :: opaque
Fn :: alias func() void
main func() void {
_ = size_of(void)
_ = align_of(anyopaque)
_ = size_of(Fn)
_ = size_of(Opaque)
_ = align_of(1)
}
`
source_file := source.Source{path="test.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)
bad_layout_targets := 0
target_type_error := false
for diagnostic in diagnostics.items {
bad_layout_targets += 1 if strings.contains(diagnostic.message, "layout target must be a sized runtime value type") else 0
target_type_error = target_type_error || strings.contains(diagnostic.message, "layout target must be a type")
}
testing.expect_value(t, bad_layout_targets, 4)
testing.expect(t, target_type_error)
}
@(test)
slicing_an_array_variable_takes_its_address_implicitly :: proc(t: ^testing.T) {
// Milestone 10: `arr[a..b]` on an array variable slices without an explicit
@@ -3109,7 +3263,7 @@ allocator_contract_compiles_and_runs :: proc(t: ^testing.T) {
}
@(test)
allocator_contract_heap_global_lowers :: proc(t: ^testing.T) {
allocator_contract_c_allocator_global_lowers :: proc(t: ^testing.T) {
sources := source.init_store()
defer source.destroy_store(&sources)
diagnostics := source.init_store_diagnostics(&sources)
@@ -3125,15 +3279,15 @@ allocator_contract_heap_global_lowers :: proc(t: ^testing.T) {
ir_module := lower.lower(&hir_module)
defer ir.destroy_module(&ir_module)
found_heap := false
found_c_allocator := false
found_anyopaque_context := false
found_alloc_callback := false
found_free_callback := false
for global in hir_module.globals {
if symbol.resolve(&symbols, global.name) != "heap" {
if symbol.resolve(&symbols, global.name) != "c_allocator" {
continue
}
found_heap = true
found_c_allocator = true
for field in types.fields_for(&hir_module.types, global.type) {
name := symbol.resolve(&symbols, symbol.Id(field.name))
callback_pointer, _, _, callable := types.function_pointer(field.type, &hir_module.types)
@@ -3155,31 +3309,31 @@ allocator_contract_heap_global_lowers :: proc(t: ^testing.T) {
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, len(ir_module.functions) > 0)
testing.expect(t, found_heap)
testing.expect(t, found_c_allocator)
testing.expect(t, found_anyopaque_context)
testing.expect(t, found_alloc_callback)
testing.expect(t, found_free_callback)
}
@(test)
milestone_25_heap_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-heap"
milestone_25_c_allocator_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-c-allocator"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/heap", output, nil, target.DEFAULT, cimport.Options{}, ".")
status := compiler_core.compile_package("examples/programs/mem_allocator", output, nil, target.DEFAULT, cimport.Options{}, ".")
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
milestone_25_heap_emits_libc_alloc_declarations :: proc(t: ^testing.T) {
milestone_25_c_allocator_emits_libc_alloc_declarations :: proc(t: ^testing.T) {
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)
ast_module, loaded := loader.load("examples/programs/heap", &sources, &diagnostics, &symbols, context.allocator, context.allocator, cimport.Options{}, target.DEFAULT, ".")
ast_module, loaded := loader.load("examples/programs/mem_allocator", &sources, &diagnostics, &symbols, context.allocator, context.allocator, cimport.Options{}, target.DEFAULT, ".")
defer ast.destroy_module(&ast_module)
testing.expect(t, loaded)
@@ -3192,6 +3346,7 @@ milestone_25_heap_emits_libc_alloc_declarations :: proc(t: ^testing.T) {
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, strings.contains(llvm_text, "declare ptr @malloc(i64)"))
testing.expect(t, strings.contains(llvm_text, "declare i32 @posix_memalign(ptr, i64, i64)"))
testing.expect(t, strings.contains(llvm_text, "declare void @free(ptr)"))
}