feat: execute tagged-union matches in type procedures
Preserve resolver failures on synthetic selector captures before elaborating payloads; selector bodies omit the original capture uses. Verified bro build, bro test (250 passed), and tests/native.py (536 interpreter/native programs at -O0 and -O2, allocator, LLVM and CLI checks).
This commit is contained in:
@@ -102,7 +102,9 @@ type-producing bodies support mutable type and value locals, direct binding assi
|
|||||||
|
|
||||||
type-producing bodies also execute scalar/enum `match` statements and type-valued match expressions in initializers, assignments and returns, such as `return match wide { true: i16, false: i8 }`. selected expression arms may themselves be matches. an ordinary checked IR match returns the selected arm index, preserving subject/pattern typing, range boundaries, overlap, unreachable-arm and exhaustiveness diagnostics without checking unexecuted bodies. statement arms retain scoped locals, assignments, early returns and enclosing-loop break/continue; expression selection shares the type-control step bound.
|
type-producing bodies also execute scalar/enum `match` statements and type-valued match expressions in initializers, assignments and returns, such as `return match wide { true: i16, false: i8 }`. selected expression arms may themselves be matches. an ordinary checked IR match returns the selected arm index, preserving subject/pattern typing, range boundaries, overlap, unreachable-arm and exhaustiveness diagnostics without checking unexecuted bodies. statement arms retain scoped locals, assignments, early returns and enclosing-loop break/continue; expression selection shares the type-control step bound.
|
||||||
|
|
||||||
matches also select nested types, array lengths, generic arguments, callback signatures and field defaults in type-producing bodies. for example, `return struct { values [2](match wide { true: i16, false: i8 }) }` generates concrete storage without elaborating the unselected arm. snapshots preserve loop-local type/value bindings, selected literals retain their spelling and source provenance, and nominal results are cached before their recursive fields are expanded. deferred type selection shares the producer's execution bound; failed elaboration retains its original diagnostic without leaving unchecked generated types behind. tagged-union payload captures, general type-valued storage and broader generic-body match specialization remain open.
|
matches also select nested types, array lengths, generic arguments, callback signatures and field defaults in type-producing bodies. for example, `return struct { values [2](match wide { true: i16, false: i8 }) }` generates concrete storage without elaborating the unselected arm. snapshots preserve loop-local type/value bindings, selected literals retain their spelling and source provenance, and nominal results are cached before their recursive fields are expanded. deferred type selection shares the producer's execution bound; failed elaboration retains its original diagnostic without leaving unchecked generated types behind. general type-valued storage and broader generic-body match specialization remain open.
|
||||||
|
|
||||||
|
tagged-union matches in type-producing bodies support immutable by-value payload captures in statements, expressions and nested type/storage positions. for example, `match choice { .count |length|: return [length]i16, .empty: return bool }` uses a captured `usize` to generate storage. grouped variants require identical nonvoid payload types; invalid captures in unselected arms are still diagnosed. selected payloads become owned typed constants, preserving narrow integers, nested aggregates and strings across generated lengths/defaults, loop snapshots and repeatable compilation. pointer/slice/callback-bearing active payloads retain the constant-materialization escape boundary below.
|
||||||
|
|
||||||
ordinary procedures may compute integer, boolean, float and string compile-time arguments and return nested records/tuples, fixed arrays, tagged unions and explicit fallible results from constant inputs, such as `Fixed(i16, length(2))`. calls reuse the ordinary checker, typed IR and interpreter, preserving local type inference, checked arithmetic, mutable locals, range/while loops, break/continue and recursive or specialized helper calls. aggregate and fallible arguments receive their declared context, including dependent `[N]T` parameters; cached specializations compare normalized values, not literal spelling or evaluation identity. returned constants become owned source literals before temporary interpreter storage is released, preserving by-value copies, exact integer types, string bytes, enum/optional fields and IEEE float bits. tagged unions retain their actual discriminant and only the active payload, including nested aggregates and void/zero-cell variants. fallible constants retain success/error identity, composed or payload-bearing errors and void success; `catch` can consume them in generated lengths, defaults and runtime expressions.
|
ordinary procedures may compute integer, boolean, float and string compile-time arguments and return nested records/tuples, fixed arrays, tagged unions and explicit fallible results from constant inputs, such as `Fixed(i16, length(2))`. calls reuse the ordinary checker, typed IR and interpreter, preserving local type inference, checked arithmetic, mutable locals, range/while loops, break/continue and recursive or specialized helper calls. aggregate and fallible arguments receive their declared context, including dependent `[N]T` parameters; cached specializations compare normalized values, not literal spelling or evaluation identity. returned constants become owned source literals before temporary interpreter storage is released, preserving by-value copies, exact integer types, string bytes, enum/optional fields and IEEE float bits. tagged unions retain their actual discriminant and only the active payload, including nested aggregates and void/zero-cell variants. fallible constants retain success/error identity, composed or payload-bearing errors and void success; `catch` can consume them in generated lengths, defaults and runtime expressions.
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ explicit `$()` expressions use the same constant evaluation path. compile-time
|
|||||||
`if` selection and type comparisons specialize only the selected body.
|
`if` selection and type comparisons specialize only the selected body.
|
||||||
type-producing procedures also accept ordinary
|
type-producing procedures also accept ordinary
|
||||||
constant inputs, cached and erased like explicit compile-time arguments. mutable
|
constant inputs, cached and erased like explicit compile-time arguments. mutable
|
||||||
type/value locals, direct assignments, branches, scalar/enum matching and bounded
|
type/value locals, direct assignments, branches, scalar/enum/tagged-union matching
|
||||||
integer-range/while loops now generate types and storage. matches select nested
|
and bounded integer-range/while loops now generate types and storage. payload
|
||||||
constructors, lengths, signatures and defaults; general type-valued execution,
|
captures and matches select nested constructors, lengths, signatures and defaults;
|
||||||
reflection and generic library acceptance remain open.
|
general type-valued execution, reflection and generic library acceptance remain open.
|
||||||
the driver retains frontend storage and source provenance through
|
the driver retains frontend storage and source provenance through
|
||||||
`@source/compilation`, with compilation-wide AST node identities.
|
`@source/compilation`, with compilation-wide AST node identities.
|
||||||
file commands retain single-file behavior; directory commands compile the
|
file commands retain single-file behavior; directory commands compile the
|
||||||
@@ -71,9 +71,10 @@ test discovery and FFI remain outside these completed slices.
|
|||||||
- [x] conditional specialization checkpoint: explicit compile-time `if` guards and side-effect-free constant guards in concrete generic procedures select a body before specialization/checking. type equality retains aliases, nominal identity and structural signatures; composed error-set identity/cache keys ignore ordering, grouping and repetition. guard checking, selected-body diagnostics, scoped cleanup, runtime guards and repeatable compilation work through interpreter/native execution; both arms remain parsed and name-resolved.
|
- [x] conditional specialization checkpoint: explicit compile-time `if` guards and side-effect-free constant guards in concrete generic procedures select a body before specialization/checking. type equality retains aliases, nominal identity and structural signatures; composed error-set identity/cache keys ignore ordering, grouping and repetition. guard checking, selected-body diagnostics, scoped cleanup, runtime guards and repeatable compilation work through interpreter/native execution; both arms remain parsed and name-resolved.
|
||||||
- [x] constant-input type-procedure checkpoint: type-producing procedures accept ordinary parameters alongside `$` parameters, require every argument explicitly and evaluate/cache/erase all inputs. generated record/array storage, nominal cache identity, nested calls, dependent contextual aggregate inputs, exact erased-argument checking, source diagnostics, imported producers and repeatable compilation work through interpreter/native execution.
|
- [x] constant-input type-procedure checkpoint: type-producing procedures accept ordinary parameters alongside `$` parameters, require every argument explicitly and evaluate/cache/erase all inputs. generated record/array storage, nominal cache identity, nested calls, dependent contextual aggregate inputs, exact erased-argument checking, source diagnostics, imported producers and repeatable compilation work through interpreter/native execution.
|
||||||
- [x] mutable type-execution checkpoint: type-producing bodies execute mutable type/value locals, direct binding assignments, scoped blocks, if/else and early returns, integer-range/while loops, break/continue and nested/recursive producers. generated nested types, scalar-computed lengths/defaults, nominal cache identity, exact value types, overflow/guard/assignment diagnostics, imported producers and repeatable compilation work through interpreter/native execution. type-control execution shares a 4096-step bound across nested producers; scalar operations reuse checked IR execution. field/index updates, optional captures, cleanup and type-valued aggregate storage in these bodies remain open.
|
- [x] mutable type-execution checkpoint: type-producing bodies execute mutable type/value locals, direct binding assignments, scoped blocks, if/else and early returns, integer-range/while loops, break/continue and nested/recursive producers. generated nested types, scalar-computed lengths/defaults, nominal cache identity, exact value types, overflow/guard/assignment diagnostics, imported producers and repeatable compilation work through interpreter/native execution. type-control execution shares a 4096-step bound across nested producers; scalar operations reuse checked IR execution. field/index updates, optional captures, cleanup and type-valued aggregate storage in these bodies remain open.
|
||||||
- [x] type-match checkpoint: type-producing bodies execute scalar/enum match statements and direct type-valued match expressions in initializers, assignments and returns, including nested selected arms. ordinary checking/IR execution select the arm and validate subject/pattern types, ranges, overlap, unreachable arms and exhaustiveness; only the selected body is elaborated. generated storage, scoped locals, loop transfers, early returns, nominal cache identity, selected-body diagnostics, dependency provenance and repeatable compilation work through interpreter/native execution. all bodies remain parsed/name-resolved and execution shares the type-control bound. tagged-union payload captures remain open.
|
- [x] type-match checkpoint: type-producing bodies execute scalar/enum match statements and direct type-valued match expressions in initializers, assignments and returns, including nested selected arms. ordinary checking/IR execution select the arm and validate subject/pattern types, ranges, overlap, unreachable arms and exhaustiveness; only the selected body is elaborated. generated storage, scoped locals, loop transfers, early returns, nominal cache identity, selected-body diagnostics, dependency provenance and repeatable compilation work through interpreter/native execution. all bodies remain parsed/name-resolved and execution shares the type-control bound.
|
||||||
- [x] nested type-match checkpoint: matches select types inside constructors, array lengths, generic arguments, local annotations, callback signatures and field defaults in type-producing bodies. selection precedes ordinary expansion, preserves literal spelling/source provenance and loop snapshots, and caches nominal results before expanding recursive fields. selected-body and pattern diagnostics, failed deferred elaboration, imported producers and repeatable compilation work through interpreter/native execution; deferred selection shares the producer's type-control bound.
|
- [x] nested type-match checkpoint: matches select types inside constructors, array lengths, generic arguments, local annotations, callback signatures and field defaults in type-producing bodies. selection precedes ordinary expansion, preserves literal spelling/source provenance and loop snapshots, and caches nominal results before expanding recursive fields. selected-body and pattern diagnostics, failed deferred elaboration, imported producers and repeatable compilation work through interpreter/native execution; deferred selection shares the producer's type-control bound.
|
||||||
- [x] tagged/fallible constant checkpoint: explicit `$()` expressions, generic value parameters and type-procedure inputs materialize tagged unions and both fallible channels through interpreter/native execution. preserve active tags/payloads, nested raw aggregate projections, string ownership, void/zero-cell variants, composed/payload errors, declared channel types, normalized cache identity and repeatable compilation. `catch` computes generated lengths and runtime values. untagged unions and storage-bearing active payloads remain escape diagnostics rather than retaining temporary interpreter handles.
|
- [x] tagged/fallible constant checkpoint: explicit `$()` expressions, generic value parameters and type-procedure inputs materialize tagged unions and both fallible channels through interpreter/native execution. preserve active tags/payloads, nested raw aggregate projections, string ownership, void/zero-cell variants, composed/payload errors, declared channel types, normalized cache identity and repeatable compilation. `catch` computes generated lengths and runtime values. untagged unions and storage-bearing active payloads remain escape diagnostics rather than retaining temporary interpreter handles.
|
||||||
|
- [x] tagged type-match checkpoint: type-producing bodies execute tagged-union matches with immutable by-value payload captures in statement, expression and nested storage positions. ordinary match checking validates every arm's captures, including grouped variants; selected payloads retain exact scalar/aggregate types and owned strings. generated storage/defaults, loop snapshots/transfers, cache identity, selected-body diagnostics, imported producers, dependency provenance and repeatable compilation work through interpreter/native execution. storage-bearing payloads retain the constant-materialization escape boundary.
|
||||||
- [ ] remaining gate: general type-valued compile-time execution, broader compile-time match/inline expansion and reflection (`typeinfo!`, `field!`, `struct_type!`, generated type/default arrays), broader constant materialization (including untagged unions and storage-bearing values), plus existing-library acceptance. required collection/index iteration, expression-valued control flow, compound updates, formatting parsing and imported C-binding support may interleave with later gates.
|
- [ ] remaining gate: general type-valued compile-time execution, broader compile-time match/inline expansion and reflection (`typeinfo!`, `field!`, `struct_type!`, generated type/default arrays), broader constant materialization (including untagged unions and storage-bearing values), plus existing-library acceptance. required collection/index iteration, expression-valued control flow, compound updates, formatting parsing and imported C-binding support may interleave with later gates.
|
||||||
- [ ] remaining source constructs: collection/index iteration, expression-valued control flow and `yield`, compound updates and multiline strings. examples: `source/parser/parse.hon`, `source/llvm/runtime.hon`.
|
- [ ] remaining source constructs: collection/index iteration, expression-valued control flow and `yield`, compound updates and multiline strings. examples: `source/parser/parse.hon`, `source/llvm/runtime.hon`.
|
||||||
- [ ] standard-library milestone: compile/run a multi-package program exercising existing allocation and IO, including error cleanup; no replacement miniature standard library.
|
- [ ] standard-library milestone: compile/run a multi-package program exercising existing allocation and IO, including error cleanup; no replacement miniature standard library.
|
||||||
|
|||||||
@@ -116,6 +116,15 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
` }
|
` }
|
||||||
`}
|
`}
|
||||||
`Token proc($value Packet) type { return struct {} }
|
`Token proc($value Packet) type { return struct {} }
|
||||||
|
`Captured proc(choice Choice) type {
|
||||||
|
` match choice {
|
||||||
|
` .payload |detail|: return struct {
|
||||||
|
` values [match detail.text.len { 2: usize(2), else: usize(0) }]i16
|
||||||
|
` bias i16 = match choice { .payload |selected|: selected.values[1], .empty: 0 }
|
||||||
|
` },
|
||||||
|
` .empty: return bool,
|
||||||
|
` }
|
||||||
|
`}
|
||||||
`main proc() i16 {
|
`main proc() i16 {
|
||||||
` if $(i16 != i16) { return $(i8(128)) }
|
` if $(i16 != i16) { return $(i8(128)) }
|
||||||
` values :: $(data())
|
` values :: $(data())
|
||||||
@@ -124,6 +133,8 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
` same Fixed(2) :: fixed
|
` same Fixed(2) :: fixed
|
||||||
` packet :: $(packet_data())
|
` packet :: $(packet_data())
|
||||||
` if (Token(packet_data()) != Token(packet)) return 0
|
` if (Token(packet_data()) != Token(packet)) return 0
|
||||||
|
` captured Captured(packet.choices[0]) :: { values = [19, 21] }
|
||||||
|
` if (captured.values[0] + captured.values[1] + captured.bias != 42) return 0
|
||||||
` payload :: packet.choices[0].payload
|
` payload :: packet.choices[0].payload
|
||||||
` if (payload.values[0] != 40 or payload.values[1] != 2 or payload.text.len != 2) return 0
|
` if (payload.values[0] != 40 or payload.values[1] != 2 or payload.text.len != 2) return 0
|
||||||
` match packet.choices[1] { .empty: {}, .payload: return 0 }
|
` match packet.choices[1] { .empty: {}, .payload: return 0 }
|
||||||
|
|||||||
@@ -1117,6 +1117,14 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
|||||||
for (0..ast.match_arm_count(&state.tree, id)) |i| {
|
for (0..ast.match_arm_count(&state.tree, id)) |i| {
|
||||||
arm_id :: ast.match_arm(&state.tree, id, i)
|
arm_id :: ast.match_arm(&state.tree, id, i)
|
||||||
arm :: ast.match_arm_view(&state.tree, arm_id)
|
arm :: ast.match_arm_view(&state.tree, arm_id)
|
||||||
|
# Selector bodies omit source capture uses, but must retain their resolver failures.
|
||||||
|
if (arm.capture) |capture| {
|
||||||
|
resolved :: binding(state, capture)
|
||||||
|
if (resolved.kind == .invalid) {
|
||||||
|
state.evaluation_diagnostic = resolved.data.diagnostic
|
||||||
|
return no_index
|
||||||
|
}
|
||||||
|
}
|
||||||
if (i > 0 and arm.expression != ast.match_arm_view(&state.tree,
|
if (i > 0 and arm.expression != ast.match_arm_view(&state.tree,
|
||||||
ast.match_arm(&state.tree, id, 0)).expression) {
|
ast.match_arm(&state.tree, id, 0)).expression) {
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
@@ -1144,6 +1152,48 @@ type_match_arm proc(state @mut State, id ast.NodeId) usize ! Error {
|
|||||||
return usize(selected.magnitude)
|
return usize(selected.magnitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Match checking has already validated every capture. Copy the active payload
|
||||||
|
# out of owned constant storage, never an address in the temporary interpreter.
|
||||||
|
@hide:file
|
||||||
|
type_match_capture proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
||||||
|
subject :: ast.match_expr(&state.tree, id).subject
|
||||||
|
annotation :: try expression_type(state, subject, 0)
|
||||||
|
value :: try evaluate_ir(state, subject, annotation)
|
||||||
|
if (value.kind != .aggregate or ast.expr_kind(&state.tree, value.node) != .record_literal) return no_node
|
||||||
|
payload :: ast.field(&state.tree, ast.record_field(&state.tree, value.node, 0))
|
||||||
|
for (0..ast.record_field_count(&state.tree, annotation)) |i| {
|
||||||
|
field :: ast.field(&state.tree, ast.record_field(&state.tree, annotation, i))
|
||||||
|
if (field.name != null and payload.name != null
|
||||||
|
and ast.name_id(&state.tree, field.name?) == ast.name_id(&state.tree, payload.name?)) {
|
||||||
|
result :: try ast.add_typed_constant(state.builder,
|
||||||
|
ast.node_span(&state.tree, subject), field.type?, payload.value?)
|
||||||
|
try refresh(state)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return no_node
|
||||||
|
}
|
||||||
|
|
||||||
|
@hide:file
|
||||||
|
type_match_expression_body proc(state @mut State, id ast.NodeId, arm ast.MatchArmView) ast.NodeId ! Error {
|
||||||
|
if (!arm.expression) return no_node
|
||||||
|
if (arm.capture == null) return arm.body
|
||||||
|
value :: try type_match_capture(state, id)
|
||||||
|
if (value == no_node) return no_node
|
||||||
|
execution := TypeExecution{
|
||||||
|
locals = arraylist.init(state.allocator),
|
||||||
|
mapping = try mem.alloc(ast.NodeId, state.allocator, ast.node_count(&state.tree)),
|
||||||
|
first_owned = ast.NodeId(0),
|
||||||
|
}
|
||||||
|
defer arraylist.deinit(&execution.locals)
|
||||||
|
defer mem.free(state.allocator, execution.mapping)
|
||||||
|
mem.set(execution.mapping, no_node)
|
||||||
|
try arraylist.append(&execution.locals, TypeLocal{
|
||||||
|
declaration = arm.capture?, value = value, annotation = ast.typed_constant(&state.tree, value).type,
|
||||||
|
})
|
||||||
|
return try type_snapshot(state, &execution, arm.body)
|
||||||
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
type_value_expression proc(state @mut State, input ast.NodeId) ast.NodeId ! Error {
|
type_value_expression proc(state @mut State, input ast.NodeId) ast.NodeId ! Error {
|
||||||
expression := input
|
expression := input
|
||||||
@@ -1152,8 +1202,8 @@ type_value_expression proc(state @mut State, input ast.NodeId) ast.NodeId ! Erro
|
|||||||
index :: try type_match_arm(state, expression)
|
index :: try type_match_arm(state, expression)
|
||||||
if (index == no_index) return no_node
|
if (index == no_index) return no_node
|
||||||
arm :: ast.match_arm_view(&state.tree, ast.match_arm(&state.tree, expression, index))
|
arm :: ast.match_arm_view(&state.tree, ast.match_arm(&state.tree, expression, index))
|
||||||
if (!arm.expression or arm.capture != null) return no_node
|
expression = try type_match_expression_body(state, expression, arm)
|
||||||
expression = arm.body
|
if (expression == no_node) return no_node
|
||||||
}
|
}
|
||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
@@ -1334,10 +1384,18 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
|
|||||||
index :: try type_match_arm(state, snapshot)
|
index :: try type_match_arm(state, snapshot)
|
||||||
if (index == no_index) return .invalid
|
if (index == no_index) return .invalid
|
||||||
arm :: ast.match_arm_view(&state.tree, ast.match_arm(&state.tree, expression, index))
|
arm :: ast.match_arm_view(&state.tree, ast.match_arm(&state.tree, expression, index))
|
||||||
if (arm.capture != null) return .invalid
|
saved :: execution.locals.items.len
|
||||||
|
defer execution.locals.items = execution.locals.items[..saved]
|
||||||
|
if (arm.capture) |capture| {
|
||||||
|
value :: try type_match_capture(state, snapshot)
|
||||||
|
if (value == no_node) return .invalid
|
||||||
|
try arraylist.append(&execution.locals, TypeLocal{
|
||||||
|
declaration = capture, value = value, annotation = ast.typed_constant(&state.tree, value).type,
|
||||||
|
})
|
||||||
|
}
|
||||||
if (!arm.expression) return try execute_type_block(state, execution, arm.body)
|
if (!arm.expression) return try execute_type_block(state, execution, arm.body)
|
||||||
value :: try type_value_expression(state, ast.match_arm_view(&state.tree,
|
body :: try type_snapshot(state, execution, arm.body)
|
||||||
ast.match_arm(&state.tree, snapshot, index)).body)
|
value :: try type_value_expression(state, body)
|
||||||
if (value == no_node) return .invalid
|
if (value == no_node) return .invalid
|
||||||
annotation :: try expression_type(state, value, 0)
|
annotation :: try expression_type(state, value, 0)
|
||||||
if (try type_execution_value(state, value, annotation) == no_node) return .invalid
|
if (try type_execution_value(state, value, annotation) == no_node) return .invalid
|
||||||
|
|||||||
+153
-2
@@ -11,6 +11,133 @@ HONEY = ROOT / "build/honey"
|
|||||||
|
|
||||||
# Explicit expectations guard against frontend bugs shared by both execution paths.
|
# Explicit expectations guard against frontend bugs shared by both execution paths.
|
||||||
CASES = {
|
CASES = {
|
||||||
|
"type_match_tagged_capture_preserves_resolution_errors": (
|
||||||
|
"""
|
||||||
|
data proc() [2]i16 { return [40, 2] }
|
||||||
|
Choice :: union(enum) { payload struct { values [2]i16 }, empty void }
|
||||||
|
Storage proc(input Choice) type {
|
||||||
|
return match input {
|
||||||
|
.payload |data|: struct { bias i16 = data.values[1] },
|
||||||
|
.empty: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main proc() Storage(.payload{ values = [40, 2] }) { return {} }
|
||||||
|
""", b"", b"duplicate name",
|
||||||
|
),
|
||||||
|
"type_match_tagged_capture_statements_and_cache_identity": (
|
||||||
|
"""
|
||||||
|
Tag :: enum(i16) { empty = -7, second = 3, count = 90 }
|
||||||
|
Choice :: union(Tag) { count usize, second usize, empty void }
|
||||||
|
choice proc() Choice { return .second{2} }
|
||||||
|
Storage proc(input Choice) type {
|
||||||
|
Result := i8
|
||||||
|
match input {
|
||||||
|
.count, .second |length|: {
|
||||||
|
Result = struct { values [length]i16 }
|
||||||
|
}
|
||||||
|
.empty: return struct { values [0]i16 },
|
||||||
|
}
|
||||||
|
return Result
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Storage(choice()) :: { values = [40, 2] }
|
||||||
|
cached Storage(.second{2}) :: value
|
||||||
|
empty Storage(.empty) :: { values = [] }
|
||||||
|
return cached.values[0] + cached.values[1] + i16(empty.values.len)
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_match_tagged_capture_nested_expressions_and_storage": (
|
||||||
|
"""
|
||||||
|
Inner :: union(enum) { count usize, empty void }
|
||||||
|
Detail :: struct { inner Inner, wide bool, bias i16, text string }
|
||||||
|
Choice :: union(enum) { detail Detail, unused i8 }
|
||||||
|
Box proc(T type) type { return struct { value T } }
|
||||||
|
Storage proc(input Choice) type {
|
||||||
|
Element :: match input {
|
||||||
|
.detail |detail|: match detail.wide { true: i16, false: i8 },
|
||||||
|
.unused |small|: [small]bool,
|
||||||
|
}
|
||||||
|
return struct {
|
||||||
|
values [match input {
|
||||||
|
.detail |detail|: match detail.inner { .count |n|: n, .empty: usize(0) },
|
||||||
|
.unused |small|: usize(small),
|
||||||
|
}]Element
|
||||||
|
boxed Box(match input { .detail |detail|: Element, .unused: bool })
|
||||||
|
bias i16 = match input { .detail |detail|: detail.bias, .unused: i8(128) }
|
||||||
|
label string = match input { .detail |detail|: detail.text, .unused: "" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Storage(.detail{ inner = .count{2}, wide = true, bias = 2, text = "owned" }) :: {
|
||||||
|
values = [19, 21], boxed = { value = 0 },
|
||||||
|
}
|
||||||
|
if (value.label.len != 5 or value.label[0] != 111) return -1
|
||||||
|
return value.values[0] + value.values[1] + value.boxed.value + value.bias
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_match_tagged_capture_loop_snapshots_and_transfers": (
|
||||||
|
"""
|
||||||
|
Choice :: union(enum) { count usize, empty void }
|
||||||
|
Nest proc() type {
|
||||||
|
Result := i16
|
||||||
|
current Choice := .count{0}
|
||||||
|
for (0..usize(4)) |index| {
|
||||||
|
current = .count{index}
|
||||||
|
match current {
|
||||||
|
.count |length|: {
|
||||||
|
if (length == 0) continue
|
||||||
|
if (length == 3) break
|
||||||
|
Result = [length]Result
|
||||||
|
}
|
||||||
|
.empty: return bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Result
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Nest() :: [[40], [2]]
|
||||||
|
return value[0][0] + value[1][0]
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_match_tagged_capture_checks_unselected_capture_types": (
|
||||||
|
"""
|
||||||
|
Choice :: union(enum) { count usize, small i8, empty void }
|
||||||
|
Bad proc(input Choice) type {
|
||||||
|
return match input { .count, .small |value|: [value]i16, .empty: bool }
|
||||||
|
}
|
||||||
|
main proc() Bad(.empty) { return true }
|
||||||
|
""", b"", b"payload capture requires compatible nonvoid tagged union variants",
|
||||||
|
),
|
||||||
|
"type_match_tagged_capture_preserves_exact_payload_type": (
|
||||||
|
"""
|
||||||
|
Choice :: union(enum) { small i8, empty void }
|
||||||
|
Bad proc(input Choice) type {
|
||||||
|
match input {
|
||||||
|
.small |value|: {
|
||||||
|
next :: value + 1
|
||||||
|
return [usize(next)]i16
|
||||||
|
}
|
||||||
|
.empty: return bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main proc() Bad(.small{127}) { return [] }
|
||||||
|
""", b"", b"integer arithmetic overflow",
|
||||||
|
),
|
||||||
|
"type_match_tagged_capture_is_immutable": (
|
||||||
|
"""
|
||||||
|
Choice :: union(enum) { count usize }
|
||||||
|
Bad proc(input Choice) type {
|
||||||
|
match input { .count |length|: {
|
||||||
|
length = 2
|
||||||
|
return [length]i16
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
main proc() Bad(.count{1}) { return [0, 0] }
|
||||||
|
""", b"", b"mutable local binding",
|
||||||
|
),
|
||||||
"nested_type_match_failure_preserves_shared_return_types": (
|
"nested_type_match_failure_preserves_shared_return_types": (
|
||||||
"""
|
"""
|
||||||
Original :: alias [2]i16
|
Original :: alias [2]i16
|
||||||
@@ -3713,6 +3840,13 @@ def check_specialization_packages(temporary, executable):
|
|||||||
}
|
}
|
||||||
return struct { values [match length { 0: usize(0), else: length }]mut Element }
|
return struct { values [match length { 0: usize(0), else: length }]mut Element }
|
||||||
}
|
}
|
||||||
|
Choice :: union(enum) { count usize, empty void }
|
||||||
|
Captured proc(T type, choice Choice) type {
|
||||||
|
return match choice {
|
||||||
|
.count |length|: struct { values [length]T },
|
||||||
|
.empty: bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
""").strip() + "\n")
|
""").strip() + "\n")
|
||||||
(library / "functions.hon").write_text(textwrap.dedent("""
|
(library / "functions.hon").write_text(textwrap.dedent("""
|
||||||
identity proc($T type, value T) T { return value }
|
identity proc($T type, value T) T { return value }
|
||||||
@@ -3733,8 +3867,9 @@ def check_specialization_packages(temporary, executable):
|
|||||||
main proc() i16 {
|
main proc() i16 {
|
||||||
imported ImportedBox :: { value = 40 }
|
imported ImportedBox :: { value = 40 }
|
||||||
copy lib.Box(i16) :: imported
|
copy lib.Box(i16) :: imported
|
||||||
|
captured lib.Captured(ImportedBox, .count{2}) :: { values = [imported, copy] }
|
||||||
fixed lib.Fixed(i16, lib.count()) :: { values = [2, 0] }
|
fixed lib.Fixed(i16, lib.count()) :: { values = [2, 0] }
|
||||||
return copy.value + lib.identity(lib.first(&fixed))
|
return captured.values[0].value + lib.identity(lib.first(&fixed))
|
||||||
}
|
}
|
||||||
""").strip() + "\n")
|
""").strip() + "\n")
|
||||||
check(app, executable, (b"42\n", None))
|
check(app, executable, (b"42\n", None))
|
||||||
@@ -3782,7 +3917,23 @@ def check_specialization_packages(temporary, executable):
|
|||||||
interpreted = run(str(HONEY), "run", str(app))
|
interpreted = run(str(HONEY), "run", str(app))
|
||||||
assert interpreted.stderr.startswith(f"{sizing.resolve()}:2:".encode()), outcome(interpreted)
|
assert interpreted.stderr.startswith(f"{sizing.resolve()}:2:".encode()), outcome(interpreted)
|
||||||
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
||||||
return 4
|
sizing.write_text(textwrap.dedent("""
|
||||||
|
SizingChoice :: union(enum) { small i8 }
|
||||||
|
Bad proc() type {
|
||||||
|
input SizingChoice :: .small{127}
|
||||||
|
match input {
|
||||||
|
.small |value|: {
|
||||||
|
next :: value + 1
|
||||||
|
return [usize(next)]i16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""").strip() + "\n")
|
||||||
|
check(app, executable, (b"", b"integer arithmetic overflow"))
|
||||||
|
interpreted = run(str(HONEY), "run", str(app))
|
||||||
|
assert interpreted.stderr.startswith(f"{sizing.resolve()}:6:".encode()), outcome(interpreted)
|
||||||
|
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
||||||
|
return 5
|
||||||
|
|
||||||
|
|
||||||
def check_dependency_provenance(temporary, executable):
|
def check_dependency_provenance(temporary, executable):
|
||||||
|
|||||||
Reference in New Issue
Block a user