add constcast and immutable free
This commit is contained in:
@@ -2184,6 +2184,148 @@ main func() void {
|
||||
testing.expect(t, found_target_type)
|
||||
}
|
||||
|
||||
@(test)
|
||||
constcast_restores_pointer_and_slice_mutability :: proc(t: ^testing.T) {
|
||||
text := `main func() i32 {
|
||||
values [2]mut u8 = [1, 2]
|
||||
immutable_slice []u8 = values[..]
|
||||
mutable_slice []mut u8 = constcast!(immutable_slice)
|
||||
mutable_slice[0] = 3
|
||||
|
||||
immutable_many *u8 = immutable_slice.ptr
|
||||
mutable_many *mut u8 = constcast!(immutable_many)
|
||||
mutable_many[1] = 4
|
||||
|
||||
number i32 = 5
|
||||
immutable_single @i32 = &number
|
||||
mutable_single @mut i32 = constcast!(immutable_single)
|
||||
mutable_single^ = 6
|
||||
|
||||
maybe ?@i32 = immutable_single
|
||||
mutable_maybe ?@mut i32 = constcast!(maybe)
|
||||
if mutable_maybe |pointer| { pointer^ = 7 }
|
||||
|
||||
already_mutable []mut u8 = constcast!(mutable_slice)
|
||||
if already_mutable[0] != 3 or already_mutable[1] != 4 or number != 7 { return 1 }
|
||||
return 0
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="constcast.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)
|
||||
ir_module := lower.lower(&hir_module)
|
||||
defer ir.destroy_module(&ir_module)
|
||||
|
||||
hir_casts, ir_casts := 0, 0
|
||||
for expr in hir_module.exprs {
|
||||
hir_casts += 1 if expr.kind == .Const_Cast else 0
|
||||
}
|
||||
for function in ir_module.functions {
|
||||
for instruction in function.instructions {
|
||||
ir_casts += 1 if instruction.op == .Const_Cast else 0
|
||||
}
|
||||
}
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, hir_casts, 5)
|
||||
testing.expect_value(t, ir_casts, 5)
|
||||
|
||||
directory := "/tmp/brolang-test-constcast"
|
||||
main_path := "/tmp/brolang-test-constcast/main.bro"
|
||||
output := "/tmp/brolang-test-constcast-output"
|
||||
_ = 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), 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
invalid_constcasts_are_rejected :: proc(t: ^testing.T) {
|
||||
text := `main func() void {
|
||||
values [2]mut u8 = [1, 2]
|
||||
_ = constcast!()
|
||||
_ = constcast!(1, 2)
|
||||
_ = constcast!(1)
|
||||
_ = constcast!(values)
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="invalid_constcast.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_arity, bad_operand := false, false
|
||||
for diagnostic in diagnostics.items {
|
||||
bad_arity = bad_arity || strings.contains(diagnostic.message, "constcast! expects 1 argument")
|
||||
bad_operand = bad_operand || strings.contains(diagnostic.message, "constcast! operand must be a pointer, optional pointer, or slice")
|
||||
}
|
||||
testing.expect(t, bad_arity && bad_operand)
|
||||
}
|
||||
|
||||
@(test)
|
||||
immutable_allocations_can_be_freed_and_constcast_keeps_slice_bounds_checks :: proc(t: ^testing.T) {
|
||||
free_text := `mem :: import "@std/mem"
|
||||
main func() i32 {
|
||||
memory []mut u8 :: mem.alloc(u8, mem.c_allocator, 4) catch |_| { return 1 }
|
||||
memory[0] = 42
|
||||
immutable []u8 = memory
|
||||
mem.free(mem.c_allocator, immutable)
|
||||
empty []u8 = mem.empty(u8)
|
||||
mem.free(mem.c_allocator, empty)
|
||||
return 0
|
||||
}
|
||||
`
|
||||
directory := "/tmp/brolang-test-immutable-free"
|
||||
main_path := "/tmp/brolang-test-immutable-free/main.bro"
|
||||
output := "/tmp/brolang-test-immutable-free-output"
|
||||
_ = 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)free_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)
|
||||
|
||||
bounds_text := `main func() void {
|
||||
values [2]mut u8 = [1, 2]
|
||||
immutable []u8 = values[..]
|
||||
mutable []mut u8 = constcast!(immutable)
|
||||
_ = mutable[mutable.len]
|
||||
}
|
||||
`
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)bounds_text))
|
||||
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
|
||||
bounds_state, stdout, stderr, err := os2.process_exec(
|
||||
os2.Process_Desc{command=[]string{output}}, context.allocator,
|
||||
)
|
||||
defer delete(stdout)
|
||||
defer delete(stderr)
|
||||
testing.expect(t, err == nil)
|
||||
testing.expect(t, !bounds_state.success)
|
||||
testing.expect(t, strings.contains(string(stderr), "index out of bounds"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
old_intrinsic_spellings_are_not_recognized :: proc(t: ^testing.T) {
|
||||
text := `main func() void {
|
||||
|
||||
Reference in New Issue
Block a user