comptime type params

This commit is contained in:
2026-07-02 20:30:02 +02:00
parent a98b26446d
commit b94687c30a
8 changed files with 393 additions and 31 deletions
+159
View File
@@ -204,6 +204,37 @@ main func() void {}
testing.expect(t, !module.functions[0].params[1].comptime_value)
}
@(test)
parser_accepts_comptime_type_params_and_builtin_type_args :: proc(t: ^testing.T) {
text := `id func($T type, value T) T {
return value
}
main func() void {
_ = id(i32, 42)
}
`
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)
call := module.exprs[module.statements[module.functions[1].body[0]].expr]
type_item, type_ok := types.node(&module.type_store, module.functions[0].params[0].type)
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, module.functions[0].params[0].comptime_value)
testing.expect_value(t, symbol.resolve(&symbols, module.functions[0].params[0].name), "T")
testing.expect(t, type_ok)
testing.expect_value(t, symbol.resolve(&symbols, symbol.Id(type_item.name)), "type")
testing.expect_value(t, call.kind, ast.Expr_Kind.Call)
testing.expect_value(t, module.exprs[call.args[0]].kind, ast.Expr_Kind.Type)
}
@(test)
parser_accepts_sentinel_many_item_pointer_types :: proc(t: ^testing.T) {
text := `zero func(value [*;0]u8) void {}
@@ -2086,6 +2117,124 @@ main func() void {
testing.expect(t, found_address)
}
@(test)
comptime_type_params_specialize_by_type_and_omit_runtime_args :: proc(t: ^testing.T) {
text := `Point :: struct {
x i32
}
id func($T type, value T) T {
return value
}
zero func($T type) T {
value T = undefined
return value
}
buffer func($T type, $N usize, value T) [N]T {
data [N]T = undefined
return data
}
main func() void {
a i32 :: 42
b u8 :: 7
p Point :: Point { x = 9 }
_ = id(i32, a)
_ = id(u8, b)
_ = id(Point, p)
_ = zero(i32)
_ = buffer(u8, 4, b)
}
`
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)
id_specs := 0
zero_specs := 0
buffer_specs := 0
for function in hir_module.functions {
name := symbol.resolve(&symbols, function.name)
if name == "id" {
id_specs += 1
testing.expect_value(t, len(function.params), 1)
} else if name == "zero" {
zero_specs += 1
testing.expect_value(t, len(function.params), 0)
} else if name == "buffer" {
buffer_specs += 1
testing.expect_value(t, len(function.params), 1)
}
}
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect_value(t, id_specs, 3)
testing.expect_value(t, zero_specs, 1)
testing.expect_value(t, buffer_specs, 1)
testing.expect(t, strings.contains(llvm_text, "@bro__p0__id__i32__cti32"))
testing.expect(t, strings.contains(llvm_text, "@bro__p0__zero__cti32"))
testing.expect(t, strings.contains(llvm_text, "@bro__p0__buffer__u8__ctu8__cv4"))
}
@(test)
comptime_type_params_diagnose_invalid_uses :: proc(t: ^testing.T) {
text := `id func($T type, value T) T {
return value
}
bad_c c_func($T type) void
bad_value func($T type) void {
_ = T
}
bad_assign func($T type) void {
T = 1
}
main func() void {
x i32 = 1
_ = id(x, x)
bad_value(i32)
bad_assign(i32)
}
`
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_arg := false
found_c_func := false
found_value := false
found_assign := false
for diagnostic in diagnostics.items {
message := diagnostic.message
found_arg = found_arg || strings.contains(message, "argument for comptime type parameter 'T' must be a type")
found_c_func = found_c_func || strings.contains(message, "comptime parameters require 'func', not 'c_func'")
found_value = found_value || strings.contains(message, "type parameter 'T' is not a runtime value")
found_assign = found_assign || strings.contains(message, "cannot assign comptime parameter 'T'")
}
testing.expect(t, found_arg)
testing.expect(t, found_c_func)
testing.expect(t, found_value)
testing.expect(t, found_assign)
}
@(test)
unused_function_signatures_are_validated_eagerly :: proc(t: ^testing.T) {
text := `broken func(value, value i8, nope void) void {}
@@ -5456,6 +5605,16 @@ comptime_value_params_compile_and_run :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 0)
}
@(test)
comptime_type_params_compile_and_run :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-comptime-type-params"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/comptime_type_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"