fix comptime specialization, implement io.print

This commit is contained in:
2026-07-15 08:24:50 +02:00
parent 0b2055d64b
commit 1165cfb7c0
7 changed files with 733 additions and 155 deletions
+71 -26
View File
@@ -210,6 +210,7 @@ ct_place_id :: proc(index: int) -> Ct_Place_Id {
Ct_Value_Kind :: enum u8 {
Invalid,
Void,
Undefined,
Integer,
Float,
Bool,
@@ -460,6 +461,34 @@ ct_child_slice :: proc(state: ^Ct_State, value: Ct_Value) -> []Ct_Value_Id {
return state.children[start:end]
}
ct_value_contains_undefined :: proc(state: ^Ct_State, id: Ct_Value_Id, depth := 0) -> bool {
if depth > 64 || id == INVALID_CT_VALUE || int(id) >= len(state.values) {
return false
}
value := state.values[id]
if value.kind == .Undefined {
return true
}
for child in ct_child_slice(state, value) {
if ct_value_contains_undefined(state, child, depth+1) {
return true
}
}
return false
}
ct_observe_value :: proc(state: ^Ct_State, id: Ct_Value_Id, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
if id == INVALID_CT_VALUE || int(id) >= len(state.values) {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
if state.values[id].kind == .Undefined {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Not_Comptime, span, "cannot read an undefined value at comptime",
)
}
return id, ct_flow(.Normal), true
}
ct_fail :: proc(state: ^Ct_State, kind: Ct_Error_Kind, span: source.Span, message: string) -> bool {
if state.error == .None {
state.error = kind
@@ -795,6 +824,11 @@ ct_materialize_value :: proc(
}
value := state.values[materialized]
#partial switch value.kind {
case .Undefined:
if state.diagnostic == source.INVALID_DIAGNOSTIC {
state.diagnostic = source.add(checker.diagnostics, span, "cannot materialize an undefined comptime value")
}
return invalid_hir_expr(checker, span, state.diagnostic, value.type)
case .Integer:
if types.is_enum(value.type, &checker.module.types) {
int_value := i64(value.integer) if value.integer < 0 else transmute(i64)u64(value.integer)
@@ -920,20 +954,8 @@ ct_undefined_value :: proc(state: ^Ct_State, value_type: types.Type, depth := 0)
return INVALID_CT_VALUE, false
}
store := &state.checker.module.types
if types.is_concrete_integer(value_type) || types.is_enum(value_type, store) {
return ct_add_value(state, Ct_Value{kind=.Integer, type=value_type}), true
}
if types.is_bool(value_type) {
return ct_add_value(state, Ct_Value{kind=.Bool, type=value_type}), true
}
if types.is_float(value_type, state.checker.target) {
return ct_add_value(state, Ct_Value{kind=.Float, type=value_type}), true
}
item, ok := types.node(store, value_type)
if !ok {
return INVALID_CT_VALUE, false
}
if item.kind == .Array {
if ok && item.kind == .Array {
children := make([]Ct_Value_Id, int(item.count), state.checker.allocator)
defer delete(children, state.checker.allocator)
for &child in children {
@@ -944,7 +966,7 @@ ct_undefined_value :: proc(state: ^Ct_State, value_type: types.Type, depth := 0)
append(&state.children, ..children)
return ct_add_value(state, Ct_Value{kind=.Array, type=value_type, start=start, count=u32(len(children))}), true
}
if item.kind == .Struct && !item.opaque {
if ok && item.kind == .Struct && !item.opaque {
fields := types.fields_for(store, value_type)
children := make([]Ct_Value_Id, len(fields), state.checker.allocator)
defer delete(children, state.checker.allocator)
@@ -956,7 +978,7 @@ ct_undefined_value :: proc(state: ^Ct_State, value_type: types.Type, depth := 0)
append(&state.children, ..children)
return ct_add_value(state, Ct_Value{kind=.Struct, type=value_type, start=start, count=u32(len(children))}), true
}
return INVALID_CT_VALUE, false
return ct_add_value(state, Ct_Value{kind=.Undefined, type=value_type}), true
}
ct_eval_expr :: proc(
@@ -992,7 +1014,7 @@ ct_eval_expr :: proc(
case .Name:
if !symbol.is_valid(expr.qualifier) {
if index, ok := ct_find_binding_index(state, expr.name); ok {
return ct_binding_value(state, index), ct_flow(.Normal), true
return ct_observe_value(state, ct_binding_value(state, index), expr.span)
}
if value, ok := current_comptime_value(checker, expr.name); ok {
if value.kind == .Integer {
@@ -1113,7 +1135,10 @@ ct_eval_expr :: proc(
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime slice index out of bounds")
}
value, value_ok := ct_place_get(state, place)
return value, ct_flow(.Normal), value_ok
if !value_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
return ct_observe_value(state, value, expr.span)
}
if base.kind == .Pointer {
pointer_item, pointer_ok := types.node(store, base.type)
@@ -1124,7 +1149,10 @@ ct_eval_expr :: proc(
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime pointer index out of bounds")
}
value, value_ok := ct_place_get(state, place)
return value, ct_flow(.Normal), value_ok
if !value_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
return ct_observe_value(state, value, expr.span)
}
if array_item, array_ok := types.node(store, pointer_item.child); array_ok && array_item.kind == .Array {
base_place, _, _ := ct_pointer_place(state, base)
@@ -1133,7 +1161,10 @@ ct_eval_expr :: proc(
}
place := ct_extend_place(state, base_place, Ct_Path_Elem{kind=.Index, index=u32(index_value)}, array_item.child, pointer_item.mutable && array_item.mutable)
value, value_ok := ct_place_get(state, place)
return value, ct_flow(.Normal), value_ok
if !value_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
return ct_observe_value(state, value, expr.span)
}
}
}
@@ -1249,7 +1280,7 @@ ct_eval_expr :: proc(
if !value_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime pointer no longer points to live storage")
}
return value, ct_flow(.Normal), true
return ct_observe_value(state, value, expr.span)
case .Slice:
return ct_eval_slice_expr(state, expr, depth+1)
case .Undefined:
@@ -1611,7 +1642,7 @@ ct_eval_field_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, name: symbol
}
field_place := ct_extend_place(state, place, Ct_Path_Elem{kind=.Field, index=u32(index)}, field.type, false)
if value, value_ok := ct_place_get(state, field_place); value_ok {
return value, ct_flow(.Normal), true
return ct_observe_value(state, value, span)
}
}
}
@@ -1627,12 +1658,12 @@ ct_eval_field_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, name: symbol
if int(base.active) != index || len(children) == 0 || children[0] == INVALID_CT_VALUE {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, span, "variant '%s' has no payload to read", field_name)
}
return children[0], ct_flow(.Normal), true
return ct_observe_value(state, children[0], span)
}
if index < 0 || index >= len(children) || types.is_void(field.type) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, span, "field '%s' has no value", field_name)
}
return children[index], ct_flow(.Normal), true
return ct_observe_value(state, children[index], span)
}
ct_eval_tuple_field_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index: u64, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
@@ -1649,14 +1680,17 @@ ct_eval_tuple_field_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index:
}
field_place := ct_extend_place(state, place, Ct_Path_Elem{kind=.Field, index=u32(field_index)}, field.type, false)
value, value_ok := ct_place_get(state, field_place)
return value, ct_flow(.Normal), value_ok
if !value_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
return ct_observe_value(state, value, span)
}
field_index, _, ok := find_tuple_field(state.checker, base_type, index)
children := ct_child_slice(state, base)
if !ok || field_index < 0 || field_index >= len(children) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "tuple field index is out of bounds")
}
return children[field_index], ct_flow(.Normal), true
return ct_observe_value(state, children[field_index], span)
}
ct_eval_index_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index: int, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
@@ -1670,7 +1704,7 @@ ct_eval_index_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index: int,
if index < 0 || index >= len(children) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime array index out of bounds")
}
return children[index], ct_flow(.Normal), true
return ct_observe_value(state, children[index], span)
}
if base.kind == .String {
if base.index >= u64(len(checker.ast_module.strings)) || index < 0 || index >= len(checker.ast_module.strings[base.index]) {
@@ -2546,6 +2580,12 @@ ct_eval_template_call :: proc(
if !ok {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
if ct_value_contains_undefined(state, value) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Not_Comptime, checker.ast_module.exprs[args[index]].span,
"cannot pass an undefined value at comptime",
)
}
append(&runtime_values, value)
append(&runtime_types, param_type)
append(&runtime_names, param.name)
@@ -2583,6 +2623,11 @@ ct_eval_template_call :: proc(
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, span, "comptime function '%s' did not return a value", symbol_text(checker, function.name))
}
result := flow.value
if ct_value_contains_undefined(state, result) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Not_Comptime, span, "comptime function returned an undefined value",
)
}
if ct_value_references_dead_storage(state, result) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime function returned a pointer to expired storage")
}