Files
l-language/test/test_codegen.c
T
Serendipity da9a7065dd feat: struct参数/返回值 + SourceLoc + 测试补全
- struct 可作函数参数和返回值 (fn make_point -> Point)
- SourceLoc 抽象: 所有 ast_make_* 参数 + AstNode 迁移完毕
- sema: +4 struct 类型检查测试 (字段类型/未定义/数量/嵌套)
- codegen: +2 struct IR 生成测试 (decl + field_access)
- 新增集成测试 14_struct_fn.l

测试: 104 单元 + 14 集成 = 全部通过
2026-06-05 13:29:31 +08:00

246 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "test_utils.h"
#include "codegen.h"
#include "ast.h"
#include "arena.h"
#include <llvm-c/Analysis.h>
void test_codegen_simple_function() {
Arena a = arena_create(1);
// 构造 AST: fn main() -> i64 { return 42; }
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 42, loc_at(1, 1)), loc_at(1, 1));
AstNode* stmts[] = { ret };
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_mod", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
// 验证模块
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
void test_codegen_if_else() {
Arena a = arena_create(1);
// fn main() -> i64 { if true { return 1; } else { return 0; } }
AstNode* then_ret = ast_make_return(&a, ast_make_literal_i64(&a, 1, loc_at(1, 1)), loc_at(1, 1));
AstNode* then_stmts[] = { then_ret };
AstNode* then_block = ast_make_block(&a, then_stmts, 1, loc_at(1, 1));
AstNode* else_ret = ast_make_return(&a, ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1));
AstNode* else_stmts[] = { else_ret };
AstNode* else_block = ast_make_block(&a, else_stmts, 1, loc_at(1, 1));
AstNode* if_stmt = ast_make_if(&a,
ast_make_literal_bool(&a, true, loc_at(1, 1)), then_block, else_block, loc_at(1, 1));
AstNode* stmts[] = { if_stmt };
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx2 = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_mod2", &err, &ctx2);
ASSERT(mod != NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx2);
arena_destroy(&a);
}
void test_codegen_binary_ops() {
Arena a = arena_create(1);
// fn main() -> i64 { return 1 + 2 * 3; }
AstNode* expr = ast_make_binary(&a, OP_ADD,
ast_make_literal_i64(&a, 1, loc_at(1, 1)),
ast_make_binary(&a, OP_MUL,
ast_make_literal_i64(&a, 2, loc_at(1, 1)),
ast_make_literal_i64(&a, 3, loc_at(1, 1)), loc_at(1, 1)),
loc_at(1, 1));
AstNode* ret = ast_make_return(&a, expr, loc_at(1, 1));
AstNode* stmts[] = { ret };
AstNode* body = ast_make_block(&a, stmts, 1, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx3 = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_mod3", &err, &ctx3);
ASSERT(mod != NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx3);
arena_destroy(&a);
}
void test_codegen_while_loop() {
Arena a = arena_create(1);
// fn main() -> i64 { while true { return 0; } return 1; }
AstNode* while_body_stmts[] = {
ast_make_return(&a, ast_make_literal_i64(&a, 0, loc_at(1, 1)), loc_at(1, 1))
};
AstNode* while_body = ast_make_block(&a, while_body_stmts, 1, loc_at(1, 1));
AstNode* while_stmt = ast_make_while(&a,
ast_make_literal_bool(&a, true, loc_at(1, 1)), while_body, loc_at(1, 1));
AstNode* ret = ast_make_return(&a, ast_make_literal_i64(&a, 1, loc_at(1, 1)), loc_at(1, 1));
AstNode* stmts[] = { while_stmt, ret };
AstNode* fn_body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, fn_body, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx4 = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_while", &err, &ctx4);
ASSERT(mod != NULL);
char* verify_err = NULL;
ASSERT(!LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err));
LLVMDisposeModule(mod);
LLVMContextDispose(ctx4);
arena_destroy(&a);
}
/* === struct IR 生成测试 === */
void test_codegen_struct_decl() {
Arena a = arena_create(1);
/* 构造 AST: struct Point { x: i64, y: i64 } */
AstNode* fields[2];
fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, loc_at(1, 1));
fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl };
/* 构造 fn main() -> i64 { let p = Point { x: 1, y: 2 }; return p.x; } */
const char* fnames[] = {"x", "y"};
AstNode* fvals[] = {
ast_make_literal_i64(&a, 1, loc_at(1, 1)),
ast_make_literal_i64(&a, 2, loc_at(1, 1))
};
AstNode* init = ast_make_struct_init(&a, "Point", fnames, fvals, 2, loc_at(1, 1));
/* 手动设置类型(绕过 semacodegen 需要读取 type 字段) */
init->type.kind = TYPE_STRUCT;
init->type.struct_name = "Point";
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
init, NULL, loc_at(1, 1));
/* return p.x; */
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
p_ident->type.kind = TYPE_STRUCT;
p_ident->type.struct_name = "Point";
AstNode* field_x = ast_make_field_access(&a, p_ident, "x", loc_at(1, 1));
field_x->as.field_access.field_index = 0; /* x 是第 0 个字段 */
field_x->type.kind = TYPE_I64;
AstNode* ret = ast_make_return(&a, field_x, loc_at(1, 1));
AstNode* stmts[] = { let_stmt, ret };
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_struct_decl", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
void test_codegen_struct_field_access() {
Arena a = arena_create(1);
/* 构造 AST: struct Point { x: i64, y: i64 } */
AstNode* fields[2];
fields[0] = ast_make_parameter(&a, "x", TYPE_I64, NULL, loc_at(1, 1));
fields[1] = ast_make_parameter(&a, "y", TYPE_I64, NULL, loc_at(1, 1));
AstNode* struct_decl = ast_make_struct_decl(&a, "Point", fields, 2, loc_at(1, 1));
AstNode* structs[] = { struct_decl };
/* 构造 fn main() -> i64 { let p = Point { x: 5, y: 10 }; return p.y; } */
const char* fnames[] = {"x", "y"};
AstNode* fvals[] = {
ast_make_literal_i64(&a, 5, loc_at(1, 1)),
ast_make_literal_i64(&a, 10, loc_at(1, 1))
};
AstNode* init = ast_make_struct_init(&a, "Point", fnames, fvals, 2, loc_at(1, 1));
init->type.kind = TYPE_STRUCT;
init->type.struct_name = "Point";
AstNode* let_stmt = ast_make_let(&a, "p", TYPE_UNKNOWN, false, false,
init, NULL, loc_at(1, 1));
/* return p.y; */
AstNode* p_ident = ast_make_ident(&a, "p", loc_at(1, 1));
p_ident->type.kind = TYPE_STRUCT;
p_ident->type.struct_name = "Point";
AstNode* field_y = ast_make_field_access(&a, p_ident, "y", loc_at(1, 1));
field_y->as.field_access.field_index = 1; /* y 是第 1 个字段 */
field_y->type.kind = TYPE_I64;
AstNode* ret = ast_make_return(&a, field_y, loc_at(1, 1));
AstNode* stmts[] = { let_stmt, ret };
AstNode* body = ast_make_block(&a, stmts, 2, loc_at(1, 1));
AstNode* fn = ast_make_function(&a, "main", NULL, 0, TYPE_I64, NULL, body, loc_at(1, 1));
AstNode* fns[] = { fn };
AstNode* prog = ast_make_program(&a, fns, 1, structs, 1, loc_at(1, 1));
const char* err = NULL;
LLVMContextRef ctx = NULL;
LLVMModuleRef mod = codegen_module(prog, &a, "test_struct_field", &err, &ctx);
ASSERT(mod != NULL);
ASSERT(err == NULL);
char* verify_err = NULL;
int failed = LLVMVerifyModule(mod, LLVMReturnStatusAction, &verify_err);
ASSERT(!failed);
LLVMDisposeModule(mod);
LLVMContextDispose(ctx);
arena_destroy(&a);
}
int main(void) {
TEST_RUN(test_codegen_simple_function);
TEST_RUN(test_codegen_if_else);
TEST_RUN(test_codegen_binary_ops);
TEST_RUN(test_codegen_while_loop);
TEST_RUN(test_codegen_struct_decl);
TEST_RUN(test_codegen_struct_field_access);
return test_summary();
}