better comptime match-statement support
This commit is contained in:
@@ -3657,13 +3657,91 @@ ct_exec_for :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool,
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "comptime for-loop iterable must be a range, array, slice, or pointer-to-array")
|
||||
}
|
||||
|
||||
Ct_Match_Selection :: struct {
|
||||
arm: ast.Stmt_Id,
|
||||
payload: Ct_Value_Id,
|
||||
payload_field: int,
|
||||
payload_type: types.Type,
|
||||
}
|
||||
|
||||
ct_select_match_arm :: proc(
|
||||
state: ^Ct_State,
|
||||
statement: ast.Stmt,
|
||||
subject: Ct_Value_Id,
|
||||
depth: int,
|
||||
) -> (Ct_Match_Selection, bool) {
|
||||
checker := state.checker
|
||||
if subject == INVALID_CT_VALUE || int(subject) >= len(state.values) {
|
||||
return {}, false
|
||||
}
|
||||
subject_value := state.values[subject]
|
||||
for arm_id in statement.body {
|
||||
arm := checker.ast_module.statements[arm_id]
|
||||
if arm.kind != .Match_Arm {
|
||||
continue
|
||||
}
|
||||
matched := len(arm.patterns) == 0
|
||||
selection := Ct_Match_Selection{
|
||||
arm=arm_id,
|
||||
payload=INVALID_CT_VALUE,
|
||||
payload_field=-1,
|
||||
payload_type=types.INVALID,
|
||||
}
|
||||
if !matched {
|
||||
for pattern_id in arm.patterns {
|
||||
pattern := checker.ast_module.exprs[pattern_id]
|
||||
if subject_value.kind == .Struct && types.is_tagged_union(subject_value.type, &checker.module.types) {
|
||||
if pattern.kind != .Enum_Literal {
|
||||
return {}, false
|
||||
}
|
||||
field_index, field, found := find_struct_field(checker, subject_value.type, pattern.name)
|
||||
if !found {
|
||||
return {}, false
|
||||
}
|
||||
if field_index == int(subject_value.active) {
|
||||
matched = true
|
||||
selection.payload_field = field_index
|
||||
selection.payload_type = field.type
|
||||
children := ct_child_slice(state, subject_value)
|
||||
if len(children) > 0 {
|
||||
selection.payload = children[0]
|
||||
}
|
||||
break
|
||||
}
|
||||
} else if pattern.kind == .Range {
|
||||
probe, range_flow, range_ok := ct_eval_expr(state, pattern_id, subject_value.type, depth)
|
||||
if !range_ok || range_flow.kind != .Normal {
|
||||
return {}, false
|
||||
}
|
||||
if ct_range_contains(state, probe, subject) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
probe, pattern_flow, pattern_ok := ct_eval_expr(state, pattern_id, subject_value.type, depth)
|
||||
if !pattern_ok || pattern_flow.kind != .Normal {
|
||||
return {}, false
|
||||
}
|
||||
if ct_values_equal(state, subject, probe) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if matched {
|
||||
return selection, true
|
||||
}
|
||||
}
|
||||
return {}, false
|
||||
}
|
||||
|
||||
ct_exec_match :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool, depth: int) -> (Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
subject, flow, ok := ct_eval_expr(state, statement.expr, types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return flow, ok
|
||||
}
|
||||
subject_value := state.values[subject]
|
||||
wants_pointer := false
|
||||
for arm_id in statement.body {
|
||||
arm := checker.ast_module.statements[arm_id]
|
||||
@@ -3683,69 +3761,28 @@ ct_exec_match :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, statement.span, "comptime match pointer captures require an addressable tagged-union subject")
|
||||
}
|
||||
}
|
||||
for arm_id in statement.body {
|
||||
arm := checker.ast_module.statements[arm_id]
|
||||
if arm.kind != .Match_Arm {
|
||||
continue
|
||||
}
|
||||
matched := len(arm.patterns) == 0
|
||||
payload := INVALID_CT_VALUE
|
||||
payload_field := -1
|
||||
payload_type := types.INVALID
|
||||
if !matched {
|
||||
for pattern_id in arm.patterns {
|
||||
pattern := checker.ast_module.exprs[pattern_id]
|
||||
if subject_value.kind == .Struct && types.is_tagged_union(subject_value.type, &checker.module.types) {
|
||||
if pattern.kind != .Enum_Literal {
|
||||
continue
|
||||
}
|
||||
if field_index, field, found := find_struct_field(checker, subject_value.type, pattern.name); found && field_index == int(subject_value.active) {
|
||||
matched = true
|
||||
payload_field = field_index
|
||||
payload_type = field.type
|
||||
children := ct_child_slice(state, subject_value)
|
||||
if len(children) > 0 {
|
||||
payload = children[0]
|
||||
}
|
||||
break
|
||||
}
|
||||
} else if pattern.kind == .Range {
|
||||
probe, range_flow, range_ok := ct_eval_expr(state, pattern_id, subject_value.type, depth+1)
|
||||
if range_ok && range_flow.kind == .Normal && ct_range_contains(state, probe, subject) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
probe, pattern_flow, pattern_ok := ct_eval_expr(state, pattern_id, subject_value.type, depth+1)
|
||||
if pattern_ok && pattern_flow.kind == .Normal && ct_values_equal(state, subject, probe) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
selection, selected := ct_select_match_arm(state, statement, subject, depth+1)
|
||||
if selected {
|
||||
arm := checker.ast_module.statements[selection.arm]
|
||||
scope_start := len(state.bindings)
|
||||
if len(arm.captures) > 0 && payload != INVALID_CT_VALUE {
|
||||
if len(arm.captures) > 0 && selection.payload != INVALID_CT_VALUE {
|
||||
capture := arm.captures[0]
|
||||
if arm.pointer_capture {
|
||||
if subject_place == INVALID_CT_PLACE || payload_field < 0 || !types.is_valid(payload_type) {
|
||||
if subject_place == INVALID_CT_PLACE || selection.payload_field < 0 || !types.is_valid(selection.payload_type) {
|
||||
ct_pop_bindings(state, scope_start)
|
||||
return ct_flow(.Normal), ct_fail(state, .Not_Comptime, arm.span, "comptime match pointer capture requires a tagged-union payload")
|
||||
}
|
||||
payload_place := ct_extend_place(
|
||||
state, subject_place, Ct_Path_Elem{kind=.Field, index=u32(payload_field)},
|
||||
payload_type, subject_writable,
|
||||
state, subject_place, Ct_Path_Elem{kind=.Field, index=u32(selection.payload_field)},
|
||||
selection.payload_type, subject_writable,
|
||||
)
|
||||
pointer_type := types.pointer(&checker.module.types, payload_type, subject_writable, false)
|
||||
pointer_type := types.pointer(&checker.module.types, selection.payload_type, subject_writable, false)
|
||||
pointer := ct_add_value(state, Ct_Value{kind=.Pointer, type=pointer_type, index=u64(payload_place), active=-1})
|
||||
if capture != checker.sink_symbol {
|
||||
ct_bind_value(state, capture, pointer_type, pointer, false)
|
||||
}
|
||||
} else if capture != checker.sink_symbol {
|
||||
ct_bind_value(state, capture, state.values[payload].type, payload, false)
|
||||
ct_bind_value(state, capture, state.values[selection.payload].type, selection.payload, false)
|
||||
}
|
||||
}
|
||||
if yield_returns && len(arm.body) == 1 && checker.ast_module.statements[arm.body[0]].kind == .Expression {
|
||||
|
||||
Reference in New Issue
Block a user