whole-function comptime folding for zero-runtime value calls
This commit is contained in:
+116
-26
@@ -189,6 +189,13 @@ Type_Factory_Origin :: struct {
|
||||
values: []Comptime_Value,
|
||||
}
|
||||
|
||||
Call_Fold :: enum u8 {
|
||||
Unknown,
|
||||
Runtime,
|
||||
Value,
|
||||
Compile_Error,
|
||||
}
|
||||
|
||||
Call_Resolution :: struct {
|
||||
expr: ast.Expr_Id,
|
||||
ctx: []Comptime_Value,
|
||||
@@ -196,6 +203,9 @@ Call_Resolution :: struct {
|
||||
mapping: []int,
|
||||
comptime_values: []Comptime_Value,
|
||||
runtime_types: []types.Type,
|
||||
fold: Call_Fold,
|
||||
folded_value: Ct_Value_Id,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
}
|
||||
|
||||
Checker :: struct {
|
||||
@@ -257,6 +267,7 @@ Checker :: struct {
|
||||
generated_types: [dynamic]Generated_Type_Entry,
|
||||
type_factory_origins: [dynamic]Type_Factory_Origin,
|
||||
call_resolutions: [dynamic]Call_Resolution,
|
||||
building_hir: bool,
|
||||
target: target.Target,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
@@ -1106,6 +1117,13 @@ runtime_param_count :: proc(function: ast.Function) -> int {
|
||||
return count
|
||||
}
|
||||
|
||||
can_fold_zero_runtime_call :: proc(checker: ^Checker, function: ast.Function) -> bool {
|
||||
return function.has_body && !function.c_abi && runtime_param_count(function) == 0 &&
|
||||
!types.is_void(function.result) &&
|
||||
!types.is_constraint(function.result) &&
|
||||
!is_type_metatype_syntax(checker, function.result)
|
||||
}
|
||||
|
||||
comptime_param_count :: proc(function: ast.Function) -> int {
|
||||
count := 0
|
||||
for param in function.params {
|
||||
@@ -3066,13 +3084,41 @@ find_call_resolution :: proc(
|
||||
return -1, false
|
||||
}
|
||||
|
||||
call_resolution_matches :: proc(
|
||||
entry: Call_Resolution,
|
||||
mapping: []int,
|
||||
comptime_values: []Comptime_Value,
|
||||
runtime_types: []types.Type,
|
||||
) -> bool {
|
||||
if len(entry.mapping) != len(mapping) || len(entry.runtime_types) != len(runtime_types) ||
|
||||
!comptime_values_equal(entry.comptime_values, comptime_values) {
|
||||
return false
|
||||
}
|
||||
for value, index in mapping {
|
||||
if entry.mapping[index] != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for value, index in runtime_types {
|
||||
if !types.equal(entry.runtime_types[index], value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
store_call_resolution :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr_Id,
|
||||
mapping: []int,
|
||||
comptime_values: []Comptime_Value,
|
||||
runtime_types: []types.Type,
|
||||
) {
|
||||
) -> int {
|
||||
if index, ok := find_call_resolution(checker, expr); ok && call_resolution_matches(
|
||||
checker.call_resolutions[index], mapping, comptime_values, runtime_types,
|
||||
) {
|
||||
return index
|
||||
}
|
||||
entry := Call_Resolution{
|
||||
expr=expr,
|
||||
ctx=clone_comptime_values(checker.current_comptime_values, checker.allocator),
|
||||
@@ -3080,6 +3126,8 @@ store_call_resolution :: proc(
|
||||
mapping=slice.clone(mapping, checker.allocator),
|
||||
comptime_values=clone_comptime_values(comptime_values, checker.allocator),
|
||||
runtime_types=slice.clone(runtime_types, checker.allocator),
|
||||
folded_value=INVALID_CT_VALUE,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}
|
||||
if index, ok := find_call_resolution(checker, expr); ok {
|
||||
previous := checker.call_resolutions[index]
|
||||
@@ -3089,9 +3137,10 @@ store_call_resolution :: proc(
|
||||
delete(previous.comptime_values, checker.allocator)
|
||||
delete(previous.runtime_types, checker.allocator)
|
||||
checker.call_resolutions[index] = entry
|
||||
return
|
||||
return index
|
||||
}
|
||||
append(&checker.call_resolutions, entry)
|
||||
return len(checker.call_resolutions)-1
|
||||
}
|
||||
|
||||
resolved_call_arg_expected :: proc(
|
||||
@@ -5511,39 +5560,56 @@ infer_expr :: proc(
|
||||
defer delete(comptime_values, checker.allocator)
|
||||
if comptime_ok &&
|
||||
can_specialize(checker, function, stack[frame_index].args, comptime_values) {
|
||||
store_call_resolution(
|
||||
resolution := store_call_resolution(
|
||||
checker, frame.expr, frame.mapping,
|
||||
comptime_values, stack[frame_index].args,
|
||||
)
|
||||
previous_comptime := checker.current_comptime_values
|
||||
checker.current_comptime_values = comptime_values
|
||||
for source_index in 0..<len(expr.args) {
|
||||
param_index := call_param_index(frame.mapping, source_index)
|
||||
if param_index >= len(function.params) || function.params[param_index].comptime_value {
|
||||
continue
|
||||
}
|
||||
demand := call_arg_expected(checker, function, param_index)
|
||||
record_demand(checker, expr.args[source_index], demand, locals, local_types, pkg, file)
|
||||
fold := Call_Fold.Runtime
|
||||
if can_fold_zero_runtime_call(checker, function) {
|
||||
fold = cache_zero_runtime_call(checker, resolution, frame.expr, pkg, file)
|
||||
}
|
||||
checker.current_comptime_values = previous_comptime
|
||||
spec := INVALID_SPEC
|
||||
if demanded == nil {
|
||||
spec = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
} else {
|
||||
spec = find_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
if spec == INVALID_SPEC {
|
||||
spec = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
if fold == .Compile_Error {
|
||||
last = types.INVALID
|
||||
} else if fold == .Value {
|
||||
// Folded specializations are still inferred to validate their bodies and
|
||||
// participate in the type-demand fixpoint. Leaving them undemanded keeps
|
||||
// prune_specs from emitting an otherwise unused runtime function.
|
||||
if !checker.building_hir && demanded == nil && !function_has_comptime_params(function) {
|
||||
_ = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
}
|
||||
mark_spec_demanded(checker, spec, demanded)
|
||||
}
|
||||
if spec != INVALID_SPEC {
|
||||
last = checker.specs[spec].result
|
||||
value := checker.call_resolutions[resolution].folded_value
|
||||
last = checker.static_state.values[value].type
|
||||
} else {
|
||||
previous_comptime := checker.current_comptime_values
|
||||
checker.current_comptime_values = comptime_values
|
||||
declared := function_channel_type(checker, function)
|
||||
for source_index in 0..<len(expr.args) {
|
||||
param_index := call_param_index(frame.mapping, source_index)
|
||||
if param_index >= len(function.params) || function.params[param_index].comptime_value {
|
||||
continue
|
||||
}
|
||||
demand := call_arg_expected(checker, function, param_index)
|
||||
record_demand(checker, expr.args[source_index], demand, locals, local_types, pkg, file)
|
||||
}
|
||||
checker.current_comptime_values = previous_comptime
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) || types.is_noreturn(declared) else types.INVALID
|
||||
spec := INVALID_SPEC
|
||||
if demanded == nil {
|
||||
spec = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
} else {
|
||||
spec = find_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
if spec == INVALID_SPEC {
|
||||
spec = ensure_spec(checker, frame.template, stack[frame_index].args, comptime_values)
|
||||
}
|
||||
mark_spec_demanded(checker, spec, demanded)
|
||||
}
|
||||
if spec != INVALID_SPEC {
|
||||
last = checker.specs[spec].result
|
||||
} else {
|
||||
previous_comptime = checker.current_comptime_values
|
||||
checker.current_comptime_values = comptime_values
|
||||
declared := function_channel_type(checker, function)
|
||||
checker.current_comptime_values = previous_comptime
|
||||
last = declared if is_runtime_type(checker, declared) || types.is_void(declared) || types.is_noreturn(declared) else types.INVALID
|
||||
}
|
||||
}
|
||||
} else {
|
||||
declared := function_channel_type(checker, function)
|
||||
@@ -9619,6 +9685,29 @@ build_expr :: proc(
|
||||
}
|
||||
checker.current_comptime_values = previous_comptime
|
||||
}
|
||||
fold := Call_Fold.Runtime
|
||||
if comptime_ok && can_fold_zero_runtime_call(checker, function) &&
|
||||
frame.resolution >= 0 && frame.resolution < len(checker.call_resolutions) {
|
||||
fold = cache_zero_runtime_call(checker, frame.resolution, frame.expr, pkg, file)
|
||||
}
|
||||
if comptime_ok && arg_violation == source.INVALID_DIAGNOSTIC &&
|
||||
(fold == .Value || fold == .Compile_Error) {
|
||||
if fold == .Value {
|
||||
value := checker.call_resolutions[frame.resolution].folded_value
|
||||
last = ct_materialize_value(&checker.static_state, value, expr.span, frame.expected)
|
||||
} else {
|
||||
diagnostic := checker.call_resolutions[frame.resolution].diagnostic
|
||||
last = invalid_hir_expr(checker, expr.span, diagnostic, frame.expected)
|
||||
}
|
||||
delete(stack[frame_index].arg_types, checker.allocator)
|
||||
stack[frame_index].arg_types = nil
|
||||
delete(stack[frame_index].built_args, checker.allocator)
|
||||
stack[frame_index].built_args = nil
|
||||
delete(stack[frame_index].mapping, checker.allocator)
|
||||
stack[frame_index].mapping = nil
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
spec := INVALID_SPEC
|
||||
if comptime_ok {
|
||||
if frame.resolution >= 0 && frame.resolution < len(checker.call_resolutions) {
|
||||
@@ -14274,6 +14363,7 @@ check :: proc(
|
||||
finalize_record_field_inference(&checker)
|
||||
validate_external_globals(&checker)
|
||||
prune_specs(&checker)
|
||||
checker.building_hir = true
|
||||
build_globals(&checker)
|
||||
for index := 0; index < len(checker.specs); index += 1 {
|
||||
build_function(&checker, spec_id(index))
|
||||
|
||||
@@ -236,6 +236,7 @@ Ct_Value_Kind :: enum u8 {
|
||||
Ct_Error_Kind :: enum u8 {
|
||||
None,
|
||||
Not_Comptime,
|
||||
Compile_Error,
|
||||
Overflow,
|
||||
Div_By_Zero,
|
||||
Non_Exact,
|
||||
@@ -325,6 +326,7 @@ Ct_State :: struct {
|
||||
error: Ct_Error_Kind,
|
||||
diagnostic: source.Diagnostic_Id,
|
||||
silent: bool,
|
||||
foldable: bool,
|
||||
demanded: ^[dynamic]Spec_Id,
|
||||
promoted_cells: [dynamic]Ct_Cell_Id,
|
||||
promoted_globals: [dynamic]hir.Global_Id,
|
||||
@@ -353,6 +355,7 @@ ct_state_make :: proc(
|
||||
state.error = .None
|
||||
state.diagnostic = source.INVALID_DIAGNOSTIC
|
||||
state.silent = !diagnose
|
||||
state.foldable = true
|
||||
state.demanded = demanded
|
||||
state.values.allocator = checker.allocator
|
||||
state.children.allocator = checker.allocator
|
||||
@@ -479,7 +482,15 @@ ct_pop_bindings :: proc(state: ^Ct_State, start: int) {
|
||||
resize(&state.bindings, start)
|
||||
}
|
||||
|
||||
ct_value_has_children :: proc(kind: Ct_Value_Kind) -> bool {
|
||||
return kind == .Range || kind == .Array || kind == .Struct ||
|
||||
kind == .Optional_Some || kind == .Fallible
|
||||
}
|
||||
|
||||
ct_child_slice :: proc(state: ^Ct_State, value: Ct_Value) -> []Ct_Value_Id {
|
||||
if !ct_value_has_children(value.kind) {
|
||||
return nil
|
||||
}
|
||||
start := int(value.start)
|
||||
end := start+int(value.count)
|
||||
if start < 0 || end > len(state.children) {
|
||||
@@ -850,6 +861,88 @@ ct_value_references_dead_storage :: proc(state: ^Ct_State, id: Ct_Value_Id) -> b
|
||||
return false
|
||||
}
|
||||
|
||||
ct_retain_value_storage :: proc(state: ^Ct_State, id: Ct_Value_Id, depth := 0) {
|
||||
if depth > 64 || id == INVALID_CT_VALUE || int(id) >= len(state.values) {
|
||||
return
|
||||
}
|
||||
value := state.values[id]
|
||||
if value.kind == .Pointer || value.kind == .Slice {
|
||||
place_id := Ct_Place_Id(value.index)
|
||||
if place_id != INVALID_CT_PLACE && int(place_id) < len(state.places) {
|
||||
cell := state.places[place_id].cell
|
||||
if cell != INVALID_CT_CELL && int(cell) < len(state.cells) {
|
||||
state.cells[cell].live = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for child in ct_child_slice(state, value) {
|
||||
ct_retain_value_storage(state, child, depth+1)
|
||||
}
|
||||
}
|
||||
|
||||
ct_value_can_materialize :: 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]
|
||||
#partial switch value.kind {
|
||||
case .Invalid, .Undefined, .Function, .Type:
|
||||
return false
|
||||
case .Void, .Integer, .Float, .Bool, .String, .Null:
|
||||
return true
|
||||
case .Pointer:
|
||||
store := &state.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 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 false
|
||||
}
|
||||
root_id := state.cells[place.cell].value
|
||||
if root_id == INVALID_CT_VALUE || int(root_id) >= len(state.values) {
|
||||
return false
|
||||
}
|
||||
root := state.values[root_id]
|
||||
array, array_ok := types.node(store, root.type)
|
||||
return 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))) &&
|
||||
ct_value_can_materialize(state, root_id, depth+1)
|
||||
case .Slice:
|
||||
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 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 false
|
||||
}
|
||||
root_id := state.cells[place.cell].value
|
||||
if root_id == INVALID_CT_VALUE || int(root_id) >= len(state.values) {
|
||||
return false
|
||||
}
|
||||
root := state.values[root_id]
|
||||
array, array_ok := types.node(store, root.type)
|
||||
return array_ok && array.kind == .Array && u64(value.count) == array.count &&
|
||||
types.equal(slice.child, array.child) && ct_value_can_materialize(state, root_id, depth+1)
|
||||
case .Range, .Array, .Struct, .Optional_Some, .Fallible:
|
||||
for child in ct_child_slice(state, value) {
|
||||
if child != INVALID_CT_VALUE && !ct_value_can_materialize(state, child, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
ct_materialize_array_pointer :: proc(
|
||||
state: ^Ct_State,
|
||||
value: Ct_Value,
|
||||
@@ -976,6 +1069,12 @@ ct_materialize_value :: proc(
|
||||
}
|
||||
value := state.values[materialized]
|
||||
#partial switch value.kind {
|
||||
case .Void:
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Void, span=span, type=types.VOID,
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Undefined:
|
||||
if state.diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
state.diagnostic = source.add(checker.diagnostics, span, "cannot materialize an undefined comptime value")
|
||||
@@ -3234,6 +3333,12 @@ ct_struct_type :: proc(
|
||||
|
||||
ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type, depth: int, expr_id := ast.INVALID_EXPR) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
resolution := -1
|
||||
if expr_id != ast.INVALID_EXPR {
|
||||
if index, ok := find_call_resolution(checker, expr_id); ok {
|
||||
resolution = index
|
||||
}
|
||||
}
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
callee, flow, ok := ct_eval_expr(state, expr.left, types.INVALID, depth+1)
|
||||
if !ok || flow.kind != .Normal {
|
||||
@@ -3254,7 +3359,7 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
||||
}
|
||||
}
|
||||
}
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, message)
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Compile_Error, expr.span, message)
|
||||
}
|
||||
if is_intrinsic_call(checker, expr, "some") {
|
||||
if len(expr.args) != 1 {
|
||||
@@ -3447,7 +3552,10 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
|
||||
}
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, expr.span, "unresolved function '%s'", symbol_text(checker, expr.name))
|
||||
}
|
||||
return ct_eval_template_call(state, template, expr.args, expr.span, expected, depth+1)
|
||||
if runtime_param_count(checker.ast_module.functions[template]) != 0 {
|
||||
resolution = -1
|
||||
}
|
||||
return ct_eval_template_call(state, template, expr.args, expr.span, expected, depth+1, resolution)
|
||||
}
|
||||
|
||||
ct_eval_template_call :: proc(
|
||||
@@ -3457,6 +3565,7 @@ ct_eval_template_call :: proc(
|
||||
span: source.Span,
|
||||
expected: types.Type,
|
||||
depth: int,
|
||||
resolution := -1,
|
||||
) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
if template == ast.INVALID_FUNCTION || int(template) >= len(checker.ast_module.functions) {
|
||||
@@ -3466,10 +3575,22 @@ ct_eval_template_call :: proc(
|
||||
if !function.has_body || len(function.unsupported_reason) > 0 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, span, "function '%s' is runtime-only", symbol_text(checker, function.name))
|
||||
}
|
||||
if !valid_call_arity(function, len(args)) {
|
||||
resolved := resolution >= 0 && resolution < len(checker.call_resolutions)
|
||||
if !resolved && !valid_call_arity(function, len(args)) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, span, "function '%s' arity mismatch", symbol_text(checker, function.name))
|
||||
}
|
||||
comptime_values, comptime_ok := collect_comptime_values(checker, function, args, state.pkg, state.file, false, checker.current_comptime_values)
|
||||
comptime_values: []Comptime_Value
|
||||
comptime_ok := false
|
||||
if resolved {
|
||||
comptime_values = clone_comptime_values(
|
||||
checker.call_resolutions[resolution].comptime_values, checker.allocator,
|
||||
)
|
||||
comptime_ok = true
|
||||
} else {
|
||||
comptime_values, comptime_ok = collect_comptime_values(
|
||||
checker, function, args, state.pkg, state.file, false, checker.current_comptime_values,
|
||||
)
|
||||
}
|
||||
defer delete(comptime_values, checker.allocator)
|
||||
if !comptime_ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, span, "invalid comptime argument for '%s'", symbol_text(checker, function.name))
|
||||
@@ -3479,6 +3600,9 @@ ct_eval_template_call :: proc(
|
||||
defer checker.current_comptime_values = previous_comptime
|
||||
|
||||
result_type := function_channel_type(checker, function)
|
||||
if types.is_void(result_type) || types.kind(result_type, &checker.module.types) == .Fallible {
|
||||
state.foldable = false
|
||||
}
|
||||
runtime_values: [dynamic]Ct_Value_Id
|
||||
runtime_values.allocator = checker.allocator
|
||||
runtime_types: [dynamic]types.Type
|
||||
@@ -3570,6 +3694,9 @@ ct_eval_template_call :: proc(
|
||||
state, .Not_Comptime, span, "comptime function returned an undefined value",
|
||||
)
|
||||
}
|
||||
if runtime_param_count(function) == 0 {
|
||||
ct_retain_value_storage(state, result)
|
||||
}
|
||||
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")
|
||||
}
|
||||
@@ -3616,8 +3743,10 @@ ct_clone_graph_value :: proc(ctx: ^Ct_Clone_Context, id: Ct_Value_Id) -> Ct_Valu
|
||||
}
|
||||
value := ctx.src.values[id]
|
||||
children := ct_child_slice(ctx.src, value)
|
||||
value.start = 0
|
||||
value.count = 0
|
||||
if ct_value_has_children(value.kind) {
|
||||
value.start = 0
|
||||
value.count = 0
|
||||
}
|
||||
dst_id := ct_add_value(ctx.dst, value)
|
||||
ctx.values[id] = dst_id
|
||||
if len(children) > 0 {
|
||||
@@ -3632,7 +3761,8 @@ ct_clone_graph_value :: proc(ctx: ^Ct_Clone_Context, id: Ct_Value_Id) -> Ct_Valu
|
||||
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)))
|
||||
cloned_place := ct_clone_graph_place(ctx, Ct_Place_Id(value.index))
|
||||
ctx.dst.values[dst_id].index = u64(cloned_place)
|
||||
}
|
||||
return dst_id
|
||||
}
|
||||
@@ -3692,6 +3822,47 @@ ct_clone_graph :: proc(dst, src: ^Ct_State, id: Ct_Value_Id) -> Ct_Value_Id {
|
||||
return ct_clone_graph_value(&ctx, id)
|
||||
}
|
||||
|
||||
cache_zero_runtime_call :: proc(
|
||||
checker: ^Checker,
|
||||
resolution: int,
|
||||
expr: ast.Expr_Id,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> Call_Fold {
|
||||
if resolution < 0 || resolution >= len(checker.call_resolutions) {
|
||||
return .Runtime
|
||||
}
|
||||
if checker.call_resolutions[resolution].fold != .Unknown {
|
||||
return checker.call_resolutions[resolution].fold
|
||||
}
|
||||
state := ct_state_make(
|
||||
checker, pkg, file, values=checker.current_comptime_values, diagnose=false,
|
||||
)
|
||||
value, flow, ok := ct_eval_expr(&state, expr)
|
||||
if ok && flow.kind == .Normal && state.foldable && ct_value_can_materialize(&state, value) {
|
||||
checker.call_resolutions[resolution].fold = .Value
|
||||
checker.call_resolutions[resolution].folded_value = ct_clone_graph(
|
||||
&checker.static_state, &state, value,
|
||||
)
|
||||
ct_state_destroy(&state)
|
||||
return .Value
|
||||
}
|
||||
error := state.error
|
||||
ct_state_destroy(&state)
|
||||
if error == .Compile_Error {
|
||||
diagnosed := ct_state_make(
|
||||
checker, pkg, file, values=checker.current_comptime_values,
|
||||
)
|
||||
_, _, _ = ct_eval_expr(&diagnosed, expr)
|
||||
checker.call_resolutions[resolution].fold = .Compile_Error
|
||||
checker.call_resolutions[resolution].diagnostic = diagnosed.diagnostic
|
||||
ct_state_destroy(&diagnosed)
|
||||
return .Compile_Error
|
||||
}
|
||||
checker.call_resolutions[resolution].fold = .Runtime
|
||||
return .Runtime
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user