condtional multi-unwrap and guard clauses
This commit is contained in:
+101
-41
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user