feat: materialize readonly typed compile-time slices

This commit is contained in:
2026-10-30 17:02:21 +01:00
parent cc39c96fc0
commit 32a7a1de24
10 changed files with 741 additions and 40 deletions
+75
View File
@@ -48,6 +48,9 @@ State :: struct {
arguments arraylist.ArrayList(ir.ValueId)
cleanups arraylist.ArrayList(ast.NodeId)
records []mut ir.ArrayId
slice_elements arraylist.ArrayList(typecheck.Type)
slice_backings arraylist.ArrayList(ast.NodeId)
evaluation_functions ?@mut arraylist.ArrayList(ast.NodeId) = null
function usize = 0
block ir.BlockId = ir.BlockId(0)
terminated bool = false
@@ -156,11 +159,16 @@ lower_module proc(
storage = storage,
arrays = arrays,
records = records,
slice_elements = arraylist.init(typecheck.Type, allocator),
slice_backings = arraylist.init(ast.NodeId, allocator),
evaluation_functions = functions,
arguments = arraylist.init(ir.ValueId, allocator),
cleanups = arraylist.init(ast.NodeId, allocator),
}
defer arraylist.deinit(&state.arguments)
defer arraylist.deinit(&state.cleanups)
defer arraylist.deinit(&state.slice_elements)
defer arraylist.deinit(&state.slice_backings)
# allocate every package identity before lowering any initializer or body.
for (0..ast.package_decl_count(tree)) |i| {
@@ -204,6 +212,22 @@ lower_module proc(
.import, .invalid: {}
}
}
backing_index := usize(0)
while (backing_index < state.slice_backings.items.len) {
try lower_slice_backing(&state, state.slice_backings.items[backing_index])
backing_index += 1
}
# source element shapes are metadata, not record layout dependencies. defer
# them until recursive records have cached their completed physical layout.
cursor := usize(0)
while (cursor < state.slice_elements.items.len) {
type :: state.slice_elements.items[cursor]
child :: typecheck.array_type(checked, type.array_type).element
shape :: state.arrays[usize(type.array_type)]
element_shape :: try array_shape(&state, child)
module.arrays.items[usize(shape)].element_array = element_shape
cursor += 1
}
return module
}
@@ -308,6 +332,10 @@ prepare_container proc(state @mut State, type typecheck.Type) ir.ArrayId ! (mem.
}
shape :: try ir.add_record(state.module, fields[..])
state.arrays[index] = shape
if (type.kind == .slice) {
state.module.arrays.items[usize(shape)].element = lower_type(state, descriptor.element)
try arraylist.append(&state.slice_elements, type)
}
return shape
}
@@ -448,6 +476,19 @@ lower_global proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diag
finish(state, value, declaration.span)
}
@hide:file
lower_slice_backing proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) {
global :: state.module.globals.items[usize(state.storage[usize(id)].index)]
state.function = usize(global.initializer)
state.block = ir.BlockId(0)
state.terminated = false
state.module.functions.items[state.function].result = global.type
state.module.functions.items[state.function].result_array_type = global.array_type
backing :: ast.slice_constant(state.tree, id).value
value :: try lower_expression(state, backing)
if (!state.terminated) finish(state, value, ast.node_span(state.tree, id))
}
@hide:file
lower_function proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) {
tree :: state.tree
@@ -2098,6 +2139,40 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
if (trap_type(state, id)) return ir.no_value
return try lower_as(state, ast.typed_constant(tree, id).value, typecheck.type_of(state.checked, id))
}
.slice_constant: {
if (trap_type(state, id)) return ir.no_value
if (state.storage[usize(id)].kind == .none) {
backing :: ast.slice_constant(tree, id).value
initializer :: try add_function(state.module, span)
if (state.evaluation_functions) |declarations|
try arraylist.append(declarations, maxval!(ast.NodeId))
global :: u32(state.module.globals.items.len)
try arraylist.append(&state.module.globals, ir.Global{
type = lower_type(state, typecheck.type_of(state.checked, backing)),
array_type = try array_shape(state, typecheck.type_of(state.checked, backing)),
initializer = initializer,
span = span,
})
state.storage[usize(id)] = Storage{ kind = .global, index = global }
try arraylist.append(&state.slice_backings, id)
}
location :: state.storage[usize(id)]
global :: state.module.globals.items[usize(location.index)]
address :: try emit(state, ir.Instruction{
op = .address_global, type = .address, pointee = global.type,
array_type = global.array_type, slot = location.index, span = span,
})
pointer :: try emit(state, ir.Instruction{
op = .pointer_from_address, type = .pointer, left = address,
pointee = global.type, array_type = global.array_type, span = span,
})
descriptor :: typecheck.array_type(state.checked, typecheck.type_of(state.checked, id).array_type)
backing :: ast.slice_constant(tree, id).value
elements :: typecheck.array_type(state.checked, typecheck.type_of(state.checked, backing).array_type)
extra :: if (descriptor.sentinel != null) usize(1) else usize(0)
len :: elements.len - extra
return try record_pair(state, shape, pointer, try usize_constant(state, len, span), span)
}
.union_constant: {
if (trap_type(state, id)) return ir.no_value
bytes :: ast.union_constant(tree, id).value