fix(gen): nonlocal 跨层共享与链堆预建修复(ClosureTest2 12/12 通过)

- 嵌套函数独立 nonlocal 缓冲,防共享缓冲 [0] 槽覆盖(Test 7 根因),
  translate_nested_function_def 三处退出点补齐缓冲恢复
- _prepare_chain_nonlocals 去掉模块全局 found_g 跳过:建堆+store 每次
  照发,映射变量仅首次 add(去重),修复跨测试函数残留堆地址致 env 读错堆
- 新增 trans._nl_chain_done 每函数标志(translate_function_def 入口重置),
  多嵌套函数定义共享同一堆,防重复 malloc + 二次重定向 VarEntry
- nonlocal 属性/下标优先走 env 读堆值(对象即指针,Struct/Array 不 load),
  新增 _nonlocal_subscript_ptr 经 env 读堆地址后 GEP
- _type_byte_size 按类型字节数分配 nonlocal 共享堆(数组 16 字节防越界)
This commit is contained in:
TermiNexus committed 2026-08-11 17:30:01 +08:00
1 parent c472ea9798
commit 48d9fbab4c
3 files changed
+461 -36

No files matched your search

+120 -4
View File
@@ -2585,6 +2585,52 @@ class ExprGenMixin:
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
return llvmlite.build_load(builder, pointee, alloca)
# ============================================================
# _nonlocal_subscript_ptr - nonlocal 数组/指针的下标元素指针
#
# 嵌套函数(提升函数)体内访问 nonlocal 变量时,变量的实际存储是
# 运行时共享堆(env[ni] 存的堆地址),不能 lookup_var 找外层函数
# alloca(其 SSA 编号属于外层函数,当前函数引用错乱崩溃——
# ClosureTest2 Test 4 fill() 内 arr[i] 崩溃根因)。
# 正确路径:从 env 读堆地址(_load_raw_var_ptr)→ bitcast → GEP。
# ============================================================
def _nonlocal_subscript_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
nm_id: str,
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""nonlocal 数组/指针下标:从 env 读堆地址 → bitcast → GEP,返回元素指针"""
if trans is None or nm_id is None or idx_val is None:
return None
nl_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, nm_id)
if nl_ty is None:
return None
raw_nl: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(trans, nm_id)
if raw_nl is None:
return None
match nl_ty:
case llvmlite.LLVMType.Array(elem_ty_nl, _cnt_nl):
arr_ptr_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
if arr_ptr_nl is None:
return None
return llvmlite.build_gep_array(
builder, nl_ty, elem_ty_nl, arr_ptr_nl,
self._zext_index_to_i32(builder, idx_val))
case llvmlite.LLVMType.Ptr(inner_nl):
arr_ptr_nl2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
if arr_ptr_nl2 is None:
return None
return llvmlite.build_gep(builder, inner_nl, arr_ptr_nl2, idx_val)
case _:
arr_ptr_nl3: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
if arr_ptr_nl3 is None:
return None
return llvmlite.build_gep(builder, nl_ty, arr_ptr_nl3, idx_val)
return None
# ============================================================
# 翻译下标表达式 Subscript(value, slice, ctx) — ptr[i] / arr[i]
#
@@ -2614,6 +2660,17 @@ class ExprGenMixin:
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
# 读路径:元素指针 load 出元素值
if HT.is_nonlocal_name(trans, nm.id) != 0:
nl_ptr_r: llvmlite.Value | t.CPtr = self._nonlocal_subscript_ptr(
builder, pool, trans, nm.id, idx_val)
if nl_ptr_r is not None and nl_ptr_r.Ty is not None:
nl_elem_ty: llvmlite.LLVMType | t.CPtr = nl_ptr_r.Ty.Pointee
if nl_elem_ty is not None:
return llvmlite.build_load(builder, nl_elem_ty, nl_ptr_r)
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is not None and alloca.Ty is not None:
if self.is_ptr_type(alloca.Ty) != 0:
@@ -3334,12 +3391,39 @@ class ExprGenMixin:
VLogger.error(err_buf_ma, "CD")
return None
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体)
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if HT.is_nonlocal_name(trans, nm.id) != 0:
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
trans, nm.id)
if raw_nl_a is not None:
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
trans, nm.id)
if nl_ty_a is not None:
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
# 存对象指针,需 load 出对象指针
match nl_ty_a:
case llvmlite.LLVMType.Struct(_, _, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case llvmlite.LLVMType.Array(_, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case _:
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
if nl_val_ptr_a is not None:
obj_ptr = llvmlite.build_load(
builder, nl_ty_a, nl_val_ptr_a)
else:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 非 Name 路径:翻译对象值(会 load)
if obj_ptr is None:
@@ -3543,6 +3627,11 @@ class ExprGenMixin:
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
if HT.is_nonlocal_name(trans, nm.id) != 0:
return self._nonlocal_subscript_ptr(
builder, pool, trans, nm.id, idx_val)
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is not None and alloca.Ty is not None:
if self.is_ptr_type(alloca.Ty) != 0:
@@ -3735,12 +3824,39 @@ class ExprGenMixin:
if at is None or at.value is None or at.attr is None:
return None
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体)
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if HT.is_nonlocal_name(trans, nm.id) != 0:
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
trans, nm.id)
if raw_nl_a is not None:
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
trans, nm.id)
if nl_ty_a is not None:
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
# 存对象指针,需 load 出对象指针
match nl_ty_a:
case llvmlite.LLVMType.Struct(_, _, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case llvmlite.LLVMType.Array(_, _):
obj_ptr = llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
case _:
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
if nl_val_ptr_a is not None:
obj_ptr = llvmlite.build_load(
builder, nl_ty_a, nl_val_ptr_a)
else:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 模块属性访问 (写路径): sys._mbuddy = mb → store 到全局变量 @_mbuddy
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用
+327 -32
View File
@@ -780,6 +780,9 @@ class FuncGenMixin:
trans._cur_builder = func_builder
# 清空 global/nonlocal 名称集合(新函数作用域)
HT.clear_scope_names(trans)
# 重置链内 nonlocal 堆预建标志(每函数只建一次;多嵌套函数定义
# 共享同一堆,防重复 malloc + 二次重定向 VarEntry)
trans._nl_chain_done = 0
# 预扫描函数体:
# 1. 先预扫描 Global/Nonlocal 声明(Python 语义:global/nonlocal
@@ -803,6 +806,12 @@ class FuncGenMixin:
for bi2 in range(bn2):
stmt2: ast.AST | t.CPtr = body.get(bi2)
if stmt2 is not None:
# 嵌套函数定义前:预扫描其链内 nonlocal 并建堆
# (不同层捕获不同变量的场景,见 _prepare_chain_nonlocals)
if stmt2.kind() == ast.ASTKind.FunctionDef:
self._prepare_chain_nonlocals(
trans, stmt2, pool, func_builder, mod,
llvmlite.Int8(pool), llvmlite.Ptr(pool, llvmlite.Int8(pool)))
HandlesBody.translate_stmt(trans, stmt2)
# 如果函数体最后一条语句不是 Return,添加隐式 ret
@@ -999,12 +1008,24 @@ class FuncGenMixin:
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 语义:对整个函数作用域生效)
@@ -1058,6 +1079,8 @@ class FuncGenMixin:
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 存储指针数组
@@ -1091,38 +1114,70 @@ class FuncGenMixin:
if nl_name is None:
continue
heap_store: llvmlite.Value | t.CPtr = None
local_alloca: llvmlite.Value | t.CPtr = HandlesVar.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)
eight_c: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 8)
if eight_c is not None:
eight_c.Next = None
var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call(
parent_builder, "malloc", eight_c, 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: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nl_name)
if ve_nl is not None:
heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
# 1. 优先查全局堆地址映射(_prepare_chain_nonlocals 已为
# 链内 nonlocal 建堆并 store 到 @nlheap.*;不同层捕获
# 不同变量时(ClosureTest2 Test 7)level2 的 b 经此共享)
gname_ck: t.CChar | t.CPtr = 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 = HandlesVar.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 heap_typed is not None:
ve_nl.Alloca = heap_typed
heap_store = var_storage
else:
# 父函数中该变量是 nonlocal(多层嵌套):从父函数 env
# 读堆地址(运行时链式共享同一堆),不新建
heap_store = HandlesNonlocal._load_raw_var_ptr(trans, nl_name)
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: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nl_name)
if ve_nl is not None:
heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty))
if heap_typed is not None:
ve_nl.Alloca = heap_typed
# 记录到全局映射(后续层/父函数可见)
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: llvmlite.Value | t.CPtr = \
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 = HandlesNonlocal._load_raw_var_ptr(trans, nl_name)
if heap_store is None:
continue
# env[ni] = 堆存储地址(共享)
@@ -1147,6 +1202,8 @@ class FuncGenMixin:
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
@@ -1197,14 +1254,252 @@ class FuncGenMixin:
# 两者 alloca 类型同为 i8**,_is_func_ptr_alloca 无法区分)
HandlesExprCall.register_closure_name(func_name)
# 14. 恢复 scope names
# 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
# ============================================================
# _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) -> t.CChar | t.CPtr:
"""构造 nonlocal 堆地址全局映射变量名 nlheap_{sha1}.{name}"""
if pool is None or trans is None or name is None:
return None
mangled: str = self._mangle_name(trans, name)
buf: t.CChar | t.CPtr = 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: t.CChar | t.CPtr = 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: t.CChar | t.CPtr = 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 = ch.__len__()
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 = nl_list.__len__()
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((t.CChar | t.CPtr)(
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 = HandlesVar.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: t.CChar | t.CPtr = 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: HandlesVar.VarEntry | t.CPtr = HandlesVar.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 表达式
#
+14
View File
@@ -126,6 +126,17 @@ class Translator:
# 独立于 _closure_env_count:后者随函数进入被 clear_scope_names 重置,
# lambda ID 需跨函数全局唯一(提升函数名避免冲突)
_lambda_count: t.CInt
# 函数链内 nonlocal 变量集合(预扫描收集,含嵌套函数声明的 nonlocal)。
# 用于"不同层捕获不同变量"场景(如 level1→a, level2→b):b 定义在最外层
# 但 level1 不捕获——level1 env 无 b 槽,level2 无法经 env 链获取 b 的堆。
# 解决:在最外层函数中为链内所有 nonlocal 建堆,并把堆地址 store 到
# 模块级映射变量 @nlheap.{sha1}.{name};内层闭包创建时从映射 load。
_nl_chain_names: t.CVoid | t.CPtr
_nl_chain_count: t.CInt
# 链内 nonlocal 堆预建标志(每函数只建一次):translate_function_def 进入
# 新函数时重置为 0;_prepare_chain_nonlocals 首次调用置 1。多个嵌套函数
# 定义共享同一堆(防重复 malloc + 二次重定向 VarEntry.Alloca)。
_nl_chain_done: t.CInt
# 泛型特化上下文:当前正在特化的类型参数名/实参名列表
# 由 _specialize_generic_class 设置,方法体翻译时用于将 T 替换为具体类型
@@ -182,6 +193,9 @@ class Translator:
self._closure_env_offsets = 0
self._closure_env_count = 0
self._lambda_count = 0
self._nl_chain_names = None
self._nl_chain_count = 0
self._nl_chain_done = 0
self.GenericTypeParamNames = None
self.GenericTypeArgs = None
self._declare_only = 0