add noreturn and unreachable
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user