fallible main
This commit is contained in:
+1
-1
@@ -263,7 +263,7 @@ exactly once. Bare functions named `memcopy` or `memset` remain ordinary user fu
|
|||||||
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
- `std/arraylist` generic `ArrayList(T)` with direct `items` slice access, explicit capacity, allocator ownership, fallible reserve/append, clear, and deinit
|
||||||
- `std/meta` reflection records plus `EnumFieldStruct(E, Field, default ?Field)`, implemented with `struct_type!`; it produces a record with one field per native enum member in declaration order, where outer `null` means no field default
|
- `std/meta` reflection records plus `EnumFieldStruct(E, Field, default ?Field)`, implemented with `struct_type!`; it produces a record with one field per native enum member in declaration order, where outer `null` means no field default
|
||||||
- `std/io` explicit `Io` capabilities, provider-bound `Reader`/`Writer` handles, existing-file open/close operations, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, recursively scalar-backed distinct values, and `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
- `std/io` explicit `Io` capabilities, provider-bound `Reader`/`Writer` handles, existing-file open/close operations, allocation-free `write_all`, and comptime-expanded writer-first `print`; formatting supports natural `{}`, byte `{s}`, decimal `{d}`, integer `{b}` / `{o}` / `{x}` / `{X}`, byte-character `{c}`, scientific float `{e}`, recursively scalar-backed distinct values, and `{{` / `}}`, with malformed formats and incompatible tuple fields rejected at comptime
|
||||||
- entry points are either `main func() ...` or `main func(init process.Init) ...`; `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
|
- entry points are either `main func() ...` or `main func(init process.Init) ...`; their success channel is `void`, `i32`, or `int` and may have an error channel; an unhandled entry error exits with status 1. `std/process.Init` carries startup capabilities, currently only `io`, while the system provider remains hidden inside `std/io`
|
||||||
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.Init`
|
- `std/debug.print` is an allocation-free, failure-ignoring stderr escape hatch independent of `process.Init`
|
||||||
- `std/testing` supplies fallible `expect`, expected-first `expect_equal`, and exact compile-time `expect_type`; direct calls through
|
- `std/testing` supplies fallible `expect`, expected-first `expect_equal`, and exact compile-time `expect_type`; direct calls through
|
||||||
an alias of exactly `@std/testing` receive compiler-injected source locations
|
an alias of exactly `@std/testing` receive compiler-injected source locations
|
||||||
|
|||||||
@@ -1305,6 +1305,9 @@ type_from_syntax :: proc(
|
|||||||
|
|
||||||
function_channel_type :: proc(checker: ^Checker, function: ast.Function) -> types.Type {
|
function_channel_type :: proc(checker: ^Checker, function: ast.Function) -> types.Type {
|
||||||
result := type_from_syntax(checker, function.result, function.pkg, function.file)
|
result := type_from_syntax(checker, function.result, function.pkg, function.file)
|
||||||
|
if function.pkg == 0 && function.name == checker.main_symbol && result == types.INT {
|
||||||
|
result = types.I32
|
||||||
|
}
|
||||||
if types.is_valid(function.error) {
|
if types.is_valid(function.error) {
|
||||||
return types.fallible(&checker.module.types, result, type_from_syntax(checker, function.error, function.pkg, function.file))
|
return types.fallible(&checker.module.types, result, type_from_syntax(checker, function.error, function.pkg, function.file))
|
||||||
}
|
}
|
||||||
@@ -4703,10 +4706,6 @@ ensure_spec :: proc(
|
|||||||
comptime_signature.allocator = checker.allocator
|
comptime_signature.allocator = checker.allocator
|
||||||
append(&comptime_signature, ..comptime_values)
|
append(&comptime_signature, ..comptime_values)
|
||||||
result := function_channel_type(checker, function)
|
result := function_channel_type(checker, function)
|
||||||
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT &&
|
|
||||||
!types.is_valid(function.error) {
|
|
||||||
result = types.I32
|
|
||||||
}
|
|
||||||
index := spec_id(len(checker.specs))
|
index := spec_id(len(checker.specs))
|
||||||
append(
|
append(
|
||||||
&checker.specs,
|
&checker.specs,
|
||||||
@@ -5825,13 +5824,8 @@ infer_expr :: proc(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
declared := function_channel_type(checker, function)
|
declared := function_channel_type(checker, function)
|
||||||
if function.pkg == 0 && function.name == checker.main_symbol && function.result == types.INT &&
|
|
||||||
!types.is_valid(function.error) {
|
|
||||||
last = types.I32
|
|
||||||
} else {
|
|
||||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) || types.is_noreturn(declared) else types.INVALID
|
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) || types.is_noreturn(declared) else types.INVALID
|
||||||
}
|
}
|
||||||
}
|
|
||||||
delete(stack[frame_index].args, checker.allocator)
|
delete(stack[frame_index].args, checker.allocator)
|
||||||
stack[frame_index].args = nil
|
stack[frame_index].args = nil
|
||||||
delete(stack[frame_index].mapping, checker.allocator)
|
delete(stack[frame_index].mapping, checker.allocator)
|
||||||
@@ -10402,7 +10396,10 @@ build_expr :: proc(
|
|||||||
make_link_name :: proc(checker: ^Checker, id: Spec_Id) -> string {
|
make_link_name :: proc(checker: ^Checker, id: Spec_Id) -> string {
|
||||||
spec := checker.specs[id]
|
spec := checker.specs[id]
|
||||||
function := checker.ast_module.functions[spec.template]
|
function := checker.ast_module.functions[spec.template]
|
||||||
if function.pkg == 0 && function.name == checker.main_symbol && checker.entry_point == .Plain {
|
native_main := function.pkg == 0 && function.name == checker.main_symbol &&
|
||||||
|
checker.entry_point == .Plain &&
|
||||||
|
types.kind(spec.result, &checker.module.types) != .Fallible
|
||||||
|
if native_main {
|
||||||
return fmt.aprintf("main", allocator = checker.allocator)
|
return fmt.aprintf("main", allocator = checker.allocator)
|
||||||
}
|
}
|
||||||
if function.generated {
|
if function.generated {
|
||||||
@@ -13981,7 +13978,9 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
|
|||||||
|
|
||||||
problematic := signature_diagnostic != source.INVALID_DIAGNOSTIC ||
|
problematic := signature_diagnostic != source.INVALID_DIAGNOSTIC ||
|
||||||
checker.template_diagnostics[spec.template] != source.INVALID_DIAGNOSTIC
|
checker.template_diagnostics[spec.template] != source.INVALID_DIAGNOSTIC
|
||||||
native_main := function.pkg == 0 && function.name == checker.main_symbol && checker.entry_point == .Plain
|
native_main := function.pkg == 0 && function.name == checker.main_symbol &&
|
||||||
|
checker.entry_point == .Plain &&
|
||||||
|
types.kind(spec.result, &checker.module.types) != .Fallible
|
||||||
if !function.has_body {
|
if !function.has_body {
|
||||||
if unresolved_result && signature_diagnostic == source.INVALID_DIAGNOSTIC &&
|
if unresolved_result && signature_diagnostic == source.INVALID_DIAGNOSTIC &&
|
||||||
checker.template_diagnostics[spec.template] == source.INVALID_DIAGNOSTIC {
|
checker.template_diagnostics[spec.template] == source.INVALID_DIAGNOSTIC {
|
||||||
@@ -14814,19 +14813,24 @@ check :: proc(
|
|||||||
id = source.add(
|
id = source.add(
|
||||||
diagnostics,
|
diagnostics,
|
||||||
template.span,
|
template.span,
|
||||||
"main must be unique, have a body, take no parameters or one @std/process Init, and return void, i32, or int",
|
"main must be unique, have a body, take no parameters or one @std/process Init, and return void, i32, or int, optionally with an error channel",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
checker.module.injected_main = hir.INVALID_FUNCTION
|
checker.module.injected_main = hir.INVALID_FUNCTION
|
||||||
checker.module.io_provider = hir.INVALID_FUNCTION
|
checker.module.io_provider = hir.INVALID_FUNCTION
|
||||||
replace_main_with_trap(&checker, id)
|
replace_main_with_trap(&checker, id)
|
||||||
} else if checker.entry_point == .Process {
|
} else {
|
||||||
main_spec := find_spec(&checker, main_template, nil)
|
main_spec := find_spec(&checker, main_template, nil)
|
||||||
|
if checker.entry_point == .Process {
|
||||||
provider_spec := find_spec(&checker, checker.io_provider_template, nil)
|
provider_spec := find_spec(&checker, checker.io_provider_template, nil)
|
||||||
if main_spec != INVALID_SPEC && provider_spec != INVALID_SPEC {
|
if main_spec != INVALID_SPEC && provider_spec != INVALID_SPEC {
|
||||||
checker.module.injected_main = checker.specs[main_spec].hir_id
|
checker.module.injected_main = checker.specs[main_spec].hir_id
|
||||||
checker.module.io_provider = checker.specs[provider_spec].hir_id
|
checker.module.io_provider = checker.specs[provider_spec].hir_id
|
||||||
}
|
}
|
||||||
|
} else if main_spec != INVALID_SPEC &&
|
||||||
|
types.kind(checker.specs[main_spec].result, &checker.module.types) == .Fallible {
|
||||||
|
checker.module.injected_main = checker.specs[main_spec].hir_id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+106
-20
@@ -1752,13 +1752,25 @@ lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, al
|
|||||||
|
|
||||||
append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, allocator: mem.Allocator) {
|
append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, allocator: mem.Allocator) {
|
||||||
main_index, main_ok := hir.index(hir_module.injected_main, hir.INVALID_FUNCTION, len(hir_module.functions))
|
main_index, main_ok := hir.index(hir_module.injected_main, hir.INVALID_FUNCTION, len(hir_module.functions))
|
||||||
|
if !main_ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
main_function := &hir_module.functions[main_index]
|
||||||
provider_index, provider_ok := hir.index(hir_module.io_provider, hir.INVALID_FUNCTION, len(hir_module.functions))
|
provider_index, provider_ok := hir.index(hir_module.io_provider, hir.INVALID_FUNCTION, len(hir_module.functions))
|
||||||
|
param_index := -1
|
||||||
|
if len(main_function.params) == 1 {
|
||||||
|
param_index, main_ok = hir.index(main_function.params[0], hir.INVALID_LOCAL, len(main_function.locals))
|
||||||
if !main_ok || !provider_ok {
|
if !main_ok || !provider_ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
} else if len(main_function.params) != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
instructions: [dynamic]ir.Instruction
|
instructions: [dynamic]ir.Instruction
|
||||||
instructions.allocator = allocator
|
instructions.allocator = allocator
|
||||||
|
args: []ir.Instruction_Id
|
||||||
|
if param_index >= 0 {
|
||||||
provider_call := ir.instruction_id(len(instructions))
|
provider_call := ir.instruction_id(len(instructions))
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Call,
|
op=.Call,
|
||||||
@@ -1768,11 +1780,6 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
|||||||
b=ir.INVALID_INSTRUCTION,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
main_function := &hir_module.functions[main_index]
|
|
||||||
param_index, param_ok := hir.index(main_function.params[0], hir.INVALID_LOCAL, len(main_function.locals))
|
|
||||||
if !param_ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
init_args := make([]ir.Instruction_Id, 1, allocator)
|
init_args := make([]ir.Instruction_Id, 1, allocator)
|
||||||
init_args[0] = provider_call
|
init_args[0] = provider_call
|
||||||
init_value := ir.instruction_id(len(instructions))
|
init_value := ir.instruction_id(len(instructions))
|
||||||
@@ -1785,8 +1792,9 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
|||||||
b=ir.INVALID_INSTRUCTION,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
args := make([]ir.Instruction_Id, 1, allocator)
|
args = make([]ir.Instruction_Id, 1, allocator)
|
||||||
args[0] = init_value
|
args[0] = init_value
|
||||||
|
}
|
||||||
main_call := ir.instruction_id(len(instructions))
|
main_call := ir.instruction_id(len(instructions))
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Call,
|
op=.Call,
|
||||||
@@ -1797,36 +1805,114 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
|||||||
b=ir.INVALID_INSTRUCTION,
|
b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
if types.is_void(hir_module.functions[main_index].result) {
|
|
||||||
|
if types.kind(main_function.result, &hir_module.types) == .Fallible {
|
||||||
|
success := types.fallible_success(main_function.result, &hir_module.types)
|
||||||
|
channel_slot := ir.instruction_id(len(instructions))
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Return_Void,
|
op=.Alloca, type=main_function.result, target=ir.INVALID_REF,
|
||||||
type=types.VOID,
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
target=ir.INVALID_REF,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
a=ir.INVALID_INSTRUCTION,
|
})
|
||||||
b=ir.INVALID_INSTRUCTION,
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Store, type=main_function.result, target=ir.INVALID_REF,
|
||||||
|
a=channel_slot, b=main_call, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
code := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Union_Tag, type=types.U16, target=ir.INVALID_REF,
|
||||||
|
a=channel_slot, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
zero_tag := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.U16, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
ok := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Compare, type=types.BOOL, integer=i64(ir.Compare_Predicate.Eq),
|
||||||
|
target=ir.INVALID_REF, a=code, b=zero_tag,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Cond_Br, type=types.VOID, integer=1, target=ir.Ref(0), a=ok,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Label, type=types.VOID, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
failure := ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.I32, integer=1, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Return, type=types.I32, target=ir.INVALID_REF, a=failure,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Label, type=types.VOID, integer=1, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
success_value := ir.INVALID_INSTRUCTION
|
||||||
|
if types.is_void(success) {
|
||||||
|
success_value = ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.I32, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
payload := ir.instruction_id(len(instructions))
|
||||||
append(&instructions, ir.Instruction{
|
append(&instructions, ir.Instruction{
|
||||||
op=.Return,
|
op=.Field_Address, type=success, integer=0, target=ir.INVALID_REF,
|
||||||
type=hir_module.functions[main_index].result,
|
a=channel_slot, b=ir.INVALID_INSTRUCTION,
|
||||||
target=ir.INVALID_REF,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
a=main_call,
|
})
|
||||||
b=ir.INVALID_INSTRUCTION,
|
success_value = ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Load, type=success, target=ir.INVALID_REF, a=payload,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Return, type=types.I32, target=ir.INVALID_REF, a=success_value,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
exit_value := main_call
|
||||||
|
if types.is_void(main_function.result) {
|
||||||
|
exit_value = ir.instruction_id(len(instructions))
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Const, type=types.I32, integer=0, target=ir.INVALID_REF,
|
||||||
|
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
append(&instructions, ir.Instruction{
|
||||||
|
op=.Return, type=types.I32, target=ir.INVALID_REF, a=exit_value,
|
||||||
|
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
problematic := main_function.problematic
|
||||||
|
if provider_ok {
|
||||||
|
problematic = problematic || hir_module.functions[provider_index].problematic
|
||||||
|
}
|
||||||
append(&module.functions, ir.Function{
|
append(&module.functions, ir.Function{
|
||||||
link_name=fmt.aprintf("main", allocator=allocator),
|
link_name=fmt.aprintf("main", allocator=allocator),
|
||||||
calling_convention=.C,
|
calling_convention=.C,
|
||||||
implementation=.Definition,
|
implementation=.Definition,
|
||||||
linkage=.External,
|
linkage=.External,
|
||||||
is_main=true,
|
is_main=true,
|
||||||
result=hir_module.functions[main_index].result,
|
result=types.I32,
|
||||||
instructions=instructions[:],
|
instructions=instructions[:],
|
||||||
problematic=hir_module.functions[main_index].problematic ||
|
problematic=problematic,
|
||||||
hir_module.functions[provider_index].problematic,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3129,6 +3129,79 @@ bodyless_root_main_recovers_as_a_trap_definition :: proc(t: ^testing.T) {
|
|||||||
testing.expect(t, !strings.contains(llvm_text, "declare i32 @main()"))
|
testing.expect(t, !strings.contains(llvm_text, "declare i32 @main()"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
fallible_main_maps_success_and_error_channels_to_exit_codes :: proc(t: ^testing.T) {
|
||||||
|
Case :: struct {
|
||||||
|
name: string,
|
||||||
|
source: string,
|
||||||
|
exit_code: int,
|
||||||
|
}
|
||||||
|
cases := [?]Case{
|
||||||
|
{
|
||||||
|
name="void-success",
|
||||||
|
source=`Error :: enum { failed }
|
||||||
|
succeed func() void ! Error {}
|
||||||
|
main func() void ! Error { try succeed() }
|
||||||
|
`,
|
||||||
|
exit_code=0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name="void-error",
|
||||||
|
source=`Error :: enum { failed }
|
||||||
|
fail func() void ! Error { return .failed }
|
||||||
|
main func() void ! Error { try fail() }
|
||||||
|
`,
|
||||||
|
exit_code=1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name="int-success",
|
||||||
|
source=`Error :: enum { failed }
|
||||||
|
value func() i32 ! Error { return 7 }
|
||||||
|
main func() int ! Error { return try value() }
|
||||||
|
`,
|
||||||
|
exit_code=7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name="process-error",
|
||||||
|
source=`process :: import "@std/process"
|
||||||
|
Error :: enum { failed }
|
||||||
|
fail func() void ! Error { return .failed }
|
||||||
|
main func(_ process.Init) void ! Error { try fail() }
|
||||||
|
`,
|
||||||
|
exit_code=1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for test_case in cases {
|
||||||
|
directory := fmt.tprintf("/tmp/brolang-test-fallible-main-%s", test_case.name)
|
||||||
|
output := fmt.tprintf("%s/app", directory)
|
||||||
|
_ = os2.remove_all(directory)
|
||||||
|
defer _ = os2.remove_all(directory)
|
||||||
|
testing.expect(t, os.make_directory(directory) == nil)
|
||||||
|
testing.expect(t, os.write_entire_file(
|
||||||
|
fmt.tprintf("%s/main.bro", directory),
|
||||||
|
transmute([]byte)test_case.source,
|
||||||
|
))
|
||||||
|
status := compiler_core.compile_package(
|
||||||
|
directory,
|
||||||
|
output,
|
||||||
|
nil,
|
||||||
|
target.DEFAULT,
|
||||||
|
cimport.Options{},
|
||||||
|
".",
|
||||||
|
)
|
||||||
|
testing.expect_value(t, status, 0)
|
||||||
|
state, stdout, stderr, _ := os2.process_exec(
|
||||||
|
os2.Process_Desc{command=[]string{output}},
|
||||||
|
context.allocator,
|
||||||
|
)
|
||||||
|
defer delete(stdout)
|
||||||
|
defer delete(stderr)
|
||||||
|
testing.expect_value(t, state.exit_code, test_case.exit_code)
|
||||||
|
testing.expect_value(t, len(stdout), 0)
|
||||||
|
testing.expect_value(t, len(stderr), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
milestone_33_injects_explicit_io_provider_and_runs_std_io :: proc(t: ^testing.T) {
|
milestone_33_injects_explicit_io_provider_and_runs_std_io :: proc(t: ^testing.T) {
|
||||||
sources := source.init_store()
|
sources := source.init_store()
|
||||||
|
|||||||
Reference in New Issue
Block a user