reflection foundation, tuples, debug.print
This commit is contained in:
+1634
-208
File diff suppressed because it is too large
Load Diff
@@ -7,20 +7,24 @@ import "../symbol"
|
||||
import "../types"
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
|
||||
COMPTIME_EVAL_QUOTA :: 100_000
|
||||
|
||||
Comptime_Value_Kind :: enum u8 {
|
||||
Integer,
|
||||
Type,
|
||||
String,
|
||||
}
|
||||
|
||||
Comptime_Value :: struct {
|
||||
name: symbol.Id,
|
||||
type: types.Type,
|
||||
value: i128,
|
||||
text: string,
|
||||
kind: Comptime_Value_Kind,
|
||||
}
|
||||
|
||||
@@ -57,6 +61,15 @@ current_comptime_value :: proc(checker: ^Checker, name: symbol.Id) -> (Comptime_
|
||||
return find_comptime_value(checker.current_comptime_values, name)
|
||||
}
|
||||
|
||||
current_static_binding :: proc(checker: ^Checker, name: symbol.Id) -> (Static_Binding, bool) {
|
||||
for index := len(checker.static_bindings)-1; index >= 0; index -= 1 {
|
||||
if checker.static_bindings[index].name == name {
|
||||
return checker.static_bindings[index], true
|
||||
}
|
||||
}
|
||||
return {}, false
|
||||
}
|
||||
|
||||
current_comptime_type :: proc(checker: ^Checker, name: symbol.Id) -> (types.Type, bool) {
|
||||
if value, ok := current_comptime_value(checker, name); ok && value.kind == .Type {
|
||||
return value.type, true
|
||||
@@ -71,7 +84,8 @@ comptime_values_equal :: proc(left, right: []Comptime_Value) -> bool {
|
||||
for value, index in left {
|
||||
other := right[index]
|
||||
if value.name != other.name || value.kind != other.kind || !types.equal(value.type, other.type) ||
|
||||
(value.kind == .Integer && value.value != other.value) {
|
||||
(value.kind == .Integer && value.value != other.value) ||
|
||||
(value.kind == .String && value.text != other.text) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -337,8 +351,22 @@ ct_state_make :: proc(
|
||||
} else if value.kind == .Type {
|
||||
id := ct_add_value(&state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)})
|
||||
ct_bind_value(&state, value.name, types.INVALID, id, false)
|
||||
} else if value.kind == .String {
|
||||
string_id := u64(0)
|
||||
for text, index in checker.ast_module.strings {
|
||||
if text == value.text {
|
||||
string_id = u64(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
id := ct_add_value(&state, Ct_Value{kind=.String, type=value.type, index=string_id})
|
||||
ct_bind_value(&state, value.name, value.type, id, false)
|
||||
}
|
||||
}
|
||||
for binding in checker.static_bindings {
|
||||
id := ct_clone_graph(&state, &checker.static_state, binding.value)
|
||||
ct_bind_value(&state, binding.name, binding.type, id, false)
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -543,6 +571,12 @@ ct_coerce_value :: proc(state: ^Ct_State, id: Ct_Value_Id, expected: types.Type,
|
||||
return id, true
|
||||
}
|
||||
store := &state.checker.module.types
|
||||
if value.kind == .String {
|
||||
if item, ok := types.node(store, expected); ok && item.kind == .Slice && !item.mutable && item.child == types.U8 {
|
||||
value.type = expected
|
||||
return ct_add_value(state, value), true
|
||||
}
|
||||
}
|
||||
if value.kind == .Pointer && types.can_weaken_pointer(value.type, expected, store) {
|
||||
value.type = expected
|
||||
return ct_add_value(state, value), true
|
||||
@@ -881,6 +915,50 @@ ct_materialize_value :: proc(
|
||||
return invalid_hir_expr(checker, span, source.INVALID_DIAGNOSTIC, value.type)
|
||||
}
|
||||
|
||||
ct_undefined_value :: proc(state: ^Ct_State, value_type: types.Type, depth := 0) -> (Ct_Value_Id, bool) {
|
||||
if depth > 64 || !types.is_valid(value_type) {
|
||||
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 {
|
||||
children := make([]Ct_Value_Id, int(item.count), state.checker.allocator)
|
||||
defer delete(children, state.checker.allocator)
|
||||
for &child in children {
|
||||
child, ok = ct_undefined_value(state, item.child, depth+1)
|
||||
if !ok { return INVALID_CT_VALUE, false }
|
||||
}
|
||||
start := u32(len(state.children))
|
||||
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 {
|
||||
fields := types.fields_for(store, value_type)
|
||||
children := make([]Ct_Value_Id, len(fields), state.checker.allocator)
|
||||
defer delete(children, state.checker.allocator)
|
||||
for field, index in fields {
|
||||
children[index], ok = ct_undefined_value(state, field.type, depth+1)
|
||||
if !ok { return INVALID_CT_VALUE, false }
|
||||
}
|
||||
start := u32(len(state.children))
|
||||
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
|
||||
}
|
||||
|
||||
ct_eval_expr :: proc(
|
||||
state: ^Ct_State,
|
||||
expr_id: ast.Expr_Id,
|
||||
@@ -921,6 +999,16 @@ ct_eval_expr :: proc(
|
||||
id := ct_add_value(state, Ct_Value{kind=.Integer, type=value.type, integer=value.value})
|
||||
return id, ct_flow(.Normal), true
|
||||
}
|
||||
if value.kind == .String {
|
||||
string_id := u64(0)
|
||||
for text, index in checker.ast_module.strings {
|
||||
if text == value.text {
|
||||
string_id = u64(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.String, type=value.type, index=string_id}), ct_flow(.Normal), true
|
||||
}
|
||||
return ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(value.type)}), ct_flow(.Normal), true
|
||||
}
|
||||
} else if find_import(checker, state.file, expr.qualifier) == ast.INVALID_IMPORT {
|
||||
@@ -1164,7 +1252,12 @@ ct_eval_expr :: proc(
|
||||
return value, ct_flow(.Normal), true
|
||||
case .Slice:
|
||||
return ct_eval_slice_expr(state, expr, depth+1)
|
||||
case .Undefined, .Keyed:
|
||||
case .Undefined:
|
||||
if value, ok := ct_undefined_value(state, expected); ok {
|
||||
return value, ct_flow(.Normal), true
|
||||
}
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "undefined comptime value requires a concrete scalar or aggregate type")
|
||||
case .Keyed:
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
|
||||
}
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "expression cannot be evaluated at comptime")
|
||||
@@ -1237,9 +1330,49 @@ 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)
|
||||
}
|
||||
if expr.tuple {
|
||||
resolved := types.is_valid(struct_type)
|
||||
fields := types.fields_for(store, struct_type) if resolved else nil
|
||||
if resolved {
|
||||
item, item_ok := types.node(store, struct_type)
|
||||
if !item_ok || item.kind != .Struct || !item.tuple || len(fields) != len(expr.args) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "invalid tuple construction")
|
||||
}
|
||||
}
|
||||
values := make([]Ct_Value_Id, len(expr.args), checker.allocator)
|
||||
inferred_fields := make([]types.Field, len(expr.args), checker.allocator)
|
||||
defer {
|
||||
delete(values, checker.allocator)
|
||||
delete(inferred_fields, checker.allocator)
|
||||
}
|
||||
for arg, index in expr.args {
|
||||
expected_field := fields[index].type if resolved else types.INVALID
|
||||
value, flow, ok := ct_eval_expr(state, arg, expected_field, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
if resolved {
|
||||
value, ok = ct_coerce_value(state, value, expected_field, checker.ast_module.exprs[arg].span)
|
||||
if !ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
} else {
|
||||
inferred_fields[index].type = state.values[value].type
|
||||
}
|
||||
values[index] = value
|
||||
}
|
||||
if !resolved {
|
||||
struct_type = types.struct_anonymous(store, inferred_fields, true)
|
||||
}
|
||||
start := u32(len(state.children))
|
||||
append(&state.children, ..values)
|
||||
return ct_add_value(state, Ct_Value{kind=.Struct, type=struct_type, start=start, count=u32(len(values))}), ct_flow(.Normal), true
|
||||
}
|
||||
if !types.is_record(struct_type, store) || types.is_opaque_struct(struct_type, store) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "unknown or opaque record type '%s'", symbol_text(checker, expr.name))
|
||||
}
|
||||
@@ -1502,6 +1635,30 @@ ct_eval_field_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, name: symbol
|
||||
return children[index], ct_flow(.Normal), true
|
||||
}
|
||||
|
||||
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) {
|
||||
if base_id == INVALID_CT_VALUE || int(base_id) >= len(state.values) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
base := state.values[base_id]
|
||||
base_type := base.type
|
||||
if base.kind == .Pointer {
|
||||
place, child, _ := ct_pointer_place(state, base)
|
||||
field_index, field, ok := find_tuple_field(state.checker, child, index)
|
||||
if place == INVALID_CT_PLACE || !ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "tuple field index is out of bounds")
|
||||
}
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
ct_eval_index_value :: proc(state: ^Ct_State, base_id: Ct_Value_Id, index: int, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
if base_id == INVALID_CT_VALUE || int(base_id) >= len(state.values) {
|
||||
@@ -2045,6 +2202,179 @@ ct_scalar_cast :: proc(state: ^Ct_State, id: Ct_Value_Id, target: types.Type, sp
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "scalar cast requires numeric scalar types")
|
||||
}
|
||||
|
||||
intern_comptime_string :: proc(checker: ^Checker, text: string) -> u64 {
|
||||
string_id := u64(len(checker.ast_module.strings))
|
||||
for value, index in checker.ast_module.strings {
|
||||
if value == text {
|
||||
string_id = u64(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
if string_id == u64(len(checker.ast_module.strings)) {
|
||||
append(&checker.ast_module.strings, strings.clone(text, checker.ast_module.allocator))
|
||||
append(&checker.module.strings, strings.clone(text, checker.allocator))
|
||||
}
|
||||
return string_id
|
||||
}
|
||||
|
||||
ct_reflection_string :: proc(state: ^Ct_State, text: string) -> Ct_Value_Id {
|
||||
checker := state.checker
|
||||
string_id := intern_comptime_string(checker, text)
|
||||
return ct_add_value(state, Ct_Value{
|
||||
kind=.String,
|
||||
type=types.slice(&checker.module.types, types.U8, false),
|
||||
index=string_id,
|
||||
})
|
||||
}
|
||||
|
||||
ct_value_bytes :: proc(state: ^Ct_State, id: Ct_Value_Id) -> (string, bool) {
|
||||
if id == INVALID_CT_VALUE || int(id) >= len(state.values) {
|
||||
return "", false
|
||||
}
|
||||
value := state.values[id]
|
||||
if value.kind == .String {
|
||||
if value.index < u64(len(state.checker.ast_module.strings)) {
|
||||
return state.checker.ast_module.strings[value.index], true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
item, ok := types.container(value.type, &state.checker.module.types)
|
||||
if !ok || item.child != types.U8 || item.mutable ||
|
||||
(value.kind != .Array && value.kind != .Slice) {
|
||||
return "", false
|
||||
}
|
||||
count := int(value.count)
|
||||
bytes := make([]u8, count, state.checker.allocator)
|
||||
defer delete(bytes, state.checker.allocator)
|
||||
for index in 0..<count {
|
||||
child := INVALID_CT_VALUE
|
||||
if value.kind == .Array {
|
||||
children := ct_child_slice(state, value)
|
||||
if index >= len(children) { return "", false }
|
||||
child = children[index]
|
||||
} else {
|
||||
place, _, place_ok := ct_slice_element_place(state, value, index)
|
||||
if !place_ok { return "", false }
|
||||
child, place_ok = ct_place_get(state, place)
|
||||
if !place_ok { return "", false }
|
||||
}
|
||||
integer, integer_ok := ct_integer_value(state, child)
|
||||
if !integer_ok || integer < 0 || integer > 255 { return "", false }
|
||||
bytes[index] = u8(integer)
|
||||
}
|
||||
string_id := intern_comptime_string(state.checker, string(bytes))
|
||||
return state.checker.ast_module.strings[string_id], true
|
||||
}
|
||||
|
||||
ct_struct_value :: proc(state: ^Ct_State, value_type: types.Type, children: []Ct_Value_Id) -> Ct_Value_Id {
|
||||
start := u32(len(state.children))
|
||||
append(&state.children, ..children)
|
||||
return ct_add_value(state, Ct_Value{kind=.Struct, type=value_type, start=start, count=u32(len(children))})
|
||||
}
|
||||
|
||||
ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
store := &checker.module.types
|
||||
typeinfo_type := std_named_type(checker, "@std/meta", "TypeInfo")
|
||||
fieldinfo_type := std_named_type(checker, "@std/meta", "FieldInfo")
|
||||
recordinfo_type := std_named_type(checker, "@std/meta", "RecordInfo")
|
||||
layout_type := std_named_type(checker, "@std/meta", "Layout")
|
||||
if !types.is_valid(typeinfo_type) || !types.is_valid(fieldinfo_type) ||
|
||||
!types.is_valid(recordinfo_type) || !types.is_valid(layout_type) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
|
||||
state, .Not_Comptime, span,
|
||||
"typeinfo! requires importing @std/meta",
|
||||
)
|
||||
}
|
||||
resolved := types.resolve_alias(target, store)
|
||||
item, item_ok := types.node(store, resolved)
|
||||
tag := "invalid"
|
||||
if !item_ok {
|
||||
if types.is_void(resolved) { tag = "void" }
|
||||
else if types.is_anyopaque(resolved) { tag = "anyopaque" }
|
||||
else if types.is_bool(resolved) { tag = "bool" }
|
||||
else if types.is_concrete_integer(resolved) { tag = "integer" }
|
||||
else if types.is_float(resolved, checker.target) { tag = "float" }
|
||||
} else {
|
||||
#partial switch item.kind {
|
||||
case .Array: tag = "array"
|
||||
case .Pointer: tag = "pointer"
|
||||
case .Slice: tag = "slice"
|
||||
case .Range: tag = "range"
|
||||
case .Optional: tag = "optional"
|
||||
case .Function: tag = "function"
|
||||
case .Enum: tag = "enum"
|
||||
case .Struct: tag = "record"
|
||||
case .Union: tag = "union"
|
||||
case .Fallible: tag = "fallible"
|
||||
case .Distinct: tag = "distinct"
|
||||
case .Alias:
|
||||
tag = "invalid"
|
||||
case:
|
||||
tag = "invalid"
|
||||
}
|
||||
}
|
||||
variant_index, _, variant_ok := find_struct_field(checker, typeinfo_type, symbol.intern(checker.symbols, tag))
|
||||
if !variant_ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "@std/meta TypeInfo is malformed")
|
||||
}
|
||||
if tag != "record" {
|
||||
start := u32(len(state.children))
|
||||
return ct_add_value(state, Ct_Value{
|
||||
kind=.Struct, type=typeinfo_type, start=start, count=0, active=i64(variant_index),
|
||||
}), ct_flow(.Normal), true
|
||||
}
|
||||
fields := types.fields_for(store, resolved)
|
||||
field_values := make([]Ct_Value_Id, len(fields), checker.allocator)
|
||||
defer delete(field_values, checker.allocator)
|
||||
for field, index in fields {
|
||||
name := ""
|
||||
if item.tuple {
|
||||
name = fmt.aprintf("%d", index, allocator=checker.allocator)
|
||||
} else {
|
||||
name = symbol_text(checker, symbol.Id(field.name))
|
||||
}
|
||||
name_value := ct_reflection_string(state, name)
|
||||
if item.tuple {
|
||||
delete(name, checker.allocator)
|
||||
}
|
||||
type_value := ct_add_value(state, Ct_Value{kind=.Type, type=types.INVALID, index=u64(field.type)})
|
||||
index_value := ct_add_value(state, Ct_Value{kind=.Integer, type=types.USIZE, integer=i128(index)})
|
||||
children := []Ct_Value_Id{name_value, type_value, index_value}
|
||||
field_values[index] = ct_struct_value(state, fieldinfo_type, children)
|
||||
}
|
||||
fields_start := u32(len(state.children))
|
||||
append(&state.children, ..field_values)
|
||||
fields_type := types.array(store, fieldinfo_type, u64(len(field_values)), false)
|
||||
fields_value := ct_add_value(state, Ct_Value{
|
||||
kind=.Array, type=fields_type, start=fields_start, count=u32(len(field_values)),
|
||||
})
|
||||
name_optional_type := types.optional(store, types.slice(store, types.U8, false))
|
||||
record_name := ct_add_value(state, Ct_Value{kind=.None, type=name_optional_type})
|
||||
if item.name != 0 {
|
||||
name_value := ct_reflection_string(state, symbol_text(checker, symbol.Id(item.name)))
|
||||
name_start := u32(len(state.children))
|
||||
append(&state.children, name_value)
|
||||
record_name = ct_add_value(state, Ct_Value{
|
||||
kind=.Optional_Some, type=name_optional_type, start=name_start, count=1,
|
||||
})
|
||||
}
|
||||
tuple_value := ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if item.tuple else 0})
|
||||
layout_name := symbol.intern(checker.symbols, "c" if item.c_layout else "auto")
|
||||
layout_member, layout_ok := find_enum_member(checker, layout_type, layout_name)
|
||||
if !layout_ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "@std/meta Layout is malformed")
|
||||
}
|
||||
layout_value := ct_add_value(state, Ct_Value{kind=.Integer, type=layout_type, integer=layout_member.value})
|
||||
record_children := []Ct_Value_Id{record_name, fields_value, tuple_value, layout_value}
|
||||
record_value := ct_struct_value(state, recordinfo_type, record_children)
|
||||
payload_start := u32(len(state.children))
|
||||
append(&state.children, record_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
|
||||
}
|
||||
|
||||
ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
@@ -2057,6 +2387,49 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
||||
}
|
||||
return ct_eval_template_call(state, ast.Function_Id(u32(state.values[callee].index)), expr.args, expr.span, expected, depth+1)
|
||||
}
|
||||
if is_intrinsic_call(checker, expr, "compile_error") {
|
||||
message := "compile_error! requires one comptime string argument"
|
||||
if len(expr.args) == 1 {
|
||||
value, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
|
||||
if ok && flow.kind == .Normal {
|
||||
if text, text_ok := ct_value_bytes(state, value); text_ok {
|
||||
message = text
|
||||
}
|
||||
}
|
||||
}
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, message)
|
||||
}
|
||||
if is_intrinsic_call(checker, expr, "field") {
|
||||
if len(expr.args) != 2 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "field! expects 2 arguments, got %d", len(expr.args))
|
||||
}
|
||||
base, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, flow, ok
|
||||
}
|
||||
name_value, name_flow, name_ok := ct_eval_expr(state, expr.args[1], types.INVALID, depth+1)
|
||||
if !name_ok || name_flow.kind != .Normal {
|
||||
return INVALID_CT_VALUE, name_flow, name_ok
|
||||
}
|
||||
name, text_ok := ct_value_bytes(state, name_value)
|
||||
if !text_ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "field! name must be a comptime immutable byte string")
|
||||
}
|
||||
if index, numeric := canonical_decimal_index(name); numeric {
|
||||
return ct_eval_tuple_field_value(state, base, index, expr.span)
|
||||
}
|
||||
return ct_eval_field_value(state, base, symbol.intern(checker.symbols, name), expr.span)
|
||||
}
|
||||
if is_intrinsic_call(checker, expr, "typeinfo") {
|
||||
if len(expr.args) != 1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "typeinfo! expects 1 argument, got %d", len(expr.args))
|
||||
}
|
||||
target, ok := resolve_type_argument(checker, expr.args[0], state.pkg, state.file)
|
||||
if !ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "typeinfo! argument must be a type")
|
||||
}
|
||||
return ct_typeinfo_value(state, target, expr.span)
|
||||
}
|
||||
if builtin := type_builtin_call(checker, expr); builtin != .None {
|
||||
if len(expr.args) != 1 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "%s! expects 1 argument, got %d", symbol_text(checker, expr.name), len(expr.args))
|
||||
@@ -2234,6 +2607,107 @@ ct_clone_value :: proc(dst, src: ^Ct_State, id: Ct_Value_Id) -> Ct_Value_Id {
|
||||
return ct_add_value(dst, value)
|
||||
}
|
||||
|
||||
Ct_Clone_Context :: struct {
|
||||
dst, src: ^Ct_State,
|
||||
values: []Ct_Value_Id,
|
||||
cells: []Ct_Cell_Id,
|
||||
places: []Ct_Place_Id,
|
||||
}
|
||||
|
||||
ct_clone_graph_value :: proc(ctx: ^Ct_Clone_Context, id: Ct_Value_Id) -> Ct_Value_Id {
|
||||
if id == INVALID_CT_VALUE || int(id) >= len(ctx.src.values) {
|
||||
return INVALID_CT_VALUE
|
||||
}
|
||||
if ctx.values[id] != INVALID_CT_VALUE {
|
||||
return ctx.values[id]
|
||||
}
|
||||
value := ctx.src.values[id]
|
||||
children := ct_child_slice(ctx.src, value)
|
||||
value.start = 0
|
||||
value.count = 0
|
||||
dst_id := ct_add_value(ctx.dst, value)
|
||||
ctx.values[id] = dst_id
|
||||
if len(children) > 0 {
|
||||
cloned := make([]Ct_Value_Id, len(children), ctx.dst.checker.allocator)
|
||||
defer delete(cloned, ctx.dst.checker.allocator)
|
||||
for child, index in children {
|
||||
cloned[index] = ct_clone_graph_value(ctx, child)
|
||||
}
|
||||
start := u32(len(ctx.dst.children))
|
||||
append(&ctx.dst.children, ..cloned)
|
||||
ctx.dst.values[dst_id].start = start
|
||||
ctx.dst.values[dst_id].count = u32(len(children))
|
||||
}
|
||||
if value.kind == .Pointer || value.kind == .Slice {
|
||||
ctx.dst.values[dst_id].index = u64(ct_clone_graph_place(ctx, Ct_Place_Id(value.index)))
|
||||
}
|
||||
return dst_id
|
||||
}
|
||||
|
||||
ct_clone_graph_cell :: proc(ctx: ^Ct_Clone_Context, id: Ct_Cell_Id) -> Ct_Cell_Id {
|
||||
if id == INVALID_CT_CELL || int(id) >= len(ctx.src.cells) {
|
||||
return INVALID_CT_CELL
|
||||
}
|
||||
if ctx.cells[id] != INVALID_CT_CELL {
|
||||
return ctx.cells[id]
|
||||
}
|
||||
cell := ctx.src.cells[id]
|
||||
cell.value = INVALID_CT_VALUE
|
||||
dst_id := ct_cell_id(len(ctx.dst.cells))
|
||||
append(&ctx.dst.cells, cell)
|
||||
ctx.cells[id] = dst_id
|
||||
ctx.dst.cells[dst_id].value = ct_clone_graph_value(ctx, ctx.src.cells[id].value)
|
||||
return dst_id
|
||||
}
|
||||
|
||||
ct_clone_graph_place :: proc(ctx: ^Ct_Clone_Context, id: Ct_Place_Id) -> Ct_Place_Id {
|
||||
if id == INVALID_CT_PLACE || int(id) >= len(ctx.src.places) {
|
||||
return INVALID_CT_PLACE
|
||||
}
|
||||
if ctx.places[id] != INVALID_CT_PLACE {
|
||||
return ctx.places[id]
|
||||
}
|
||||
place := ctx.src.places[id]
|
||||
path := ct_place_path(ctx.src, place)
|
||||
place.start = u32(len(ctx.dst.paths))
|
||||
place.count = u32(len(path))
|
||||
append(&ctx.dst.paths, ..path)
|
||||
place.cell = INVALID_CT_CELL
|
||||
dst_id := Ct_Place_Id(len(ctx.dst.places))
|
||||
append(&ctx.dst.places, place)
|
||||
ctx.places[id] = dst_id
|
||||
ctx.dst.places[dst_id].cell = ct_clone_graph_cell(ctx, ctx.src.places[id].cell)
|
||||
return dst_id
|
||||
}
|
||||
|
||||
ct_clone_graph :: proc(dst, src: ^Ct_State, id: Ct_Value_Id) -> Ct_Value_Id {
|
||||
ctx := Ct_Clone_Context{
|
||||
dst=dst,
|
||||
src=src,
|
||||
values=make([]Ct_Value_Id, len(src.values), dst.checker.allocator),
|
||||
cells=make([]Ct_Cell_Id, len(src.cells), dst.checker.allocator),
|
||||
places=make([]Ct_Place_Id, len(src.places), dst.checker.allocator),
|
||||
}
|
||||
defer {
|
||||
delete(ctx.values, dst.checker.allocator)
|
||||
delete(ctx.cells, dst.checker.allocator)
|
||||
delete(ctx.places, dst.checker.allocator)
|
||||
}
|
||||
for &value in ctx.values { value = INVALID_CT_VALUE }
|
||||
for &cell in ctx.cells { cell = INVALID_CT_CELL }
|
||||
for &place in ctx.places { place = INVALID_CT_PLACE }
|
||||
return ct_clone_graph_value(&ctx, id)
|
||||
}
|
||||
|
||||
store_static_binding :: proc(checker: ^Checker, source: ^Ct_State, id: Ct_Value_Id, name: symbol.Id) -> Static_Binding {
|
||||
value := ct_clone_graph(&checker.static_state, source, id)
|
||||
value_type := types.INVALID
|
||||
if value != INVALID_CT_VALUE && int(value) < len(checker.static_state.values) {
|
||||
value_type = checker.static_state.values[value].type
|
||||
}
|
||||
return Static_Binding{name=name, type=value_type, value=value}
|
||||
}
|
||||
|
||||
ct_eval_try_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
if state.defer_depth > 0 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "cannot 'try' inside a 'defer'")
|
||||
|
||||
Reference in New Issue
Block a user