fix comptime specialization, implement io.print
This commit is contained in:
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user