bug fixes

This commit is contained in:
2026-07-13 23:34:14 +02:00
parent 0eeacc2e37
commit 0b0b00a050
6 changed files with 114 additions and 17 deletions
+25 -9
View File
@@ -5913,6 +5913,7 @@ build_compound_expr :: proc(
body: []hir.Stmt_Id
capture := hir.INVALID_LOCAL
block_handler := false
void_fallthrough := false
fallback := hir.INVALID_EXPR
if expr.right != ast.INVALID_EXPR {
fallback = build_nested_expr(checker, expr.right, locals, global_reads, calls, success, pkg, file)
@@ -5932,14 +5933,19 @@ build_compound_expr :: proc(
handler: [dynamic]hir.Stmt_Id
handler.allocator = checker.allocator
fallback, _ = build_value_source(ctx, &handler, expr.body, success, expr.span, allow_exit=true)
void_fallthrough = fallback == hir.INVALID_EXPR && !all_paths_exit(&checker.module, handler[:])
body = handler[:]
resize(ctx.locals, capture_start)
}
catch_mode := hir.CATCH_EXPRESSION
if block_handler {
catch_mode = hir.CATCH_VOID_FALLTHROUGH if void_fallthrough else hir.CATCH_BLOCK
}
return add_hir_expr(checker, hir.Expr{
kind=.Catch,
span=expr.span,
type=success,
integer=1 if block_handler else 0,
integer=catch_mode,
left=channel,
right=fallback,
body=body,
@@ -8236,10 +8242,11 @@ build_block :: proc(
}
// build_value_block builds a `{ ... yield v }` value block whose final statement
// must be a `yield`. Catch handlers may instead exit on every path, in which case
// `allow_exit` leaves the fallback expression invalid. Otherwise it builds the leading
// statements inline, evaluates the yield in their scope, then captures the value before
// running defers. `expected` is the binding's type (INVALID for an untyped `::`).
// must be a `yield`. Catch handlers may instead exit on every path or complete with
// void, in which case `allow_exit` leaves the fallback expression invalid. Otherwise
// it builds the leading statements inline, evaluates the yield in their scope, then
// captures the value before running defers. `expected` is the binding's type (INVALID
// for an untyped `::`).
build_value_block :: proc(
ctx: ^Build_Ctx,
body: ^[dynamic]hir.Stmt_Id,
@@ -8257,7 +8264,7 @@ build_value_block :: proc(
for s in inner {
append(body, s)
}
if allow_exit && all_paths_exit(&checker.module, inner) {
if allow_exit && (types.is_void(expected) || all_paths_exit(&checker.module, inner)) {
delete(inner, checker.allocator)
return hir.INVALID_EXPR, expected
}
@@ -9785,7 +9792,16 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
}
delete(block, checker.allocator)
if !types.is_void(spec.result) && !returns {
fallible_void := types.kind(spec.result, &checker.module.types) == .Fallible &&
types.is_void(types.fallible_success(spec.result, &checker.module.types))
if !returns && fallible_void {
value := fallible_aggregate(checker, function.span, spec.result, hir.INVALID_EXPR, false)
append(&body, hir.stmt_id(len(checker.module.statements)))
append(&checker.module.statements, hir.Stmt{
kind=.Return, span=function.span, expr=value, local=hir.INVALID_LOCAL,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else if !types.is_void(spec.result) && !returns {
id := source.addf(
checker.diagnostics,
function.span,
@@ -9944,8 +9960,8 @@ build_globals :: proc(checker: ^Checker) {
}
expr := build_expr(checker, global.expr, nil, &dependencies, &calls, expected, global.pkg, global.file)
global_type := checker.global_types[global_index]
if is_runtime_type(checker, declared) {
expr = coerce_expr(checker, expr, declared, global.span)
if is_runtime_type(checker, checker.global_types[global_index]) {
expr = coerce_expr(checker, expr, checker.global_types[global_index], global.span)
global_type = checker.module.exprs[expr].type
} else if is_runtime_type(checker, checker.module.exprs[expr].type) {
global_type = checker.module.exprs[expr].type
+9
View File
@@ -2196,6 +2196,11 @@ ct_eval_template_call :: proc(
result := ct_add_value(state, Ct_Value{kind=.Void, type=types.VOID})
return result, ct_flow(.Normal), true
}
if flow.kind == .Normal && types.kind(result_type, &checker.module.types) == .Fallible &&
types.is_void(types.fallible_success(result_type, &checker.module.types)) {
result := ct_make_fallible(state, result_type, INVALID_CT_VALUE, false)
return result, ct_flow(.Normal), true
}
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, span, "comptime function '%s' did not return a value", symbol_text(checker, function.name))
}
result := flow.value
@@ -2304,6 +2309,10 @@ ct_eval_catch_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Typ
}
return handler.value, ct_flow(.Normal), true
}
if handler.kind == .Normal && types.is_void(success) {
value := ct_add_value(state, Ct_Value{kind=.Void, type=types.VOID})
return value, ct_flow(.Normal), true
}
return INVALID_CT_VALUE, handler, ct_fail(state, .Not_Comptime, expr.span, "catch block must yield a value")
}
+7 -3
View File
@@ -132,6 +132,10 @@ Expr_Kind :: enum u8 {
Call,
}
CATCH_EXPRESSION :: i64(0)
CATCH_BLOCK :: i64(1)
CATCH_VOID_FALLTHROUGH :: i64(2)
Expr :: struct {
span: source.Span,
type: types.Type,
@@ -140,9 +144,9 @@ Expr :: struct {
// encoded as Expr_Id because it otherwise has no args.
args: []Expr_Id,
// `Catch` block handlers use `body` for the handler statements and `target`
// for the optional captured error local. A missing `right` means the handler
// exits on every path and therefore has no fallback value. `Try` uses `body`
// for active error-exit cleanup.
// for the optional captured error local. `integer` selects the catch mode;
// a missing `right` means the block exits or completes with void. `Try` uses
// `body` for active error-exit cleanup.
body: []Stmt_Id,
target: Ref,
left: Expr_Id,
+8 -1
View File
@@ -468,7 +468,7 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
if expr.integer != 0 {
if expr.integer != hir.CATCH_EXPRESSION {
capture := hir.as_local(expr.target)
if capture != hir.INVALID_LOCAL && int(capture) < len(state.func_locals) {
error_type := state.func_locals[capture].type
@@ -491,6 +491,13 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
})
}
lower_statements(state, expr.body)
if expr.integer == hir.CATCH_VOID_FALLTHROUGH {
append_instruction(state, ir.Instruction{
op=.Br, span=expr.span, type=types.VOID, integer=merge_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
}
if expr.right != hir.INVALID_EXPR {
fallback := lower_nested_expr(state, expr.right)