bare func declaration identities (comptime)
This commit is contained in:
+162
-8
@@ -2829,17 +2829,21 @@ main func() void {
|
||||
|
||||
@(test)
|
||||
comptime_function_parameters_specialize_and_lower_directly :: proc(t: ^testing.T) {
|
||||
text := `Callback_Config :: struct { call @func(value i32) i32 }
|
||||
text := `Callback_Config :: struct { call func(value i32) i32 }
|
||||
Callback_Choice :: union(enum) {
|
||||
call func(value i32) i32
|
||||
empty void
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
apply_c func($callback c_func(value i32) i32, value i32) i32 {
|
||||
return callback(value)
|
||||
}
|
||||
|
||||
@@ -2847,6 +2851,33 @@ apply_config func($config Callback_Config, value i32) i32 {
|
||||
return config.call(value)
|
||||
}
|
||||
|
||||
apply_array func($callbacks [2]func(value i32) i32, value i32) i32 {
|
||||
return callbacks[0](value) + callbacks[1](value)
|
||||
}
|
||||
|
||||
apply_optional func($callback ?func(value i32) i32, value i32) i32 {
|
||||
return callback?(value)
|
||||
}
|
||||
|
||||
apply_choice func($choice Callback_Choice, value i32) i32 {
|
||||
return match choice {
|
||||
.call |callback|: callback(value)
|
||||
.empty: value
|
||||
}
|
||||
}
|
||||
|
||||
apply_pointer func($callback @func(value i32) i32, value i32) i32 {
|
||||
return callback(value)
|
||||
}
|
||||
|
||||
call_pointer func(callback @func(value i32) i32, value i32) i32 {
|
||||
return callback(value)
|
||||
}
|
||||
|
||||
materialize func($callback func(value i32) i32, value i32) i32 {
|
||||
return call_pointer(callback, value)
|
||||
}
|
||||
|
||||
main func() i32 {
|
||||
a i32 :: apply(increment, 1)
|
||||
b i32 :: apply(increment, 2)
|
||||
@@ -2854,7 +2885,12 @@ main func() i32 {
|
||||
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
|
||||
g i32 :: apply_array([increment, decrement], 7)
|
||||
h i32 :: apply_optional(increment, 8)
|
||||
i i32 :: apply_choice(Callback_Choice {call = increment}, 9)
|
||||
j i32 :: apply_pointer(increment, 10)
|
||||
k i32 :: materialize(increment, 11)
|
||||
return a + b + c + d + e + f + g + h + i + j + k
|
||||
}
|
||||
`
|
||||
stable_names: [dynamic]string
|
||||
@@ -2875,11 +2911,21 @@ main func() i32 {
|
||||
|
||||
apply_count := 0
|
||||
callback_specializations := 0
|
||||
found_materialization := false
|
||||
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__")
|
||||
strings.contains(function.link_name, "bro__p0__apply_config__") ||
|
||||
strings.contains(function.link_name, "bro__p0__apply_array__") ||
|
||||
strings.contains(function.link_name, "bro__p0__apply_optional__") ||
|
||||
strings.contains(function.link_name, "bro__p0__apply_choice__") ||
|
||||
strings.contains(function.link_name, "bro__p0__apply_pointer__")
|
||||
if strings.contains(function.link_name, "bro__p0__materialize__") {
|
||||
for instruction in function.instructions {
|
||||
found_materialization = found_materialization || instruction.op == .Function_Address
|
||||
}
|
||||
}
|
||||
if !callback_specialization {
|
||||
continue
|
||||
}
|
||||
@@ -2906,7 +2952,8 @@ main func() i32 {
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, apply_count, 3)
|
||||
testing.expect_value(t, callback_specializations, 5)
|
||||
testing.expect_value(t, callback_specializations, 9)
|
||||
testing.expect(t, found_materialization)
|
||||
|
||||
ir.destroy_module(&ir_module)
|
||||
hir.destroy_module(&hir_module)
|
||||
@@ -2917,6 +2964,110 @@ main func() i32 {
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
comptime_only_function_identities_reject_runtime_storage_and_abi_use :: proc(t: ^testing.T) {
|
||||
text := `Callback :: alias func(value i32) i32
|
||||
Config :: struct { callback Callback }
|
||||
Bad_C :: c_struct { callback c_func(value i32) i32 }
|
||||
|
||||
increment func(value i32) i32 { return value + 1 }
|
||||
bad_param func(callback Callback) i32 { return callback(1) }
|
||||
bad_config func(config Config) i32 { return config.callback(1) }
|
||||
bad_result func() Callback { return increment }
|
||||
stored Callback = increment
|
||||
|
||||
main func() void {
|
||||
local Callback = increment
|
||||
_ = bad_result()
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found_param := false
|
||||
found_config := false
|
||||
found_result := false
|
||||
found_global := false
|
||||
found_local := false
|
||||
found_c_field := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found_param = found_param || strings.contains(diagnostic.message, "parameter 'callback' has a comptime-only type")
|
||||
found_config = found_config || strings.contains(diagnostic.message, "parameter 'config' has a comptime-only type")
|
||||
found_result = found_result || strings.contains(diagnostic.message, "has a comptime-only result")
|
||||
found_global = found_global || strings.contains(diagnostic.message, "global 'stored' has a comptime-only type")
|
||||
found_local = found_local || strings.contains(diagnostic.message, "local 'local' has a comptime-only type")
|
||||
found_c_field = found_c_field || strings.contains(diagnostic.message, "c_struct fields must have C-layout-compatible types")
|
||||
}
|
||||
testing.expect(t, found_param)
|
||||
testing.expect(t, found_config)
|
||||
testing.expect(t, found_result)
|
||||
testing.expect(t, found_global)
|
||||
testing.expect(t, found_local)
|
||||
testing.expect(t, found_c_field)
|
||||
}
|
||||
|
||||
@(test)
|
||||
function_pointers_do_not_coerce_back_to_bare_identities :: proc(t: ^testing.T) {
|
||||
text := `Callback :: alias func(value i32) i32
|
||||
increment func(value i32) i32 { return value + 1 }
|
||||
apply func($callback Callback, value i32) i32 { return callback(value) }
|
||||
pointer @func(value i32) i32 :: increment
|
||||
main func() i32 { return apply(pointer, 1) }
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "comptime argument")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
bodyless_c_function_identity_is_not_comptime_executable :: proc(t: ^testing.T) {
|
||||
text := `external c_func(value i32) i32
|
||||
apply_c func($callback c_func(value i32) i32, value i32) i32 { return callback(value) }
|
||||
answer :: $apply_c(external, 1)
|
||||
main func() i32 { return answer }
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
diagnostics := source.init_diagnostics(&source_file)
|
||||
defer source.destroy_diagnostics(&diagnostics)
|
||||
symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&symbols)
|
||||
stream := lexer.lex(&source_file, &diagnostics, &symbols)
|
||||
defer delete(stream.items)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
defer ast.destroy_module(&ast_module)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
defer hir.destroy_module(&hir_module)
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "runtime-only")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
milestone_37_expand_loop_control_must_be_statically_resolvable :: proc(t: ^testing.T) {
|
||||
text := `main func() void {
|
||||
@@ -4732,7 +4883,7 @@ main func() void {
|
||||
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, "call target is not a function pointer")
|
||||
found = found || strings.contains(diagnostic.message, "call target is not callable")
|
||||
}
|
||||
testing.expect(t, found)
|
||||
}
|
||||
@@ -8591,10 +8742,12 @@ Point :: alias facade.RenamedPoint
|
||||
counter :: alias facade.counter
|
||||
answer :: alias facade.answer
|
||||
`
|
||||
app_text := `dep :: import "../dep"
|
||||
app_text := `dep :: import "../dep"
|
||||
facade :: import "../facade"
|
||||
top :: import "../top"
|
||||
|
||||
apply func($callback func() i32) i32 { return callback() }
|
||||
|
||||
main func() i32 {
|
||||
box top.Box(i32) :: top.Box(i32) { value = 2 }
|
||||
point top.Point :: top.Point { value = 3 }
|
||||
@@ -8603,6 +8756,7 @@ main func() i32 {
|
||||
top.counter = 7
|
||||
if box.value != 2 or point.value != 3 { return 1 }
|
||||
if scalar != 5 or top.answer() != 40 or facade.local_answer() != 40 or dep.counter != 7 { return 2 }
|
||||
if apply(top.answer) != 40 or apply(facade.answer) != 40 { return 3 }
|
||||
_ = maybe
|
||||
return 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user