feat: support existing enum map initialization and lookup

This commit is contained in:
2026-10-29 16:09:16 +01:00
parent 6f892bbfcc
commit 026fe36325
12 changed files with 409 additions and 27 deletions
+47 -4
View File
@@ -924,6 +924,20 @@ collect_declaration proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
state.cells[usize(id)].fixed = true
state.cells[usize(id)].type = invalid_type(binding.data.diagnostic)
}
if (ast.expr_kind(state.tree, declaration.initializer) == .literal_undefined) {
type :: state.cells[usize(id)].type
if (
!state.cells[usize(id)].local or declaration.mutability != .variable
or type.kind == .none or type.kind == .type_value or has_inferred_count(state, type)
) {
try fail(state, declaration.initializer, .invalid_undefined, ast.node_span(state.tree, declaration.initializer))
} else {
state.cells[usize(declaration.initializer)].role = .expression
seed(state, declaration.initializer, type)
}
try depend(state, declaration.initializer, id)
return
}
try collect_expression(state, declaration.initializer)
if (
type_syntax(ast.expr_kind(state.tree, declaration.initializer))
@@ -1201,7 +1215,7 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
.literal_string: state.cells[usize(id)].type = Type{ kind = .string }
.literal_null: state.cells[usize(id)].type = Type{ kind = .null }
.literal_void: state.cells[usize(id)].type = Type{ kind = .void }
.literal_undefined: try fail(state, id, .comptime_argument_required, ast.node_span(tree, id))
.literal_undefined: try fail(state, id, .invalid_undefined, ast.node_span(tree, id))
.builtin_call: try collect_builtin(state, id)
.slice: {
view :: ast.slice_expr(tree, id)
@@ -1550,6 +1564,17 @@ demand proc(state @mut State, id ast.NodeId, expected Type, code diagnostics.Cod
seed(state, id, expected)
return
}
if (mem.eql(name, "some") and expected.kind != .fallible) {
state.cells[index].expected = expected
if (expected.kind == .optional) {
seed(state, id, expected)
if (ast.call_arg_count(state.tree, id) == 1) {
try demand(state, ast.call_arg(state.tree, id, 0),
state.arrays.items[usize(expected.array_type)].element, code)
}
}
return
}
}
if (expected.kind == .fallible) {
descriptor :: state.arrays.items[usize(expected.array_type)]
@@ -2368,7 +2393,7 @@ c_abi_type proc(state @State, type Type) bool {
@hide:file
builtin_name proc(state @State, id ast.NodeId) []u8 {
return ast.source_text(state.tree, ast.node_span(state.tree, ast.builtin_call(state.tree, id).name))
return ast.literal_text(state.tree, ast.builtin_call(state.tree, id).name)
}
@hide:file
@@ -2460,13 +2485,31 @@ infer_builtin proc(state @mut State, id ast.NodeId, defaults bool) Type ! (mem.A
layout :: mem.eql(name, "sizeof") or mem.eql(name, "alignof")
cast :: mem.eql(name, "ptrcast") or mem.eql(name, "constcast")
memory :: mem.eql(name, "memcopy") or mem.eql(name, "memset")
valid_count :: (layout and count == 1) or (cast and (count == 1 or count == 2)) or (memory and count == 2)
some :: mem.eql(name, "some")
valid_count :: (layout and count == 1) or (cast and (count == 1 or count == 2)) or (memory and count == 2) or (some and count == 1)
if (!valid_count) {
code diagnostics.Code :: if (layout or cast or memory) .argument_count_mismatch else .invalid_operand
code diagnostics.Code :: if (layout or cast or memory or some) .argument_count_mismatch else .invalid_operand
if (defaults) try fail(state, id, code, span)
return group_type(state, id)
}
if (layout) return group_type(state, id)
if (some) {
result :: state.cells[root(state, usize(id))].expected
if (result.kind != .optional) {
if (defaults) try fail(state, id, .invalid_operand, span)
return group_type(state, id)
}
child :: state.arrays.items[usize(result.array_type)].element
operand :: ast.call_arg(state.tree, id, 0)
try demand(state, operand, child, .type_mismatch)
actual :: try infer_shape(state, operand, defaults)
if (
defaults and actual.kind != .none and actual.kind != .invalid
and !assignable_value(state, operand, actual, child)
) try fail(state, operand, .type_mismatch, ast.node_span(state.tree, operand))
seed(state, id, result)
return group_type(state, id)
}
if (cast) {
result := state.cells[root(state, usize(id))].expected
if (count == 2) result = state.types[usize(ast.call_arg(state.tree, id, 0))]
+37 -4
View File
@@ -1571,10 +1571,19 @@ specialize_call proc(state @mut State, call, function ast.NodeId) void ! Error {
if (view.comptime) continue
argument :: runtime_arguments.items[position]
position += 1
contextual :: flexible(state, argument)
contextual := flexible(state, argument) or contextual_literal(state, argument)
if (ast.expr_kind(&state.tree, argument) == .record_literal) {
annotation :: ast.record_literal(&state.tree, argument).type
contextual = annotation == null or ast.expr_kind(&state.tree, annotation?) == .enum_literal
}
if (contextual != (phase == 1)) continue
actual :: try expression_type(state, argument, 0)
if (actual == no_node or !try infer(state, function, view.type, actual, arguments, contextual)) {
# contextual literals need not have an independent type; the rewritten
# call checks them against the substituted signature after inference.
if (
(actual == no_node or !try infer(state, function, view.type, actual, arguments, contextual))
and !contextual
) {
try reject(state, call, .cannot_infer_generic)
return
}
@@ -1872,6 +1881,8 @@ evaluation_owned proc(state @State, id ast.NodeId, flags []mut u8) void {
@hide:file
contextual_literal proc(state @State, id ast.NodeId) bool {
kind :: ast.expr_kind(&state.tree, id)
if (kind == .builtin_call)
return mem.eql(ast.literal_text(&state.tree, ast.builtin_call(&state.tree, id).name), "some")
return kind == .record_literal or kind == .array_literal or kind == .enum_literal
}
@@ -1937,6 +1948,17 @@ serialize_value proc(
state @mut State, module @ir.Module, checked @typecheck.Result,
type typecheck.Type, value ir.Value, span ast.Span,
) ast.NodeId ! Error {
uninitialized := value.uninitialized
if (ir.aggregate(value.type)) {
if (module.arrays.items[usize(value.array_type)].is_union)
uninitialized = ir.union_tag(module, value).uninitialized
else if (type.kind == .optional)
uninitialized = ir.aggregate_item(module, value, 0).uninitialized
}
if (uninitialized) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .read_before_initialization, span)
return no_node
}
result := no_node
if (type.kind == .array) {
array :: typecheck.array_type(checked, type.array_type)
@@ -2013,7 +2035,12 @@ serialize_value proc(
result = try ast.add_literal(state.builder, .null, span)
} else {
child :: ir.aggregate_item(module, value, 1)
return try serialize_value(state, module, checked, container.element, child, span)
payload :: try serialize_value(state, module, checked, container.element, child, span)
if (payload == no_node) return no_node
name :: try ast.add_generated_identifier(state.builder, span,
try strpool.intern(state.strings, "some"), "some")
arguments [1]ast.NodeId :: [payload]
result = try ast.add_builtin_call(state.builder, span, name, arguments[..])
}
} else if (ir.integer_bits(value.type) != 0 and type.kind != .pointer) {
negative :: !ir.unsigned_integer(value.type) and value.data.integer < 0
@@ -2063,6 +2090,9 @@ constant_equal proc(state @mut State, left, right ast.NodeId) bool ! Error {
}
return true
}
if (kind == .builtin_call and contextual_literal(state, left) and contextual_literal(state, right)) {
return try constant_equal(state, ast.call_arg(&state.tree, left, 0), ast.call_arg(&state.tree, right, 0))
}
if (kind == .enum_literal)
return ast.name_id(&state.tree, ast.enum_literal(&state.tree, left)) == ast.name_id(&state.tree, ast.enum_literal(&state.tree, right))
return try value_equal(state, try evaluate(state, left), try evaluate(state, right))
@@ -2092,6 +2122,8 @@ constant_hash proc(state @mut State, id ast.NodeId) usize ! Error {
}
return result
}
if (kind == .builtin_call and contextual_literal(state, id))
return mix(hash, try constant_hash(state, ast.call_arg(&state.tree, id, 0)))
if (kind == .enum_literal) return mix(hash, usize(ast.name_id(&state.tree, ast.enum_literal(&state.tree, id))))
return try value_hash(state, try evaluate(state, id))
}
@@ -2199,7 +2231,8 @@ evaluate_ir_body proc(state @mut State, at, body, result_type ast.NodeId) Value
source_type :: typecheck.type_of(&checked, result_type)
node :: try serialize_value(state, &module, &checked, source_type, value, span)
if (node == no_node) {
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_value_escape, span)
if (state.evaluation_diagnostic == diagnostics.no_diagnostic)
state.evaluation_diagnostic = try diagnostics.add(state.diagnostics, .comptime_value_escape, span)
return Value{}
}
if (ir.aggregate(value.type) or source_type.kind == .enumeration or source_type.kind == .void)