feat: 闭包变量捕获 — 环境结构体 + 堆分配
lambda 可捕获外层变量, 自动构建环境结构体:
let base = 100;
let f = fn(x: i64) -> i64 { return x + base; }; // 捕获 base
f(50); // → 150
全流水线实现:
- Sema: collect_free_vars 遍历 AST 收集自由变量
- AST function: captured/cap_types/cap_count 字段存储捕获信息
- Codegen: 闭包类型改为 struct {fn_ptr: i64, env_ptr: ptr}
- Codegen: lambda 表达式 malloc 环境结构体 + 存储捕获值
- Codegen: 生成函数签名添加 env_ptr 首个参数 (capturing only)
- Codegen: 函数体内通过 GEP 注册捕获变量到 var_table
- Codegen: 闭包调用自动提取 fn_ptr/env_ptr, 条件传递 env
非捕获 lambda 兼容: env_ptr=NULL, 不额外传参
嵌套 lambda 正确处理: 内层不穿透捕获外层变量
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+45
-11
@@ -391,17 +391,25 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
// 第一遍:声明所有 L 函数
|
||||
for (size_t i = 0; i < ast->as.program.fn_count; i++) {
|
||||
AstNode* fn = ast->as.program.functions[i];
|
||||
bool has_env = fn->as.function.cap_count > 0;
|
||||
size_t total_params = fn->as.function.param_count + (has_env ? 1 : 0);
|
||||
LLVMTypeRef* ptypes = arena_alloc(ctx.arena,
|
||||
fn->as.function.param_count * sizeof(LLVMTypeRef));
|
||||
total_params * 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));
|
||||
if (total_params > 0) {
|
||||
out_params = arena_alloc(ctx.arena, total_params * sizeof(bool));
|
||||
for (size_t j = 0; j < total_params; j++) out_params[j] = false;
|
||||
}
|
||||
// 若有捕获, 第一个参数是 env_ptr
|
||||
size_t poff = 0;
|
||||
if (has_env) {
|
||||
ptypes[0] = LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0);
|
||||
poff = 1;
|
||||
}
|
||||
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;
|
||||
if (out_params) out_params[j + poff] = is_out;
|
||||
LLVMTypeRef inner_ty;
|
||||
if (param->as.parameter.type == TYPE_STRUCT &&
|
||||
param->as.parameter.struct_type_name) {
|
||||
@@ -409,7 +417,7 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
} else {
|
||||
inner_ty = to_llvm_type(&ctx, param->as.parameter.type);
|
||||
}
|
||||
ptypes[j] = is_out ? LLVMPointerType(inner_ty, 0) : inner_ty;
|
||||
ptypes[j + poff] = is_out ? LLVMPointerType(inner_ty, 0) : inner_ty;
|
||||
}
|
||||
LLVMTypeRef ret_ty;
|
||||
if (fn->as.function.return_type == TYPE_STRUCT &&
|
||||
@@ -419,10 +427,9 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
ret_ty = to_llvm_type(&ctx, fn->as.function.return_type);
|
||||
}
|
||||
LLVMTypeRef fty = LLVMFunctionType(ret_ty,
|
||||
ptypes, (unsigned)fn->as.function.param_count, false);
|
||||
ptypes, (unsigned)total_params, false);
|
||||
LLVMValueRef lfn = LLVMAddFunction(ctx.module, fn->as.function.name, fty);
|
||||
add_fn(&ctx, fn->as.function.name, lfn, out_params,
|
||||
fn->as.function.param_count);
|
||||
add_fn(&ctx, fn->as.function.name, lfn, out_params, total_params);
|
||||
}
|
||||
|
||||
// 第二遍:生成函数体
|
||||
@@ -435,9 +442,37 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
// 清空变量表(每个函数独立作用域)
|
||||
ctx.var_table = NULL;
|
||||
|
||||
// 捕获变量: 第一个参数是 env_ptr, 通过 GEP 注册捕获变量
|
||||
bool has_env = fn->as.function.cap_count > 0;
|
||||
LLVMValueRef env_ptr = NULL;
|
||||
size_t param_offset = 0;
|
||||
if (has_env) {
|
||||
env_ptr = LLVMGetParam(lfn, 0);
|
||||
param_offset = 1;
|
||||
// 生成 env struct 类型并注册捕获变量
|
||||
LLVMTypeRef* ef = arena_alloc(ctx.arena,
|
||||
fn->as.function.cap_count * sizeof(LLVMTypeRef));
|
||||
for (size_t ci = 0; ci < fn->as.function.cap_count; ci++)
|
||||
ef[ci] = to_llvm_type(&ctx, fn->as.function.cap_types[ci]);
|
||||
LLVMTypeRef env_ty = LLVMStructTypeInContext(ctx.context, ef,
|
||||
(unsigned)fn->as.function.cap_count, false);
|
||||
LLVMValueRef typed_env = LLVMBuildBitCast(ctx.builder, env_ptr,
|
||||
LLVMPointerType(env_ty, 0), "env_typed");
|
||||
for (size_t ci = 0; ci < fn->as.function.cap_count; ci++) {
|
||||
LLVMValueRef gep_idx[] = {
|
||||
LLVMConstInt(LLVMInt32TypeInContext(ctx.context), 0, false),
|
||||
LLVMConstInt(LLVMInt32TypeInContext(ctx.context), (unsigned)ci, false)
|
||||
};
|
||||
LLVMValueRef field_ptr = LLVMBuildGEP2(ctx.builder, env_ty,
|
||||
typed_env, gep_idx, 2, "cap_ptr");
|
||||
add_var(&ctx, fn->as.function.captured[ci], field_ptr,
|
||||
to_llvm_type(&ctx, fn->as.function.cap_types[ci]));
|
||||
}
|
||||
}
|
||||
|
||||
// 将参数注册为变量
|
||||
for (size_t j = 0; j < fn->as.function.param_count; j++) {
|
||||
LLVMValueRef param = LLVMGetParam(lfn, (unsigned)j);
|
||||
LLVMValueRef param = LLVMGetParam(lfn, (unsigned)(j + param_offset));
|
||||
AstNode* pnode = fn->as.function.params[j];
|
||||
LLVMTypeRef param_ty;
|
||||
if (pnode->as.parameter.type == TYPE_STRUCT &&
|
||||
@@ -447,7 +482,6 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
param_ty = to_llvm_type(&ctx, pnode->as.parameter.type);
|
||||
}
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user