sentinel pointers and c strings
This commit is contained in:
+201
-1
@@ -174,6 +174,58 @@ main :: func() void {}
|
||||
testing.expect_value(t, len(module.functions[0].params), 2)
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_accepts_sentinel_many_item_pointer_types :: proc(t: ^testing.T) {
|
||||
text := `zero :: func(value [*;0]u8) void {}
|
||||
newline :: func(value [*;'\n']mut u8) void {}
|
||||
nullable :: func(value ?[*;0]u8) void {}
|
||||
main :: func() void {}
|
||||
`
|
||||
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)
|
||||
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&module)
|
||||
|
||||
zero, zero_ok := types.node(&module.type_store, module.functions[0].params[0].type)
|
||||
newline, newline_ok := types.node(&module.type_store, module.functions[1].params[0].type)
|
||||
nullable, nullable_ok := types.node(&module.type_store, module.functions[2].params[0].type)
|
||||
nullable_child, nullable_child_ok := types.node(&module.type_store, nullable.child)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, zero_ok && zero.kind == .Pointer && zero.many && zero.has_sentinel && zero.sentinel == 0)
|
||||
testing.expect(t, newline_ok && newline.kind == .Pointer && newline.many && newline.mutable &&
|
||||
newline.has_sentinel && newline.sentinel == '\n')
|
||||
testing.expect(t, nullable_ok && nullable.kind == .Optional)
|
||||
testing.expect(t, nullable_child_ok && nullable_child.kind == .Pointer &&
|
||||
nullable_child.many && nullable_child.has_sentinel)
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_diagnoses_malformed_sentinel_pointer_types :: proc(t: ^testing.T) {
|
||||
text := `bad :: func(value [*0]u8) void {}
|
||||
main :: func() void {}
|
||||
`
|
||||
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)
|
||||
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "expected ';' after '*' in sentinel pointer type")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_accepts_single_statement_one_line_functions :: proc(t: ^testing.T) {
|
||||
text := `give :: func() i8 { return 7 }
|
||||
@@ -742,7 +794,7 @@ main :: func() void {
|
||||
maybe ?i32 = 5
|
||||
_ = c
|
||||
_ = values[2]
|
||||
_ = values.ptr + 1
|
||||
_ = (&values).ptr + 1
|
||||
_ = values.len
|
||||
_ = values[0..2]
|
||||
_ = "hello".ptr
|
||||
@@ -784,6 +836,134 @@ main :: func() void {
|
||||
testing.expect(t, strings.contains(llvm_text, "orelse_some"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
string_literals_preserve_static_length_and_sentinel_through_pointer_views :: proc(t: ^testing.T) {
|
||||
text := `take_sentinel_pointer :: func(value [*;0]u8) void {}
|
||||
take_mut_sentinel_pointer :: func(value [*;0]mut u8) void {}
|
||||
take_pointer :: func(value *u8) void {}
|
||||
take_sentinel_slice :: func(value [;0]u8) void {}
|
||||
take_mut_sentinel_slice :: func(value [;0]mut u8) void {}
|
||||
take_slice :: func(value []u8) void {}
|
||||
take_c_string :: c_func(value *c_char) c_int
|
||||
take_c_sentinel :: c_func(value [*;0]c_char) c_int
|
||||
main :: func() void {
|
||||
text :: "hello"
|
||||
values [2;0]mut u8 = [1, 2]
|
||||
pointer :: &values
|
||||
_ = text.len
|
||||
_ = text.ptr
|
||||
_ = text[0]
|
||||
_ = text[1..]
|
||||
_ = pointer.len
|
||||
_ = pointer.ptr
|
||||
_ = pointer[0]
|
||||
_ = pointer[1..]
|
||||
offset [*;0]u8 :: text.ptr + 1
|
||||
suffix [*;0]u8 :: text[1..].ptr
|
||||
middle []u8 :: text[1..3]
|
||||
_ = offset
|
||||
_ = suffix
|
||||
_ = middle
|
||||
take_sentinel_pointer(text)
|
||||
take_pointer(text)
|
||||
take_sentinel_slice(text)
|
||||
take_slice(text)
|
||||
take_mut_sentinel_pointer(pointer)
|
||||
take_mut_sentinel_slice(pointer)
|
||||
take_sentinel_pointer(pointer)
|
||||
take_sentinel_slice(pointer)
|
||||
_ = take_c_string(text)
|
||||
_ = take_c_sentinel(text)
|
||||
_ = take_c_string(text.ptr)
|
||||
_ = take_c_sentinel(text.ptr)
|
||||
}
|
||||
`
|
||||
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)
|
||||
ir_module := lower.lower(&hir_module)
|
||||
defer ir.destroy_module(&ir_module)
|
||||
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||
defer delete(llvm_text)
|
||||
|
||||
string_type := types.INVALID
|
||||
decays := 0
|
||||
for expr in hir_module.exprs {
|
||||
if expr.kind == .String {
|
||||
string_type = expr.type
|
||||
}
|
||||
if expr.kind == .Decay_Array_Pointer {
|
||||
decays += 1
|
||||
}
|
||||
}
|
||||
pointer, array, string_ok := types.array_pointer(string_type, &hir_module.types)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, string_ok && !pointer.mutable && array.child == types.U8 &&
|
||||
array.count == 5 && array.has_sentinel && array.sentinel == 0)
|
||||
testing.expect(t, decays >= 6)
|
||||
testing.expect(t, strings.contains(llvm_text, "@bro.str.0 = private unnamed_addr constant [6 x i8] c\"hello\\00\""))
|
||||
testing.expect(t, strings.contains(llvm_text, "declare i32 @take_c_string(ptr)"))
|
||||
testing.expect(t, strings.contains(llvm_text, "declare i32 @take_c_sentinel(ptr)"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
array_pointer_and_c_string_coercion_restrictions_are_diagnosed :: proc(t: ^testing.T) {
|
||||
text := `take_c_string :: c_func(value *c_char) c_int
|
||||
take_mut_c_string :: c_func(value *mut c_char) c_int
|
||||
take_pointer :: func(value *u8) void {}
|
||||
take_mut_pointer :: func(value *mut u8) void {}
|
||||
take_slice :: func(value []u8) void {}
|
||||
bad_sentinel :: func(value [*;256]u8) void {}
|
||||
main :: func() void {
|
||||
values [1;0]mut u8 = [1]
|
||||
_ = values.ptr
|
||||
take_pointer(values)
|
||||
take_slice(values)
|
||||
ordinary *u8 :: "hello"
|
||||
nonzero [1;'\n']mut u8 = [1]
|
||||
take_pointer("hello"[1..])
|
||||
_ = take_c_string(ordinary)
|
||||
_ = take_c_string((&nonzero).ptr)
|
||||
_ = take_c_string(1)
|
||||
take_mut_pointer("hello")
|
||||
_ = take_mut_c_string("hello")
|
||||
}
|
||||
`
|
||||
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)
|
||||
|
||||
conversion_errors := 0
|
||||
array_ptr_error := false
|
||||
sentinel_error := false
|
||||
for diagnostic in diagnostics.items {
|
||||
conversion_errors += 1 if strings.contains(diagnostic.message, "cannot implicitly convert") else 0
|
||||
array_ptr_error = array_ptr_error ||
|
||||
strings.contains(diagnostic.message, "arrays do not expose '.ptr'")
|
||||
sentinel_error = sentinel_error ||
|
||||
strings.contains(diagnostic.message, "sentinel value does not fit array, slice, or pointer")
|
||||
}
|
||||
testing.expect_value(t, conversion_errors, 8)
|
||||
testing.expect(t, array_ptr_error)
|
||||
testing.expect(t, sentinel_error)
|
||||
}
|
||||
|
||||
@(test)
|
||||
c_variadic_calls_promote_extras_and_emit_variadic_llvm :: proc(t: ^testing.T) {
|
||||
text := `variadic :: c_func(tag c_int, ...) c_int
|
||||
@@ -1471,6 +1651,26 @@ valid_program_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
c_printf_accepts_a_string_literal :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-printf"
|
||||
defer _ = os.remove(output)
|
||||
status := compiler_core.compile_package("examples/interop/printf", output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
sentinel_pointer_views_compile_and_run :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-sentinel-pointer"
|
||||
defer _ = os.remove(output)
|
||||
status := compiler_core.compile_package("examples/programs/sentinel_pointer", output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 303)
|
||||
}
|
||||
|
||||
@(test)
|
||||
foreign_function_links_from_c_source :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-foreign-source"
|
||||
|
||||
Reference in New Issue
Block a user