feat: execute aggregate updates in type-producing procedures
This commit is contained in:
@@ -98,7 +98,9 @@ main proc() i16 {
|
|||||||
|
|
||||||
the specialization checkpoint supports type arguments and constant integer, boolean, string, callback and null value arguments; declared value-parameter types survive specialization, including narrow checked arithmetic and optional captures/unwrap. integer arguments are range-checked even when unused, and erased argument expressions still receive ordinary checking. immutable constant expressions may use unary signs, `!`, `+`, `-`, `*`, comparisons and logical operators. type-producing procedures accept ordinary parameters as well as `$` parameters: every argument must be supplied explicitly and evaluate to a constant, and every input participates in specialization caching. for example, `Fixed proc(T type, count usize) type { return struct { values [count]T } }` accepts `Fixed(i16, 2)` and reuses its nominal type for equivalent inputs. use `$` when a parameter must be visible in subsequent signature annotations. runaway specialization is diagnosed at 128 nested evaluation/specialization calls or 4096 instances.
|
the specialization checkpoint supports type arguments and constant integer, boolean, string, callback and null value arguments; declared value-parameter types survive specialization, including narrow checked arithmetic and optional captures/unwrap. integer arguments are range-checked even when unused, and erased argument expressions still receive ordinary checking. immutable constant expressions may use unary signs, `!`, `+`, `-`, `*`, comparisons and logical operators. type-producing procedures accept ordinary parameters as well as `$` parameters: every argument must be supplied explicitly and evaluate to a constant, and every input participates in specialization caching. for example, `Fixed proc(T type, count usize) type { return struct { values [count]T } }` accepts `Fixed(i16, 2)` and reuses its nominal type for equivalent inputs. use `$` when a parameter must be visible in subsequent signature annotations. runaway specialization is diagnosed at 128 nested evaluation/specialization calls or 4096 instances.
|
||||||
|
|
||||||
type-producing bodies support mutable type and value locals, direct binding assignment, scoped blocks, conditional/early returns, integer range and while loops, break/continue and nested/recursive type-producing calls. for example, start with `Result := T`, repeat `Result = [2]Result` in a loop, then `return Result` to generate nested array storage. snapshots retain each local's current value without changing the source AST; scalar operations reuse checking, typed IR and interpreter execution, preserving exact types and overflow diagnostics. type-control execution is limited to 4096 statement/loop-guard steps shared with nested producers; individual scalar evaluations retain the interpreter limits below. only executed bodies are elaborated, while every body is still parsed and name-resolved. field/index assignment, cleanup and type-valued aggregate storage in type-producing bodies remain outside this checkpoint.
|
type-producing bodies support mutable type and value locals, direct binding assignment, scoped blocks, conditional/early returns, integer range and while loops, break/continue and nested/recursive type-producing calls. for example, start with `Result := T`, repeat `Result = [2]Result` in a loop, then `return Result` to generate nested array storage. snapshots retain each local's current value without changing the source AST; scalar operations reuse checking, typed IR and interpreter execution, preserving exact types and overflow diagnostics. type-control execution is limited to 4096 statement/loop-guard steps shared with nested producers; individual scalar evaluations retain the interpreter limits below. only executed bodies are elaborated, while every body is still parsed and name-resolved. cleanup and type-valued aggregate storage in type-producing bodies remain outside this checkpoint.
|
||||||
|
|
||||||
|
field/index assignments also update initialized, materializable aggregate locals in type-producing bodies, including nested record fields and mutable fixed-array elements. for example, `counts [2]mut usize := [0, 0]` followed by `counts[1] = 2` can generate `[counts[1]]i16` storage. each update uses ordinary checked IR and returns a new owned constant: earlier copies remain unchanged, while later loop iterations, generated lengths and defaults observe the update. ordinary mutability, index-type/bounds, field and exact value-type checks still apply, including immutable payload captures and narrow arithmetic overflow. imported producers and repeated compilation retain source diagnostics and nominal cache identity; pointer/slice-backed mutation and type-valued aggregate storage remain open.
|
||||||
|
|
||||||
optional `if`/`while` captures also execute in type-producing bodies: `if count |length| { return [length]i16 }` generates storage from an optional `usize`. ordinary checked IR validates each executed guard; present payloads become immutable owned constants with exact scalar/aggregate types and strings, and absent optionals skip the body. while guards observe updated locals on each iteration; captures stay body-local across break/continue and early return. generated lengths/defaults, imported producers, cache identity and repeated compilation preserve these values. null data/function pointers materialize as null, but nonnull pointer/slice/callback-bearing payloads retain the constant-materialization escape boundary.
|
optional `if`/`while` captures also execute in type-producing bodies: `if count |length| { return [length]i16 }` generates storage from an optional `usize`. ordinary checked IR validates each executed guard; present payloads become immutable owned constants with exact scalar/aggregate types and strings, and absent optionals skip the body. while guards observe updated locals on each iteration; captures stay body-local across break/continue and early return. generated lengths/defaults, imported producers, cache identity and repeated compilation preserve these values. null data/function pointers materialize as null, but nonnull pointer/slice/callback-bearing payloads retain the constant-materialization escape boundary.
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ explicit `$()` expressions use the same constant evaluation path. compile-time
|
|||||||
including owned by-value tagged payload captures in ordinary generic procedures.
|
including owned by-value tagged payload captures in ordinary generic procedures.
|
||||||
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/tagged-union matching
|
type/value locals, direct and aggregate field/index assignments, branches,
|
||||||
and bounded integer-range/while loops now generate types and storage. optional
|
scalar/enum/tagged-union matching and bounded integer-range/while loops now generate types and storage. optional
|
||||||
if/while captures and tagged payload matches select nested constructors, lengths, signatures and defaults;
|
if/while captures and tagged payload matches select nested constructors, lengths, signatures and defaults;
|
||||||
general type-valued execution, 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
|
||||||
@@ -71,13 +71,14 @@ test discovery and FFI remain outside these completed slices.
|
|||||||
- [x] aggregate/explicit-expression checkpoint: computed nested records/tuples and fixed arrays, contextual/dependent aggregate value parameters, normalized specialization caching and explicit `$()` expressions through interpreter/native execution. preserve by-value isolation, zero-cell shapes, integer widths, string ownership, enum/optional fields and exact IEEE float bits; reject runtime dependencies and values that would retain temporary interpreter storage. repeatable compilation restores erased expressions.
|
- [x] aggregate/explicit-expression checkpoint: computed nested records/tuples and fixed arrays, contextual/dependent aggregate value parameters, normalized specialization caching and explicit `$()` expressions through interpreter/native execution. preserve by-value isolation, zero-cell shapes, integer widths, string ownership, enum/optional fields and exact IEEE float bits; reject runtime dependencies and values that would retain temporary interpreter storage. repeatable compilation restores erased expressions.
|
||||||
- [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, 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. aggregate field/index updates are covered below; 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.
|
- [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.
|
- [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.
|
||||||
- [x] generic match-specialization checkpoint: side-effect-free constant scalar/enum/tagged-union subjects in concrete generic procedures and explicit `match $(subject)` expressions/statements select an arm before body specialization/checking. reuse ordinary checked IR for pattern types, ranges, overlap, exhaustiveness and every arm's capture validation; only the selected body is elaborated. exact by-value payload types/owned strings, runtime parameters, nested expression matches, cleanup, return/loop transfers, imported producers and repeatable compilation work through interpreter/native execution. ordinary call/runtime/mutable subjects stay runtime; contextual aggregates with possible runtime defaults are not implicitly evaluated. all bodies remain parsed/name-resolved; storage-bearing payloads retain the constant-materialization escape boundary.
|
- [x] generic match-specialization checkpoint: side-effect-free constant scalar/enum/tagged-union subjects in concrete generic procedures and explicit `match $(subject)` expressions/statements select an arm before body specialization/checking. reuse ordinary checked IR for pattern types, ranges, overlap, exhaustiveness and every arm's capture validation; only the selected body is elaborated. exact by-value payload types/owned strings, runtime parameters, nested expression matches, cleanup, return/loop transfers, imported producers and repeatable compilation work through interpreter/native execution. ordinary call/runtime/mutable subjects stay runtime; contextual aggregates with possible runtime defaults are not implicitly evaluated. all bodies remain parsed/name-resolved; storage-bearing payloads retain the constant-materialization escape boundary.
|
||||||
- [x] optional type-control checkpoint: type-producing bodies execute optional if/while guards with immutable by-value payload captures. ordinary checked IR validates guards; selected scalar/aggregate payloads retain exact types and owned strings across nested captures, generated storage/defaults, loop updates/transfers and early returns. absent optionals skip unselected bodies; invalid guards, capture mutation/shadowing, overflow and execution bounds retain diagnostics. imported producers, dependency provenance, nominal cache identity and repeatable compilation work through interpreter/native execution. null data/function pointers materialize correctly; nonnull storage-bearing payloads retain the constant-materialization escape boundary.
|
- [x] optional type-control checkpoint: type-producing bodies execute optional if/while guards with immutable by-value payload captures. ordinary checked IR validates guards; selected scalar/aggregate payloads retain exact types and owned strings across nested captures, generated storage/defaults, loop updates/transfers and early returns. absent optionals skip unselected bodies; invalid guards, capture mutation/shadowing, overflow and execution bounds retain diagnostics. imported producers, dependency provenance, nominal cache identity and repeatable compilation work through interpreter/native execution. null data/function pointers materialize correctly; nonnull storage-bearing payloads retain the constant-materialization escape boundary.
|
||||||
|
- [x] aggregate type-update checkpoint: type-producing bodies update initialized, materializable aggregate locals through nested record fields and mutable fixed-array indices. ordinary checked IR validates and executes each assignment, retaining exact value types, mutability (including immutable payload captures), index types/bounds, unknown-field and narrow-overflow diagnostics. new owned constants preserve earlier by-value copies while later loops, generated storage and defaults observe updates. imported producers, dependency provenance, nominal cache identity and repeatable compilation work through interpreter/native execution. pointer/slice-backed mutation, uninitialized aggregate construction and type-valued aggregate storage remain open.
|
||||||
- [ ] remaining gate: general type-valued compile-time execution, inline match/loop 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, inline match/loop 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.
|
||||||
|
|||||||
@@ -134,6 +134,18 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
` }
|
` }
|
||||||
` return Result
|
` return Result
|
||||||
`}
|
`}
|
||||||
|
`Updated proc() type {
|
||||||
|
` config struct { counts [2]mut usize, bias i16 } := { counts = [0, 0], bias = 0 }
|
||||||
|
` original :: config
|
||||||
|
` for (usize(0)..2) |index| {
|
||||||
|
` config.counts[index] = index + 1
|
||||||
|
` config.bias = config.bias + 21
|
||||||
|
` }
|
||||||
|
` return struct {
|
||||||
|
` values [config.counts[1]]i16
|
||||||
|
` bias i16 = config.bias + i16(original.counts[1])
|
||||||
|
` }
|
||||||
|
`}
|
||||||
`read_selected proc($choice Choice, value i16) i16 {
|
`read_selected proc($choice Choice, value i16) i16 {
|
||||||
` match choice {
|
` match choice {
|
||||||
` .payload |detail|: {
|
` .payload |detail|: {
|
||||||
@@ -159,6 +171,8 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
|
|||||||
` if (captured.values[0] + captured.values[1] + captured.bias != 42) return 0
|
` if (captured.values[0] + captured.values[1] + captured.bias != 42) return 0
|
||||||
` optional Optional() :: captured.values
|
` optional Optional() :: captured.values
|
||||||
` if (optional[0] + optional[1] != 40) return 0
|
` if (optional[0] + optional[1] != 40) return 0
|
||||||
|
` updated Updated() :: { values = optional }
|
||||||
|
` if (updated.bias != 42 or updated.values[1] != 21) 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 }
|
||||||
|
|||||||
@@ -1399,6 +1399,42 @@ type_capture_guard proc(state @mut State, expression, capture ast.NodeId) Value
|
|||||||
return try evaluate_ir_body(state, expression, body, try builtin(state, expression, .bool))
|
return try evaluate_ir_body(state, expression, body, try builtin(state, expression, .bool))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# update one owned aggregate through ordinary assignment checking and execution.
|
||||||
|
# only the target's root becomes a temporary local; indices and RHS retain their
|
||||||
|
# pre-assignment snapshots, and no interpreter storage survives the evaluation.
|
||||||
|
@hide:file
|
||||||
|
type_assignment_value proc(
|
||||||
|
state @mut State, execution @mut TypeExecution, statement ast.NodeId, index usize,
|
||||||
|
) ast.NodeId ! Error {
|
||||||
|
assignment :: ast.assignment_stmt(&state.tree, statement)
|
||||||
|
local :: execution.locals.items[index]
|
||||||
|
span :: ast.node_span(&state.tree, statement)
|
||||||
|
value :: try type_snapshot(state, execution, assignment.value)
|
||||||
|
mutability :: if (ast.node_class(&state.tree, local.declaration) == .statement)
|
||||||
|
ast.statement_decl(&state.tree, local.declaration).mutability else ast.Mutability.constant
|
||||||
|
temporary :: try ast.add_statement_decl(state.builder, span, span, strpool.no_str,
|
||||||
|
local.annotation, mutability, local.value)
|
||||||
|
try refresh(state)
|
||||||
|
target := no_node
|
||||||
|
{
|
||||||
|
execution.locals.items[index].value = try reference(state, assignment.target, temporary)
|
||||||
|
defer execution.locals.items[index].value = local.value
|
||||||
|
target = try type_snapshot(state, execution, assignment.target)
|
||||||
|
}
|
||||||
|
if (!try expand_type_matches(state, target) or !try expand_type_matches(state, value)) return no_node
|
||||||
|
update :: try ast.add_assignment_stmt(state.builder, span, target, value)
|
||||||
|
returned :: try ast.add_return_stmt(state.builder, span, try reference(state, statement, temporary))
|
||||||
|
statements [3]ast.NodeId :: [temporary, update, returned]
|
||||||
|
body :: try ast.add_block(state.builder, span, statements[..])
|
||||||
|
try refresh(state)
|
||||||
|
result :: try evaluate_ir_body(state, statement, body, local.annotation)
|
||||||
|
if (result.kind == .invalid) return no_node
|
||||||
|
concrete :: try materialize(state, result)
|
||||||
|
owned :: try ast.add_typed_constant(state.builder, span, local.annotation, concrete.node)
|
||||||
|
try refresh(state)
|
||||||
|
return owned
|
||||||
|
}
|
||||||
|
|
||||||
@hide:file
|
@hide:file
|
||||||
execute_type_block proc(state @mut State, execution @mut TypeExecution, block ast.NodeId) TypeFlow ! Error {
|
execute_type_block proc(state @mut State, execution @mut TypeExecution, block ast.NodeId) TypeFlow ! Error {
|
||||||
saved :: execution.locals.items.len
|
saved :: execution.locals.items.len
|
||||||
@@ -1449,7 +1485,15 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
|
|||||||
}
|
}
|
||||||
.assignment_stmt: {
|
.assignment_stmt: {
|
||||||
assignment :: ast.assignment_stmt(&state.tree, statement)
|
assignment :: ast.assignment_stmt(&state.tree, statement)
|
||||||
resolved :: binding(state, assignment.target)
|
root := assignment.target
|
||||||
|
while (true) {
|
||||||
|
kind :: ast.expr_kind(&state.tree, root)
|
||||||
|
if (kind == .selection and binding(state, root).kind == .none)
|
||||||
|
root = ast.selection_expr(&state.tree, root).base
|
||||||
|
else if (kind == .index) root = ast.index_expr(&state.tree, root).base
|
||||||
|
else break
|
||||||
|
}
|
||||||
|
resolved :: binding(state, root)
|
||||||
index := no_index
|
index := no_index
|
||||||
if (resolved.kind == .declaration) {
|
if (resolved.kind == .declaration) {
|
||||||
for (execution.locals.items) |local, i| {
|
for (execution.locals.items) |local, i| {
|
||||||
@@ -1458,13 +1502,20 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
index == no_index
|
index == no_index
|
||||||
or ast.node_class(&state.tree, execution.locals.items[index].declaration) != .statement
|
or (root == assignment.target
|
||||||
or ast.statement_decl(&state.tree, execution.locals.items[index].declaration).mutability != .variable
|
and (ast.node_class(&state.tree, execution.locals.items[index].declaration) != .statement
|
||||||
|
or ast.statement_decl(&state.tree, execution.locals.items[index].declaration).mutability != .variable))
|
||||||
) {
|
) {
|
||||||
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics,
|
||||||
.invalid_assignment_target, ast.node_span(&state.tree, assignment.target))
|
.invalid_assignment_target, ast.node_span(&state.tree, assignment.target))
|
||||||
return .invalid
|
return .invalid
|
||||||
}
|
}
|
||||||
|
if (root != assignment.target) {
|
||||||
|
value :: try type_assignment_value(state, execution, statement, index)
|
||||||
|
if (value == no_node) return .invalid
|
||||||
|
execution.locals.items[index].value = value
|
||||||
|
return .next
|
||||||
|
}
|
||||||
expression :: try type_snapshot(state, execution, assignment.value)
|
expression :: try type_snapshot(state, execution, assignment.value)
|
||||||
value :: try type_execution_value(state, expression, execution.locals.items[index].annotation)
|
value :: try type_execution_value(state, expression, execution.locals.items[index].annotation)
|
||||||
if (value == no_node) return .invalid
|
if (value == no_node) return .invalid
|
||||||
|
|||||||
+179
-1
@@ -11,6 +11,156 @@ 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_execution_aggregate_updates_preserve_copies_and_generate_defaults": (
|
||||||
|
"""
|
||||||
|
Config :: struct { values [2]mut i16, count usize, text string }
|
||||||
|
Storage proc() type {
|
||||||
|
config Config := { values = [17, 19], count = 0, text = "old" }
|
||||||
|
original :: config
|
||||||
|
for (usize(0)..2) |index| {
|
||||||
|
config.values[index] = config.values[index] + 1
|
||||||
|
config.count = config.count + 1
|
||||||
|
}
|
||||||
|
config.text = "owned"
|
||||||
|
config.values[config.count - 1] = match true {
|
||||||
|
true: config.values[0] + 4,
|
||||||
|
false: i8(128),
|
||||||
|
}
|
||||||
|
return struct {
|
||||||
|
values [config.count]mut i16 = config.values
|
||||||
|
bias i16 = original.values[0] - 17 + i16(config.text.len) - 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Storage() :: {}
|
||||||
|
same Storage() :: value
|
||||||
|
return same.values[0] + same.values[1] + same.bias
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_execution_nested_array_record_updates_and_loop_transfers": (
|
||||||
|
"""
|
||||||
|
Item :: struct { length usize, value i16 }
|
||||||
|
Storage proc() type {
|
||||||
|
items [2]mut Item := [{ length = 0, value = 0 }, { length = 0, value = 0 }]
|
||||||
|
index usize := 0
|
||||||
|
while (index < 3) {
|
||||||
|
if (index == 2) break
|
||||||
|
items[index].length = index + 1
|
||||||
|
items[index].value = i16(index) + 20
|
||||||
|
index = index + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return struct {
|
||||||
|
values [items[1].length]i16
|
||||||
|
bias i16 = items[0].value + items[1].value + i16(items[0].length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
main proc() i16 {
|
||||||
|
value Storage() :: { values = [0, 0] }
|
||||||
|
return value.bias
|
||||||
|
}
|
||||||
|
""", b"42\n", None,
|
||||||
|
),
|
||||||
|
"type_execution_field_update_rejects_constant_root": (
|
||||||
|
"""
|
||||||
|
Config :: struct { count usize }
|
||||||
|
Bad proc() type {
|
||||||
|
config Config :: { count = 1 }
|
||||||
|
config.count = 2
|
||||||
|
return [config.count]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0, 0] }
|
||||||
|
""", b"", b"assignment requires a mutable local binding",
|
||||||
|
),
|
||||||
|
"type_execution_index_update_rejects_readonly_array": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
counts [1]usize := [1]
|
||||||
|
counts[0] = 2
|
||||||
|
return [counts[0]]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0, 0] }
|
||||||
|
""", b"", b"indexed assignment requires a writable array element",
|
||||||
|
),
|
||||||
|
"type_execution_index_update_checks_bounds": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
counts [1]mut usize := [1]
|
||||||
|
counts[1] = 2
|
||||||
|
return [counts[0]]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0] }
|
||||||
|
""", b"", b"index out of bounds",
|
||||||
|
),
|
||||||
|
"type_execution_index_update_checks_index_type": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
counts [1]mut usize := [1]
|
||||||
|
index u8 :: 0
|
||||||
|
counts[index] = 2
|
||||||
|
return [counts[0]]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0, 0] }
|
||||||
|
""", b"", b"array index must have type usize",
|
||||||
|
),
|
||||||
|
"type_execution_field_update_checks_exact_value_type": (
|
||||||
|
"""
|
||||||
|
Config :: struct { count usize }
|
||||||
|
Bad proc() type {
|
||||||
|
config Config := { count = 1 }
|
||||||
|
value u8 :: 2
|
||||||
|
config.count = value
|
||||||
|
return [config.count]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0, 0] }
|
||||||
|
""", b"", b"incompatible value type",
|
||||||
|
),
|
||||||
|
"type_execution_index_update_preserves_narrow_overflow": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
values [1]mut i8 := [127]
|
||||||
|
values[0] = values[0] + 1
|
||||||
|
return [usize(values[0])]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0] }
|
||||||
|
""", b"", b"integer arithmetic overflow",
|
||||||
|
),
|
||||||
|
"type_execution_field_update_rejects_payload_capture_mutation": (
|
||||||
|
"""
|
||||||
|
Config :: struct { count usize }
|
||||||
|
Bad proc() type {
|
||||||
|
input ?Config :: { count = 1 }
|
||||||
|
if input |config| {
|
||||||
|
config.count = 2
|
||||||
|
return [config.count]i16
|
||||||
|
}
|
||||||
|
return i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0, 0] }
|
||||||
|
""", b"", b"assignment requires a mutable local binding",
|
||||||
|
),
|
||||||
|
"type_execution_index_update_rejects_constant_array_root": (
|
||||||
|
"""
|
||||||
|
Bad proc() type {
|
||||||
|
counts [1]mut usize :: [1]
|
||||||
|
counts[0] = 2
|
||||||
|
return [counts[0]]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0, 0] }
|
||||||
|
""", b"", b"indexed assignment requires a writable array element",
|
||||||
|
),
|
||||||
|
"type_execution_field_update_checks_member": (
|
||||||
|
"""
|
||||||
|
Config :: struct { count usize }
|
||||||
|
Bad proc() type {
|
||||||
|
config Config := { count = 1 }
|
||||||
|
config.missing = 2
|
||||||
|
return [config.count]i16
|
||||||
|
}
|
||||||
|
main proc() Bad() { return [0] }
|
||||||
|
""", b"", b"record has no such field",
|
||||||
|
),
|
||||||
"type_execution_optional_if_selects_storage_and_cache_identity": (
|
"type_execution_optional_if_selects_storage_and_cache_identity": (
|
||||||
"""
|
"""
|
||||||
Count :: alias ?usize
|
Count :: alias ?usize
|
||||||
@@ -4342,7 +4492,35 @@ 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()}:4:".encode()), outcome(interpreted)
|
assert interpreted.stderr.startswith(f"{sizing.resolve()}:4:".encode()), outcome(interpreted)
|
||||||
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
||||||
return 8
|
sizing.write_text(textwrap.dedent("""
|
||||||
|
Updated proc() type {
|
||||||
|
config struct { counts [2]mut usize, bias i16 } := { counts = [0, 0], bias = 40 }
|
||||||
|
config.counts[1] = 2
|
||||||
|
config.bias = config.bias + i16(config.counts[1])
|
||||||
|
return struct { values [config.counts[1]]i16, bias i16 = config.bias }
|
||||||
|
}
|
||||||
|
""").strip() + "\n")
|
||||||
|
source.write_text(textwrap.dedent("""
|
||||||
|
lib :: import "../library"
|
||||||
|
main proc() i16 {
|
||||||
|
value lib.Updated() :: { values = [0, 0] }
|
||||||
|
return value.bias
|
||||||
|
}
|
||||||
|
""").strip() + "\n")
|
||||||
|
check(app, executable, (b"42\n", None))
|
||||||
|
sizing.write_text(textwrap.dedent("""
|
||||||
|
Updated proc() type {
|
||||||
|
counts [1]mut usize := [1]
|
||||||
|
counts[1] = 2
|
||||||
|
return [counts[0]]i16
|
||||||
|
}
|
||||||
|
""").strip() + "\n")
|
||||||
|
source.write_text('lib :: import "../library"\nmain proc() lib.Updated() { return [0] }\n')
|
||||||
|
check(app, executable, (b"", b"index out of bounds"))
|
||||||
|
interpreted = run(str(HONEY), "run", str(app))
|
||||||
|
assert interpreted.stderr.startswith(f"{sizing.resolve()}:3:".encode()), outcome(interpreted)
|
||||||
|
assert len(interpreted.stderr.splitlines()) == 1, outcome(interpreted)
|
||||||
|
return 10
|
||||||
|
|
||||||
|
|
||||||
def check_dependency_provenance(temporary, executable):
|
def check_dependency_provenance(temporary, executable):
|
||||||
|
|||||||
Reference in New Issue
Block a user