feat: in/out 参数 — out 关键字引用传递

fn swap(out x: i64, out y: i64) 声明 out 参数,codegen 层面
函数签名变为 T* 指针,调用点自动传 &variable 地址。
in 是默认行为(值传递),无需显式标注。

Token → Parser → Sema → Codegen 全流水线:
- TOK_OUT + "out" 关键字注册
- AST parameter.is_out 字段
- parse_function 解析 out 前缀
- Sema: out 参数注册为 SYM_VARIABLE+is_mut(可赋值)
- Codegen: LLVM 函数签名使用 T*,调用点传 alloca

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:45:38 +08:00
parent 0a0667776a
commit c8da286d31
14 changed files with 162 additions and 28 deletions
+31 -9
View File
@@ -23,17 +23,24 @@ LLVMValueRef find_fn(CgCtx* ctx, const char* name) {
return NULL;
}
void add_fn(CgCtx* ctx, const char* name, LLVMValueRef fn) {
void add_fn(CgCtx* ctx, const char* name, LLVMValueRef fn, bool* out_params, size_t pc) {
FnEntry* e = arena_alloc(ctx->arena, sizeof(*e));
if (!e) return;
e->name = name; e->fn = fn;
e->ret = TYPE_VOID;
e->params = NULL;
e->pc = 0;
e->out_params = out_params;
e->pc = pc;
e->next = ctx->fn_table;
ctx->fn_table = e;
}
FnEntry* find_fn_entry(CgCtx* ctx, const char* name) {
for (FnEntry* e = ctx->fn_table; e; e = e->next)
if (strcmp(e->name, name) == 0) return e;
return NULL;
}
// === 结构体类型表 ===
void add_struct_type(CgCtx* ctx, const char* name, LLVMTypeRef ty, size_t fc) {
StructTypeEntry* e = arena_alloc(ctx->arena, sizeof(*e));
@@ -379,14 +386,23 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
AstNode* fn = ast->as.program.functions[i];
LLVMTypeRef* ptypes = arena_alloc(ctx.arena,
fn->as.function.param_count * sizeof(LLVMTypeRef));
bool* out_params = NULL;
if (fn->as.function.param_count > 0) {
out_params = arena_alloc(ctx.arena,
fn->as.function.param_count * sizeof(bool));
}
for (size_t j = 0; j < fn->as.function.param_count; j++) {
AstNode* param = fn->as.function.params[j];
bool is_out = param->as.parameter.is_out;
if (out_params) out_params[j] = is_out;
LLVMTypeRef inner_ty;
if (param->as.parameter.type == TYPE_STRUCT &&
param->as.parameter.struct_type_name) {
ptypes[j] = find_struct_type(&ctx, param->as.parameter.struct_type_name);
inner_ty = find_struct_type(&ctx, param->as.parameter.struct_type_name);
} else {
ptypes[j] = to_llvm_type(&ctx, param->as.parameter.type);
inner_ty = to_llvm_type(&ctx, param->as.parameter.type);
}
ptypes[j] = is_out ? LLVMPointerType(inner_ty, 0) : inner_ty;
}
LLVMTypeRef ret_ty;
if (fn->as.function.return_type == TYPE_STRUCT &&
@@ -398,7 +414,8 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
LLVMTypeRef fty = LLVMFunctionType(ret_ty,
ptypes, (unsigned)fn->as.function.param_count, false);
LLVMValueRef lfn = LLVMAddFunction(ctx.module, fn->as.function.name, fty);
add_fn(&ctx, fn->as.function.name, lfn);
add_fn(&ctx, fn->as.function.name, lfn, out_params,
fn->as.function.param_count);
}
// 第二遍:生成函数体
@@ -422,10 +439,15 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
} else {
param_ty = to_llvm_type(&ctx, pnode->as.parameter.type);
}
LLVMValueRef alloca = LLVMBuildAlloca(ctx.builder,
param_ty, pnode->as.parameter.name);
LLVMBuildStore(ctx.builder, param, alloca);
add_var(&ctx, pnode->as.parameter.name, alloca, param_ty);
if (pnode->as.parameter.is_out) {
// out 参数: param 已是指向调用者变量的指针, 直接用作 alloca
add_var(&ctx, pnode->as.parameter.name, param, param_ty);
} else {
LLVMValueRef alloca = LLVMBuildAlloca(ctx.builder,
param_ty, pnode->as.parameter.name);
LLVMBuildStore(ctx.builder, param, alloca);
add_var(&ctx, pnode->as.parameter.name, alloca, param_ty);
}
}
ctx.defer_count = 0;