fallible ergonomics
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user