fallible ergonomics

This commit is contained in:
2026-06-30 20:32:13 +02:00
parent 07c560b750
commit 7ca7e33033
8 changed files with 289 additions and 25 deletions
+47 -10
View File
@@ -168,6 +168,7 @@ Checker :: struct {
main_symbol: symbol.Id,
sink_symbol: symbol.Id,
current_result: types.Type,
current_build_ctx: ^Build_Ctx,
target: target.Target,
allocator: mem.Allocator,
}
@@ -3429,9 +3430,20 @@ build_compound_expr :: proc(
id := source.add(checker.diagnostics, expr.span, "'try' requires a fallible expression")
return invalid_hir_expr(checker, expr.span, id)
}
if !types.equal(channel_type, checker.current_result) {
// ponytail: exact channel propagation; add fallible-error widening when cross-error-set try matters.
id := source.add(checker.diagnostics, expr.span, "'try' can only propagate the enclosing function's exact error channel in v1")
enclosing_success := types.fallible_success(checker.current_result, store)
enclosing_error := types.fallible_error(checker.current_result, store)
if !types.is_valid(enclosing_success) {
id := source.add(checker.diagnostics, expr.span, "'try' requires an enclosing fallible function")
return invalid_hir_expr(checker, expr.span, id, success)
}
if !types.equal(success, enclosing_success) {
id := source.add(checker.diagnostics, expr.span, "'try' success type must match the enclosing fallible result")
return invalid_hir_expr(checker, expr.span, id, success)
}
error_type := types.fallible_error(channel_type, store)
if !types.equal(error_type, enclosing_error) &&
!types.can_sum_widen(error_type, enclosing_error, store) {
id := source.add(checker.diagnostics, expr.span, "'try' error channel cannot be widened to the enclosing error channel")
return invalid_hir_expr(checker, expr.span, id, success)
}
return add_hir_expr(checker, hir.Expr{
@@ -3451,20 +3463,42 @@ build_compound_expr :: proc(
id := source.add(checker.diagnostics, expr.span, "'catch' requires a fallible expression")
return invalid_hir_expr(checker, expr.span, id)
}
if expr.right == ast.INVALID_EXPR {
// ponytail: catch blocks need Build_Ctx threading through expression build; fallback catch covers v1.
id := source.add(checker.diagnostics, expr.span, "catch block form is not implemented in v1")
return invalid_hir_expr(checker, expr.span, id, success)
body: []hir.Stmt_Id
capture := hir.INVALID_LOCAL
block_handler := 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)
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 = hir.local_id(len(ctx.hir_locals^))
append(ctx.hir_locals, hir.Local{name=expr.name, type=error_type, mutable=false})
append(ctx.locals, Build_Local{name=expr.name, type=error_type, mutable=false, id=capture})
}
handler: [dynamic]hir.Stmt_Id
handler.allocator = checker.allocator
fallback, _ = build_value_source(ctx, &handler, expr.body, success, expr.span)
body = handler[:]
resize(ctx.locals, capture_start)
}
fallback := build_nested_expr(checker, expr.right, locals, global_reads, calls, success, pkg, file)
fallback = coerce_expr(checker, fallback, success, checker.module.exprs[fallback].span)
return add_hir_expr(checker, hir.Expr{
kind=.Catch,
span=expr.span,
type=success,
integer=1 if block_handler else 0,
left=channel,
right=fallback,
target=hir.INVALID_REF,
body=body,
target=hir.local_ref(capture) if capture != hir.INVALID_LOCAL else hir.INVALID_REF,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Range:
@@ -6727,9 +6761,12 @@ build_function :: proc(checker: ^Checker, id: Spec_Id) {
yield_targets = &yield_targets,
}
previous_result := checker.current_result
previous_ctx := checker.current_build_ctx
checker.current_result = spec.result
checker.current_build_ctx = &ctx
block := build_block(&ctx, function.body)
checker.current_result = previous_result
checker.current_build_ctx = previous_ctx
returns := all_paths_return(&checker.module, block)
for block_stmt in block {
append(&body, block_stmt)