diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index fcff3df..ccd9227 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -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 是已导入模块名且不是普通变量时,查找/创建全局变量引用 diff --git a/App/lib/core/Gen/FuncGenMixin.vp b/App/lib/core/Gen/FuncGenMixin.vp index 395e67b..2afb08f 100644 --- a/App/lib/core/Gen/FuncGenMixin.vp +++ b/App/lib/core/Gen/FuncGenMixin.vp @@ -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 表达式 # diff --git a/App/lib/core/Handles/HandlesTranslator.vp b/App/lib/core/Handles/HandlesTranslator.vp index c9fd161..a78a03f 100644 --- a/App/lib/core/Handles/HandlesTranslator.vp +++ b/App/lib/core/Handles/HandlesTranslator.vp @@ -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