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:
@@ -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| {
|
||||
arm_id :: ast.match_arm(&state.tree, id, i)
|
||||
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,
|
||||
ast.match_arm(&state.tree, id, 0)).expression) {
|
||||
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)
|
||||
}
|
||||
|
||||
# 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
|
||||
type_value_expression proc(state @mut State, input ast.NodeId) ast.NodeId ! Error {
|
||||
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)
|
||||
if (index == no_index) return no_node
|
||||
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 = arm.body
|
||||
expression = try type_match_expression_body(state, expression, arm)
|
||||
if (expression == no_node) return no_node
|
||||
}
|
||||
return expression
|
||||
}
|
||||
@@ -1334,10 +1384,18 @@ execute_type_statement proc(state @mut State, execution @mut TypeExecution, stat
|
||||
index :: try type_match_arm(state, snapshot)
|
||||
if (index == no_index) return .invalid
|
||||
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)
|
||||
value :: try type_value_expression(state, ast.match_arm_view(&state.tree,
|
||||
ast.match_arm(&state.tree, snapshot, index)).body)
|
||||
body :: try type_snapshot(state, execution, arm.body)
|
||||
value :: try type_value_expression(state, body)
|
||||
if (value == no_node) return .invalid
|
||||
annotation :: try expression_type(state, value, 0)
|
||||
if (try type_execution_value(state, value, annotation) == no_node) return .invalid
|
||||
|
||||
Reference in New Issue
Block a user