function values as comptime params

This commit is contained in:
2026-07-18 01:35:39 +02:00
parent 9f433af724
commit 85693e57e1
7 changed files with 254 additions and 12 deletions
+91 -4
View File
@@ -2789,9 +2789,7 @@ milestone_39_rejects_values_without_stable_identity :: proc(t: ^testing.T) {
text := `BadUnion :: union { number i32, flag bool }
Config :: struct { value i32 }
identity func() i32 { return 1 }
reject_pointer func($value @i32) void {}
reject_function func($value @func() i32) void {}
reject_slice func($value []i32) void {}
reject_range func($value range) void {}
reject_union func($value BadUnion) void {}
@@ -2802,7 +2800,6 @@ items [2]i32 :: [1, 2]
main func() void {
reject_pointer(&stored)
reject_function(identity)
reject_slice(items[..])
reject_range(0..3)
reject_union(BadUnion {number = 1})
@@ -2827,7 +2824,97 @@ main func() void {
found += 1
}
}
testing.expect(t, found >= 6)
testing.expect(t, found >= 5)
}
@(test)
comptime_function_parameters_specialize_and_lower_directly :: proc(t: ^testing.T) {
text := `Callback_Config :: struct { call @func(value i32) i32 }
increment func(value i32) i32 { return value + 1 }
decrement func(value i32) i32 { return value - 1 }
external c_func(value i32) i32
apply func($callback @func(value i32) i32, value i32) i32 {
return callback(value)
}
apply_c func($callback *c_func(value i32) i32, value i32) i32 {
return callback(value)
}
apply_config func($config Callback_Config, value i32) i32 {
return config.call(value)
}
main func() i32 {
a i32 :: apply(increment, 1)
b i32 :: apply(increment, 2)
c i32 :: apply(decrement, 3)
d i32 :: apply(func(value i32) i32 { return value + 2 }, 4)
e i32 :: apply_c(external, 5)
f i32 :: apply_config(Callback_Config {call = increment}, 6)
return a + b + c + d + e + f
}
`
stable_names: [dynamic]string
defer {
for name in stable_names {
delete(name)
}
delete(stable_names)
}
for pass := 0; pass < 2; pass += 1 {
source_file := source.Source{path="test.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
symbols := symbol.init_table()
stream := lexer.lex(&source_file, &diagnostics, &symbols)
ast_module := parser.parse(&stream, &source_file, &diagnostics)
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
ir_module := lower.lower(&hir_module)
apply_count := 0
callback_specializations := 0
for function in ir_module.functions {
plain_apply := strings.contains(function.link_name, "bro__p0__apply__")
callback_specialization := plain_apply ||
strings.contains(function.link_name, "bro__p0__apply_c__") ||
strings.contains(function.link_name, "bro__p0__apply_config__")
if !callback_specialization {
continue
}
if plain_apply {
apply_count += 1
}
if pass == 0 {
append(&stable_names, strings.clone(function.link_name))
} else {
testing.expect_value(t, function.link_name, stable_names[callback_specializations])
}
callback_specializations += 1
testing.expect_value(t, len(function.param_types), 1)
found_direct_call := false
for instruction in function.instructions {
testing.expect(t, instruction.op != .Function_Address)
if instruction.op == .Call {
testing.expect(t, ir.as_function(instruction.target) != ir.INVALID_FUNCTION)
found_direct_call = true
}
}
testing.expect(t, found_direct_call)
}
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect_value(t, apply_count, 3)
testing.expect_value(t, callback_specializations, 5)
ir.destroy_module(&ir_module)
hir.destroy_module(&hir_module)
ast.destroy_module(&ast_module)
delete(stream.items)
symbol.destroy_table(&symbols)
source.destroy_diagnostics(&diagnostics)
}
}
@(test)