bug fixes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user