comptime value-params

This commit is contained in:
2026-07-02 19:38:31 +02:00
parent b1ddecfc2e
commit 7cda126924
10 changed files with 579 additions and 56 deletions
+149
View File
@@ -175,6 +175,35 @@ main func() void {}
testing.expect_value(t, len(module.functions[0].params), 2)
}
@(test)
parser_accepts_comptime_value_params :: proc(t: ^testing.T) {
text := `make func($N usize, value i32) i32 {
return value
}
main func() void {}
`
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)
module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&module)
found_dollar := false
for tok in stream.items {
found_dollar = found_dollar || tok.kind == token.Kind.Dollar
}
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, found_dollar)
testing.expect_value(t, len(module.functions[0].params), 2)
testing.expect(t, module.functions[0].params[0].comptime_value)
testing.expect(t, !module.functions[0].params[1].comptime_value)
}
@(test)
parser_accepts_sentinel_many_item_pointer_types :: proc(t: ^testing.T) {
text := `zero func(value [*;0]u8) void {}
@@ -1945,6 +1974,116 @@ main func() i32 {
}
}
@(test)
comptime_value_params_specialize_by_value_and_omit_runtime_args :: proc(t: ^testing.T) {
text := `make_array func($N usize) [N]u8 {
data [N]u8 = undefined
return data
}
main func() void {
four [4]u8 :: make_array(4)
eight [8]u8 :: make_array(8)
again [4]u8 :: make_array(4)
_ = four
_ = eight
_ = again
}
`
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)
ir_module := lower.lower(&hir_module)
defer ir.destroy_module(&ir_module)
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
defer delete(llvm_text)
make_specs := 0
for function in hir_module.functions {
if symbol.resolve(&symbols, function.name) == "make_array" {
make_specs += 1
testing.expect_value(t, len(function.params), 0)
}
}
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect_value(t, make_specs, 2)
testing.expect(t, strings.contains(llvm_text, "@bro__p0__make_array__cv4"))
testing.expect(t, strings.contains(llvm_text, "@bro__p0__make_array__cv8"))
}
@(test)
comptime_value_params_diagnose_invalid_uses :: proc(t: ^testing.T) {
text := `make func($N usize) i32 {
return N
}
tiny func($N u8) i32 {
return N
}
bad_type func($T bool) void {}
bad_use func($N usize) void {
N = 1
_ = &N
}
main func() void {
x usize = 4
_ = make(x)
_ = make()
_ = make(1, 2)
_ = make(-1)
_ = tiny(300)
bad_use(4)
}
`
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_runtime_arg := false
found_missing := false
found_extra := false
found_negative := false
found_range := false
found_bad_type := false
found_assignment := false
found_address := false
for diagnostic in diagnostics.items {
message := diagnostic.message
found_runtime_arg = found_runtime_arg || strings.contains(message, "must be a compile-time integer expression")
found_missing = found_missing || strings.contains(message, "expects 1 arguments, got 0")
found_extra = found_extra || strings.contains(message, "expects 1 arguments, got 2")
found_negative = found_negative || strings.contains(message, "integer constant -1 does not fit in usize")
found_range = found_range || strings.contains(message, "integer constant 300 does not fit in u8")
found_bad_type = found_bad_type || strings.contains(message, "requires a concrete integer type")
found_assignment = found_assignment || strings.contains(message, "cannot assign comptime parameter 'N'")
found_address = found_address || strings.contains(message, "'&' requires an addressable location")
}
testing.expect(t, found_runtime_arg)
testing.expect(t, found_missing)
testing.expect(t, found_extra)
testing.expect(t, found_negative)
testing.expect(t, found_range)
testing.expect(t, found_bad_type)
testing.expect(t, found_assignment)
testing.expect(t, found_address)
}
@(test)
unused_function_signatures_are_validated_eagerly :: proc(t: ^testing.T) {
text := `broken func(value, value i8, nope void) void {}
@@ -5305,6 +5444,16 @@ cross_package_generic_specializes_from_folded_argument :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 128)
}
@(test)
comptime_value_params_compile_and_run :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-comptime-value-params"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/comptime_value_params", output)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
lazy_function_body_marks_import_as_used_without_resolving_it :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-package-lazy-import"