conditionally unwrapping while loops
This commit is contained in:
+208
-126
@@ -363,7 +363,7 @@ block_reads_name :: proc(checker: ^Checker, statements: []ast.Stmt_Id, name: sym
|
||||
append(&statement_stack, ..statement.body)
|
||||
append(&statement_stack, ..statement.else_body)
|
||||
case .While:
|
||||
append(&expr_stack, statement.expr)
|
||||
append(&expr_stack, statement.expr, statement.guard)
|
||||
append(&statement_stack, ..statement.body)
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
append(&statement_stack, statement.update)
|
||||
@@ -3800,6 +3800,9 @@ mark_block_imports_used :: proc(checker: ^Checker, statements: []ast.Stmt_Id, fi
|
||||
mark_block_imports_used(checker, statement.else_body, file)
|
||||
case .While:
|
||||
mark_expr_imports_used(checker, statement.expr, file)
|
||||
if statement.guard != ast.INVALID_EXPR {
|
||||
mark_expr_imports_used(checker, statement.guard, file)
|
||||
}
|
||||
mark_block_imports_used(checker, statement.body, file)
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
update := [1]ast.Stmt_Id{statement.update}
|
||||
@@ -6309,12 +6312,40 @@ infer_statements :: proc(
|
||||
}
|
||||
}
|
||||
case .While:
|
||||
_ = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
||||
capture_start := len(locals^)
|
||||
if len(statement.captures) > 0 {
|
||||
operands: [dynamic]ast.Expr_Id
|
||||
operands.allocator = checker.allocator
|
||||
flatten_conditional_unwrap_operands(checker.ast_module, statement.expr, &operands)
|
||||
operand_types := make([]types.Type, len(operands), checker.allocator)
|
||||
for operand, index in operands {
|
||||
operand_types[index] = infer_expr(checker, operand, locals^[:], pkg, file, demanded, local_types)
|
||||
}
|
||||
for capture, index in statement.captures {
|
||||
if capture == checker.sink_symbol {
|
||||
continue
|
||||
}
|
||||
capture_type := types.INVALID
|
||||
if index < len(operand_types) &&
|
||||
types.is_optional(operand_types[index], &checker.module.types) {
|
||||
capture_type = types.child_type(operand_types[index], &checker.module.types)
|
||||
}
|
||||
append(locals, Infer_Local{name=capture, type=capture_type, declared=capture_type, statement=ast.INVALID_STMT})
|
||||
}
|
||||
if statement.guard != ast.INVALID_EXPR {
|
||||
_ = infer_expr(checker, statement.guard, locals^[:], pkg, file, demanded, local_types)
|
||||
}
|
||||
delete(operand_types, checker.allocator)
|
||||
delete(operands)
|
||||
} else {
|
||||
_ = infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded, local_types)
|
||||
}
|
||||
infer_statements(checker, statement.body, locals, local_types, pkg, file, demanded, result, result_hint)
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
update := [1]ast.Stmt_Id{statement.update}
|
||||
infer_statements(checker, update[:], locals, local_types, pkg, file, demanded, result, result_hint)
|
||||
}
|
||||
resize(locals, capture_start)
|
||||
case .For:
|
||||
if statement.expand {
|
||||
bindings, expand_error := expand_field_bindings(checker, statement.expr, statement.name, pkg, file)
|
||||
@@ -11042,6 +11073,122 @@ flatten_expand_iteration :: proc(
|
||||
return .Normal
|
||||
}
|
||||
|
||||
build_conditional_unwrap_header :: proc(
|
||||
ctx: ^Build_Ctx,
|
||||
statement: ast.Stmt,
|
||||
keyword: string,
|
||||
) -> (
|
||||
unwraps: []hir.Conditional_Unwrap,
|
||||
guard: hir.Expr_Id,
|
||||
capture_start: int,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
valid: bool,
|
||||
) {
|
||||
checker := ctx.checker
|
||||
ast_operands: [dynamic]ast.Expr_Id
|
||||
ast_operands.allocator = checker.allocator
|
||||
defer delete(ast_operands)
|
||||
flatten_conditional_unwrap_operands(checker.ast_module, statement.expr, &ast_operands)
|
||||
|
||||
valid = true
|
||||
diagnostic = source.INVALID_DIAGNOSTIC
|
||||
if len(ast_operands) != len(statement.captures) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"'%s' unwrap has %d operands but %d captures",
|
||||
keyword,
|
||||
len(ast_operands),
|
||||
len(statement.captures),
|
||||
)
|
||||
valid = false
|
||||
}
|
||||
|
||||
values := make([]hir.Expr_Id, len(ast_operands), checker.allocator)
|
||||
defer delete(values, checker.allocator)
|
||||
child_types := make([]types.Type, len(ast_operands), checker.allocator)
|
||||
defer delete(child_types, checker.allocator)
|
||||
for operand, index in ast_operands {
|
||||
child_types[index] = types.INVALID
|
||||
value := build_expr(
|
||||
checker, operand, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.INVALID, ctx.pkg, ctx.file,
|
||||
)
|
||||
values[index] = value
|
||||
value_type := checker.module.exprs[value].type
|
||||
if checker.module.exprs[value].kind == .Invalid {
|
||||
valid = false
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = checker.module.exprs[value].diagnostic
|
||||
}
|
||||
} else if !types.is_optional(value_type, &checker.module.types) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
checker.ast_module.exprs[operand].span,
|
||||
"'%s' unwrap requires an optional value (operand %d)",
|
||||
keyword,
|
||||
index + 1,
|
||||
)
|
||||
valid = false
|
||||
} else {
|
||||
child_types[index] = types.child_type(value_type, &checker.module.types)
|
||||
}
|
||||
}
|
||||
|
||||
capture_start = len(ctx.locals^)
|
||||
unwrap_list: [dynamic]hir.Conditional_Unwrap
|
||||
unwrap_list.allocator = checker.allocator
|
||||
for capture, index in statement.captures {
|
||||
child := child_types[index] if index < len(child_types) else types.INVALID
|
||||
local := hir.INVALID_LOCAL
|
||||
if capture != checker.sink_symbol {
|
||||
if _, duplicate := find_build_local(ctx.locals^[capture_start:], capture); duplicate {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"'%s' unwrap captures must have distinct names",
|
||||
keyword,
|
||||
)
|
||||
valid = false
|
||||
} else if id := add_shadow_diagnostic(
|
||||
checker, statement.span, capture, "capture",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:capture_start], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = id
|
||||
valid = false
|
||||
}
|
||||
local = append_build_local(ctx, capture, child, false, statement.span)
|
||||
}
|
||||
if index < len(values) {
|
||||
append(&unwrap_list, hir.Conditional_Unwrap{expr=values[index], local=local})
|
||||
}
|
||||
}
|
||||
|
||||
guard = hir.INVALID_EXPR
|
||||
if statement.guard != ast.INVALID_EXPR {
|
||||
guard = build_expr(
|
||||
checker, statement.guard, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.BOOL, ctx.pkg, ctx.file,
|
||||
)
|
||||
if checker.module.exprs[guard].kind == .Invalid {
|
||||
valid = false
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = checker.module.exprs[guard].diagnostic
|
||||
}
|
||||
} else if !types.is_bool(checker.module.exprs[guard].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[guard].type) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
checker.ast_module.exprs[statement.guard].span,
|
||||
"'%s' unwrap guard must be a bool",
|
||||
keyword,
|
||||
)
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
return unwrap_list[:], guard, capture_start, diagnostic, valid
|
||||
}
|
||||
|
||||
build_block :: proc(
|
||||
ctx: ^Build_Ctx,
|
||||
statements: []ast.Stmt_Id,
|
||||
@@ -11763,101 +11910,8 @@ build_block :: proc(
|
||||
}
|
||||
}
|
||||
if len(statement.captures) > 0 {
|
||||
ast_operands: [dynamic]ast.Expr_Id
|
||||
ast_operands.allocator = checker.allocator
|
||||
flatten_conditional_unwrap_operands(checker.ast_module, statement.expr, &ast_operands)
|
||||
|
||||
valid_unwrap := true
|
||||
diagnostic := source.INVALID_DIAGNOSTIC
|
||||
if len(ast_operands) != len(statement.captures) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"'if' unwrap has %d operands but %d captures",
|
||||
len(ast_operands),
|
||||
len(statement.captures),
|
||||
)
|
||||
valid_unwrap = false
|
||||
}
|
||||
|
||||
values := make([]hir.Expr_Id, len(ast_operands), checker.allocator)
|
||||
child_types := make([]types.Type, len(ast_operands), checker.allocator)
|
||||
for operand, index in ast_operands {
|
||||
child_types[index] = types.INVALID
|
||||
value := build_expr(
|
||||
checker, operand, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.INVALID, ctx.pkg, ctx.file,
|
||||
)
|
||||
values[index] = value
|
||||
value_type := checker.module.exprs[value].type
|
||||
if checker.module.exprs[value].kind == .Invalid {
|
||||
valid_unwrap = false
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = checker.module.exprs[value].diagnostic
|
||||
}
|
||||
} else if !types.is_optional(value_type, &checker.module.types) {
|
||||
diagnostic = source.addf(
|
||||
checker.diagnostics,
|
||||
checker.ast_module.exprs[operand].span,
|
||||
"'if' unwrap requires an optional value (operand %d)",
|
||||
index + 1,
|
||||
)
|
||||
valid_unwrap = false
|
||||
} else {
|
||||
child_types[index] = types.child_type(value_type, &checker.module.types)
|
||||
}
|
||||
}
|
||||
|
||||
capture_start := len(ctx.locals^)
|
||||
unwraps: [dynamic]hir.Conditional_Unwrap
|
||||
unwraps.allocator = checker.allocator
|
||||
for capture, index in statement.captures {
|
||||
child := child_types[index] if index < len(child_types) else types.INVALID
|
||||
local := hir.INVALID_LOCAL
|
||||
if capture != checker.sink_symbol {
|
||||
if _, duplicate := find_build_local(ctx.locals^[capture_start:], capture); duplicate {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
statement.span,
|
||||
"'if' unwrap captures must have distinct names",
|
||||
)
|
||||
valid_unwrap = false
|
||||
} else if id := add_shadow_diagnostic(
|
||||
checker, statement.span, capture, "capture",
|
||||
ctx.pkg, ctx.file, ctx.locals^[:capture_start], ctx.loop_labels^[:], ctx.yield_targets^[:],
|
||||
); id != source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = id
|
||||
valid_unwrap = false
|
||||
}
|
||||
local = append_build_local(ctx, capture, child, false, statement.span)
|
||||
}
|
||||
if index < len(values) {
|
||||
append(&unwraps, hir.Conditional_Unwrap{expr=values[index], local=local})
|
||||
}
|
||||
}
|
||||
|
||||
guard := hir.INVALID_EXPR
|
||||
if statement.guard != ast.INVALID_EXPR {
|
||||
guard = build_expr(
|
||||
checker, statement.guard, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.BOOL, ctx.pkg, ctx.file,
|
||||
)
|
||||
if checker.module.exprs[guard].kind == .Invalid {
|
||||
valid_unwrap = false
|
||||
if diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
diagnostic = checker.module.exprs[guard].diagnostic
|
||||
}
|
||||
} else if !types.is_bool(checker.module.exprs[guard].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[guard].type) {
|
||||
diagnostic = source.add(
|
||||
checker.diagnostics,
|
||||
checker.ast_module.exprs[statement.guard].span,
|
||||
"'if' unwrap guard must be a bool",
|
||||
)
|
||||
valid_unwrap = false
|
||||
}
|
||||
}
|
||||
|
||||
unwraps, guard, capture_start, diagnostic, valid_unwrap :=
|
||||
build_conditional_unwrap_header(ctx, statement, "if")
|
||||
then_body := build_block(ctx, statement.body, capture_start)
|
||||
resize(ctx.locals, capture_start)
|
||||
else_body: []hir.Stmt_Id = nil
|
||||
@@ -11870,7 +11924,7 @@ build_block :: proc(
|
||||
kind=.If,
|
||||
span=statement.span,
|
||||
expr=hir.INVALID_EXPR,
|
||||
unwraps=unwraps[:],
|
||||
unwraps=unwraps,
|
||||
guard=guard,
|
||||
then_body=then_body,
|
||||
else_body=else_body,
|
||||
@@ -11879,7 +11933,7 @@ build_block :: proc(
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
delete(unwraps)
|
||||
delete(unwraps, checker.allocator)
|
||||
delete(then_body, checker.allocator)
|
||||
delete(else_body, checker.allocator)
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
@@ -11893,9 +11947,6 @@ build_block :: proc(
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
delete(values, checker.allocator)
|
||||
delete(child_types, checker.allocator)
|
||||
delete(ast_operands)
|
||||
continue
|
||||
}
|
||||
condition := build_expr(checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls, types.BOOL, ctx.pkg, ctx.file)
|
||||
@@ -11921,16 +11972,27 @@ build_block :: proc(
|
||||
})
|
||||
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[condition].kind == .Invalid
|
||||
case .While:
|
||||
condition := build_expr(
|
||||
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.BOOL, ctx.pkg, ctx.file,
|
||||
)
|
||||
if checker.module.exprs[condition].kind != .Invalid &&
|
||||
!types.is_bool(checker.module.exprs[condition].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[condition].type) {
|
||||
id := source.add(checker.diagnostics, statement.span, "'while' condition must be a bool")
|
||||
condition = invalid_hir_expr(checker, statement.span, id, types.BOOL)
|
||||
ctx.problematic^ = true
|
||||
condition := hir.INVALID_EXPR
|
||||
unwraps: []hir.Conditional_Unwrap = nil
|
||||
guard := hir.INVALID_EXPR
|
||||
capture_start := len(ctx.locals^)
|
||||
unwrap_diagnostic := source.INVALID_DIAGNOSTIC
|
||||
valid_unwrap := true
|
||||
if len(statement.captures) > 0 {
|
||||
unwraps, guard, capture_start, unwrap_diagnostic, valid_unwrap =
|
||||
build_conditional_unwrap_header(ctx, statement, "while")
|
||||
} else {
|
||||
condition = build_expr(
|
||||
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
|
||||
types.BOOL, ctx.pkg, ctx.file,
|
||||
)
|
||||
if checker.module.exprs[condition].kind != .Invalid &&
|
||||
!types.is_bool(checker.module.exprs[condition].type) &&
|
||||
!types.is_noreturn(checker.module.exprs[condition].type) {
|
||||
id := source.add(checker.diagnostics, statement.span, "'while' condition must be a bool")
|
||||
condition = invalid_hir_expr(checker, statement.span, id, types.BOOL)
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
}
|
||||
if id := add_label_shadow_diagnostic(ctx, statement.span, statement.label);
|
||||
id != source.INVALID_DIAGNOSTIC {
|
||||
@@ -11944,32 +12006,52 @@ build_block :: proc(
|
||||
append(ctx.loop_defer_starts, len(ctx.defers^))
|
||||
append(ctx.loop_labels, statement.label)
|
||||
append(ctx.loop_is_loop, true)
|
||||
loop_body := build_block(ctx, statement.body)
|
||||
loop_body := build_block(ctx, statement.body, capture_start)
|
||||
pop(ctx.loop_is_loop)
|
||||
pop(ctx.loop_labels)
|
||||
pop(ctx.loop_defer_starts)
|
||||
update := hir.INVALID_STMT
|
||||
if statement.update != ast.INVALID_STMT {
|
||||
update_ast := [1]ast.Stmt_Id{statement.update}
|
||||
update_body := build_block(ctx, update_ast[:])
|
||||
update_body := build_block(ctx, update_ast[:], capture_start)
|
||||
if len(update_body) > 0 {
|
||||
update = update_body[0]
|
||||
}
|
||||
delete(update_body, checker.allocator)
|
||||
}
|
||||
resize(ctx.locals, capture_start)
|
||||
append(&body, hir.stmt_id(len(checker.module.statements)))
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.While,
|
||||
span=statement.span,
|
||||
label=statement.label,
|
||||
expr=condition,
|
||||
then_body=loop_body,
|
||||
update=update,
|
||||
local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[condition].kind == .Invalid
|
||||
if valid_unwrap {
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.While,
|
||||
span=statement.span,
|
||||
label=statement.label,
|
||||
expr=condition,
|
||||
unwraps=unwraps,
|
||||
guard=guard,
|
||||
then_body=loop_body,
|
||||
update=update,
|
||||
local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
delete(unwraps, checker.allocator)
|
||||
delete(loop_body, checker.allocator)
|
||||
append(&checker.module.statements, hir.Stmt{
|
||||
kind=.Trap,
|
||||
span=statement.span,
|
||||
expr=hir.INVALID_EXPR,
|
||||
guard=hir.INVALID_EXPR,
|
||||
local=hir.INVALID_LOCAL,
|
||||
target=hir.INVALID_EXPR,
|
||||
diagnostic=unwrap_diagnostic,
|
||||
})
|
||||
ctx.problematic^ = true
|
||||
}
|
||||
if condition != hir.INVALID_EXPR {
|
||||
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[condition].kind == .Invalid
|
||||
}
|
||||
case .For:
|
||||
if statement.expand {
|
||||
if statement.pointer_capture {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user