structs as errors in fallibles
This commit is contained in:
+1
-1
@@ -44,7 +44,7 @@ roadmap and milestone history.
|
|||||||
- named native struct fields may declare defaults with `field T = expression`; keyed literals use defaults for omitted fields and explicit initializers override them
|
- named native struct fields may declare defaults with `field T = expression`; keyed literals use defaults for omitted fields and explicit initializers override them
|
||||||
- void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction
|
- void-payload tagged-union variants, anonymous struct payloads, contextual `.variant`, `.variant{payload}`, and `.variant{field = value}` construction
|
||||||
- native sum composition with `A | B` for unbacked enums and tagged unions, optionally grouped as `(A | B)`, using program-global `u16` variant ids
|
- native sum composition with `A | B` for unbacked enums and tagged unions, optionally grouped as `(A | B)`, using program-global `u16` variant ids
|
||||||
- fallible channel types `T ! E`, where `E` is a native enum/tagged union or supported sum composition; `void ! E` functions complete successfully on fallthrough, and void-success `catch` handlers may fall through without `yield`
|
- fallible channel types `T ! E`, where `E` is a native enum, native struct, tagged union, or supported sum composition; `void ! E` functions complete successfully on fallthrough, and void-success `catch` handlers may fall through without `yield`
|
||||||
|
|
||||||
#### distinct types
|
#### distinct types
|
||||||
|
|
||||||
|
|||||||
@@ -3976,7 +3976,8 @@ validate_declarations :: proc(checker: ^Checker) {
|
|||||||
}
|
}
|
||||||
if types.is_valid(function.error) && !signature_poisoned {
|
if types.is_valid(function.error) && !signature_poisoned {
|
||||||
error_type := type_from_syntax(checker, function.error, function.pkg, function.file)
|
error_type := type_from_syntax(checker, function.error, function.pkg, function.file)
|
||||||
error_sum := types.is_enum(error_type, &checker.module.types) ||
|
error_channel := types.is_enum(error_type, &checker.module.types) ||
|
||||||
|
types.is_struct(error_type, &checker.module.types) ||
|
||||||
types.is_tagged_union(error_type, &checker.module.types)
|
types.is_tagged_union(error_type, &checker.module.types)
|
||||||
if function.c_abi {
|
if function.c_abi {
|
||||||
checker.template_diagnostics[function_id] = source.add(
|
checker.template_diagnostics[function_id] = source.add(
|
||||||
@@ -3984,11 +3985,11 @@ validate_declarations :: proc(checker: ^Checker) {
|
|||||||
function.span,
|
function.span,
|
||||||
"fallible functions must use 'func', not 'c_func'",
|
"fallible functions must use 'func', not 'c_func'",
|
||||||
)
|
)
|
||||||
} else if !error_sum {
|
} else if !error_channel {
|
||||||
checker.template_diagnostics[function_id] = source.add(
|
checker.template_diagnostics[function_id] = source.add(
|
||||||
checker.diagnostics,
|
checker.diagnostics,
|
||||||
function.span,
|
function.span,
|
||||||
"fallible function error type must be a native enum or tagged union",
|
"fallible function error type must be a native enum, struct, or tagged union",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4487,6 +4487,19 @@ ct_return_value :: proc(state: ^Ct_State, expr_id: ast.Expr_Id, span: source.Spa
|
|||||||
if expr.kind == .Enum_Literal && types.sum_has_name(&checker.module.types, error_type, u32(expr.name)) {
|
if expr.kind == .Enum_Literal && types.sum_has_name(&checker.module.types, error_type, u32(expr.name)) {
|
||||||
error_path = true
|
error_path = true
|
||||||
expected = error_type
|
expected = error_type
|
||||||
|
} else if expr.kind == .Struct_Literal {
|
||||||
|
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, true)
|
||||||
|
named := types.find_named(
|
||||||
|
&checker.module.types,
|
||||||
|
u32(target_pkg),
|
||||||
|
u32(expr.name),
|
||||||
|
file=u32(expr_lookup_file(expr, state.file)),
|
||||||
|
) if available else types.INVALID
|
||||||
|
named = types.resolve_alias(named, &checker.module.types)
|
||||||
|
if can_implicitly_convert_type(checker, named, error_type) {
|
||||||
|
error_path = true
|
||||||
|
expected = error_type
|
||||||
|
}
|
||||||
} else if expr.kind == .Name && !symbol.is_valid(expr.qualifier) {
|
} else if expr.kind == .Name && !symbol.is_valid(expr.qualifier) {
|
||||||
if index, found := ct_find_binding_index(state, expr.name); found {
|
if index, found := ct_find_binding_index(state, expr.name); found {
|
||||||
actual := state.bindings[index].type
|
actual := state.bindings[index].type
|
||||||
|
|||||||
@@ -1050,6 +1050,16 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
|
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
|
||||||
fmt.sbprintf(&emitter.builder, " call void @llvm.memcpy.p0.p0.i64(ptr %%fallible_payload%d, ptr %%fallible_error_payload%d, i64 %d, i1 false)\n", instruction_index, instruction_index, error_payload_size)
|
fmt.sbprintf(&emitter.builder, " call void @llvm.memcpy.p0.p0.i64(ptr %%fallible_payload%d, ptr %%fallible_error_payload%d, i64 %d, i1 false)\n", instruction_index, instruction_index, error_payload_size)
|
||||||
}
|
}
|
||||||
|
} else if types.is_struct(error_type, &emitter.module.types) {
|
||||||
|
if !valid_value(instructions, instruction.args[0], error_type, &emitter.module.types) {
|
||||||
|
emit_recovery_value(emitter, instruction_index, instruction, "invalid fallible struct error operand")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.sbprintf(&emitter.builder, " store i16 1, ptr %%fallible_slot%d\n", instruction_index)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%fallible_payload%d = getelementptr i8, ptr %%fallible_slot%d, i64 %d\n", instruction_index, instruction_index, payload_offset)
|
||||||
|
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(error_type, &emitter.module.types))
|
||||||
|
write_operand(&emitter.builder, instructions, instruction.args[0], error_type, &emitter.module.types)
|
||||||
|
fmt.sbprintf(&emitter.builder, ", ptr %%fallible_payload%d\n", instruction_index)
|
||||||
}
|
}
|
||||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_slot%d\n", instruction_index, type_name, instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_slot%d\n", instruction_index, type_name, instruction_index)
|
||||||
continue
|
continue
|
||||||
@@ -1389,6 +1399,12 @@ emit_instruction_stream :: proc(
|
|||||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_error_slot%d\n", instruction_index, type_name, instruction_index)
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_error_slot%d\n", instruction_index, type_name, instruction_index)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if types.is_struct(error_type, &emitter.module.types) {
|
||||||
|
source_offset := types.fallible_payload_offset(channel_type, &emitter.module.types, emitter.module.target)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%fallible_error_source%d = getelementptr i8, ptr %%v%d, i64 %d\n", instruction_index, instruction.a, source_offset)
|
||||||
|
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%fallible_error_source%d\n", instruction_index, llvm_type(error_type, &emitter.module.types), instruction_index)
|
||||||
|
continue
|
||||||
|
}
|
||||||
emit_recovery_value(emitter, instruction_index, instruction, "unsupported fallible error type")
|
emit_recovery_value(emitter, instruction_index, instruction, "unsupported fallible error type")
|
||||||
case .Store:
|
case .Store:
|
||||||
if !valid_address(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
if !valid_address(instructions, instruction.a, instruction.type, &emitter.module.types) ||
|
||||||
|
|||||||
@@ -6953,6 +6953,51 @@ main func() i32 {
|
|||||||
testing.expect_value(t, state.exit_code, 0)
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
struct_error_channels_compile_and_run_at_runtime_and_comptime :: proc(t: ^testing.T) {
|
||||||
|
text := `ScanError :: struct {
|
||||||
|
code i32
|
||||||
|
start usize
|
||||||
|
end usize
|
||||||
|
}
|
||||||
|
scan func(fail bool) i32 ! ScanError {
|
||||||
|
if fail {
|
||||||
|
return ScanError { code = 5, start = 7, end = 11 }
|
||||||
|
}
|
||||||
|
return 41
|
||||||
|
}
|
||||||
|
scan_local func() i32 ! ScanError {
|
||||||
|
err ScanError := ScanError { code = 3, start = 13, end = 17 }
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
forward func(fail bool) i32 ! ScanError {
|
||||||
|
return try scan(fail)
|
||||||
|
}
|
||||||
|
score_comptime func() i32 {
|
||||||
|
return scan(true) catch |err| err.code + i32(err.start) + i32(err.end)
|
||||||
|
}
|
||||||
|
main func() i32 {
|
||||||
|
direct :: scan(true) catch |err| err.code + i32(err.start) + i32(err.end)
|
||||||
|
local :: scan_local() catch |err| err.code + i32(err.start) + i32(err.end)
|
||||||
|
forwarded :: forward(true) catch |err| err.code + i32(err.start) + i32(err.end)
|
||||||
|
success :: forward(false) catch 0
|
||||||
|
comptime_score i32 :: $score_comptime()
|
||||||
|
return direct + local + forwarded + success + comptime_score - 143
|
||||||
|
}
|
||||||
|
`
|
||||||
|
directory := "/tmp/brolang-test-struct-error-channel"
|
||||||
|
main_path := "/tmp/brolang-test-struct-error-channel/main.bro"
|
||||||
|
output := "/tmp/brolang-test-struct-error-channel-output"
|
||||||
|
_ = 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), 0)
|
||||||
|
state := run_executable(output)
|
||||||
|
testing.expect_value(t, state.exit_code, 0)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
matched_error_residuals_return_at_runtime_and_comptime :: proc(t: ^testing.T) {
|
matched_error_residuals_return_at_runtime_and_comptime :: proc(t: ^testing.T) {
|
||||||
text := `KeyError :: enum { key_exists }
|
text := `KeyError :: enum { key_exists }
|
||||||
|
|||||||
Reference in New Issue
Block a user