condtional multi-unwrap and guard clauses

This commit is contained in:
2026-06-22 20:37:37 +02:00
parent 27f42dd253
commit 663f4dc658
9 changed files with 567 additions and 96 deletions
+101 -41
View File
@@ -669,57 +669,117 @@ lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
diagnostic=statement.diagnostic,
})
case .If:
// A conditional unwrap (`if opt |v| { ... }`) carries the binding local id in
// `statement.local`; `expr` is then the optional, not a bool condition. Test it
// for presence, and inside the then-block bind the unwrapped value to the local.
is_unwrap := statement.local != hir.INVALID_LOCAL
opt := ir.INVALID_INSTRUCTION
cond: ir.Instruction_Id
if is_unwrap {
opt = lower_expr(state, statement.expr)
cond = append_instruction(state, ir.Instruction{
op=.Optional_Is_Some, span=statement.span, type=types.BOOL,
target=ir.INVALID_REF, a=opt, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
cond = lower_expr(state, statement.expr)
}
has_else := statement.else_body != nil
then_lbl := fresh_label(state)
else_lbl := fresh_label(state) if has_else else then_lbl
merge_lbl := fresh_label(state)
false_target := else_lbl if has_else else merge_lbl
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=cond, integer=then_lbl, target=ir.Ref(u32(false_target)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
if len(statement.unwraps) > 0 {
// Evaluate each optional exactly once, entering the next operand only
// after the previous one is present. Capture storage is initialized in
// these success blocks so the optional guard can use every binding.
for unwrap in statement.unwraps {
optional := lower_expr(state, unwrap.expr)
present := append_instruction(state, ir.Instruction{
op=.Optional_Is_Some,
span=statement.span,
type=types.BOOL,
target=ir.INVALID_REF,
a=optional,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
success_lbl := fresh_label(state)
append_instruction(state, ir.Instruction{
op=.Cond_Br,
span=statement.span,
type=types.VOID,
a=present,
integer=success_lbl,
target=ir.Ref(u32(false_target)),
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label,
span=statement.span,
type=types.VOID,
integer=success_lbl,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
if unwrap.local != hir.INVALID_LOCAL && int(unwrap.local) < len(state.func_locals) {
local := state.func_locals[unwrap.local]
slot := append_instruction(state, ir.Instruction{
op=.Alloca,
span=statement.span,
type=local.type,
target=ir.local_ref(ir.Local_Id(unwrap.local)),
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_slots[unwrap.local] = slot
inner := append_instruction(state, ir.Instruction{
op=.Optional_Value,
span=statement.span,
type=local.type,
target=ir.INVALID_REF,
a=optional,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store,
span=statement.span,
type=local.type,
target=ir.INVALID_REF,
a=slot,
b=inner,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
}
if statement.guard != hir.INVALID_EXPR {
guard := lower_expr(state, statement.guard)
append_instruction(state, ir.Instruction{
op=.Cond_Br,
span=statement.span,
type=types.VOID,
a=guard,
integer=then_lbl,
target=ir.Ref(u32(false_target)),
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
append_instruction(state, ir.Instruction{
op=.Br,
span=statement.span,
type=types.VOID,
integer=then_lbl,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
} else {
cond := lower_expr(state, statement.expr)
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=cond, integer=then_lbl, target=ir.Ref(u32(false_target)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
}
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=then_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
if is_unwrap && int(statement.local) < len(state.func_locals) {
local := state.func_locals[statement.local]
slot := append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=local.type,
target=ir.local_ref(ir.Local_Id(statement.local)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_slots[statement.local] = slot
inner := append_instruction(state, ir.Instruction{
op=.Optional_Value, span=statement.span, type=local.type,
target=ir.INVALID_REF, a=opt, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=local.type,
target=ir.INVALID_REF, a=slot, b=inner,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
lower_statements(state, statement.then_body)
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=merge_lbl,