errdefer and try/defer fix

This commit is contained in:
2026-07-13 19:20:22 +02:00
parent 9e75549d02
commit 6de4d9f9f3
23 changed files with 113190 additions and 105482 deletions
+54 -6
View File
@@ -290,7 +290,8 @@ Ct_State :: struct {
places: [dynamic]Ct_Place,
paths: [dynamic]Ct_Path_Elem,
bindings: [dynamic]Ct_Binding,
defers: [dynamic]ast.Stmt_Id,
defers: [dynamic]Ct_Defer,
defer_depth: int,
steps: int,
error: Ct_Error_Kind,
diagnostic: source.Diagnostic_Id,
@@ -298,6 +299,12 @@ Ct_State :: struct {
demanded: ^[dynamic]Spec_Id,
}
Ct_Defer :: struct {
statement: ast.Stmt_Id,
error_only: bool,
capture: symbol.Id,
}
ct_state_make :: proc(
checker: ^Checker,
pkg: ast.Package_Id,
@@ -2217,6 +2224,9 @@ ct_clone_value :: proc(dst, src: ^Ct_State, id: Ct_Value_Id) -> Ct_Value_Id {
}
ct_eval_try_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
if state.defer_depth > 0 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "cannot 'try' inside a 'defer'")
}
checker := state.checker
channel, flow, ok := ct_eval_expr(state, expr.left, types.INVALID, depth+1)
if !ok || flow.kind != .Normal {
@@ -2465,7 +2475,17 @@ ct_exec_statements :: proc(
case .Block:
flow, ok = ct_exec_statements(state, statement.body, yield_returns, depth+1)
case .Defer:
append(&state.defers, statement.update)
if statement.error_only && types.kind(state.result, &checker.module.types) != .Fallible {
ok = ct_fail(state, .Not_Comptime, statement.span, "'errdefer' requires an enclosing fallible function")
} else {
capture := symbol.INVALID
if len(statement.captures) > 0 {
capture = statement.captures[0]
}
append(&state.defers, Ct_Defer{
statement=statement.update, error_only=statement.error_only, capture=capture,
})
}
case .Match:
flow, ok = ct_exec_match(state, statement, yield_returns, depth+1)
case .Match_Arm:
@@ -2477,22 +2497,50 @@ ct_exec_statements :: proc(
return flow, false
}
if flow.kind != .Normal {
if !ct_flush_defers(state, defer_start, depth+1) {
if !ct_flush_defers(state, defer_start, flow, depth+1) {
return flow, false
}
return flow, true
}
}
if !ct_flush_defers(state, defer_start, depth+1) {
if !ct_flush_defers(state, defer_start, ct_flow(.Normal), depth+1) {
return ct_flow(.Normal), false
}
return ct_flow(.Normal), true
}
ct_flush_defers :: proc(state: ^Ct_State, start: int, depth: int) -> bool {
ct_flush_defers :: proc(state: ^Ct_State, start: int, exit: Ct_Flow, depth: int) -> bool {
error_exit := false
error_value := INVALID_CT_VALUE
if exit.kind == .Return && exit.value != INVALID_CT_VALUE && int(exit.value) < len(state.values) {
returned := state.values[exit.value]
if returned.kind == .Fallible && returned.active != 0 {
error_exit = true
children := ct_child_slice(state, returned)
if len(children) > 0 {
error_value = children[0]
}
}
}
for index := len(state.defers) - 1; index >= start; index -= 1 {
stmt := [1]ast.Stmt_Id{state.defers[index]}
entry := state.defers[index]
if entry.error_only && !error_exit {
continue
}
binding_start := len(state.bindings)
bound_capture := false
if entry.error_only && symbol.is_valid(entry.capture) &&
entry.capture != state.checker.sink_symbol && error_value != INVALID_CT_VALUE {
ct_bind_value(state, entry.capture, state.values[error_value].type, error_value, false)
bound_capture = true
}
stmt := [1]ast.Stmt_Id{entry.statement}
state.defer_depth += 1
flow, ok := ct_exec_statements(state, stmt[:], false, depth+1)
state.defer_depth -= 1
if bound_capture {
ct_pop_bindings(state, binding_start)
}
if !ok || flow.kind != .Normal {
return false
}