unify catch fallback value sources

This commit is contained in:
2026-07-22 00:37:48 +02:00
parent 17508ff751
commit 5f343ad2d3
12 changed files with 1048397 additions and 324314 deletions
+28 -19
View File
@@ -4737,23 +4737,21 @@ infer_compound_expr :: proc(
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded, local_types, left_expected)
success := types.fallible_success(value, store)
error_type := types.fallible_error(value, store)
fallback_locals: [dynamic]Infer_Local
fallback_locals.allocator = checker.allocator
defer delete(fallback_locals)
append(&fallback_locals, ..locals)
if symbol.is_valid(expr.name) && expr.name != checker.sink_symbol && types.is_valid(error_type) {
append(&fallback_locals, Infer_Local{name=expr.name, type=error_type, declared=error_type, statement=ast.INVALID_STMT})
}
if expr.right != ast.INVALID_EXPR {
fallback := infer_nested_expr(checker, expr.right, locals, pkg, file, demanded, local_types)
fallback := infer_nested_expr(checker, expr.right, fallback_locals[:], pkg, file, demanded, local_types)
if types.is_valid(success) && types.is_valid(fallback) && !types.equal(success, fallback) {
return types.widest(success, fallback)
}
return success if types.is_valid(success) else fallback
}
block_locals: [dynamic]Infer_Local
block_locals.allocator = checker.allocator
defer delete(block_locals)
append(&block_locals, ..locals)
capture_start := len(block_locals)
if symbol.is_valid(expr.name) && expr.name != checker.sink_symbol && types.is_valid(error_type) {
append(&block_locals, Infer_Local{name=expr.name, type=error_type, declared=error_type, statement=ast.INVALID_STMT})
}
infer_statements(checker, expr.body, &block_locals, local_types, pkg, file, demanded, &success, success)
resize(&block_locals, capture_start)
infer_statements(checker, expr.body, &fallback_locals, local_types, pkg, file, demanded, &success, success)
return success
case .Struct_Literal:
value := types.INVALID
@@ -8139,26 +8137,37 @@ build_compound_expr :: proc(
block_handler := false
void_fallthrough := false
fallback := hir.INVALID_EXPR
ctx := checker.current_build_ctx
capture_start := 0
if symbol.is_valid(expr.name) && expr.name != checker.sink_symbol {
if ctx == nil {
id := source.add(checker.diagnostics, expr.span, "a captured catch fallback is only valid in a function body")
return invalid_hir_expr(checker, expr.span, id, success)
}
capture_start = len(ctx.locals^)
error_type := types.fallible_error(channel_type, store)
capture = append_build_local(ctx, expr.name, error_type, false, expr.span)
}
if expr.right != ast.INVALID_EXPR {
fallback = build_nested_expr(checker, expr.right, locals, global_reads, calls, success, pkg, file)
fallback_locals := locals
if capture != hir.INVALID_LOCAL {
fallback_locals = ctx.locals^[:]
}
fallback = build_nested_expr(checker, expr.right, fallback_locals, global_reads, calls, success, pkg, file)
fallback = coerce_expr(checker, fallback, success, checker.module.exprs[fallback].span)
} else {
block_handler = true
ctx := checker.current_build_ctx
if ctx == nil {
id := source.add(checker.diagnostics, expr.span, "catch block form is only valid in a function body")
return invalid_hir_expr(checker, expr.span, id, success)
}
capture_start := len(ctx.locals^)
error_type := types.fallible_error(channel_type, store)
if symbol.is_valid(expr.name) && expr.name != checker.sink_symbol {
capture = append_build_local(ctx, expr.name, error_type, false, expr.span)
}
handler: [dynamic]hir.Stmt_Id
handler.allocator = checker.allocator
fallback, _ = build_value_source(ctx, &handler, expr.body, success, expr.span, allow_exit=true)
fallback, _ = build_value_source(ctx, &handler, expr.body, success, expr.span, value_control_flow=expr.integer != 0, allow_exit=true)
void_fallthrough = fallback == hir.INVALID_EXPR && !all_paths_exit(&checker.module, handler[:])
body = handler[:]
}
if capture != hir.INVALID_LOCAL {
resize(ctx.locals, capture_start)
}
catch_mode := hir.CATCH_EXPRESSION
+5 -3
View File
@@ -3763,14 +3763,16 @@ ct_eval_catch_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Typ
if value.active == 0 {
return payload, ct_flow(.Normal), true
}
if expr.right != ast.INVALID_EXPR {
return ct_eval_expr(state, expr.right, success, depth+1)
}
scope_start := len(state.bindings)
if symbol.is_valid(expr.name) && expr.name != checker.sink_symbol && payload != INVALID_CT_VALUE {
error_type := types.fallible_error(value.type, &checker.module.types)
ct_bind_value(state, expr.name, error_type, payload, false)
}
if expr.right != ast.INVALID_EXPR {
result, result_flow, result_ok := ct_eval_expr(state, expr.right, success, depth+1)
ct_pop_bindings(state, scope_start)
return result, result_flow, result_ok
}
handler, handler_ok := ct_exec_statements(state, expr.body, true, depth+1)
ct_pop_bindings(state, scope_start)
if !handler_ok {