feat: expand composed error-set inline match arms
This commit is contained in:
@@ -291,6 +291,21 @@ expand proc(state @mut State, id ast.NodeId) void ! Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (class == .expression and ast.expr_kind(&state.tree, id) == .catch_expr) {
|
||||
expression :: ast.catch_expr(&state.tree, id)
|
||||
if (expression.capture) |capture| {
|
||||
try expand(state, expression.operand)
|
||||
annotation :: try expression_type(state, expression.operand, 0)
|
||||
if (annotation != no_node and ast.expr_kind(&state.tree, annotation) == .fallible_type) {
|
||||
hashmap.put(&state.capture_types, capture, ast.binary_expr(&state.tree, annotation).right) catch |err| {
|
||||
match err {
|
||||
.key_exists: {}
|
||||
else: return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (class == .statement and ast.stmt_kind(&state.tree, id) == .expression) {
|
||||
expression :: ast.statement_expr(&state.tree, id)
|
||||
if (
|
||||
@@ -707,6 +722,90 @@ expand_struct_type proc(state @mut State, id ast.NodeId) void ! Error {
|
||||
try expand(state, id)
|
||||
}
|
||||
|
||||
# composed struct variants use declaration IDs internally, but generated syntax
|
||||
# must not embed those declarations and make dependency discovery treat them as owned.
|
||||
@hide:file
|
||||
member_name proc(state @mut State, name ast.NodeId) ast.NodeId ! Error {
|
||||
if (ast.node_class(&state.tree, name) != .statement) return name
|
||||
result :: try ast.add_identifier(state.builder, ast.node_span(&state.tree, name), ast.name_id(&state.tree, name))
|
||||
try refresh(state)
|
||||
return result
|
||||
}
|
||||
|
||||
# flatten source member names and payload annotations only; ordinary checking still
|
||||
# validates composition, variant identity and collisions on the original sum type.
|
||||
@hide:file
|
||||
inline_sum_fields proc(state @mut State, input ast.NodeId, fields @mut arraylist.ArrayList(ast.NodeId)) bool ! Error {
|
||||
if (!try type_step(state, input)) return false
|
||||
type :: try canonical_type(state, input)
|
||||
if (type == no_node) return false
|
||||
kind :: ast.expr_kind(&state.tree, type)
|
||||
if (kind == .sum_type) {
|
||||
sum :: ast.binary_expr(&state.tree, type)
|
||||
if (!try inline_sum_fields(state, sum.left, fields)) return false
|
||||
return try inline_sum_fields(state, sum.right, fields)
|
||||
}
|
||||
if (kind == .record_type) {
|
||||
for (0..ast.node_count(&state.tree)) |i| {
|
||||
declaration :: ast.NodeId(i)
|
||||
if (ast.node_class(&state.tree, declaration) != .statement
|
||||
or ast.stmt_kind(&state.tree, declaration) != .declaration) continue
|
||||
if (ast.statement_decl(&state.tree, declaration).initializer != type) continue
|
||||
name :: try member_name(state, declaration)
|
||||
field :: try ast.add_field(state.builder, ast.node_span(&state.tree, type), name, type, null)
|
||||
try refresh(state)
|
||||
try arraylist.append(fields, field)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (kind != .enum_type and kind != .union_type) return false
|
||||
count :: if (kind == .enum_type) ast.enum_member_count(&state.tree, type)
|
||||
else ast.record_field_count(&state.tree, type)
|
||||
for (0..count) |i| {
|
||||
if (!try type_step(state, input)) return false
|
||||
member :: if (kind == .enum_type) ast.enum_member(&state.tree, type, i)
|
||||
else ast.record_field(&state.tree, type, i)
|
||||
field :: ast.field(&state.tree, member)
|
||||
if (field.name == null) return false
|
||||
if (kind == .union_type and field.type == null) return false
|
||||
duplicate := false
|
||||
for (fields.items) |previous| {
|
||||
if (ast.name_id(&state.tree, ast.field(&state.tree, previous).name?)
|
||||
== ast.name_id(&state.tree, field.name?)) duplicate = true
|
||||
}
|
||||
if (duplicate) continue
|
||||
payload :: if (kind == .enum_type) try builtin(state, member, .void) else field.type?
|
||||
generated :: try ast.add_field(state.builder, field.span, field.name, payload, null)
|
||||
try refresh(state)
|
||||
try arraylist.append(fields, generated)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@hide:file
|
||||
inline_match_shape proc(state @mut State, annotation ast.NodeId) ast.NodeId ! Error {
|
||||
if (ast.expr_kind(&state.tree, annotation) != .sum_type) return annotation
|
||||
if (state.type_execution_depth == 0) state.type_execution_remaining = 4096
|
||||
state.type_execution_depth += 1
|
||||
defer state.type_execution_depth -= 1
|
||||
fields := arraylist.init(ast.NodeId, state.allocator)
|
||||
defer arraylist.deinit(&fields)
|
||||
if (!try inline_sum_fields(state, annotation, &fields)) return no_node
|
||||
tagged := false
|
||||
for (fields.items) |member| {
|
||||
payload :: try canonical_type(state, ast.field(&state.tree, member).type?)
|
||||
if (payload == no_node) return no_node
|
||||
resolved :: binding(state, payload)
|
||||
if (resolved.kind != .builtin or resolved.data.builtin != .void) tagged = true
|
||||
}
|
||||
span :: ast.node_span(&state.tree, annotation)
|
||||
result :: if (tagged) try ast.add_union_type(state.builder, span, null, true, fields.items)
|
||||
else try ast.add_enum_type(state.builder, span, null, fields.items)
|
||||
try refresh(state)
|
||||
return result
|
||||
}
|
||||
|
||||
# expand dispatch, not the subject: ordinary checking/lowering retains its single
|
||||
# evaluation and by-value payload captures. only tags are compile-time constants.
|
||||
@hide:file
|
||||
@@ -752,16 +851,18 @@ expand_inline_match proc(state @mut State, id, subject_type ast.NodeId) void ! E
|
||||
tag_only :: kind == .union_tag_type
|
||||
members_type := annotation
|
||||
if (tag_only) members_type = try canonical_type(state, ast.type_operand(&state.tree, annotation))
|
||||
if (members_type != no_node) members_type = try inline_match_shape(state, members_type)
|
||||
if (members_type == no_node) {
|
||||
try reject(state, id, .invalid_inline_match_subject)
|
||||
if (state.evaluation_diagnostic != diagnostics.no_diagnostic) try reject_evaluation(state, id)
|
||||
else try reject(state, id, .invalid_sum_type)
|
||||
return
|
||||
}
|
||||
tagged := false
|
||||
if (kind == .union_type) {
|
||||
view :: ast.union_type(&state.tree, annotation)
|
||||
if (!tag_only and ast.expr_kind(&state.tree, members_type) == .union_type) {
|
||||
view :: ast.union_type(&state.tree, members_type)
|
||||
tagged = view.auto_tag or view.tag != null
|
||||
}
|
||||
if (kind != .enum_type and !tag_only and !tagged) {
|
||||
if (ast.expr_kind(&state.tree, members_type) != .enum_type and !tag_only and !tagged) {
|
||||
try reject(state, id, .invalid_inline_match_subject)
|
||||
return
|
||||
}
|
||||
@@ -791,8 +892,9 @@ expand_inline_match proc(state @mut State, id, subject_type ast.NodeId) void ! E
|
||||
tag_type := annotation
|
||||
if (static_capture != null) {
|
||||
if (tagged) {
|
||||
union_view :: ast.union_type(&state.tree, annotation)
|
||||
if (union_view.tag) |tag| tag_type = try canonical_type(state, tag)
|
||||
union_view :: ast.union_type(&state.tree, members_type)
|
||||
if (kind != .sum_type and union_view.tag != null)
|
||||
tag_type = try canonical_type(state, union_view.tag?)
|
||||
else {
|
||||
concrete :: try materialize(state, Value{ kind = .type, node = annotation })
|
||||
tag_type = try ast.add_union_tag_type(state.builder, span, concrete.node)
|
||||
@@ -900,7 +1002,10 @@ specialize_match proc(state @mut State, id, statement ast.NodeId) bool ! Error {
|
||||
arm :: ast.match_arm_view(&state.tree, arm_id)
|
||||
if (explicit or arm.capture == null or ast.match_pattern_count(&state.tree, arm_id) == 0) continue
|
||||
if (hashmap.get(&state.capture_types, arm.capture?) != null) continue
|
||||
if (annotation == no_node) annotation = try expression_type(state, subject, 0)
|
||||
if (annotation == no_node) {
|
||||
annotation = try expression_type(state, subject, 0)
|
||||
if (annotation != no_node) annotation = try inline_match_shape(state, annotation)
|
||||
}
|
||||
if (annotation == no_node or ast.expr_kind(&state.tree, annotation) != .union_type) break
|
||||
pattern :: ast.match_pattern(&state.tree, arm_id, 0)
|
||||
if (ast.expr_kind(&state.tree, pattern) != .enum_literal) continue
|
||||
@@ -1521,14 +1626,17 @@ serialize_value proc(
|
||||
for (record.fields) |field, i| {
|
||||
if (record.is_union and !ir.union_active(module, value, i)) continue
|
||||
if (record.is_union and field.type.kind == .void) {
|
||||
result = try ast.add_enum_literal(state.builder, span, field.name?)
|
||||
name :: try member_name(state, field.name?)
|
||||
result = try ast.add_enum_literal(state.builder, span, name)
|
||||
try refresh(state)
|
||||
return result
|
||||
}
|
||||
child :: ir.aggregate_item(module, value, i)
|
||||
node :: try serialize_value(state, module, checked, field.type, child, span)
|
||||
if (node == no_node) return no_node
|
||||
generated :: try ast.add_field(state.builder, span, field.name, null, node)
|
||||
name := field.name
|
||||
if (name != null) name = try member_name(state, name?)
|
||||
generated :: try ast.add_field(state.builder, span, name, null, node)
|
||||
try refresh(state)
|
||||
try arraylist.append(&fields, generated)
|
||||
}
|
||||
@@ -1556,7 +1664,8 @@ serialize_value proc(
|
||||
magnitude :: if (negative) u64(-(value.data.integer + 1)) + 1 else value.data.unsigned
|
||||
for (enumeration.members) |member| {
|
||||
if (member.magnitude == magnitude and member.negative == negative) {
|
||||
result = try ast.add_enum_literal(state.builder, span, member.name)
|
||||
name :: try member_name(state, member.name)
|
||||
result = try ast.add_enum_literal(state.builder, span, name)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -2096,8 +2205,10 @@ type_match_capture proc(state @mut State, id ast.NodeId) ast.NodeId ! Error {
|
||||
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))
|
||||
shape :: try inline_match_shape(state, annotation)
|
||||
if (shape == no_node) return no_node
|
||||
for (0..ast.record_field_count(&state.tree, shape)) |i| {
|
||||
field :: ast.field(&state.tree, ast.record_field(&state.tree, shape, i))
|
||||
if (
|
||||
field.name != null and payload.name != null
|
||||
and ast.name_id(&state.tree, field.name?) == ast.name_id(&state.tree, payload.name?)
|
||||
|
||||
Reference in New Issue
Block a user