feat: execute aggregate updates in type-producing procedures

This commit is contained in:
2026-10-27 13:27:22 +01:00
parent 08b171d3ff
commit 0f5d3c222a
5 changed files with 254 additions and 8 deletions
+14
View File
@@ -134,6 +134,18 @@ recompiles_specialized_calls_without_borrowing_previous_ir test {
` }
` 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 {
` match choice {
` .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
` optional Optional() :: captured.values
` 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
` 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 }
+54 -3
View File
@@ -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))
}
# 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
execute_type_block proc(state @mut State, execution @mut TypeExecution, block ast.NodeId) TypeFlow ! Error {
saved :: execution.locals.items.len
@@ -1449,7 +1485,15 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
}
.assignment_stmt: {
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
if (resolved.kind == .declaration) {
for (execution.locals.items) |local, i| {
@@ -1458,13 +1502,20 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
}
if (
index == no_index
or ast.node_class(&state.tree, execution.locals.items[index].declaration) != .statement
or ast.statement_decl(&state.tree, execution.locals.items[index].declaration).mutability != .variable
or (root == assignment.target
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,
.invalid_assignment_target, ast.node_span(&state.tree, assignment.target))
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)
value :: try type_execution_value(state, expression, execution.locals.items[index].annotation)
if (value == no_node) return .invalid