array reflection

This commit is contained in:
2026-07-22 10:12:12 +02:00
parent 0d04925b3a
commit 07e89e23e1
12 changed files with 675 additions and 68 deletions
+153 -14
View File
@@ -325,6 +325,8 @@ Ct_State :: struct {
diagnostic: source.Diagnostic_Id,
silent: bool,
demanded: ^[dynamic]Spec_Id,
promoted_cells: [dynamic]Ct_Cell_Id,
promoted_globals: [dynamic]hir.Global_Id,
}
Ct_Defer :: struct {
@@ -359,6 +361,8 @@ ct_state_make :: proc(
state.bindings.allocator = checker.allocator
state.error_refinements.allocator = checker.allocator
state.defers.allocator = checker.allocator
state.promoted_cells.allocator = checker.allocator
state.promoted_globals.allocator = checker.allocator
for value in values {
if value.kind == .Integer {
id := ct_add_value(&state, Ct_Value{kind=.Integer, type=value.type, integer=value.value})
@@ -397,6 +401,8 @@ ct_state_destroy :: proc(state: ^Ct_State) {
delete(state.bindings)
delete(state.error_refinements)
delete(state.defers)
delete(state.promoted_cells)
delete(state.promoted_globals)
}
ct_add_value :: proc(state: ^Ct_State, value: Ct_Value) -> Ct_Value_Id {
@@ -703,7 +709,7 @@ ct_coerce_value :: proc(state: ^Ct_State, id: Ct_Value_Id, expected: types.Type,
}
return INVALID_CT_VALUE, ct_failf(
state, .Not_Comptime, span, "cannot implicitly convert %s to %s at comptime",
types.name(value.type), types.name(expected),
type_label(state.checker, value.type), type_label(state.checker, expected),
)
}
@@ -841,6 +847,112 @@ ct_value_references_dead_storage :: proc(state: ^Ct_State, id: Ct_Value_Id) -> b
return false
}
ct_materialize_array_pointer :: proc(
state: ^Ct_State,
value: Ct_Value,
span: source.Span,
) -> (hir.Expr_Id, bool) {
checker := state.checker
store := &checker.module.types
pointer, pointer_ok := types.node(store, value.type)
place_id := Ct_Place_Id(value.index)
if !pointer_ok || pointer.kind != .Pointer || pointer.mutable ||
place_id == INVALID_CT_PLACE || int(place_id) >= len(state.places) {
return hir.INVALID_EXPR, false
}
place := state.places[place_id]
if place.cell == INVALID_CT_CELL || int(place.cell) >= len(state.cells) || len(ct_place_path(state, place)) != 0 {
return hir.INVALID_EXPR, false
}
root_id := state.cells[place.cell].value
if root_id == INVALID_CT_VALUE || int(root_id) >= len(state.values) {
return hir.INVALID_EXPR, false
}
root := state.values[root_id]
array, array_ok := types.node(store, root.type)
if !array_ok || array.kind != .Array ||
(pointer.many && (value.active != 0 || !types.equal(pointer.child, array.child))) ||
(!pointer.many && (value.active != -1 || !types.equal(pointer.child, root.type))) {
return hir.INVALID_EXPR, false
}
global_id := hir.INVALID_GLOBAL
for cell, index in state.promoted_cells {
if cell == place.cell {
global_id = state.promoted_globals[index]
break
}
}
if global_id == hir.INVALID_GLOBAL {
root_expr := ct_materialize_value(state, root_id, span, root.type)
if root_expr == hir.INVALID_EXPR || expr_problematic(checker, root_expr) {
return hir.INVALID_EXPR, false
}
global_id = hir.Global_Id(len(checker.ast_module.globals)+len(checker.anon_globals))
append(&checker.anon_globals, hir.Global{
name=symbol.intern(checker.symbols, "__comptime.array"),
type=root.type,
expr=root_expr,
eager=true,
writable=false,
external=false,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append(&state.promoted_cells, place.cell)
append(&state.promoted_globals, global_id)
}
if checker.current_build_ctx != nil {
add_unique_global(checker.current_build_ctx.global_reads, global_id)
}
global := add_hir_expr(checker, hir.Expr{
kind=.Global, span=span, type=root.type, target=hir.global_ref(global_id),
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
})
address := add_hir_expr(checker, hir.Expr{
kind=.Address, span=span, type=types.pointer(store, root.type, false, false), left=global,
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
})
return coerce_expr(checker, address, value.type, span), true
}
ct_materialize_array_slice :: proc(
state: ^Ct_State,
value: Ct_Value,
span: source.Span,
) -> (hir.Expr_Id, bool) {
store := &state.checker.module.types
slice, slice_ok := types.node(store, value.type)
place_id := Ct_Place_Id(value.index)
if !slice_ok || slice.kind != .Slice || slice.mutable || value.start != 0 ||
place_id == INVALID_CT_PLACE || int(place_id) >= len(state.places) {
return hir.INVALID_EXPR, false
}
place := state.places[place_id]
if place.cell == INVALID_CT_CELL || int(place.cell) >= len(state.cells) || len(ct_place_path(state, place)) != 0 {
return hir.INVALID_EXPR, false
}
root_id := state.cells[place.cell].value
if root_id == INVALID_CT_VALUE || int(root_id) >= len(state.values) {
return hir.INVALID_EXPR, false
}
root := state.values[root_id]
array, array_ok := types.node(store, root.type)
if !array_ok || array.kind != .Array || u64(value.count) != array.count ||
!types.equal(slice.child, array.child) {
return hir.INVALID_EXPR, false
}
pointer := Ct_Value{
kind=.Pointer,
type=types.pointer(store, root.type, false, false),
index=value.index,
active=-1,
}
address, ok := ct_materialize_array_pointer(state, pointer, span)
if !ok {
return hir.INVALID_EXPR, false
}
return coerce_expr(state.checker, address, value.type, span), true
}
ct_materialize_value :: proc(
state: ^Ct_State,
id: Ct_Value_Id,
@@ -893,11 +1005,13 @@ ct_materialize_value :: proc(
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .String:
return add_hir_expr(checker, hir.Expr{
kind=.String, span=span, type=value.type, integer=i64(value.index),
literal_type := string_literal_type(checker, value.index)
literal := add_hir_expr(checker, hir.Expr{
kind=.String, span=span, type=literal_type, integer=i64(value.index),
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return coerce_expr(checker, literal, value.type, span)
case .Range:
children := ct_child_slice(state, value)
args := make([]hir.Expr_Id, 2, checker.allocator)
@@ -942,9 +1056,20 @@ ct_materialize_value :: proc(
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Pointer, .Slice:
case .Pointer:
if result, ok := ct_materialize_array_pointer(state, value, span); ok {
return result
}
if state.diagnostic == source.INVALID_DIAGNOSTIC {
state.diagnostic = source.add(checker.diagnostics, span, "comptime storage pointers and slices cannot materialize as runtime memory")
state.diagnostic = source.add(checker.diagnostics, span, "only immutable pointers to whole comptime arrays can materialize as runtime memory")
}
return invalid_hir_expr(checker, span, state.diagnostic, value.type)
case .Slice:
if result, ok := ct_materialize_array_slice(state, value, span); ok {
return result
}
if state.diagnostic == source.INVALID_DIAGNOSTIC {
state.diagnostic = source.add(checker.diagnostics, span, "only immutable full-array comptime slices can materialize as runtime memory")
}
return invalid_hir_expr(checker, span, state.diagnostic, value.type)
case .Function:
@@ -1184,6 +1309,9 @@ ct_eval_expr :: proc(
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
if !symbol.is_valid(expr.name) {
return ct_eval_tuple_field_value(state, base_id, expr.integer, expr.span)
}
return ct_eval_field_value(state, base_id, expr.name, expr.span)
case .Index:
index_id, index_flow, index_ok := ct_eval_expr(state, expr.right, types.USIZE, depth+1)
@@ -1419,8 +1547,9 @@ ct_eval_array_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Typ
has_expected = false
result_type = types.INVALID
}
start := u32(len(state.children))
for arg in expr.args {
values := make([]Ct_Value_Id, len(expr.args), checker.allocator)
defer delete(values, checker.allocator)
for arg, index in expr.args {
value, flow, ok := ct_eval_expr(state, arg, element_type, depth+1)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
@@ -1430,7 +1559,7 @@ ct_eval_array_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Typ
} else if !types.equal(element_type, state.values[value].type) {
element_type = types.widest(element_type, state.values[value].type)
}
append(&state.children, value)
values[index] = value
}
if !types.is_valid(element_type) {
element_type = types.I64
@@ -1438,14 +1567,15 @@ ct_eval_array_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Typ
if !has_expected {
result_type = types.array(store, element_type, u64(len(expr.args)), false)
}
children := state.children[int(start):int(start)+len(expr.args)]
for &child in children {
for &child in values {
coerced, ok := ct_coerce_value(state, child, element_type, expr.span)
if !ok {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
child = coerced
}
start := u32(len(state.children))
append(&state.children, ..values)
return ct_add_value(state, Ct_Value{kind=.Array, type=result_type, start=start, count=u32(len(expr.args))}), ct_flow(.Normal), true
}
@@ -1460,8 +1590,6 @@ ct_eval_struct_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Ty
target_pkg, available := expr_package(checker, expr, state.pkg, state.file, false)
struct_type = types.find_named(store, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, state.file))) if available else types.INVALID
struct_type = types.resolve_alias(struct_type, store)
} else if expr.tuple {
struct_type = types.INVALID
} else {
struct_type = types.resolve_alias(expected, store)
}
@@ -2694,11 +2822,12 @@ ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Spa
checker := state.checker
store := &checker.module.types
typeinfo_type := std_named_type(checker, "@std/meta", "TypeInfo")
arrayinfo_type := std_named_type(checker, "@std/meta", "ArrayInfo")
fieldinfo_type := std_named_type(checker, "@std/meta", "FieldInfo")
recordinfo_type := std_named_type(checker, "@std/meta", "RecordInfo")
enuminfo_type := std_named_type(checker, "@std/meta", "EnumInfo")
layout_type := std_named_type(checker, "@std/meta", "Layout")
if !types.is_valid(typeinfo_type) || !types.is_valid(fieldinfo_type) ||
if !types.is_valid(typeinfo_type) || !types.is_valid(arrayinfo_type) || !types.is_valid(fieldinfo_type) ||
!types.is_valid(recordinfo_type) || !types.is_valid(enuminfo_type) || !types.is_valid(layout_type) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Not_Comptime, span,
@@ -2738,6 +2867,16 @@ ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Spa
if !variant_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "@std/meta TypeInfo is malformed")
}
if tag == "array" {
child_value := ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(item.child)})
len_value := ct_add_value(state, Ct_Value{kind=.Integer, type=types.USIZE, integer=i128(item.count)})
array_value := ct_struct_value(state, arrayinfo_type, []Ct_Value_Id{child_value, len_value})
payload_start := u32(len(state.children))
append(&state.children, array_value)
return ct_add_value(state, Ct_Value{
kind=.Struct, type=typeinfo_type, start=payload_start, count=1, active=i64(variant_index),
}), ct_flow(.Normal), true
}
if tag == "enum" {
members := types.enum_members_for(store, resolved)
field_values := make([]Ct_Value_Id, len(members), checker.allocator)
@@ -3659,7 +3798,7 @@ eval_static_comptime_value :: proc(
values: []Comptime_Value = nil,
diagnose := false,
) -> (Comptime_Value, bool) {
state := ct_state_make(checker, pkg, file, values=values, diagnose=false)
state := ct_state_make(checker, pkg, file, values=values, diagnose=diagnose)
defer ct_state_destroy(&state)
id, flow, ok := ct_eval_expr(&state, expr, declared, 0)
if ok && flow.kind == .Normal {