better comptime match-statement support
This commit is contained in:
@@ -8637,39 +8637,23 @@ specialization_match_body :: proc(
|
|||||||
if !ok || flow.kind != .Normal || subject == INVALID_CT_VALUE || int(subject) >= len(state.values) {
|
if !ok || flow.kind != .Normal || subject == INVALID_CT_VALUE || int(subject) >= len(state.values) {
|
||||||
return nil, {}, false, false
|
return nil, {}, false, false
|
||||||
}
|
}
|
||||||
value := state.values[subject]
|
selection, selected := ct_select_match_arm(&state, statement, subject, 0)
|
||||||
if value.kind != .Struct || !types.is_tagged_union(value.type, &checker.module.types) {
|
if !selected {
|
||||||
return nil, {}, false, false
|
return nil, {}, false, false
|
||||||
}
|
}
|
||||||
for arm_id in statement.body {
|
arm := checker.ast_module.statements[selection.arm]
|
||||||
arm := checker.ast_module.statements[arm_id]
|
if arm.pointer_capture {
|
||||||
if arm.kind != .Match_Arm || arm.pointer_capture {
|
return nil, {}, false, false
|
||||||
continue
|
}
|
||||||
|
if len(arm.captures) > 0 {
|
||||||
|
if selection.payload == INVALID_CT_VALUE || types.is_void(selection.payload_type) {
|
||||||
|
return nil, {}, false, false
|
||||||
}
|
}
|
||||||
if len(arm.patterns) == 0 {
|
if arm.captures[0] != checker.sink_symbol {
|
||||||
return arm.body, {}, false, true
|
return arm.body, store_static_binding(checker, &state, selection.payload, arm.captures[0]), true, true
|
||||||
}
|
|
||||||
for pattern_id in arm.patterns {
|
|
||||||
if pattern_id == ast.INVALID_EXPR || int(pattern_id) >= len(checker.ast_module.exprs) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pattern := checker.ast_module.exprs[pattern_id]
|
|
||||||
if pattern.kind != .Enum_Literal {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if index, field, found := find_struct_field(checker, value.type, pattern.name); found && index == int(value.active) {
|
|
||||||
if len(arm.captures) > 0 && arm.captures[0] != checker.sink_symbol && !types.is_void(field.type) {
|
|
||||||
children := ct_child_slice(&state, value)
|
|
||||||
if len(children) == 0 {
|
|
||||||
return nil, {}, false, false
|
|
||||||
}
|
|
||||||
return arm.body, store_static_binding(checker, &state, children[0], arm.captures[0]), true, true
|
|
||||||
}
|
|
||||||
return arm.body, {}, false, true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, {}, false, false
|
return arm.body, {}, false, true
|
||||||
}
|
}
|
||||||
|
|
||||||
Inline_Binding_Error :: enum u8 {
|
Inline_Binding_Error :: enum u8 {
|
||||||
|
|||||||
@@ -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")
|
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) {
|
ct_exec_match :: proc(state: ^Ct_State, statement: ast.Stmt, yield_returns: bool, depth: int) -> (Ct_Flow, bool) {
|
||||||
checker := state.checker
|
checker := state.checker
|
||||||
subject, flow, ok := ct_eval_expr(state, statement.expr, types.INVALID, depth+1)
|
subject, flow, ok := ct_eval_expr(state, statement.expr, types.INVALID, depth+1)
|
||||||
if !ok || flow.kind != .Normal {
|
if !ok || flow.kind != .Normal {
|
||||||
return flow, ok
|
return flow, ok
|
||||||
}
|
}
|
||||||
subject_value := state.values[subject]
|
|
||||||
wants_pointer := false
|
wants_pointer := false
|
||||||
for arm_id in statement.body {
|
for arm_id in statement.body {
|
||||||
arm := checker.ast_module.statements[arm_id]
|
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")
|
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 {
|
selection, selected := ct_select_match_arm(state, statement, subject, depth+1)
|
||||||
arm := checker.ast_module.statements[arm_id]
|
if selected {
|
||||||
if arm.kind != .Match_Arm {
|
arm := checker.ast_module.statements[selection.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
|
|
||||||
}
|
|
||||||
scope_start := len(state.bindings)
|
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]
|
capture := arm.captures[0]
|
||||||
if arm.pointer_capture {
|
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)
|
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")
|
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(
|
payload_place := ct_extend_place(
|
||||||
state, subject_place, Ct_Path_Elem{kind=.Field, index=u32(payload_field)},
|
state, subject_place, Ct_Path_Elem{kind=.Field, index=u32(selection.payload_field)},
|
||||||
payload_type, subject_writable,
|
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})
|
pointer := ct_add_value(state, Ct_Value{kind=.Pointer, type=pointer_type, index=u64(payload_place), active=-1})
|
||||||
if capture != checker.sink_symbol {
|
if capture != checker.sink_symbol {
|
||||||
ct_bind_value(state, capture, pointer_type, pointer, false)
|
ct_bind_value(state, capture, pointer_type, pointer, false)
|
||||||
}
|
}
|
||||||
} else if capture != checker.sink_symbol {
|
} 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 {
|
if yield_returns && len(arm.body) == 1 && checker.ast_module.statements[arm.body[0]].kind == .Expression {
|
||||||
|
|||||||
@@ -2818,6 +2818,53 @@ main func() void {
|
|||||||
testing.expect_value(t, len(diagnostics.items), 0)
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
milestone_37_inline_match_specialization_prunes_unselected_arms :: proc(t: ^testing.T) {
|
||||||
|
text := `Kind :: enum { integer, string, stop }
|
||||||
|
IntToken :: struct { kind Kind, value i8 }
|
||||||
|
StringToken :: struct { kind Kind, value []u8 }
|
||||||
|
StopToken :: struct { kind Kind, value bool }
|
||||||
|
take_i8 func(value i8) void { _ = value }
|
||||||
|
main func() void {
|
||||||
|
inline for {
|
||||||
|
IntToken {kind = .integer, value = 1},
|
||||||
|
StringToken {kind = .string, value = "ok"},
|
||||||
|
StopToken {kind = .stop, value = false},
|
||||||
|
} |token| {
|
||||||
|
match token.kind {
|
||||||
|
.integer: take_i8(token.value)
|
||||||
|
.string: {
|
||||||
|
_ = token.value.len
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
.stop: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inline for {i8(2), "skip", "stop"} |value, index| {
|
||||||
|
match index {
|
||||||
|
0: {}
|
||||||
|
1..=1, 7: continue
|
||||||
|
else: break
|
||||||
|
}
|
||||||
|
take_i8(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
source_file := source.Source{path="test.bro", text=text}
|
||||||
|
diagnostics := source.init_diagnostics(&source_file)
|
||||||
|
defer source.destroy_diagnostics(&diagnostics)
|
||||||
|
symbols := symbol.init_table()
|
||||||
|
defer symbol.destroy_table(&symbols)
|
||||||
|
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||||
|
defer delete(stream.items)
|
||||||
|
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||||
|
defer ast.destroy_module(&ast_module)
|
||||||
|
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||||
|
defer hir.destroy_module(&hir_module)
|
||||||
|
|
||||||
|
testing.expect_value(t, len(diagnostics.items), 0)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
milestone_33_rejects_an_incompatible_runtime_write_declaration :: proc(t: ^testing.T) {
|
milestone_33_rejects_an_incompatible_runtime_write_declaration :: proc(t: ^testing.T) {
|
||||||
text := `write c_func(_ c_int, _ c_int, _ c_ulong) c_long
|
text := `write c_func(_ c_int, _ c_int, _ c_ulong) c_long
|
||||||
|
|||||||
+12
-40
@@ -355,48 +355,20 @@ hide write_default func(writer Writer, $T type, value T) void ! WriteError {
|
|||||||
|
|
||||||
print func(writer Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
print func(writer Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
||||||
inline for parse_format(format.len, format, Args) |token| {
|
inline for parse_format(format.len, format, Args) |token| {
|
||||||
if (token.kind == .unused) {
|
match token.kind {
|
||||||
break
|
.unused: break
|
||||||
|
.literal: try write_all(writer, format[token.start..token.end])
|
||||||
|
.string: try write_all(writer, field!(args, token.field))
|
||||||
|
.default: try write_default(writer, field!(args, token.field))
|
||||||
|
.decimal: try write_decimal(writer, field!(args, token.field))
|
||||||
|
.binary: try write_integer(writer, field!(args, token.field), 2, false)
|
||||||
|
.octal: try write_integer(writer, field!(args, token.field), 8, false)
|
||||||
|
.hex_lower: try write_integer(writer, field!(args, token.field), 16, false)
|
||||||
|
.hex_upper: try write_integer(writer, field!(args, token.field), 16, true)
|
||||||
|
.character: try write_character(writer, field!(args, token.field))
|
||||||
|
else: try write_float(writer, field!(args, token.field), true)
|
||||||
}
|
}
|
||||||
if (token.kind == .literal) {
|
|
||||||
try write_all(writer, format[token.start..token.end])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .string) {
|
|
||||||
try write_all(writer, field!(args, token.field))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .default) {
|
|
||||||
try write_default(writer, field!(args, token.field))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .decimal) {
|
|
||||||
try write_decimal(writer, field!(args, token.field))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .binary) {
|
|
||||||
try write_integer(writer, field!(args, token.field), 2, false)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .octal) {
|
|
||||||
try write_integer(writer, field!(args, token.field), 8, false)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .hex_lower) {
|
|
||||||
try write_integer(writer, field!(args, token.field), 16, false)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .hex_upper) {
|
|
||||||
try write_integer(writer, field!(args, token.field), 16, true)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (token.kind == .character) {
|
|
||||||
try write_character(writer, field!(args, token.field))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
try write_float(writer, field!(args, token.field), true)
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
hide system_read func(_ ?*mut anyopaque, stream ReadStream, buffer []mut u8) usize ! ReadError {
|
||||||
|
|||||||
Reference in New Issue
Block a user