function pointers and callbacks
This commit is contained in:
+120
-11
@@ -204,6 +204,34 @@ main :: func() void {}
|
||||
nullable_child.many && nullable_child.has_sentinel)
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_accepts_c_function_pointer_types :: proc(t: ^testing.T) {
|
||||
text := `take :: c_func(callback ?*c_func(value c_int) c_int) 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)
|
||||
|
||||
optional, optional_ok := types.node(&module.type_store, module.functions[0].params[0].type)
|
||||
pointer, pointer_ok := types.node(&module.type_store, optional.child)
|
||||
function, function_ok := types.node(&module.type_store, pointer.child)
|
||||
params := types.params_for(&module.type_store, pointer.child)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, optional_ok && optional.kind == .Optional)
|
||||
testing.expect(t, pointer_ok && pointer.kind == .Pointer && pointer.many && !pointer.mutable)
|
||||
testing.expect(t, function_ok && function.kind == .Function && function.c_abi && !function.variadic)
|
||||
testing.expect(t, function.child == types.C_INT)
|
||||
testing.expect_value(t, len(params), 1)
|
||||
testing.expect(t, params[0].type == types.C_INT)
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_diagnoses_malformed_sentinel_pointer_types :: proc(t: ^testing.T) {
|
||||
text := `bad :: func(value [*0]u8) void {}
|
||||
@@ -1026,13 +1054,18 @@ main :: func() void {
|
||||
|
||||
@(test)
|
||||
c_variadic_restrictions_and_extra_argument_types_are_diagnosed :: proc(t: ^testing.T) {
|
||||
text := `foreign :: c_func(...) void
|
||||
text := `Record :: c_struct {
|
||||
value c_int
|
||||
}
|
||||
foreign :: c_func(...) void
|
||||
requires :: c_func(value c_int, ...) void
|
||||
native :: func(...) void
|
||||
bodyful :: c_func(...) void {}
|
||||
main :: func() void {
|
||||
values [1]u8 :: [1]
|
||||
record Record :: Record { value = 1 }
|
||||
foreign(values)
|
||||
foreign(record)
|
||||
requires()
|
||||
}
|
||||
`
|
||||
@@ -1072,15 +1105,21 @@ variadicness_is_part_of_c_function_signature_compatibility :: proc(t: ^testing.T
|
||||
}
|
||||
|
||||
@(test)
|
||||
c_structs_are_pointer_only_and_may_be_opaque :: proc(t: ^testing.T) {
|
||||
c_structs_are_by_value_and_may_be_opaque :: proc(t: ^testing.T) {
|
||||
text := `Defined :: c_struct {
|
||||
value c_int
|
||||
}
|
||||
Opaque :: c_struct
|
||||
Empty :: c_struct {}
|
||||
Bad :: c_struct {
|
||||
values []i32
|
||||
}
|
||||
read :: c_func(value @Defined) c_int
|
||||
bad_param :: c_func(value Defined) void
|
||||
bad_result :: c_func() Defined
|
||||
main :: func() void {}
|
||||
pass :: c_func(value Defined) Defined
|
||||
bad_opaque :: c_func(value Opaque) void
|
||||
main :: func() void {
|
||||
_ = pass(Defined { value = 1 })
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
@@ -1094,14 +1133,72 @@ main :: func() void {}
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found_param := false
|
||||
found_result := false
|
||||
found_opaque := false
|
||||
found_bad_layout := false
|
||||
found_empty := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found_param = found_param || strings.contains(diagnostic.message, "cannot be passed by value")
|
||||
found_result = found_result || strings.contains(diagnostic.message, "cannot be returned by value")
|
||||
found_opaque = found_opaque || strings.contains(diagnostic.message, "cannot be passed by value")
|
||||
found_bad_layout = found_bad_layout || strings.contains(diagnostic.message, "C-layout-compatible")
|
||||
found_empty = found_empty || strings.contains(diagnostic.message, "at least one field")
|
||||
}
|
||||
testing.expect(t, found_param)
|
||||
testing.expect(t, found_result)
|
||||
testing.expect(t, found_opaque)
|
||||
testing.expect(t, found_bad_layout)
|
||||
testing.expect(t, found_empty)
|
||||
}
|
||||
|
||||
@(test)
|
||||
aarch64_c_record_abi_classifies_fixed_parameters_and_results :: proc(t: ^testing.T) {
|
||||
text := `Small :: c_struct {
|
||||
left c_int
|
||||
right c_int
|
||||
}
|
||||
Medium :: c_struct {
|
||||
first c_int
|
||||
second c_int
|
||||
third c_int
|
||||
}
|
||||
Hfa :: c_struct {
|
||||
x c_float
|
||||
y c_float
|
||||
}
|
||||
Large :: c_struct {
|
||||
first c_long
|
||||
second c_long
|
||||
third c_long
|
||||
}
|
||||
small :: c_func(value Small) Small
|
||||
medium :: c_func(value Medium) Medium
|
||||
hfa :: c_func(value Hfa) Hfa
|
||||
large :: c_func(value Large) Large
|
||||
main :: func() void {
|
||||
_ = small(Small { left = 1, right = 2 })
|
||||
_ = medium(Medium { first = 1, second = 2, third = 3 })
|
||||
_ = hfa(Hfa { x = 1.0, y = 2.0 })
|
||||
_ = large(Large { first = 1, second = 2, third = 3 })
|
||||
}
|
||||
`
|
||||
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)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, strings.contains(llvm_text, "declare i64 @small(i64)"))
|
||||
testing.expect(t, strings.contains(llvm_text, "declare [2 x i64] @medium([2 x i64])"))
|
||||
testing.expect(t, strings.contains(llvm_text, "@hfa([2 x float])"))
|
||||
testing.expect(t, strings.contains(llvm_text, "declare void @large(ptr sret("))
|
||||
testing.expect(t, strings.contains(llvm_text, "call void @llvm.memcpy.p0.p0.i64"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -1697,6 +1794,18 @@ restricted_c_header_imports_compile_and_link :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
by_value_c_records_and_unions_compile_and_link :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-records"
|
||||
defer _ = os.remove(output)
|
||||
arguments := []linker.Argument{{kind=.Input, value="examples/interop/records/native.c"}}
|
||||
c_options := cimport.Options{include_paths=[]string{"examples/interop/records/include"}}
|
||||
status := compiler_core.compile_package("examples/interop/records/app", output, arguments, target.DEFAULT, c_options)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 1)
|
||||
}
|
||||
|
||||
@(test)
|
||||
unsupported_c_header_members_diagnose_only_when_referenced :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-header-unsupported"
|
||||
|
||||
Reference in New Issue
Block a user