feat: expand nominal inline match arms

This commit is contained in:
2026-10-28 12:39:28 +01:00
parent dc7b2e40e2
commit 5cf9e15661
13 changed files with 908 additions and 49 deletions
+296
View File
@@ -11,6 +11,290 @@ HONEY = ROOT / "build/honey"
# Explicit expectations guard against frontend bugs shared by both execution paths.
CASES = {
"inline_match_runtime_enum_static_capture_expression_and_single_subject": (
"""
Mode :: enum(i16) { handled = -7, first = 3, second = 8, answer = 42, zero = 90 }
selected proc(value Mode, calls @mut i16) Mode {
calls^ = calls^ + 1
return value
}
remaining proc($tag Mode) i16 {
if (tag == .answer) { return 42 }
else if (tag == .zero) { return 0 }
else { return false }
}
read proc(value Mode, calls @mut i16) i16 {
return match selected(value, calls) {
.handled: 0,
.first, .second: 0,
inline |tag|: remaining(tag),
}
}
main proc() [2]i16 {
calls i16 := 0
total :: (
read(.handled, &calls) + read(.first, &calls) + read(.second, &calls)
+ read(.answer, &calls) + read(.zero, &calls)
)
return [total, calls]
}
""", b"[42, 5]\n", None,
),
"inline_match_generic_heterogeneous_payloads_static_tags_and_nested_selection": (
"""
Tag :: enum(i16) { handled = -7, first = 3, second = 8, number = 42, record = 90, text = 100, empty = 120 }
Pair :: struct { values [2]i16 }
Choice :: union(Tag) { text string, empty void, record Pair, number i8, handled bool, second bool, first bool }
narrow proc($tag Tag, value i8) i16 {
if (tag == .number) return i16(value) else return false
}
pair proc(value Pair) i16 { return value.values[0] + value.values[1] }
read proc($T type, input T) i16 {
return match input {
.handled: 0,
.first, .second: 0,
inline |payload, tag|: match tag {
.number: narrow(tag, payload),
.record: pair(payload),
.text: i16(payload.len),
.empty: 1,
else: false,
},
}
}
nested proc(input Choice) i16 {
match input {
.handled, .first, .second: return 0,
inline |payload, tag|: {
match tag {
inline |selected|: {
if (selected == .number) { return narrow(tag, payload) }
else if (selected == .record) { return pair(payload) }
else if (selected == .text) { return i16(payload.len) }
else {
payload
return 1
}
},
}
},
}
}
main proc() i16 {
if (read(Choice, .handled{false}) + read(Choice, .first{true}) + read(Choice, .second{false}) != 0) return -1
if (nested(.number{19}) + nested(.record{values = [9, 11]}) + nested(.text{"ok"}) + nested(.empty) != 42) return -2
return (
read(Choice, .number{19}) + read(Choice, .record{values = [9, 11]})
+ read(Choice, .text{"ok"}) + read(Choice, .empty)
)
}
""", b"42\n", None,
),
"inline_match_union_subject_once_and_payload_copy_isolation_without_tag": (
"""
Pair :: struct { values [2]mut i16 }
Choice :: union(enum) { record Pair, other Pair }
exact proc(value Pair) i16 { return value.values[0] + value.values[1] }
selected proc(input @Choice, calls @mut i16) Choice {
calls^ = calls^ + 1
return input^
}
main proc() i16 {
input Choice := .record{values = [19, 23]}
calls i16 := 0
match selected(&input, &calls) {
inline |payload|: {
input.record.values[0] = 0
copied := payload
copied.values[1] = 0
if (calls != 1 or input.record.values[0] != 0 or copied.values[1] != 0) return -1
return exact(payload)
},
}
}
""", b"42\n", None,
),
"inline_match_return_break_continue_run_arm_cleanup": (
"""
Mode :: enum { skip, add, stop }
finish proc(input Mode, cleanups @mut i16) i16 {
match input {
.skip, .stop: return 0,
inline |tag|: {
defer cleanups^ = cleanups^ + 1
if (tag == .add) return 19 else return false
},
}
}
main proc() i16 {
total i16 := 0
cleanups i16 := 0
for [Mode.skip, .add, .stop, .add] |input| {
match input {
inline |tag|: {
defer cleanups = cleanups + 1
if (tag == .skip) { continue }
else if (tag == .stop) { break }
else { total = total + 19 }
},
}
}
result :: finish(.add, &cleanups)
return total + result + cleanups
}
""", b"42\n", None,
),
"inline_match_rejects_scalar_subject": (
"main proc() i16 { return match i16(42) { inline |value|: value } }",
b"", b"inline match arm requires an enum or tagged union",
),
"inline_match_rejects_untagged_union_subject": (
"Choice :: union { number i16 }\nmain proc() i16 { input Choice :: { number = 42 }\nreturn match input { inline |value|: value } }",
b"", b"inline match arm requires an enum or tagged union",
),
"inline_match_rejects_second_enum_capture": (
"Mode :: enum { answer }\nmain proc() i16 { return match Mode.answer { inline |value, tag|: 42 } }",
b"", b"inline match arm requires one enum capture",
),
"inline_match_rejects_redundant_remaining_arm": (
"Mode :: enum { first, second }\nmain proc() i16 { return match Mode.first { .first, .second: 42, inline |_|: 0 } }",
b"", b"match arm is unreachable",
),
"inline_match_rejects_following_arm": (
"Mode :: enum { first, second }\nmain proc() i16 { return match Mode.first { inline |_|: 42, .second: 0 } }",
b"", b"match arm is unreachable",
),
"inline_match_rejects_arm_after_else": (
"Mode :: enum { first, second }\nmain proc() i16 { return match Mode.first { else: 42, inline |_|: 0 } }",
b"", b"match arm is unreachable",
),
"inline_match_checks_preceding_duplicate_patterns": (
"Mode :: enum { first, second }\nmain proc() i16 { return match Mode.first { .first: 42, .first: 0, inline |_|: 0 } }",
b"", b"duplicate or overlapping match pattern",
),
"inline_match_rejects_duplicate_capture_names": (
"Choice :: union(enum) { number i8 }\nmain proc() i16 { input Choice :: .number{42}\nreturn match input { inline |value, value|: 42 } }",
b"", b"duplicate name or forbidden shadowing",
),
"inline_match_rejects_shadowed_enum_capture": (
"Mode :: enum { answer }\nmain proc() i16 { value :: 42\nreturn match Mode.answer { inline |value|: 42 } }",
b"", b"duplicate name or forbidden shadowing",
),
"inline_match_rejects_shadowed_tag_capture": (
"Choice :: union(enum) { number i8 }\nmain proc() i16 { tag :: 42\ninput Choice :: .number{42}\nreturn match input { inline |value, tag|: 42 } }",
b"", b"duplicate name or forbidden shadowing",
),
"inline_match_enum_capture_is_immutable": (
"Mode :: enum { first, second }\nmain proc() i16 { match Mode.first { inline |value|: value = .second }\nreturn 42 }",
b"", b"assignment requires a mutable local binding",
),
"inline_match_payload_capture_is_immutable": (
"Choice :: union(enum) { number i8 }\nmain proc() i16 { input Choice :: .number{42}\nmatch input { inline |value|: value = 0 }\nreturn 42 }",
b"", b"assignment requires a mutable local binding",
),
"inline_match_tag_capture_is_immutable": (
"Choice :: union(enum) { number i8, empty void }\nmain proc() i16 { input Choice :: .number{42}\nmatch input { inline |value, tag|: tag = .empty }\nreturn 42 }",
b"", b"assignment requires a mutable local binding",
),
"inline_match_payload_preserves_narrow_type": (
"Choice :: union(enum) { number i8 }\nread proc(input Choice) i16 { match input { inline |value|: return value } }\nmain proc() i16 { return read(.number{42}) }",
b"", b"return does not match",
),
"inline_match_payload_is_not_a_comptime_binding": (
"Choice :: union(enum) { number i8 }\nknown proc($value i8) i16 { return i16(value) }\nread proc(input Choice) i16 { return match input { inline |value|: known(value) } }\nmain proc() i16 { return read(.number{42}) }",
b"", b"compile-time",
),
"inline_match_resolves_unselected_bodies": (
"Mode :: enum { first, second }\nmain proc() i16 { return match Mode.first { inline |tag|: match tag { .first: 42, .second: missing } } }",
b"42\n", b"unknown name",
),
"inline_match_rejects_pointer_capture_syntax": (
"Choice :: union(enum) { number i8 }\nmain proc() i16 { input Choice :: .number{42}\nreturn match input { inline |*value|: 42 } }",
b"", b"unexpected token",
),
"inline_match_nested_optional_loop_and_ordinary_match_captures": (
"""
Mode :: enum { skip, answer }
Choice :: union(enum) { mode Mode }
score proc(value Mode) i16 {
return match value { inline |tag|: match tag { .skip: 0, .answer: 14 } }
}
read proc(input ?Mode, choice Choice) i16 {
total i16 := 0
if input |value| {
total = match value { inline |tag|: score(tag) }
}
pending ?Mode := input
while pending |value| {
total = total + match value { inline |tag|: score(tag) }
pending = null
}
match choice {
.mode |value|: {
total = total + match value { inline |tag|: score(tag) }
}
}
return total
}
main proc() i16 { return read(.answer, .mode{.answer}) + read(null, .mode{.skip}) }
""", b"42\n", None,
),
"inline_match_type_producer_selects_generated_storage": (
"""
Mode :: enum { small, wide }
Storage proc(input Mode) type {
match input { inline |tag|: {
if (tag == .small) { return struct { value i8 = 19 } }
else { return struct { value i16 = 23 } }
} }
}
main proc() i16 {
small Storage(.small) :: {}
wide Storage(.wide) :: {}
return i16(small.value) + wide.value
}
""", b"42\n", None,
),
"inline_match_auto_tag_nested_expansion_infers_exact_payload_generics": (
"""
Choice :: union(enum) { number i8, empty void }
identity proc($T type, value T) T { return value }
read proc(input Choice) i16 {
match input { inline |payload, tag|: match tag {
inline |value|: {
if (value == .number) { return i16(identity(payload)) }
else {
payload
return 2
}
}
} }
}
main proc() i16 { return read(.number{40}) + read(.empty) }
""", b"42\n", None,
),
"inline_match_type_producer_updates_outer_type_and_value_locals": (
"""
Choice :: union(enum) { number u8, empty void }
Storage proc(input Choice) type {
length usize := 1
Element := i8
for [input] |item| {
match item { inline |payload, tag|: {
if (tag == .number) {
length = usize(payload)
Element = i16
} else { length = 2 }
} }
}
return struct { values [length]Element }
}
main proc() i16 {
value Storage(.number{2}) :: { values = [19, 23] }
return value.values[0] + value.values[1]
}
""", b"42\n", None,
),
"inline_for_heterogeneous_tuple_exact_captures_and_nested_values": (
"""
Choice :: union(enum) { number i8, empty void }
@@ -6979,6 +7263,18 @@ def check_dependency_provenance(temporary, executable):
return 42
}
""", 5, 16, False),
("""
Choice :: union(enum) { number int, empty void }
fail proc() int {
input Choice :: .number{9223372036854775807}
return match input {
inline |payload, tag|: match tag {
.number: payload + 1,
.empty: 0,
},
}
}
""", 8, 22, False),
("""
fail proc() int {
a :: [42]