comptime eval

This commit is contained in:
2026-07-02 21:31:53 +02:00
parent b94687c30a
commit e00a4e929a
8 changed files with 500 additions and 36 deletions
+159
View File
@@ -567,6 +567,42 @@ main func() void {}
testing.expect_value(t, module.exprs[chained.left].kind, ast.Expr_Kind.Negate)
}
@(test)
parser_accepts_comptime_prefix_and_block_expressions :: proc(t: ^testing.T) {
text := `sum func(a, b int) int { return a + b }
literal :: $32
call :: $sum(1, 2) + 3
block :: ${
yield 4
}
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)
literal := module.exprs[module.globals[0].expr]
call_add := module.exprs[module.globals[1].expr]
call := module.exprs[call_add.left]
block := module.exprs[module.globals[2].expr]
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect_value(t, literal.kind, ast.Expr_Kind.Comptime)
testing.expect_value(t, module.exprs[literal.left].kind, ast.Expr_Kind.Integer)
testing.expect_value(t, call_add.kind, ast.Expr_Kind.Add)
testing.expect_value(t, call.kind, ast.Expr_Kind.Comptime)
testing.expect_value(t, module.exprs[call.left].kind, ast.Expr_Kind.Call)
testing.expect_value(t, block.kind, ast.Expr_Kind.Comptime)
testing.expect_value(t, len(block.body), 1)
testing.expect_value(t, module.statements[block.body[0]].kind, ast.Stmt_Kind.Yield)
}
nested_expression_source :: proc(call: bool, depth: int) -> string {
builder := strings.builder_make()
defer strings.builder_destroy(&builder)
@@ -2235,6 +2271,119 @@ main func() void {
testing.expect(t, found_assign)
}
@(test)
comptime_expression_forces_integer_evaluation :: proc(t: ^testing.T) {
text := `sum func(a, b int) int {
return a + b
}
max func(a, b int) int {
if a > b {
return a
}
return b
}
nested func(value int) int {
two :: 2
return sum(value, two)
}
make_array func($N usize) [N]u8 {
data [N]u8 = undefined
return data
}
forced :: $sum(1, 2)
main func() i32 {
value i32 :: $sum(20, 22)
choice i32 :: $max(9, 3)
blocked i32 :: ${
local :: 5
yield sum(local, 6)
}
bytes [_]u8 :: make_array($nested(2))
if forced != 3 {
return 1
}
if value != 42 {
return 2
}
if choice != 9 {
return 3
}
if blocked != 11 {
return 4
}
if bytes.len != 4 {
return 5
}
return 0
}
`
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)
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, strings.contains(llvm_text, "@bro.g.0 = internal constant i8 3"))
}
@(test)
comptime_expression_diagnoses_unsupported_v1_evaluation :: proc(t: ^testing.T) {
text := `loop func() int {
while true {
return 1
}
return 0
}
missing func() int {
if true {
}
}
recurse func(value int) int {
return recurse(value)
}
main func() void {
runtime i32 = 1
_ = $runtime
_ = $loop()
_ = $missing()
_ = $recurse(1)
_ = ${
value :: 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 := 0
for diagnostic in diagnostics.items {
if strings.contains(diagnostic.message, "expression cannot be evaluated at comptime") {
found += 1
}
}
testing.expect(t, found >= 5)
}
@(test)
unused_function_signatures_are_validated_eagerly :: proc(t: ^testing.T) {
text := `broken func(value, value i8, nope void) void {}
@@ -5615,6 +5764,16 @@ comptime_type_params_compile_and_run :: proc(t: ^testing.T) {
testing.expect_value(t, state.exit_code, 0)
}
@(test)
comptime_eval_compile_and_run :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-comptime-eval"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/comptime_eval", 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"