conditionally unwrapping while loops

This commit is contained in:
2026-08-11 18:33:54 +02:00
parent ba6052eef3
commit fa53ca2219
8 changed files with 486 additions and 307 deletions
+96 -51
View File
@@ -5258,6 +5258,70 @@ ct_exec_assignment :: proc(state: ^Ct_State, statement: ast.Stmt, depth: int) ->
return ct_flow(.Normal), true
}
ct_eval_conditional_unwrap :: proc(
state: ^Ct_State,
statement: ast.Stmt,
keyword: string,
depth: int,
) -> (matched: bool, scope_start: int, flow: Ct_Flow, ok: bool) {
checker := state.checker
operands: [dynamic]ast.Expr_Id
operands.allocator = checker.allocator
defer delete(operands)
flatten_conditional_unwrap_operands(checker.ast_module, statement.expr, &operands)
if len(operands) != len(statement.captures) {
return false, len(state.bindings), ct_flow(.Normal), ct_failf(
state, .Not_Comptime, statement.span,
"'%s' unwrap capture count mismatch",
keyword,
)
}
scope_start = len(state.bindings)
matched = true
for operand, index in operands {
value, operand_flow, operand_ok := ct_eval_expr(state, operand, types.INVALID, depth+1)
if !operand_ok || operand_flow.kind != .Normal {
ct_pop_bindings(state, scope_start)
return false, scope_start, operand_flow, operand_ok
}
v := state.values[value]
if v.kind == .Null {
matched = false
break
}
if v.kind != .Optional_Some {
ct_pop_bindings(state, scope_start)
return false, scope_start, ct_flow(.Normal), ct_failf(
state, .Not_Comptime, statement.span,
"'%s' unwrap requires an optional value",
keyword,
)
}
children := ct_child_slice(state, v)
if len(children) > 0 && statement.captures[index] != checker.sink_symbol {
ct_bind_value(state, statement.captures[index], state.values[children[0]].type, children[0], false)
}
}
if matched && statement.guard != ast.INVALID_EXPR {
guard, guard_flow, guard_ok := ct_eval_expr(state, statement.guard, types.BOOL, depth+1)
if !guard_ok || guard_flow.kind != .Normal {
ct_pop_bindings(state, scope_start)
return false, scope_start, guard_flow, guard_ok
}
guard_value, bool_ok := ct_bool_value(state, guard)
if !bool_ok {
ct_pop_bindings(state, scope_start)
return false, scope_start, ct_flow(.Normal), ct_failf(
state, .Not_Comptime, statement.span,
"'%s' unwrap guard must be a bool",
keyword,
)
}
matched = guard_value
}
return matched, scope_start, ct_flow(.Normal), true
}
ct_exec_if :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool, depth: int) -> (Ct_Flow, bool) {
checker := state.checker
if yield_returns && statement.else_body == nil {
@@ -5284,47 +5348,9 @@ ct_exec_if :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool, d
}
return ct_exec_statements(state, body, false, depth+1)
}
operands: [dynamic]ast.Expr_Id
operands.allocator = checker.allocator
defer delete(operands)
flatten_conditional_unwrap_operands(checker.ast_module, statement.expr, &operands)
if len(operands) != len(statement.captures) {
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'if' unwrap capture count mismatch")
}
scope_start := len(state.bindings)
matched := true
for operand, index in operands {
value, flow, ok := ct_eval_expr(state, operand, types.INVALID, depth+1)
if !ok || flow.kind != .Normal {
ct_pop_bindings(state, scope_start)
return flow, ok
}
v := state.values[value]
if v.kind == .Null {
matched = false
break
}
if v.kind != .Optional_Some {
ct_pop_bindings(state, scope_start)
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'if' unwrap requires an optional value")
}
children := ct_child_slice(state, v)
if len(children) > 0 && statement.captures[index] != checker.sink_symbol {
ct_bind_value(state, statement.captures[index], state.values[children[0]].type, children[0], false)
}
}
if matched && statement.guard != ast.INVALID_EXPR {
guard, flow, ok := ct_eval_expr(state, statement.guard, types.BOOL, depth+1)
if !ok || flow.kind != .Normal {
ct_pop_bindings(state, scope_start)
return flow, ok
}
guard_value, guard_ok := ct_bool_value(state, guard)
if !guard_ok {
ct_pop_bindings(state, scope_start)
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'if' unwrap guard must be a bool")
}
matched = guard_value
matched, scope_start, unwrap_flow, unwrap_ok := ct_eval_conditional_unwrap(state, statement, "if", depth)
if !unwrap_ok || unwrap_flow.kind != .Normal {
return unwrap_flow, unwrap_ok
}
body := statement.body if matched else statement.else_body
flow := ct_flow(.Normal)
@@ -5357,34 +5383,53 @@ ct_exec_while :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool
if !ct_step(state, statement.span) {
return ct_flow(.Normal), false
}
condition, flow, ok := ct_eval_expr(state, statement.expr, types.BOOL, depth+1)
if !ok || flow.kind != .Normal {
return flow, ok
}
value, bool_ok := ct_bool_value(state, condition)
if !bool_ok {
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'while' condition must be a bool")
}
if !value {
return ct_flow(.Normal), true
scope_start := len(state.bindings)
if len(statement.captures) > 0 {
matched, unwrap_scope_start, flow, ok :=
ct_eval_conditional_unwrap(state, statement, "while", depth)
scope_start = unwrap_scope_start
if !ok || flow.kind != .Normal {
return flow, ok
}
if !matched {
ct_pop_bindings(state, scope_start)
return ct_flow(.Normal), true
}
} else {
condition, flow, ok := ct_eval_expr(state, statement.expr, types.BOOL, depth+1)
if !ok || flow.kind != .Normal {
return flow, ok
}
value, bool_ok := ct_bool_value(state, condition)
if !bool_ok {
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "'while' condition must be a bool")
}
if !value {
return ct_flow(.Normal), true
}
}
body_flow, body_ok := ct_exec_statements(state, statement.body, yield_returns, depth+1)
if !body_ok {
ct_pop_bindings(state, scope_start)
return body_flow, false
}
if ct_loop_consumes_flow(body_flow, statement.label, false) {
ct_pop_bindings(state, scope_start)
return ct_flow(.Normal), true
}
if body_flow.kind != .Normal && !ct_loop_consumes_flow(body_flow, statement.label, true) {
ct_pop_bindings(state, scope_start)
return body_flow, true
}
if statement.update != ast.INVALID_STMT {
update := [1]ast.Stmt_Id{statement.update}
update_flow, update_ok := ct_exec_statements(state, update[:], false, depth+1)
if !update_ok || update_flow.kind != .Normal {
ct_pop_bindings(state, scope_start)
return update_flow, update_ok
}
}
ct_pop_bindings(state, scope_start)
}
}