Files
ViperC/App/lib/core/Gen/FuncGenMixin.vp
T
2026-08-27 09:48:20 +08:00

1565 lines
83 KiB
Plaintext
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.
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Gen.CallGenBase as CallGenBase
# ============================================================
# FuncGenMixin - 函数/方法声明与翻译 Mixin(对象化 P5-1,wiki/14-oo-refactor.md)
#
# 从 HandlesFunctions.vp 迁移:模块级纯函数(trans 首参,无全局状态)
# → 类方法(self 替代,trans 参数保留)。
#
# 迁移原则(与 P2/P3/P4 一致,行为零变化):
# 1. 方法保留原函数签名(含 trans 首参),仅函数体内的
# 本模块函数调用改为 self.xxx(...)
# 2. 外部模块调用(HandlesExprCall.xxx/HandlesType.xxx 等)保留模块前缀
# 3. HandlesFunctions.vp 原函数保留为"委托":本地模块级单例 _g_gen
# (BuildPipeline/main 注入)cast 成 Generator 类型
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# HandlesFunctions - 函数定义处理(trans 单参模式)
#
# 参考 Python 版 TransPyC FunctionHandle
#
# 函数有自己的局部作用域,通过 HandlesVar.enter_scope/exit_scope
# 管理嵌套作用域链,翻译函数体前进入函数作用域,翻译完退出。
# ============================================================
# 作用域类型常量(本地副本,与 HandlesFunctions.vp 保持一致)
SCOPE_FUNCTION: t.CDefine = 1
# ============================================================
# FuncGenMixin - 函数属性/命名/声明/翻译
# ============================================================
@t.NoVTable
class FuncGenMixin(CallGenBase.CallGenBase):
"""函数/方法翻译 Mixin(纯方法化迁移,无状态字段)"""
# ============================================================
# translate_nested_function_def - 嵌套函数提升 + 闭包创建
#
# 将嵌套函数提升为顶层函数 @__closure_{name}(i8* %env) -> i32,
# 在父函数中创建闭包对象 {i8* fn_ptr, i8* env_ptr} 并存储到
# 以函数名命名的局部变量中。
#
# 闭包结构: {i8* fn_ptr, i8* env_ptr} (16 字节, malloc 分配)
# Env 结构: {i8* ptr0, i8* ptr1, ...} (每个 nonlocal 变量 8 字节,指向父函数 alloca)
# ============================================================
def translate_nested_function_def(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译嵌套函数定义:提升为顶层函数 + 创建闭包"""
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node)
if fd is None or fd.name is None:
return 0
pool: memhub.MemBuddy | t.CPtr = trans.Pool
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
func_name: str = fd.name
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 1. 构建提升后的函数名: __closure_{name}(加 SHA1 前缀避免跨模块冲突)
promoted_name: str = pool.alloc(64)
if promoted_name is not None:
viperlib.snprintf(promoted_name, 64, "__closure_%s", func_name)
mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, promoted_name)
# 2. 创建提升后的函数: define i32 @__closure_{name}(i8* %env, args...)
func: llvmlite.Function | t.CPtr = llvmlite.create_function(
pool, mod, mangled_promoted, i32_ty)
if func is None:
return 0
llvmlite.add_param(pool, func, i8_ptr_ty, "%env")
# 2a. 嵌套函数参数:加入提升函数签名(i8* %env, 参数...),
# 类型存入 param_tys 缓冲(CVoid 跳过,与 alloca 创建索引对齐)
imported_modules: str = trans._imported_modules
from_imports: str = trans._from_imports
param_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64)
if param_tys is not None:
string.memset(param_tys, 0, 8 * 64)
param_count: int = 0
if fd.args is not None:
ags_n: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args)
if ags_n.args is not None:
alist_n: list[ast.AST | t.CPtr] | t.CPtr = ags_n.args
an_n: t.CSizeT = len(alist_n)
for ai_n in range(an_n):
arg_n: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist_n.get(ai_n))
if arg_n is None or arg_n.arg is None:
continue
if arg_n.annotation is not None:
if self.Gen.TypeGenResolve._is_t_attr(arg_n.annotation, "CVoid", imported_modules) != 0:
continue
pty_n: llvmlite.LLVMType | t.CPtr = i32_ty
if arg_n.annotation is not None:
resolved_n: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, arg_n.annotation, imported_modules, from_imports, trans, 0)
if resolved_n is not None:
match resolved_n:
case llvmlite.LLVMType.Struct(_, _, _):
pty_n = llvmlite.Ptr(pool, resolved_n)
case llvmlite.LLVMType.Array(aelem_ty_n, _):
pty_n = llvmlite.Ptr(pool, aelem_ty_n)
case _:
pty_n = resolved_n
if param_count < 64 and param_tys is not None:
pty_slot_n: str | t.CPtr = (t.CVoid | t.CPtr)(
t.CUInt64T(param_tys) + param_count * 8)
pty_slot_n[0] = pty_n
pname_n: str = pool.alloc(32)
if pname_n is not None:
viperlib.snprintf(pname_n, 32, "%%%s", arg_n.arg)
llvmlite.add_param(pool, func, pty_n, pname_n)
param_count += 1
# 3. 创建 entry 块 + builder
entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry")
if entry_blk is None:
return 0
func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func)
if func_builder is None:
return 0
llvmlite.position_at_end(func_builder, entry_blk)
# 4. 进入嵌套函数作用域(嵌套符号表)
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
# 5. 创建 _env_ptr alloca 并存储 %env 参数
env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty)
if env_alloca is not None:
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca)
env_param_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "%env")
llvmlite.build_store(func_builder, env_param_val, env_alloca)
# 5a. 创建嵌套函数参数 alloca(函数体内参数名可引用,如
# accumulate(n) 内 for i in range(n)、set_result(val) 内 result = val)
if fd.args is not None:
ags_p: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(fd.args)
if ags_p.args is not None:
alist_p: list[ast.AST | t.CPtr] | t.CPtr = ags_p.args
idx_p: int = 0
apn: t.CSizeT = len(alist_p)
for api in range(apn):
arg_p: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist_p.get(api))
if arg_p is None or arg_p.arg is None:
continue
if arg_p.annotation is not None:
if self.Gen.TypeGenResolve._is_t_attr(arg_p.annotation, "CVoid", imported_modules) != 0:
continue
if idx_p >= param_count:
break
pty2_n: llvmlite.LLVMType | t.CPtr = i32_ty
if param_tys is not None:
ty_addr_n: t.CUInt64T = t.CUInt64T(param_tys) + idx_p * 8
ty_slot_n: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr_n)
pty2_n = (llvmlite.LLVMType | t.CPtr)(ty_slot_n[0])
if pty2_n is None:
pty2_n = i32_ty
p_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, pty2_n)
if p_alloca is not None:
self.Gen.VarGen.define_var(trans.SymTab, arg_p.arg, p_alloca)
if arg_p.annotation is not None:
cls_nm_p: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
arg_p.annotation, imported_modules, pool, trans._from_imports)
if cls_nm_p is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, arg_p.arg, cls_nm_p)
pname2_n: str = pool.alloc(32)
if pname2_n is not None:
viperlib.snprintf(pname2_n, 32, "%%%s", arg_p.arg)
p_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, pty2_n, pname2_n)
llvmlite.build_store(func_builder, p_val, p_alloca)
idx_p += 1
# 6. 保存父函数作用域状态(仅非变量表相关)
old_func: llvmlite.Function | t.CPtr = trans._cur_func
old_builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
old_func_name: str = trans._cur_func_name
old_global_count: int = trans._global_name_count
old_nonlocal_count: int = trans._nonlocal_name_count
old_env_count: int = trans._closure_env_count
# nonlocal 集合独立缓冲:嵌套函数注册 nonlocal 写入共享缓冲 [0] 会
# 覆盖父函数集合(ClosureTest2 Test 7 不同变量场景:level2 的 b 覆盖
# level1 的 a → level1 env[0] 存错)。嵌套函数用独立缓冲,翻译完恢复。
saved_nl_names: t.CVoid | t.CPtr = trans._nonlocal_names
saved_nl_types: t.CVoid | t.CPtr = trans._nonlocal_types
# 7. 切换到嵌套函数作用域 + 清空 scope names
trans._cur_func = func
trans._cur_builder = func_builder
trans._cur_func_name = func_name
HT.clear_scope_names(trans)
# 嵌套函数独立 nonlocal 缓冲(避免覆盖父函数集合)
trans._nonlocal_names = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8)
trans._nonlocal_types = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8)
if trans._nonlocal_names is not None:
string.memset(trans._nonlocal_names, 0, HT.MAX_GLOBAL_NAMES * 8)
if trans._nonlocal_types is not None:
string.memset(trans._nonlocal_types, 0, HT.MAX_GLOBAL_NAMES * 8)
# 8. 预扫描函数体:
# a. 先预扫描 Global/Nonlocal 声明(Python 语义:对整个函数作用域生效)
# b. 再预扫描 AnnAssign 为局部变量提前创建 alloca
body: list[ast.AST | t.CPtr] | t.CPtr = fd.children
if body is not None:
bn: t.CSizeT = len(body)
for bi in range(bn):
stmt: ast.AST | t.CPtr = body.get(bi)
if stmt is not None:
self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
for bi in range(bn):
stmt: ast.AST | t.CPtr = body.get(bi)
if stmt is not None:
self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
# 8c. 隐式捕获扫描:嵌套函数读取的外层函数局部变量注册为 nonlocal
# (Python 语义,无需显式 nonlocal;否则 read 经 lookup_var
# 跨函数命中父函数 alloca → SSA 编号错乱 → llc undefined value)。
# 分支内 AnnAssign 局部已由 pre_scan_allocas 递归预注册
# (StmtGenDeclMixin),本扫描经 lookup_current 自然排除。
if body is not None:
bnc: t.CSizeT = len(body)
for bic in range(bnc):
stmtc: ast.AST | t.CPtr = body.get(bic)
if stmtc is not None:
self._closure_scan_captures(trans, stmtc)
# 9. 翻译函数体(Nonlocal 语句会填充 _nonlocal_names)
if body is not None:
bn2: t.CSizeT = len(body)
for bi2 in range(bn2):
stmt2: ast.AST | t.CPtr = body.get(bi2)
if stmt2 is not None:
self.Gen.StmtGen.translate_stmt(trans, stmt2)
# 10. 隐式 ret
last_is_return: int = 0
if body is not None:
bn3: t.CSizeT = len(body)
if bn3 > 0:
last_stmt: ast.AST | t.CPtr = body.get(bn3 - 1)
if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return:
last_is_return = 1
if last_is_return == 0:
zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
llvmlite.build_ret(func_builder, zero_val)
# 11. 保存嵌套函数的 nonlocal 信息
nested_nonlocal_count: int = trans._nonlocal_name_count
# 12. 退出嵌套函数作用域,回到父函数作用域(保留 _nonlocal_names 用于 env 创建)
self.Gen.VarGen.exit_scope(trans.SymTab)
trans._cur_func = old_func
trans._cur_builder = old_builder
# NOTE: _nonlocal_names 和 _nonlocal_name_count 仍为嵌套函数的值
# 13. 在父函数中创建闭包对象
parent_builder: llvmlite.IRBuilder | t.CPtr = old_builder
if parent_builder is None:
# 无 builder(模块级嵌套函数?)→ 无法创建闭包,仅恢复状态
trans._cur_func_name = old_func_name
trans._global_name_count = old_global_count
trans._nonlocal_name_count = old_nonlocal_count
trans._closure_env_count = old_env_count
trans._nonlocal_names = saved_nl_names
trans._nonlocal_types = saved_nl_types
return 0
# 13a. 创建 env: malloc(8 * nested_nonlocal_count) — env 存储指针数组
# 每个 nonlocal 变量占 8 字节(i8*),指向父函数的 alloca(共享存储)
env_ptr: llvmlite.Value | t.CPtr = None
if nested_nonlocal_count > 0:
env_size: t.CInt64T = nested_nonlocal_count * 8
env_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, env_size)
if env_size_val is not None:
env_size_val.Next = None
env_ptr = llvmlite.build_call(
parent_builder, "malloc", env_size_val, 1, i8_ptr_ty, 0)
# 为每个 nonlocal 变量建立堆存储并注册到 env:
# 运行时 env 链式共享(无需编译期映射——映射存编译期 Value 对象
# 地址会被 inttoptr 当运行时堆地址解引用崩溃;存整型也拿不到
# 运行时 malloc 结果):
# - 变量是父函数局部(lookup_current 在当前作用域找到 alloca)
# → 新建堆(malloc + 拷贝当前值),并把父函数 VarEntry.Alloca
# 重定向到堆——父函数后续读写走共享堆
# - 变量是父函数的 nonlocal(多层嵌套,当前作用域无 alloca)
# → 从父函数 env 读该变量的堆地址(_load_raw_var_ptr),
# env[ni] = 同一堆地址(运行时链式共享,各层写同一堆)
if env_ptr is not None:
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
for ni in range(nested_nonlocal_count):
# 从 _nonlocal_names 缓冲区读取名称
name_addr: t.CUInt64T = t.CUInt64T(trans._nonlocal_names) + ni * 8
nl_slot: str | t.CPtr = (t.CVoid | t.CPtr)(name_addr)
nl_name: str = nl_slot[0]
if nl_name is None:
continue
heap_store: llvmlite.Value | t.CPtr = None
# 1. 优先查全局堆地址映射(_prepare_chain_nonlocals 已为
# 链内 nonlocal 建堆并 store 到 @nlheap.*;不同层捕获
# 不同变量时(ClosureTest2 Test 7)level2 的 b 经此共享)
gname_ck: str = self._nl_heap_global_name(
pool, trans, nl_name)
if gname_ck is not None \
and self._nl_heap_global_exists(mod, gname_ck) != 0:
gv_ref0: llvmlite.Value | t.CPtr = self._nl_heap_global_ref(
pool, trans, nl_name)
if gv_ref0 is not None:
heap_store = llvmlite.build_load(
parent_builder, i8_ptr_ty, gv_ref0)
if heap_store is None:
local_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current(
trans.SymTab, nl_name)
if local_alloca is not None and local_alloca.Ty is not None:
# 父函数局部变量:新建堆存储 + 拷贝当前值 + 重定向
nl_load_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee
if nl_load_ty is not None:
cur_val_nl: llvmlite.Value | t.CPtr = llvmlite.build_load(
parent_builder, nl_load_ty, local_alloca)
# 堆存储大小按类型字节数:数组(如 [4 x i32]=16 字节)
# 不能固定 8 字节(越界写崩溃);指针/标量 ≥ 8 字节
st_size: t.CInt = self._type_byte_size(pool, nl_load_ty)
st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(
pool, st_size)
if st_size_val is not None:
st_size_val.Next = None
var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call(
parent_builder, "malloc", st_size_val, 1, i8_ptr_ty, 0)
if var_storage is not None:
# 拷贝当前值到堆存储
st_slot_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty))
if st_slot_nl is not None and cur_val_nl is not None:
llvmlite.build_store(parent_builder, cur_val_nl, st_slot_nl)
# 父函数 VarEntry.Alloca 重定向到堆(父函数读写走共享堆)
ve_nl: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nl_name)
heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty))
if ve_nl is not None and heap_typed is not None:
ve_nl.Alloca = heap_typed
# 记录到全局映射(后续层/父函数可见)
gv_ref_new: llvmlite.Value | t.CPtr = None
if gname_ck is not None \
and self._nl_heap_global_exists(mod, gname_ck) == 0:
gv_new: llvmlite.GlobalVariable | t.CPtr = \
llvmlite.new_global_variable(pool, gname_ck, i8_ptr_ty)
if gv_new is not None:
# 清除 external linkage + 零初始化(防 undefined reference)
gv_new.Linkage = None
gv_new.Initializer = "null"
llvmlite.module_add_global(mod, gv_new)
gv_ref_new = self._nl_heap_global_ref(pool, trans, nl_name)
if gv_ref_new is not None:
llvmlite.build_store(
parent_builder, var_storage, gv_ref_new)
heap_store = var_storage
else:
# 父函数中该变量是 nonlocal(多层嵌套):从父函数 env
# 读堆地址(运行时链式共享同一堆),不新建
heap_store = self.Gen.StmtGenDecl._load_raw_var_ptr(trans, nl_name)
if heap_store is None:
continue
# env[ni] = 堆存储地址(共享)
offset_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ni * 8)
slot: llvmlite.Value | t.CPtr = llvmlite.build_gep(
parent_builder, i8_ty, env_ptr, offset_val)
if slot is not None:
slot_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
parent_builder, slot, i8_ptr_ptr_ty)
if slot_typed is not None:
llvmlite.build_store(parent_builder, heap_store, slot_typed)
# 13b. 创建闭包结构: malloc(16)
closure_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 16)
if closure_size_val is not None:
closure_size_val.Next = None
closure_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call(
parent_builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0)
if closure_ptr is None:
# malloc 失败,恢复状态
trans._cur_func_name = old_func_name
trans._global_name_count = old_global_count
trans._nonlocal_name_count = old_nonlocal_count
trans._closure_env_count = old_env_count
trans._nonlocal_names = saved_nl_names
trans._nonlocal_types = saved_nl_types
return 0
# 13c. 存储 fn_ptr 到 offset 0
# 创建函数指针类型 i32(i8*)*
fn_param_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty)
fn_func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, i32_ty, fn_param_node, 1)
fn_func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, fn_func_ty)
# 创建函数引用 Value(类型为 i32(i8*)*,使用 SHA1 混淆名)
fn_ptr_name: str = pool.alloc(64)
if fn_ptr_name is not None:
# SHA1 前缀名含 '.' 需要加引号(如 @"sha1.__closure_func")
if string.strchr(mangled_promoted, '.') is not None:
viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted)
else:
viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted)
fn_ptr_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name)
# bitcast 函数指针到 i8*(闭包存储 i8* 类型)
fn_as_i8ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
parent_builder, fn_ptr_val, i8_ptr_ty)
fn_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
parent_builder, closure_ptr, llvmlite.Ptr(pool, i8_ptr_ty))
if fn_slot is not None and fn_as_i8ptr is not None:
llvmlite.build_store(parent_builder, fn_as_i8ptr, fn_slot)
# 13d. 存储 env_ptr 到 offset 8
eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 8)
env_slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(
parent_builder, i8_ty, closure_ptr, eight_val)
if env_slot_addr is not None:
env_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
parent_builder, env_slot_addr, llvmlite.Ptr(pool, i8_ptr_ty))
if env_slot is not None:
if env_ptr is not None:
llvmlite.build_store(parent_builder, env_ptr, env_slot)
else:
# 无 nonlocal 变量,存储 null
null_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "null")
llvmlite.build_store(parent_builder, null_val, env_slot)
# 13e. 将闭包指针存储到父函数的局部变量 {func_name}
closure_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(parent_builder, i8_ptr_ty)
if closure_alloca is not None:
llvmlite.build_store(parent_builder, closure_ptr, closure_alloca)
self.Gen.VarGen.define_var(trans.SymTab, func_name, closure_alloca)
# 注册闭包变量名(供调用时区分闭包 vs 函数指针变量,
# 两者 alloca 类型同为 i8**,_is_func_ptr_alloca 无法区分)
self.Gen.CallGenLeafKit.register_closure_name(func_name)
# 14. 恢复 scope names(含 nonlocal 集合指针)
trans._cur_func_name = old_func_name
trans._global_name_count = old_global_count
trans._nonlocal_name_count = old_nonlocal_count
trans._closure_env_count = old_env_count
trans._nonlocal_names = saved_nl_names
trans._nonlocal_types = saved_nl_types
return 1
# ============================================================
# _closure_scan_captures - 递归扫描嵌套函数体中的隐式捕获变量
#
# Python 语义:嵌套函数读取外层函数局部变量无需显式 nonlocal。
# 此前嵌套 def 闭包仅在显式 `nonlocal` 语句时注册捕获;无 nonlocal
# 的 read 在翻译时会经 lookup_var 跨函数命中父函数 alloca(SSA 编号
# 属于父函数,提升函数内引用 → llc "use of undefined value '%X'")。
# lambda 已有 _lambda_scan_body 预扫描,这里对嵌套 def 做等价预扫描:
# 把"读取的外层函数局部变量"注册为 nonlocal(复用 env 共享堆机制),
# 使 read 走 _load_raw_var_ptr 从 env 读堆值。
# 注意:与 lambda 快照方案不同,嵌套 def 复用显式 nonlocal 的
# env 共享堆路径,因此注册顺序必须与 step 13 的 env 构建一致
# (本扫描在 body 翻译前执行,注册顺序稳定)。
# ============================================================
def _closure_scan_captures(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""递归扫描节点中的隐式捕获并注册为 nonlocal"""
if node is None or trans is None:
return 1
k: int = node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is None:
return 1
# 当前作用域已定义(参数 / AnnAssign 局部,含分支内)→ 非捕获
if self.Gen.VarGen.lookup_current(trans.SymTab, nm.id) is not None:
return 1
# 已注册 nonlocal(显式 nonlocal 语句 / 已捕获)→ 跳过
if HT.is_nonlocal_name(trans, nm.id) != 0:
return 1
ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if ve is None or ve.Alloca is None or ve.Alloca.Ty is None:
return 1
nl_ty: llvmlite.LLVMType | t.CPtr = ve.Alloca.Ty.Pointee
if nl_ty is None:
return 1
# 模块级全局直接解析,无需 env 捕获
mod_gv: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(
trans.SymTab, nm.id)
if mod_gv is not None:
if t.CUInt64T(mod_gv) == t.CUInt64T(ve.Alloca):
return 1
# 外层函数局部变量 → 隐式捕获(注册为 nonlocal)
HT.add_nonlocal_name(trans, nm.id, nl_ty)
return 1
# ---- 语句 ----
if k == ast.ASTKind.Assign:
asn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
if asn.value is not None:
self._closure_scan_captures(trans, asn.value)
return 1
if k == ast.ASTKind.AnnAssign:
aan: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aan.value is not None:
self._closure_scan_captures(trans, aan.value)
return 1
if k == ast.ASTKind.AugAssign:
aun: ast.AugAssign | t.CPtr = (ast.AugAssign | t.CPtr)(node)
# 目标同时是读(先读旧值),参与捕获
if aun.target is not None:
self._closure_scan_captures(trans, aun.target)
if aun.value is not None:
self._closure_scan_captures(trans, aun.value)
return 1
if k == ast.ASTKind.If:
ifn: ast.If | t.CPtr = (ast.If | t.CPtr)(node)
if ifn.test is not None:
self._closure_scan_captures(trans, ifn.test)
if ifn.children is not None:
bn_if: t.CSizeT = len(ifn.children)
for bi_if in range(bn_if):
self._closure_scan_captures(trans, ifn.children.get(bi_if))
if ifn.orelse is not None:
bn_el: t.CSizeT = len(ifn.orelse)
for bi_el in range(bn_el):
self._closure_scan_captures(trans, ifn.orelse.get(bi_el))
return 1
if k == ast.ASTKind.For:
for_n: ast.For | t.CPtr = (ast.For | t.CPtr)(node)
# 循环目标是新局部变量,不参与捕获
if for_n.iter is not None:
self._closure_scan_captures(trans, for_n.iter)
if for_n.children is not None:
bn_f: t.CSizeT = len(for_n.children)
for bi_f in range(bn_f):
self._closure_scan_captures(trans, for_n.children.get(bi_f))
if for_n.orelse is not None:
bn_fe: t.CSizeT = len(for_n.orelse)
for bi_fe in range(bn_fe):
self._closure_scan_captures(trans, for_n.orelse.get(bi_fe))
return 1
if k == ast.ASTKind.While:
wh_n: ast.While | t.CPtr = (ast.While | t.CPtr)(node)
if wh_n.test is not None:
self._closure_scan_captures(trans, wh_n.test)
if wh_n.children is not None:
bn_w: t.CSizeT = len(wh_n.children)
for bi_w in range(bn_w):
self._closure_scan_captures(trans, wh_n.children.get(bi_w))
if wh_n.orelse is not None:
bn_we: t.CSizeT = len(wh_n.orelse)
for bi_we in range(bn_we):
self._closure_scan_captures(trans, wh_n.orelse.get(bi_we))
return 1
if k == ast.ASTKind.Return:
rt_n: ast.Return | t.CPtr = (ast.Return | t.CPtr)(node)
if rt_n.value is not None:
self._closure_scan_captures(trans, rt_n.value)
return 1
if k == ast.ASTKind.Expr:
ex_n: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(node)
if ex_n.value is not None:
self._closure_scan_captures(trans, ex_n.value)
return 1
if k == ast.ASTKind.With:
wth: ast.With | t.CPtr = (ast.With | t.CPtr)(node)
if wth.items is not None:
bn_wi: t.CSizeT = len(wth.items)
for bi_wi in range(bn_wi):
wi_n: ast.AST | t.CPtr = wth.items.get(bi_wi)
if wi_n is not None and wi_n.kind() == ast.ASTKind.WithItem:
wi_c: ast.WithItem | t.CPtr = (ast.WithItem | t.CPtr)(wi_n)
if wi_c.context_expr is not None:
self._closure_scan_captures(trans, wi_c.context_expr)
if wth.children is not None:
bn_wb: t.CSizeT = len(wth.children)
for bi_wb in range(bn_wb):
self._closure_scan_captures(trans, wth.children.get(bi_wb))
return 1
if k == ast.ASTKind.Raise:
rs_n: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(node)
if rs_n.exc is not None:
self._closure_scan_captures(trans, rs_n.exc)
if rs_n.cause is not None:
self._closure_scan_captures(trans, rs_n.cause)
return 1
if k == ast.ASTKind.Assert:
as_n: ast.Assert | t.CPtr = (ast.Assert | t.CPtr)(node)
if as_n.test is not None:
self._closure_scan_captures(trans, as_n.test)
if as_n.msg is not None:
self._closure_scan_captures(trans, as_n.msg)
return 1
if k == ast.ASTKind.Try:
tr_n: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node)
if tr_n.children is not None:
bn_t: t.CSizeT = len(tr_n.children)
for bi_t in range(bn_t):
self._closure_scan_captures(trans, tr_n.children.get(bi_t))
if tr_n.handlers is not None:
bn_th: t.CSizeT = len(tr_n.handlers)
for bi_th in range(bn_th):
eh_n: ast.AST | t.CPtr = tr_n.handlers.get(bi_th)
if eh_n is not None and eh_n.kind() == ast.ASTKind.ExceptHandler:
eh_c: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)(eh_n)
if eh_c.children is not None:
bn_ehb: t.CSizeT = len(eh_c.children)
for bi_ehb in range(bn_ehb):
self._closure_scan_captures(trans, eh_c.children.get(bi_ehb))
if tr_n.orelse is not None:
bn_to: t.CSizeT = len(tr_n.orelse)
for bi_to in range(bn_to):
self._closure_scan_captures(trans, tr_n.orelse.get(bi_to))
if tr_n.finalbody is not None:
bn_tf: t.CSizeT = len(tr_n.finalbody)
for bi_tf in range(bn_tf):
self._closure_scan_captures(trans, tr_n.finalbody.get(bi_tf))
return 1
if k == ast.ASTKind.Match:
mt_n: ast.Match | t.CPtr = (ast.Match | t.CPtr)(node)
if mt_n.subject is not None:
self._closure_scan_captures(trans, mt_n.subject)
if mt_n.cases is not None:
bn_mc: t.CSizeT = len(mt_n.cases)
for bi_mc in range(bn_mc):
mc_n: ast.AST | t.CPtr = mt_n.cases.get(bi_mc)
if mc_n is not None and mc_n.kind() == ast.ASTKind.MatchCase:
mc_c: ast.MatchCase | t.CPtr = (ast.MatchCase | t.CPtr)(mc_n)
if mc_c.guard is not None:
self._closure_scan_captures(trans, mc_c.guard)
if mc_c.children is not None:
bn_mcb: t.CSizeT = len(mc_c.children)
for bi_mcb in range(bn_mcb):
self._closure_scan_captures(trans, mc_c.children.get(bi_mcb))
return 1
# ---- 表达式 ----
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop.left is not None:
self._closure_scan_captures(trans, bop.left)
if bop.right is not None:
self._closure_scan_captures(trans, bop.right)
return 1
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop.operand is not None:
self._closure_scan_captures(trans, uop.operand)
return 1
if k == ast.ASTKind.BoolOp:
bop2: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
if bop2.values is not None:
bn_bo: t.CSizeT = len(bop2.values)
for bi_bo in range(bn_bo):
self._closure_scan_captures(trans, bop2.values.get(bi_bo))
return 1
if k == ast.ASTKind.Compare:
cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
if cmp.left is not None:
self._closure_scan_captures(trans, cmp.left)
if cmp.comparators is not None:
bn_cmp: t.CSizeT = len(cmp.comparators)
for bi_cmp in range(bn_cmp):
self._closure_scan_captures(trans, cmp.comparators.get(bi_cmp))
return 1
if k == ast.ASTKind.Call:
cal: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cal.func is not None:
self._closure_scan_captures(trans, cal.func)
if cal.args is not None:
bn_ca: t.CSizeT = len(cal.args)
for bi_ca in range(bn_ca):
self._closure_scan_captures(trans, cal.args.get(bi_ca))
if cal.keywords is not None:
bn_ck: t.CSizeT = len(cal.keywords)
for bi_ck in range(bn_ck):
kw_n: ast.AST | t.CPtr = cal.keywords.get(bi_ck)
if kw_n is not None and kw_n.kind() == ast.ASTKind.Keyword:
kw_c: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(kw_n)
if kw_c.value is not None:
self._closure_scan_captures(trans, kw_c.value)
return 1
if k == ast.ASTKind.Subscript:
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub.value is not None:
self._closure_scan_captures(trans, sub.value)
if sub.slice is not None:
self._closure_scan_captures(trans, sub.slice)
return 1
if k == ast.ASTKind.Attribute:
at_n: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at_n.value is not None:
self._closure_scan_captures(trans, at_n.value)
return 1
if k == ast.ASTKind.IfExp:
ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node)
if ie.test is not None:
self._closure_scan_captures(trans, ie.test)
if ie.body is not None:
self._closure_scan_captures(trans, ie.body)
if ie.orelse is not None:
self._closure_scan_captures(trans, ie.orelse)
return 1
if k == ast.ASTKind.Dict:
dc: ast.Dict | t.CPtr = (ast.Dict | t.CPtr)(node)
if dc.keys is not None:
bn_dk: t.CSizeT = len(dc.keys)
for bi_dk in range(bn_dk):
self._closure_scan_captures(trans, dc.keys.get(bi_dk))
if dc.values is not None:
bn_dv: t.CSizeT = len(dc.values)
for bi_dv in range(bn_dv):
self._closure_scan_captures(trans, dc.values.get(bi_dv))
return 1
if k == ast.ASTKind.Set:
st_n: ast.Set | t.CPtr = (ast.Set | t.CPtr)(node)
if st_n.elts is not None:
bn_set: t.CSizeT = len(st_n.elts)
for bi_set in range(bn_set):
self._closure_scan_captures(trans, st_n.elts.get(bi_set))
return 1
if k == ast.ASTKind.List:
ls_n: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
if ls_n.elts is not None:
bn_ls: t.CSizeT = len(ls_n.elts)
for bi_ls in range(bn_ls):
self._closure_scan_captures(trans, ls_n.elts.get(bi_ls))
return 1
if k == ast.ASTKind.Tuple:
tp_n: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
if tp_n.elts is not None:
bn_tp: t.CSizeT = len(tp_n.elts)
for bi_tp in range(bn_tp):
self._closure_scan_captures(trans, tp_n.elts.get(bi_tp))
return 1
if k == ast.ASTKind.Starred:
sr_n: ast.Starred | t.CPtr = (ast.Starred | t.CPtr)(node)
if sr_n.value is not None:
self._closure_scan_captures(trans, sr_n.value)
return 1
if k == ast.ASTKind.NamedExpr:
ne_n: ast.NamedExpr | t.CPtr = (ast.NamedExpr | t.CPtr)(node)
if ne_n.value is not None:
self._closure_scan_captures(trans, ne_n.value)
return 1
if k == ast.ASTKind.Slice:
sl_n: ast.Slice | t.CPtr = (ast.Slice | t.CPtr)(node)
if sl_n.lower is not None:
self._closure_scan_captures(trans, sl_n.lower)
if sl_n.upper is not None:
self._closure_scan_captures(trans, sl_n.upper)
if sl_n.step is not None:
self._closure_scan_captures(trans, sl_n.step)
return 1
if k == ast.ASTKind.JoinedStr:
js_n: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node)
if js_n.values is not None:
bn_js: t.CSizeT = len(js_n.values)
for bi_js in range(bn_js):
self._closure_scan_captures(trans, js_n.values.get(bi_js))
return 1
if k == ast.ASTKind.FormattedValue:
fv_n: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node)
if fv_n.value is not None:
self._closure_scan_captures(trans, fv_n.value)
if fv_n.format_spec is not None:
self._closure_scan_captures(trans, fv_n.format_spec)
return 1
if k == ast.ASTKind.Await:
aw_n: ast.Await | t.CPtr = (ast.Await | t.CPtr)(node)
if aw_n.value is not None:
self._closure_scan_captures(trans, aw_n.value)
return 1
if k == ast.ASTKind.Yield:
yd_n: ast.Yield | t.CPtr = (ast.Yield | t.CPtr)(node)
if yd_n.value is not None:
self._closure_scan_captures(trans, yd_n.value)
return 1
if k == ast.ASTKind.YieldFrom:
yf_n: ast.YieldFrom | t.CPtr = (ast.YieldFrom | t.CPtr)(node)
if yf_n.value is not None:
self._closure_scan_captures(trans, yf_n.value)
return 1
is_comp: int = 0
if k == ast.ASTKind.ListComp:
is_comp = 1
elif k == ast.ASTKind.SetComp:
is_comp = 1
elif k == ast.ASTKind.GeneratorExp:
is_comp = 1
if is_comp != 0:
lc_n: ast.ListComp | t.CPtr = (ast.ListComp | t.CPtr)(node)
if lc_n.elt is not None:
self._closure_scan_captures(trans, lc_n.elt)
if lc_n.generators is not None:
bn_gen: t.CSizeT = len(lc_n.generators)
for bi_gen in range(bn_gen):
cp_n: ast.AST | t.CPtr = lc_n.generators.get(bi_gen)
if cp_n is not None and cp_n.kind() == ast.ASTKind.Comprehension:
cp_c: ast.Comprehension | t.CPtr = (ast.Comprehension | t.CPtr)(cp_n)
if cp_c.iter is not None:
self._closure_scan_captures(trans, cp_c.iter)
if cp_c.ifs is not None:
bn_cpi: t.CSizeT = len(cp_c.ifs)
for bi_cpi in range(bn_cpi):
self._closure_scan_captures(trans, cp_c.ifs.get(bi_cpi))
return 1
if k == ast.ASTKind.DictComp:
dcn: ast.DictComp | t.CPtr = (ast.DictComp | t.CPtr)(node)
if dcn.key is not None:
self._closure_scan_captures(trans, dcn.key)
if dcn.value is not None:
self._closure_scan_captures(trans, dcn.value)
if dcn.generators is not None:
bn_dgen: t.CSizeT = len(dcn.generators)
for bi_dgen in range(bn_dgen):
cp_dn: ast.AST | t.CPtr = dcn.generators.get(bi_dgen)
if cp_dn is not None and cp_dn.kind() == ast.ASTKind.Comprehension:
cp_dc: ast.Comprehension | t.CPtr = (ast.Comprehension | t.CPtr)(cp_dn)
if cp_dc.iter is not None:
self._closure_scan_captures(trans, cp_dc.iter)
if cp_dc.ifs is not None:
bn_dcpi: t.CSizeT = len(cp_dc.ifs)
for bi_dcpi in range(bn_dcpi):
self._closure_scan_captures(trans, cp_dc.ifs.get(bi_dcpi))
return 1
return 1
# ============================================================
# _type_byte_size - 计算 LLVM 类型占用的字节大小(递归)
#
# 用于 nonlocal 共享堆存储分配:数组(如 [4 x i32]=16 字节)不能
# 用固定 8 字节(越界写破坏 canary/邻块 → 运行时崩溃,
# ClosureTest2 Test 4 非local 数组崩溃根因)。未知类型保守返回 8。
# ============================================================
def _type_byte_size(self, pool: memhub.MemBuddy | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> t.CInt:
"""计算 LLVM 类型占用的字节大小(递归),未知类型返回 8"""
if ty is None:
return 8
match ty:
case llvmlite.LLVMType.Int(bits):
sz_i: t.CInt = (bits + 7) // 8
if sz_i < 8:
sz_i = 8
return sz_i
case llvmlite.LLVMType.Ptr(_pe):
return 8
case llvmlite.LLVMType.Float(fb):
if fb == 64:
return 8
if fb >= 128:
return 16
return 4
case llvmlite.LLVMType.Array(et, cnt):
esz: t.CInt = self._type_byte_size(pool, et)
return esz * cnt
case _:
return 8
# ============================================================
# _nl_heap_global_name - nonlocal 堆地址全局映射变量名
#
# 变量名: nlheap_{sha1}.{name}(全局变量,跨函数可见;SSA 编号是函数
# 局部的,跨函数传递堆地址只能经全局变量/模块级映射)。
# ============================================================
def _nl_heap_global_name(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
name: str) -> str:
"""构造 nonlocal 堆地址全局映射变量名 nlheap_{sha1}.{name}"""
if pool is None or trans is None or name is None:
return None
mangled: str = self.Gen.FuncGenBase._mangle_name(trans, name)
buf: str = pool.alloc(160)
if buf is not None:
viperlib.snprintf(buf, 160, "nlheap_%s", mangled)
return buf
# ============================================================
# _nl_heap_global_ref - 全局映射变量引用 Value(i8**)
# ============================================================
def _nl_heap_global_ref(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""获取 nonlocal 堆地址全局映射变量的引用(@"..." 引号包裹)"""
if pool is None or trans is None or name is None:
return None
gname: str = self._nl_heap_global_name(pool, trans, name)
if gname is None:
return None
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
ref_buf: str = pool.alloc(176)
if ref_buf is None:
return None
viperlib.snprintf(ref_buf, 176, "@\"%s\"", gname)
return llvmlite.SSAValue(pool, llvmlite.Ptr(pool, i8_ptr_ty), ref_buf)
# ============================================================
# _nl_heap_global_exists - 检查 nonlocal 堆地址全局映射变量是否存在
# ============================================================
def _nl_heap_global_exists(self, mod: llvmlite.LLVMModule | t.CPtr,
gname: str) -> int:
"""检查全局映射变量 gname 是否已在模块中,1=存在"""
if mod is None or gname is None:
return 0
gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
while gcur is not None:
if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0:
return 1
gcur = gcur.Next
return 0
# ============================================================
# _collect_chain_nonlocals - 递归收集嵌套函数链内 nonlocal 声明
#
# 遍历 node(FunctionDef)的 children:遇嵌套 FunctionDef 递归,
# 遇 Nonlocal 语句收集变量名(去重)。返回更新后的计数。
# ============================================================
def _collect_chain_nonlocals(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr,
names: t.CVoid | t.CPtr,
count: int) -> int:
"""递归收集 node(FunctionDef)链内所有 nonlocal 声明名(去重)"""
if node is None or names is None:
return count
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node)
if fd is None or fd.children is None:
return count
ch: list[ast.AST | t.CPtr] | t.CPtr = fd.children
cn: t.CSizeT = len(ch)
for ci in range(cn):
st: ast.AST | t.CPtr = ch.get(ci)
if st is None:
continue
k: int = st.kind()
if k == ast.ASTKind.FunctionDef:
count = self._collect_chain_nonlocals(trans, st, names, count)
elif k == ast.ASTKind.Nonlocal:
nl_node: ast.Nonlocal | t.CPtr = (ast.Nonlocal | t.CPtr)(st)
if nl_node is not None and nl_node.names is not None:
nl_list: list[ast.AST | t.CPtr] | t.CPtr = nl_node.names
nn: t.CSizeT = len(nl_list)
for ni in range(nn):
nm_node: ast.AST | t.CPtr = nl_list.get(ni)
if nm_node is None:
continue
nm_x: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node)
if nm_x is None or nm_x.id is None:
continue
dup: int = 0
for di in range(count):
if string.strcmp((str)(
t.CUInt64T(names) + di * 8), nm_x.id) == 0:
dup = 1
break
if dup == 0 and count < 64:
nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)(
t.CUInt64T(names) + count * 8)
nm_slot[0] = nm_x.id
count += 1
return count
# ============================================================
# _prepare_chain_nonlocals - 预扫描嵌套函数链内 nonlocal 并建堆
#
# 在翻译嵌套函数定义(translate_nested_function_def)之前调用。
# 场景(ClosureTest2 Test 7):level1→a、level2→b、level3→c_val 各自
# nonlocal 不同变量,都定义在最外层函数——level1 env 无 b 槽,level2
# 无法经 env 链获取 b 的堆。此处在最外层函数中为链内所有 nonlocal
# 建堆 + 重定向 VarEntry + 把堆地址 store 到模块级映射
# @nlheap.{sha1}.{name}(全局变量跨函数可见),内层闭包创建时从映射 load。
# ============================================================
def _prepare_chain_nonlocals(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
i8_ty: llvmlite.LLVMType | t.CPtr,
i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> int:
"""预收集嵌套函数链内 nonlocal 并建堆,1 成功"""
if node is None or trans is None or builder is None or mod is None:
return 0
# 每函数只预建一次:多个嵌套函数定义共享同一堆(防重复 malloc +
# 二次重定向 VarEntry.Alloca 导致兄弟闭包各持一堆,计数不同步)
if trans._nl_chain_done != 0:
return 1
if trans._nl_chain_names is None:
trans._nl_chain_names = pool.alloc(8 * 64)
if trans._nl_chain_names is not None:
string.memset(trans._nl_chain_names, 0, 8 * 64)
if trans._nl_chain_names is None:
return 0
trans._nl_chain_count = self._collect_chain_nonlocals(
trans, node, trans._nl_chain_names, 0)
trans._nl_chain_done = 1
for ci in range(trans._nl_chain_count):
nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)(
t.CUInt64T(trans._nl_chain_names) + ci * 8)
nl_name: str = nm_slot[0]
if nl_name is None:
continue
# 只在"定义它的函数"(当前顶层作用域有 alloca)中建堆
local_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current(
trans.SymTab, nl_name)
if local_alloca is None or local_alloca.Ty is None:
continue
nl_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee
if nl_ty is None:
continue
gname: str = self._nl_heap_global_name(pool, trans, nl_name)
if gname is None:
continue
# 建堆(按类型字节大小)并拷贝当前值——每次本函数执行都新建共享堆,
# 并 store 到模块级映射(跨函数可见)。注意:不能因映射变量已存在
# 就跳过(前一个测试函数已把 @nlheap.* 加入模块——若跳过,本函数
# 运行时 @nlheap.* 保持上一函数的堆地址,env 读到错堆。
# ClosureTest2 Test 7 根因)。映射变量仅首次 add(模块去重),
# store 指令每次照发。
st_size: t.CInt = self._type_byte_size(pool, nl_ty)
st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, st_size)
if st_size_val is not None:
st_size_val.Next = None
var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call(
builder, "malloc", st_size_val, 1, i8_ptr_ty, 0)
if var_storage is None:
continue
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, nl_ty, local_alloca)
st_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, var_storage, llvmlite.Ptr(pool, nl_ty))
if st_slot is not None and cur_val is not None:
llvmlite.build_store(builder, cur_val, st_slot)
# 重定向 VarEntry(父函数读写走共享堆)
ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nl_name)
if ve is not None:
heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, var_storage, llvmlite.Ptr(pool, nl_ty))
if heap_typed is not None:
ve.Alloca = heap_typed
# 创建/复用全局映射变量并 store 堆地址(跨函数可见)
gv_found: llvmlite.GlobalVariable | t.CPtr = None
gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
while gcur is not None:
if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0:
gv_found = gcur
break
gcur = gcur.Next
if gv_found is None:
gv_found = llvmlite.new_global_variable(pool, gname, i8_ptr_ty)
if gv_found is not None:
# 清除 external linkage 并给零初始化,否则打印为 external
# 声明、store 引用无定义体 → 链接 undefined reference
gv_found.Linkage = None
gv_found.Initializer = "null"
llvmlite.module_add_global(mod, gv_found)
if gv_found is not None:
gv_ref: llvmlite.Value | t.CPtr = self._nl_heap_global_ref(
pool, trans, nl_name)
if gv_ref is not None:
llvmlite.build_store(builder, var_storage, gv_ref)
return 1
# ============================================================
# translate_lambda - 翻译 lambda 表达式
#
# 与 translate_nested_function_def 同架构(TPV 闭包 {i8* fn_ptr,
# i8* env_ptr},16 字节):
# 1. 提升函数 __lambda_{id}(i8* %env, [args...]) — 恒返回 i32
# 2. 捕获变量按值拷贝到堆存储(env[i] = 堆地址),避免闭包返回
# 后父栈帧销毁导致悬垂
# 3. 创建闭包 {fn_ptr, env_ptr},返回 i8* 闭包指针
# 赋值语句据此创建 i8** alloca,调用走 translate_closure_call
# ============================================================
def translate_lambda(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译 lambda 表达式,返回闭包指针(i8*)"""
if node is None or trans is None or builder is None:
return None
lam: ast.Lambda | t.CPtr = (ast.Lambda | t.CPtr)(node)
if lam is None:
return None
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
# 1. lambda id + 提升函数名
# 注意:计数器放 trans(_lambda_count)而非 self——Mixin 类自身无字段,
# self.新字段在 TPC 编译 Mixin 时查 class_members 失败触发未完成的
# _PreRegisterClassMembers fallback 崩溃(FuncGen.py L437)
lambda_id: int = trans._lambda_count
trans._lambda_count += 1
pname_buf: str = pool.alloc(64)
if pname_buf is None:
return None
viperlib.snprintf(pname_buf, 64, "__lambda_%d", lambda_id)
mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, pname_buf)
# 2. 解析参数(名称 + 类型,类型默认 i32,有注解则解析)
param_names: str = pool.alloc(32 * 64) # 64 参数 × 32 字节名
if param_names is None:
return None
string.memset(param_names, 0, 32 * 64)
param_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) # LLVMType 指针槽
if param_tys is None:
return None
string.memset(param_tys, 0, 8 * 64)
param_count: int = 0
if lam.args is not None:
ags: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(lam.args)
if ags.args is not None:
alist: list[ast.AST | t.CPtr] | t.CPtr = ags.args
pn: t.CSizeT = len(alist)
for pi in range(pn):
if param_count >= 64:
break
arg_n: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(alist.get(pi))
if arg_n is None or arg_n.arg is None:
continue
string.strncpy(param_names + param_count * 32, arg_n.arg, 31)
p_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if arg_n.annotation is not None:
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, arg_n.annotation, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is None:
# fail-fast:lambda 参数有注解但类型解析失败,不再静默保持 i32
self.Gen.TypeGenErr.fatal_error(
arg_n, f"参数 '{arg_n.arg}' 类型解析失败: {self.Gen.TypeGenResolve.annotation_to_str(arg_n.annotation)}",
"NameError")
resolved = i32_ty # 不可达(fatal_error 已退出)
if resolved is not None:
match resolved:
case llvmlite.LLVMType.Struct(_, _, _):
p_ty = llvmlite.Ptr(pool, resolved)
case llvmlite.LLVMType.Array(aelem_ty_l, _):
p_ty = llvmlite.Ptr(pool, aelem_ty_l)
case _:
p_ty = resolved
ty_slot_p: str | t.CPtr = (t.CVoid | t.CPtr)(
t.CUInt64T(param_tys) + param_count * 8)
ty_slot_p[0] = p_ty
param_count += 1
# 3. 收集捕获变量(body 中的外部 Name 引用)
cap_names: str = pool.alloc(64 * 64) # 64 捕获 × 64 字节名
if cap_names is None:
return None
string.memset(cap_names, 0, 64 * 64)
cap_allocas: t.CVoid | t.CPtr = pool.alloc(8 * 64) # 父函数 alloca 指针槽
if cap_allocas is None:
return None
string.memset(cap_allocas, 0, 8 * 64)
cap_tys: t.CVoid | t.CPtr = pool.alloc(8 * 64) # 捕获变量类型槽
if cap_tys is None:
return None
string.memset(cap_tys, 0, 8 * 64)
cap_count: int = self._lambda_scan_body(
trans, lam.body, param_names, param_count,
cap_names, cap_allocas, cap_tys, 0)
# 4. 创建提升函数 __lambda_{id}(i8* %env, [args...])
func: llvmlite.Function | t.CPtr = llvmlite.create_function(
pool, mod, mangled_promoted, i32_ty)
if func is None:
return None
llvmlite.add_param(pool, func, i8_ptr_ty, "%env")
for pi2 in range(param_count):
ty_addr2: t.CUInt64T = t.CUInt64T(param_tys) + pi2 * 8
ty_slot2: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr2)
p_ty2: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot2[0])
if p_ty2 is None:
p_ty2 = i32_ty
pname_l: str = pool.alloc(32)
if pname_l is not None:
viperlib.snprintf(pname_l, 32, "%%%s", param_names + pi2 * 32)
llvmlite.add_param(pool, func, p_ty2, pname_l)
# 5. entry 块 + builder
entry_blk: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, "entry")
if entry_blk is None:
return None
func_builder: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, func)
if func_builder is None:
return None
llvmlite.position_at_end(func_builder, entry_blk)
# 6. 进入 lambda 作用域
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
# 7. _env_ptr alloca(存储 %env 参数)
env_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, i8_ptr_ty)
if env_alloca is not None:
self.Gen.VarGen.define_var(trans.SymTab, "_env_ptr", env_alloca)
env_param_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "%env")
llvmlite.build_store(func_builder, env_param_val, env_alloca)
# 8. 保存父函数状态并切换到提升函数
old_func: llvmlite.Function | t.CPtr = trans._cur_func
old_builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
old_func_name: str = trans._cur_func_name
old_global_count: int = trans._global_name_count
old_nonlocal_count: int = trans._nonlocal_name_count
old_env_count: int = trans._closure_env_count
trans._cur_func = func
trans._cur_builder = func_builder
trans._cur_func_name = mangled_promoted
HT.clear_scope_names(trans)
# 9. 捕获变量:从 env 堆槽加载值到局部 alloca
for ci in range(cap_count):
# 直接 str 指针偏移取捕获名(与 param_names + pi*32 一致)。
# 注意:不能用 (str)(cap_addr) 整数转换——TPV 将其编译为 itoa(整数转字符串),
# 会把 cap_slot 变成数字串,define_var 键名错乱,lambda body lookup 无法命中。
cap_slot: str = cap_names + ci * 64
if cap_slot[0] == '\0':
continue
ty_addr3: t.CUInt64T = t.CUInt64T(cap_tys) + ci * 8
ty_slot3: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr3)
cap_ty: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot3[0])
if cap_ty is None:
continue
off_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ci * 8)
slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(
func_builder, i8_ty, env_param_val, off_val)
if slot_addr is None:
continue
storage_addr: llvmlite.Value | t.CPtr = llvmlite.build_load(
func_builder, i8_ptr_ty, slot_addr)
if storage_addr is None:
continue
st_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
func_builder, storage_addr, llvmlite.Ptr(pool, cap_ty))
if st_typed is None:
continue
cap_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
func_builder, cap_ty, st_typed)
if cap_val is None:
continue
var_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, cap_ty)
if var_alloca is not None:
llvmlite.build_store(func_builder, cap_val, var_alloca)
self.Gen.VarGen.define_var(trans.SymTab, cap_slot, var_alloca)
# 10. 参数 alloca
for pi3 in range(param_count):
ty_addr4: t.CUInt64T = t.CUInt64T(param_tys) + pi3 * 8
ty_slot4: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr4)
p_ty3: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(ty_slot4[0])
if p_ty3 is None:
# fail-fast:lambda 参数类型槽缺失(内部状态不一致),不再静默保持 i32
self.Gen.TypeGenErr.fatal_error(
lam, f"lambda 参数 '{param_names + pi3 * 32}' 类型信息缺失", "InternalCompilerError")
p_ty3 = i32_ty # 不可达(fatal_error 已退出)
pname_ref: str = pool.alloc(32)
if pname_ref is not None:
viperlib.snprintf(pname_ref, 32, "%%%s", param_names + pi3 * 32)
param_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, p_ty3, pname_ref)
arg_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, p_ty3)
if arg_alloca is not None:
llvmlite.build_store(func_builder, param_ref, arg_alloca)
self.Gen.VarGen.define_var(trans.SymTab, param_names + pi3 * 32, arg_alloca)
# 11. 翻译 body 表达式,coerce 到 i32 后 ret
body_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
func_builder, pool, mod, lam.body, None, 0, trans)
if body_val is not None and body_val.Ty is not None:
if self.Gen.ExprGenBase.get_llvm_type_bits(body_val.Ty) == 32 \
and self.Gen.ExprGenBase.is_ptr_type(body_val.Ty) == 0:
llvmlite.build_ret(func_builder, body_val)
else:
ret_val: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
func_builder, body_val, i32_ty, lam.body)
if ret_val is not None:
llvmlite.build_ret(func_builder, ret_val)
else:
# fail-fast:lambda 体类型转换失败,不再静默 ret 0
self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError")
else:
# fail-fast:lambda 体翻译失败,不再静默 ret 0
self.Gen.TypeGenErr.fatal_error(lam.body, "lambda 体表达式翻译失败", "TypeError")
# 12. 退出 lambda 作用域,恢复父函数状态
self.Gen.VarGen.exit_scope(trans.SymTab)
trans._cur_func = old_func
trans._cur_builder = old_builder
trans._cur_func_name = old_func_name
trans._global_name_count = old_global_count
trans._nonlocal_name_count = old_nonlocal_count
trans._closure_env_count = old_env_count
# 13. 在父函数中创建 env(每个捕获变量 8 字节堆存储,env[i] = 堆地址)
env_ptr: llvmlite.Value | t.CPtr = None
if cap_count > 0:
env_size: t.CInt64T = cap_count * 8
env_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, env_size)
if env_size_val is not None:
env_size_val.Next = None
env_ptr = llvmlite.build_call(
builder, "malloc", env_size_val, 1, i8_ptr_ty, 0)
if env_ptr is not None:
for ci3 in range(cap_count):
# 与步骤 9 一致:str 指针偏移判空,避免整数转换 itoa 语义
cap_slot3: str = cap_names + ci3 * 64
if cap_slot3[0] == '\0':
continue
al_addr: t.CUInt64T = t.CUInt64T(cap_allocas) + ci3 * 8
al_slot: str | t.CPtr = (t.CVoid | t.CPtr)(al_addr)
var_alloca2: llvmlite.Value | t.CPtr = (
llvmlite.Value | t.CPtr)(al_slot[0])
if var_alloca2 is None:
continue
ty_addr5: t.CUInt64T = t.CUInt64T(cap_tys) + ci3 * 8
ty_slot5: str | t.CPtr = (t.CVoid | t.CPtr)(ty_addr5)
cap_ty2: llvmlite.LLVMType | t.CPtr = (
llvmlite.LLVMType | t.CPtr)(ty_slot5[0])
if cap_ty2 is None:
continue
# 堆存储大小:位宽/8,至少 8 字节(容纳指针/标量)
ty_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cap_ty2)
st_size: int = 8
if ty_bits > 64:
st_size = ty_bits // 8
st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, st_size)
if st_size_val is not None:
st_size_val.Next = None
var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call(
builder, "malloc", st_size_val, 1, i8_ptr_ty, 0)
if var_storage is not None:
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, cap_ty2, var_alloca2)
if cur_val is not None:
st_slot5: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, var_storage, llvmlite.Ptr(pool, cap_ty2))
if st_slot5 is not None:
llvmlite.build_store(builder, cur_val, st_slot5)
off_val2: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, ci3 * 8)
slot2: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, i8_ty, env_ptr, off_val2)
if slot2 is not None:
slot_typed2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, slot2, i8_ptr_ptr_ty)
if slot_typed2 is not None:
llvmlite.build_store(builder, var_storage, slot_typed2)
# 14. 在父函数中创建闭包 malloc(16)
closure_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 16)
if closure_size_val is not None:
closure_size_val.Next = None
closure_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call(
builder, "malloc", closure_size_val, 1, i8_ptr_ty, 0)
if closure_ptr is None:
return None
# 15. [0] = fn_ptr(函数引用 bitcast 到 i8*)
fn_param_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty)
fn_func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, i32_ty, fn_param_node, 1)
fn_func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, fn_func_ty)
fn_ptr_name: str = pool.alloc(64)
if fn_ptr_name is not None:
if string.strchr(mangled_promoted, '.') is not None:
viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted)
else:
viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted)
fn_ptr_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name)
fn_as_i8ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, fn_ptr_val, i8_ptr_ty)
fn_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, closure_ptr, llvmlite.Ptr(pool, i8_ptr_ty))
if fn_slot is not None and fn_as_i8ptr is not None:
llvmlite.build_store(builder, fn_as_i8ptr, fn_slot)
# 16. [8] = env_ptr(无捕获存 null)
eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 8)
env_slot_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, i8_ty, closure_ptr, eight_val)
if env_slot_addr is not None:
env_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, env_slot_addr, llvmlite.Ptr(pool, i8_ptr_ty))
if env_slot is not None:
if env_ptr is not None:
llvmlite.build_store(builder, env_ptr, env_slot)
else:
null_val: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ty, "null")
llvmlite.build_store(builder, null_val, env_slot)
# 17. 返回闭包指针(i8*)
return closure_ptr
# ============================================================
# _lambda_scan_body - 递归扫描 lambda body 中的 Name 引用
#
# 收集外部变量(排除 lambda 参数、模块级全局、已捕获项),
# 记录到 cap_names/cap_allocas/cap_tys 三个并行缓冲。
# 返回更新后的捕获数量(递归调用逐层累加)。
# ============================================================
def _lambda_scan_body(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr,
param_names: str,
param_count: int,
cap_names: str,
cap_allocas: t.CVoid | t.CPtr,
cap_tys: t.CVoid | t.CPtr,
cap_count: int) -> int:
"""递归扫描 lambda body 中的 Name 引用,收集捕获变量"""
if node is None or trans is None:
return cap_count
k: int = node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is None:
return cap_count
# 排除 lambda 参数
for pi in range(param_count):
if string.strcmp(param_names + pi * 32, nm.id) == 0:
return cap_count
# 已捕获跳过
for ci in range(cap_count):
if string.strcmp(cap_names + ci * 64, nm.id) == 0:
return cap_count
# 查当前作用域链
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return cap_count
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return cap_count
# 跳过模块级全局(body 直接解析全局,无需 env 捕获)
mod_gv: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id)
if mod_gv is not None:
if t.CUInt64T(mod_gv) == t.CUInt64T(alloca):
return cap_count
if cap_count >= 64:
return cap_count
string.strncpy(cap_names + cap_count * 64, nm.id, 63)
al_slot: str | t.CPtr = (t.CVoid | t.CPtr)(
t.CUInt64T(cap_allocas) + cap_count * 8)
al_slot[0] = alloca
ty_slot: str | t.CPtr = (t.CVoid | t.CPtr)(
t.CUInt64T(cap_tys) + cap_count * 8)
ty_slot[0] = pointee
return cap_count + 1
# 递归遍历常见表达式子节点
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
cc1: int = self._lambda_scan_body(trans, bop.left, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
return self._lambda_scan_body(trans, bop.right, param_names, param_count,
cap_names, cap_allocas, cap_tys, cc1)
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
return self._lambda_scan_body(trans, uop.operand, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
if k == ast.ASTKind.BoolOp:
bop2: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
cc2: int = cap_count
if bop2.values is not None:
vs: list[ast.AST | t.CPtr] | t.CPtr = bop2.values
vn: t.CSizeT = len(vs)
for vi in range(vn):
cc2 = self._lambda_scan_body(trans, vs.get(vi), param_names, param_count,
cap_names, cap_allocas, cap_tys, cc2)
return cc2
if k == ast.ASTKind.Compare:
cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
cc3: int = self._lambda_scan_body(trans, cmp.left, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
if cmp.comparators is not None:
cl: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
cn: t.CSizeT = len(cl)
for cvi in range(cn):
cc3 = self._lambda_scan_body(trans, cl.get(cvi), param_names, param_count,
cap_names, cap_allocas, cap_tys, cc3)
return cc3
if k == ast.ASTKind.Call:
cal: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
cc4: int = self._lambda_scan_body(trans, cal.func, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
if cal.args is not None:
als: list[ast.AST | t.CPtr] | t.CPtr = cal.args
an: t.CSizeT = len(als)
for ai in range(an):
cc4 = self._lambda_scan_body(trans, als.get(ai), param_names, param_count,
cap_names, cap_allocas, cap_tys, cc4)
if cal.keywords is not None:
kws: list[ast.AST | t.CPtr] | t.CPtr = cal.keywords
kn: t.CSizeT = len(kws)
for ki in range(kn):
kw: ast.Keyword | t.CPtr = (ast.Keyword | t.CPtr)(kws.get(ki))
if kw is not None and kw.value is not None:
cc4 = self._lambda_scan_body(trans, kw.value, param_names, param_count,
cap_names, cap_allocas, cap_tys, cc4)
return cc4
if k == ast.ASTKind.Subscript:
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
cc5: int = self._lambda_scan_body(trans, sub.value, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
if sub.slice is not None:
cc5 = self._lambda_scan_body(trans, sub.slice, param_names, param_count,
cap_names, cap_allocas, cap_tys, cc5)
return cc5
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
return self._lambda_scan_body(trans, at.value, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
if k == ast.ASTKind.IfExp:
ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node)
cc6: int = self._lambda_scan_body(trans, ie.test, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
cc6 = self._lambda_scan_body(trans, ie.body, param_names, param_count,
cap_names, cap_allocas, cap_tys, cc6)
return self._lambda_scan_body(trans, ie.orelse, param_names, param_count,
cap_names, cap_allocas, cap_tys, cc6)
if k == ast.ASTKind.List:
ls: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
cc7: int = cap_count
if ls.elts is not None:
es: list[ast.AST | t.CPtr] | t.CPtr = ls.elts
en: t.CSizeT = len(es)
for ei in range(en):
cc7 = self._lambda_scan_body(trans, es.get(ei), param_names, param_count,
cap_names, cap_allocas, cap_tys, cc7)
return cc7
if k == ast.ASTKind.Lambda:
lam2: ast.Lambda | t.CPtr = (ast.Lambda | t.CPtr)(node)
return self._lambda_scan_body(trans, lam2.body, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
return cap_count