c interop type foundation
This commit is contained in:
+159
-22
@@ -14,6 +14,7 @@ import "./compiler/lower"
|
||||
import "./compiler/parser"
|
||||
import "./compiler/source"
|
||||
import "./compiler/symbol"
|
||||
import "./compiler/target"
|
||||
import "./compiler/token"
|
||||
import "./compiler/types"
|
||||
import "core:fmt"
|
||||
@@ -86,9 +87,10 @@ main :: func() void { _ = value }
|
||||
compact_ids_reserve_invalid_values_and_preserve_layout :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, size_of(source.Span), 12)
|
||||
testing.expect_value(t, size_of(token.Token), 24)
|
||||
testing.expect_value(t, size_of(ast.Expr), 64)
|
||||
testing.expect_value(t, size_of(hir.Expr), 88)
|
||||
testing.expect_value(t, size_of(ir.Instruction), 88)
|
||||
testing.expect(t, size_of(ast.Expr) <= 64)
|
||||
testing.expect(t, size_of(hir.Expr) <= 88)
|
||||
testing.expect(t, size_of(ir.Instruction) <= 88)
|
||||
testing.expect_value(t, size_of(types.Type), 4)
|
||||
|
||||
source_index, source_ok := source.source_index(source.Source_Id(0), 1)
|
||||
testing.expect_value(t, source_index, 0)
|
||||
@@ -195,8 +197,8 @@ main :: func() void { _ = give() }
|
||||
|
||||
@(test)
|
||||
parser_distinguishes_bodyless_declarations_and_definitions :: proc(t: ^testing.T) {
|
||||
text := `foreign :: c func(value i32) i32
|
||||
defined :: c func(value i32) i32
|
||||
text := `foreign :: c_func(value i32) i32
|
||||
defined :: c_func(value i32) i32
|
||||
{
|
||||
return value
|
||||
}
|
||||
@@ -229,7 +231,7 @@ main :: func() void {}
|
||||
parser_treats_c_as_contextual_only_before_func :: proc(t: ^testing.T) {
|
||||
text := `c :: 5
|
||||
x :: c
|
||||
foreign :: c func() i32
|
||||
foreign :: c_func() i32
|
||||
broken :: c 5
|
||||
main :: func() void {}
|
||||
`
|
||||
@@ -403,6 +405,8 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
|
||||
"thing",
|
||||
"--link",
|
||||
"helper.o",
|
||||
"--target",
|
||||
"aarch64-macos",
|
||||
})
|
||||
defer delete(options.link_arguments)
|
||||
|
||||
@@ -415,15 +419,18 @@ cli_parses_ordered_link_options_and_rejects_invalid_forms :: proc(t: ^testing.T)
|
||||
testing.expect_value(t, options.link_arguments[1].kind, linker.Kind.Library_Path)
|
||||
testing.expect_value(t, options.link_arguments[2].kind, linker.Kind.Library)
|
||||
testing.expect_value(t, options.link_arguments[3].value, "helper.o")
|
||||
testing.expect_value(t, target.name(options.target), "aarch64-macos")
|
||||
|
||||
_, unknown_valid := parse_cli_args([]string{"brolang", "app", "-o", "out", "--unknown", "value"})
|
||||
_, incomplete_valid := parse_cli_args([]string{"brolang", "app", "-o"})
|
||||
_, duplicate_output_valid := parse_cli_args([]string{"brolang", "app", "-o", "one", "-o", "two"})
|
||||
_, duplicate_empty_output_valid := parse_cli_args([]string{"brolang", "app", "-o", "", "-o", "two"})
|
||||
_, invalid_target := parse_cli_args([]string{"brolang", "app", "-o", "out", "--target", "x86_64-linux"})
|
||||
testing.expect(t, !unknown_valid)
|
||||
testing.expect(t, !incomplete_valid)
|
||||
testing.expect(t, !duplicate_output_valid)
|
||||
testing.expect(t, !duplicate_empty_output_valid)
|
||||
testing.expect(t, !invalid_target)
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -453,7 +460,7 @@ main :: func() void {}
|
||||
}
|
||||
|
||||
@(test)
|
||||
parser_rejects_chained_package_access :: proc(t: ^testing.T) {
|
||||
parser_accepts_chained_field_access :: proc(t: ^testing.T) {
|
||||
text := `main :: func() void {
|
||||
_ = first.second.value
|
||||
}
|
||||
@@ -468,7 +475,7 @@ parser_rejects_chained_package_access :: proc(t: ^testing.T) {
|
||||
module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&module)
|
||||
|
||||
testing.expect(t, len(diagnostics.items) > 0)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -566,7 +573,7 @@ main :: func() void {
|
||||
|
||||
@(test)
|
||||
pipeline_emits_specialized_calling_conventions_and_checked_add :: proc(t: ^testing.T) {
|
||||
text := `sum_c :: c func(a, b int) int {
|
||||
text := `sum_c :: c_func(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
sum_bro :: func(a, b int) int {
|
||||
@@ -597,16 +604,16 @@ main :: func() void {
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, llvm_text, second_llvm_text)
|
||||
testing.expect(t, strings.contains(llvm_text, "define i8 @bro_c__p0__sum_c__i8__i8"))
|
||||
testing.expect(t, strings.contains(llvm_text, "define signext i8 @bro_c__p0__sum_c__i8__i8"))
|
||||
testing.expect(t, strings.contains(llvm_text, "define internal fastcc i8 @bro__p0__sum_bro__i8__i8"))
|
||||
testing.expect(t, strings.contains(llvm_text, "@llvm.sadd.with.overflow.i8"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
pipeline_emits_only_referenced_foreign_declarations_with_exact_names :: proc(t: ^testing.T) {
|
||||
text := `used :: c func(a, b i32) i32
|
||||
unused :: c func() i32
|
||||
bodyful :: c func(value i32) i32 {
|
||||
text := `used :: c_func(a, b i32) i32
|
||||
unused :: c_func() i32
|
||||
bodyful :: c_func(value i32) i32 {
|
||||
return value
|
||||
}
|
||||
main :: func() void {
|
||||
@@ -637,9 +644,126 @@ main :: func() void {
|
||||
testing.expect(t, strings.contains(llvm_text, "define i32 @bro_c__p0__bodyful__i32"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
c_primitives_remain_distinct_with_apple_silicon_representations :: proc(t: ^testing.T) {
|
||||
testing.expect(t, types.C_CHAR != types.C_SCHAR)
|
||||
testing.expect(t, types.C_SCHAR != types.C_UCHAR)
|
||||
testing.expect(t, types.C_INT != types.I32)
|
||||
testing.expect(t, types.C_ULONG != types.U64)
|
||||
testing.expect_value(t, types.representation(types.C_CHAR), types.I8)
|
||||
testing.expect_value(t, types.representation(types.C_SCHAR), types.I8)
|
||||
testing.expect_value(t, types.representation(types.C_UCHAR), types.U8)
|
||||
testing.expect_value(t, types.representation(types.C_SHORT), types.I16)
|
||||
testing.expect_value(t, types.representation(types.C_USHORT), types.U16)
|
||||
testing.expect_value(t, types.representation(types.C_INT), types.I32)
|
||||
testing.expect_value(t, types.representation(types.C_UINT), types.U32)
|
||||
testing.expect_value(t, types.representation(types.C_LONG), types.I64)
|
||||
testing.expect_value(t, types.representation(types.C_ULONG), types.U64)
|
||||
testing.expect_value(t, types.representation(types.C_LONGLONG), types.I64)
|
||||
testing.expect_value(t, types.representation(types.C_ULONGLONG), types.U64)
|
||||
testing.expect_value(t, types.representation(types.C_FLOAT), types.F32)
|
||||
testing.expect_value(t, types.representation(types.C_DOUBLE), types.F64)
|
||||
testing.expect_value(t, types.representation(types.C_LONGDOUBLE), types.F64)
|
||||
testing.expect_value(t, target.llvm_triple(target.DEFAULT), "arm64-apple-macosx13.0.0")
|
||||
}
|
||||
|
||||
@(test)
|
||||
interop_foundation_emits_compounds_and_narrow_c_abi_attributes :: proc(t: ^testing.T) {
|
||||
text := `Point :: struct {
|
||||
x i32
|
||||
y i32
|
||||
}
|
||||
signed :: c_func(value c_char) c_char
|
||||
unsigned :: c_func(value c_uchar) c_uchar
|
||||
exact :: c_func(value u32) u32
|
||||
fallback :: func() i32 {
|
||||
return 9
|
||||
}
|
||||
main :: func() void {
|
||||
c :: 1
|
||||
values [2;0]mut u8 = [1, 2]
|
||||
point Point :: Point{x = 3, y = 4}
|
||||
maybe ?i32 = 5
|
||||
_ = c
|
||||
_ = values[2]
|
||||
_ = values.ptr + 1
|
||||
_ = values.len
|
||||
_ = values[0..2]
|
||||
_ = "hello".ptr
|
||||
_ = "hello".len
|
||||
_ = point.x
|
||||
_ = maybe?
|
||||
_ = maybe orelse 0
|
||||
_ = maybe orelse fallback()
|
||||
_ = signed(1)
|
||||
_ = unsigned(1)
|
||||
_ = exact(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)
|
||||
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, "target triple = \"arm64-apple-macosx13.0.0\""))
|
||||
testing.expect(t, strings.contains(llvm_text, "declare signext i8 @signed(i8 signext)"))
|
||||
testing.expect(t, strings.contains(llvm_text, "declare zeroext i8 @unsigned(i8 zeroext)"))
|
||||
testing.expect(t, strings.contains(llvm_text, "declare i32 @exact(i32)"))
|
||||
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, "getelementptr [3 x i8]"))
|
||||
testing.expect(t, strings.contains(llvm_text, "attempted to unwrap none"))
|
||||
testing.expect(t, strings.contains(llvm_text, "orelse_fallback"))
|
||||
testing.expect(t, strings.contains(llvm_text, "orelse_some"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
c_structs_are_pointer_only_and_may_be_opaque :: proc(t: ^testing.T) {
|
||||
text := `Defined :: c_struct {
|
||||
value c_int
|
||||
}
|
||||
Opaque :: c_struct
|
||||
read :: c_func(value @Defined) c_int
|
||||
bad_param :: c_func(value Defined) void
|
||||
bad_result :: c_func() Defined
|
||||
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)
|
||||
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_param := false
|
||||
found_result := 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")
|
||||
}
|
||||
testing.expect(t, found_param)
|
||||
testing.expect(t, found_result)
|
||||
}
|
||||
|
||||
@(test)
|
||||
invalid_foreign_declarations_are_eagerly_diagnosed_and_calls_trap :: proc(t: ^testing.T) {
|
||||
text := `bad :: c func(value int) int
|
||||
text := `bad :: c_func(value int) int
|
||||
native :: func() i32
|
||||
main :: func() void {
|
||||
_ = bad(1)
|
||||
@@ -667,7 +791,7 @@ main :: func() void {
|
||||
for diagnostic in diagnostics.items {
|
||||
found_parameter = found_parameter || strings.contains(diagnostic.message, "requires concrete parameter types")
|
||||
found_result = found_result || strings.contains(diagnostic.message, "requires a concrete or void result type")
|
||||
found_native = found_native || strings.contains(diagnostic.message, "must use 'c func'")
|
||||
found_native = found_native || strings.contains(diagnostic.message, "must use 'c_func'")
|
||||
}
|
||||
testing.expect(t, found_parameter)
|
||||
testing.expect(t, found_result)
|
||||
@@ -707,7 +831,7 @@ duplicate_foreign_symbols_across_packages_are_poisoned :: proc(t: ^testing.T) {
|
||||
|
||||
@(test)
|
||||
bodyless_root_main_recovers_as_a_trap_definition :: proc(t: ^testing.T) {
|
||||
text := "main :: c func() i32\n"
|
||||
text := "main :: c_func() i32\n"
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
@@ -974,7 +1098,7 @@ eager_global_calls_root_specializations :: proc(t: ^testing.T) {
|
||||
unused_native :: func() i32 {
|
||||
return 9
|
||||
}
|
||||
unused_foreign :: c func() i32
|
||||
unused_foreign :: c_func() i32
|
||||
value i32 :: make()
|
||||
main :: func() void {}
|
||||
`
|
||||
@@ -1040,16 +1164,16 @@ long_generic_call_chain_reaches_a_fixed_point :: proc(t: ^testing.T) {
|
||||
builder := strings.builder_make()
|
||||
defer strings.builder_destroy(&builder)
|
||||
for index in 0 ..< 70 {
|
||||
fmt.sbprintf(&builder, "f%d :: func(value int) int ", index)
|
||||
fmt.sbprintf(&builder, "fn%d :: func(value int) int ", index)
|
||||
strings.write_string(&builder, "{ return ")
|
||||
if index == 69 {
|
||||
strings.write_string(&builder, "value")
|
||||
} else {
|
||||
fmt.sbprintf(&builder, "f%d(value)", index+1)
|
||||
fmt.sbprintf(&builder, "fn%d(value)", index+1)
|
||||
}
|
||||
strings.write_string(&builder, " }\n")
|
||||
}
|
||||
strings.write_string(&builder, "main :: func() i32 { return f0(1) }\n")
|
||||
strings.write_string(&builder, "main :: func() i32 { return fn0(1) }\n")
|
||||
|
||||
source_file := source.Source{path="test.bro", text=strings.to_string(builder)}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
@@ -1195,6 +1319,17 @@ foreign_function_links_from_c_source :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, state.exit_code, 42)
|
||||
}
|
||||
|
||||
@(test)
|
||||
interop_foundation_matches_zig_compiled_apple_silicon_c_fixture :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-interop-foundation"
|
||||
defer _ = os.remove(output)
|
||||
arguments := []linker.Argument{{kind=.Input, value="examples/interop/foundation/native.c"}}
|
||||
status := compiler_core.compile_package("examples/interop/foundation", output, arguments)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 1)
|
||||
}
|
||||
|
||||
@(test)
|
||||
foreign_function_links_from_object :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-foreign-object"
|
||||
@@ -1442,6 +1577,8 @@ backend_translates_link_arguments_without_reordering_them :: proc(t: ^testing.T)
|
||||
"/usr/bin/env",
|
||||
"zig",
|
||||
"cc",
|
||||
"-target",
|
||||
"aarch64-macos",
|
||||
"-Wno-override-module",
|
||||
"module.ll",
|
||||
"native.c",
|
||||
@@ -2127,8 +2264,8 @@ package_llvm_is_deterministic_and_symbols_include_package_ids :: proc(t: ^testin
|
||||
testing.expect(t, loaded)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, llvm_text, second_llvm_text)
|
||||
testing.expect(t, strings.contains(llvm_text, "define i8 @bro_c__p1__same"))
|
||||
testing.expect(t, strings.contains(llvm_text, "define i8 @bro_c__p2__same"))
|
||||
testing.expect(t, strings.contains(llvm_text, "define signext i8 @bro_c__p1__same"))
|
||||
testing.expect(t, strings.contains(llvm_text, "define signext i8 @bro_c__p2__same"))
|
||||
testing.expect(t, strings.contains(llvm_text, "define i32 @main()"))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user