comptime expandable match statements
This commit is contained in:
@@ -2515,6 +2515,50 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
||||
}
|
||||
return ct_typeinfo_value(state, target, expr.span)
|
||||
}
|
||||
if is_intrinsic_call(checker, expr, "tag") {
|
||||
if len(expr.args) != 1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "tag! expects 1 argument, got %d", len(expr.args))
|
||||
}
|
||||
value_id, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
if value_id == INVALID_CT_VALUE || int(value_id) >= len(state.values) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
value := state.values[value_id]
|
||||
tag_type, tag_ok := tag_result_type(checker, value.type)
|
||||
if !tag_ok || value.kind != .Struct || value.active < 0 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "tag! requires a tagged-union value")
|
||||
}
|
||||
fields := types.fields_for(&checker.module.types, value.type)
|
||||
if int(value.active) >= len(fields) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
member, found := find_enum_member(checker, tag_type, symbol.Id(fields[value.active].name))
|
||||
if !found {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Integer, type=tag_type, integer=member.value}), ct_flow(.Normal), true
|
||||
}
|
||||
if is_intrinsic_call(checker, expr, "tagname") {
|
||||
if len(expr.args) != 1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "tagname! expects 1 argument, got %d", len(expr.args))
|
||||
}
|
||||
value_id, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
if value_id == INVALID_CT_VALUE || int(value_id) >= len(state.values) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
value := state.values[value_id]
|
||||
name, found := enum_member_name_from_value(checker, value.type, value.integer)
|
||||
if value.kind != .Integer || !found {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "tagname! requires a comptime-known enum value")
|
||||
}
|
||||
return ct_reflection_string(state, name), ct_flow(.Normal), true
|
||||
}
|
||||
if builtin := type_builtin_call(checker, expr); builtin != .None {
|
||||
if len(expr.args) != 1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "%s! expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
||||
@@ -3660,6 +3704,7 @@ ct_exec_for :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool,
|
||||
Ct_Match_Selection :: struct {
|
||||
arm: ast.Stmt_Id,
|
||||
payload: Ct_Value_Id,
|
||||
tag: Ct_Value_Id,
|
||||
payload_field: int,
|
||||
payload_type: types.Type,
|
||||
}
|
||||
@@ -3684,9 +3729,40 @@ ct_select_match_arm :: proc(
|
||||
selection := Ct_Match_Selection{
|
||||
arm=arm_id,
|
||||
payload=INVALID_CT_VALUE,
|
||||
tag=INVALID_CT_VALUE,
|
||||
payload_field=-1,
|
||||
payload_type=types.INVALID,
|
||||
}
|
||||
if arm.expand {
|
||||
if subject_value.kind == .Struct && types.is_tagged_union(subject_value.type, &checker.module.types) {
|
||||
fields := types.fields_for(&checker.module.types, subject_value.type)
|
||||
if subject_value.active < 0 || int(subject_value.active) >= len(fields) {
|
||||
return {}, false
|
||||
}
|
||||
field_index := int(subject_value.active)
|
||||
field := fields[field_index]
|
||||
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]
|
||||
} else if types.is_void(field.type) {
|
||||
selection.payload = ct_add_value(state, Ct_Value{kind=.Void, type=types.VOID})
|
||||
}
|
||||
tag_type := types.union_tag_enum(subject_value.type, &checker.module.types)
|
||||
member, found := find_enum_member(checker, tag_type, symbol.Id(field.name))
|
||||
if !found {
|
||||
return {}, false
|
||||
}
|
||||
selection.tag = ct_add_value(state, Ct_Value{kind=.Integer, type=tag_type, integer=member.value})
|
||||
return selection, true
|
||||
}
|
||||
if subject_value.kind == .Integer && types.is_enum(subject_value.type, &checker.module.types) {
|
||||
selection.tag = subject
|
||||
return selection, true
|
||||
}
|
||||
return {}, false
|
||||
}
|
||||
if !matched {
|
||||
for pattern_id in arm.patterns {
|
||||
pattern := checker.ast_module.exprs[pattern_id]
|
||||
@@ -3765,7 +3841,11 @@ ct_exec_match :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool
|
||||
if selected {
|
||||
arm := checker.ast_module.statements[selection.arm]
|
||||
scope_start := len(state.bindings)
|
||||
if len(arm.captures) > 0 && selection.payload != INVALID_CT_VALUE {
|
||||
if arm.expand && types.is_enum(state.values[subject].type, &checker.module.types) {
|
||||
if len(arm.captures) > 0 && arm.captures[0] != checker.sink_symbol {
|
||||
ct_bind_value(state, arm.captures[0], state.values[subject].type, subject, false)
|
||||
}
|
||||
} else if len(arm.captures) > 0 && selection.payload != INVALID_CT_VALUE {
|
||||
capture := arm.captures[0]
|
||||
if arm.pointer_capture {
|
||||
if subject_place == INVALID_CT_PLACE || selection.payload_field < 0 || !types.is_valid(selection.payload_type) {
|
||||
@@ -3785,6 +3865,10 @@ ct_exec_match :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool
|
||||
ct_bind_value(state, capture, state.values[selection.payload].type, selection.payload, false)
|
||||
}
|
||||
}
|
||||
if arm.expand && len(arm.captures) > 1 && arm.captures[1] != checker.sink_symbol &&
|
||||
selection.tag != INVALID_CT_VALUE {
|
||||
ct_bind_value(state, arm.captures[1], state.values[selection.tag].type, selection.tag, false)
|
||||
}
|
||||
if yield_returns && len(arm.body) == 1 && checker.ast_module.statements[arm.body[0]].kind == .Expression {
|
||||
expr_stmt := checker.ast_module.statements[arm.body[0]]
|
||||
value, expr_flow, expr_ok := ct_eval_expr(state, expr_stmt.expr, types.INVALID, depth+1)
|
||||
|
||||
Reference in New Issue
Block a user