add noreturn and unreachable

This commit is contained in:
2026-07-22 00:27:13 +02:00
parent 402871ef7a
commit 17508ff751
21 changed files with 286673 additions and 279245 deletions
+125
View File
@@ -14271,6 +14271,131 @@ std_meta_tests_compile_and_run :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 0)
}
@(test)
noreturn_functions_function_pointers_and_peer_types_compile_and_run :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-noreturn"
main_path := "/tmp/brolang-test-noreturn/main.bro"
output := "/tmp/brolang-test-noreturn-output"
text := `meta :: import "@std/meta"
die func() noreturn {
unreachable
}
forward func() noreturn {
die()
}
choose func(flag bool) i32 {
return if flag { yield 42 } else { yield die() }
}
reflects_bottom func($T type) bool {
match typeinfo!(T) {
.noreturn: return true
else: return false
}
}
main func() i32 {
callback @func() noreturn = forward
_ = callback
if choose(true) != 42 { return 1 }
if !reflects_bottom(noreturn) { return 2 }
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))
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)
}
@(test)
unreachable_traps_at_runtime_and_fails_at_comptime :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-unreachable"
main_path := "/tmp/brolang-test-unreachable/main.bro"
output := "/tmp/brolang-test-unreachable-output"
_ = os2.remove_all(directory)
defer _ = os2.remove_all(directory)
defer _ = os.remove(output)
testing.expect(t, os.make_directory(directory) == nil)
runtime_text := `main func() void {
unreachable
}
`
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)runtime_text))
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
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, !state.success)
testing.expect(t, strings.contains(string(stderr), "runtime trap: reached unreachable code"))
comptime_text := `value :: $unreachable
main func() void {}
`
source_file := source.Source{path="unreachable_comptime.bro", text=comptime_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 := false
for diagnostic in diagnostics.items {
found = found || strings.contains(diagnostic.message, "reached unreachable code during comptime evaluation")
}
testing.expect(t, found)
}
@(test)
noreturn_storage_c_abi_and_fallthrough_are_rejected :: proc(t: ^testing.T) {
text := `Bad :: struct { value noreturn }
foreign c_func() noreturn
fallthrough func() noreturn {}
parameter func(value noreturn) void { _ = value }
main func() void {
if false { fallthrough() }
stored noreturn = unreachable
}
`
source_file := source.Source{path="invalid_noreturn.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)
field, c_abi, missing_return, parameter, storage := false, false, false, false, false
for diagnostic in diagnostics.items {
field = field || strings.contains(diagnostic.message, "record fields must have runtime value types")
c_abi = c_abi || strings.contains(diagnostic.message, "noreturn is not supported across the C ABI")
missing_return = missing_return || strings.contains(diagnostic.message, "does not return a value")
parameter = parameter || strings.contains(diagnostic.message, "only valid as a native function result type")
storage = storage || strings.contains(diagnostic.message, "cannot store a noreturn value")
}
testing.expect(t, field && c_abi && missing_return && parameter && storage)
}
@(test)
anonymous_records_and_struct_type_report_targeted_errors :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-bad-struct-type"