comptime type params
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user