diff --git a/App/lib/StubGen/Converter.vp b/App/lib/StubGen/Converter.vp index 928e5cf..ad795a3 100644 --- a/App/lib/StubGen/Converter.vp +++ b/App/lib/StubGen/Converter.vp @@ -4,7 +4,7 @@ import ast import string import memhub import viperlib - +import stdlib # ============================================================ # StubGen.Converter - vpi 存根文件生成器 @@ -57,8 +57,8 @@ def _eval_binop_valid(node: ast.AST | t.CPtr) -> int: return 0 -def _eval_binop_int(node: ast.AST | t.CPtr) -> int: - """递归计算 BinOp 节点的整数值""" +def _eval_binop_int(node: ast.AST | t.CPtr) -> t.CInt64T: + """递归计算 BinOp 节点的整数值(64 位,避免大整数 CDefine 截断)""" if node is None: return 0 k: int = node.kind() @@ -80,16 +80,30 @@ def _eval_binop_int(node: ast.AST | t.CPtr) -> int: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) if bop.left is None or bop.right is None: return 0 - lv: int = _eval_binop_int(bop.left) - rv: int = _eval_binop_int(bop.right) + lv: t.CInt64T = _eval_binop_int(bop.left) + rv: t.CInt64T = _eval_binop_int(bop.right) if bop.op == ast.OpKind.BitOr: return lv | rv if bop.op == ast.OpKind.BitAnd: return lv & rv + if bop.op == ast.OpKind.BitXor: + return lv ^ rv if bop.op == ast.OpKind.Add: return lv + rv if bop.op == ast.OpKind.Sub: return lv - rv + if bop.op == ast.OpKind.Mult: + return lv * rv + if bop.op == ast.OpKind.Div: + return lv // rv + if bop.op == ast.OpKind.FloorDiv: + return lv // rv + if bop.op == ast.OpKind.Mod: + return lv % rv + if bop.op == ast.OpKind.LShift: + return lv << rv + if bop.op == ast.OpKind.RShift: + return lv >> rv return 0 if k == ast.ASTKind.Name: nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) @@ -103,12 +117,38 @@ def _eval_binop_int(node: ast.AST | t.CPtr) -> int: # 避免循环依赖,使用弱引用 _g_pyi_cdefine_found: int = 0 +# 本文件 CDefine 值缓存(vpi 生成专用): +# Phase1 生成 .vpi(L1614)早于翻译阶段的 CDefine 注册(L1629+),此时 +# HandlesType 全局表为空 → BinOp 内 Name 引用查表失败退化为 0(ZDEFLATE +# WINDOW_SIZE = 1<<0 = 1、MASK = 0-1 = -1,跨模块读回错误常量根因)。 +# _GeneratePyiFromAst 开头按声明顺序注册到本缓存,Name 引用先查缓存。 +# 值缓存用 C 数组(t.CInt64T*)而非 list[int]:TPC 对 list[int].get 会误解析为 +# list[str].get(返回元素指针 i8*)→ ptrtoint 返回元素地址而非值 → MASK 变成 +# 20323785277439(地址-1)。C 数组索引 [i] 直接返回 i64 值,可靠。 +_g_pyi_cdef_names: list[str] | t.CPtr = None +_g_pyi_cdef_vals: t.CInt64T | t.CPtr = None +_g_pyi_cdef_count: t.CInt = 0 -def _lookup_cdefine_for_pyi(name: str) -> int: - """从本地 CDefine 表查找常量值(用于 .vpi 生成时的 BinOp 计算)""" - import lib.core.Handles.HandlesType as HandlesType + +def _lookup_cdefine_for_pyi(name: str) -> t.CInt64T: + """从本文件缓存 / 本地 CDefine 表查找常量值(用于 .vpi 生成时的 BinOp 计算)""" global _g_pyi_cdefine_found - val: int = HandlesType.lookup_cdefine_constant(name) + # 优先查本文件 CDefine 缓存(vpi 生成早于翻译阶段注册,全局表为空) + if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None: + # 循环上限用 _g_pyi_cdef_count 而非 len(_g_pyi_cdef_names): + # list[str] | t.CPtr 联合类型下 TPV 的 len() 回退 strlen(把 list 对象 + # 指针当字符串算长度 → 垃圾小值 → 靠后注册项查不到,ZDEFLATE_HASH_MASK + # = lookup(HASH_SIZE) 失败 = 0-1 = -1 根因;WINDOW_MASK 查位置 1 恰好 + # 在 strlen 结果内所以碰巧正确)。 + pn: t.CSizeT = t.CSizeT(_g_pyi_cdef_count) + pi: t.CSizeT + for pi in range(pn): + pnm: str = _g_pyi_cdef_names.get(pi) + if pnm is not None and string.strcmp(pnm, name) == 0: + _g_pyi_cdefine_found = 1 + return _g_pyi_cdef_vals[pi] + import lib.core.Handles.HandlesType as HandlesType + val: t.CInt64T = HandlesType.lookup_cdefine_constant(name) if HandlesType.is_cdefine_found() != 0: _g_pyi_cdefine_found = 1 return val @@ -116,6 +156,36 @@ def _lookup_cdefine_for_pyi(name: str) -> int: return 0 +def _eval_cdefine_value_cache(node: ast.AST | t.CPtr) -> t.CInt64T: + """求值 CDefine 值(Constant/UnaryOp/BinOp/Call/Name),供 vpi 预处理注册缓存""" + if node is None: + return 0 + k: int = node.kind() + if k == ast.ASTKind.Constant: + cn_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) + if cn_c.const_kind == ast.CONST_INT: + return cn_c.int_val + if cn_c.const_kind == ast.CONST_BOOL: + return cn_c.int_val + # STR/FLOAT/NONE 不参与整数 BinOp 引用(注册 0 无害) + return 0 + if k == ast.ASTKind.UnaryOp: + return _eval_binop_int(node) + if k == ast.ASTKind.BinOp: + return _eval_binop_int(node) + if k == ast.ASTKind.Call: + cl_c: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl_c.args is not None and cl_c.args.__len__() > 0: + return _eval_cdefine_value_cache(cl_c.args.get(0)) + return 0 + if k == ast.ASTKind.Name: + nm_c: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm_c.id is not None: + return _lookup_cdefine_for_pyi(nm_c.id) + return 0 + return 0 + + def _pyi_cdefine_found() -> int: """返回上次 _lookup_cdefine_for_pyi 的查找结果状态""" return _g_pyi_cdefine_found @@ -510,6 +580,56 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, pos: t.CSizeT = 0 + # 预处理:按声明顺序注册本文件 CDefine 到 _g_pyi 缓存(供 BinOp 求值) + # vpi 生成(Phase1 L1614)早于翻译阶段的 CDefine 注册(L1629+),直接查 + # HandlesType 全局表为空 → BinOp 内 Name 引用退化为 0(ZDEFLATE_WINDOW_SIZE + # = 1<<0 = 1、MASK = 0-1 = -1 → 跨模块读回错误常量 → 索引越界崩溃根因)。 + # 必须 global 声明:TPV 语义下函数内对模块级变量赋值无 global 时创建局部 + # 变量(_lookup_cdefine_for_pyi 读到的仍是模块级 None → 缓存永不命中, + # 追加58 首版失效根因)。 + global _g_pyi_cdef_names + global _g_pyi_cdef_vals + global _g_pyi_cdef_count + if tree.children is not None: + pre_children: list[ast.AST | t.CPtr] | t.CPtr = tree.children + pcn: t.CSizeT = pre_children.__len__() + # 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配) + _g_pyi_cdef_count = 0 + pci0: t.CSizeT = 0 + while pci0 < pcn: + stmt_pc0: ast.AST | t.CPtr = pre_children.get(pci0) + if stmt_pc0 is not None and stmt_pc0.kind() == ast.ASTKind.AnnAssign: + aa_pc0: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt_pc0) + if aa_pc0.target is not None and aa_pc0.target.kind() == ast.ASTKind.Name \ + and aa_pc0.annotation is not None: + tgt_pc0: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa_pc0.target) + if tgt_pc0.id is not None and aa_pc0.annotation.kind() == ast.ASTKind.Attribute: + ann_pc0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(aa_pc0.annotation) + if ann_pc0.attr is not None and string.strcmp(ann_pc0.attr, "CDefine") == 0: + _g_pyi_cdef_count += 1 + pci0 += 1 + if _g_pyi_cdef_count > 0: + _g_pyi_cdef_names = list[str](mb, t.CSizeT(_g_pyi_cdef_count)) + _g_pyi_cdef_vals = (t.CInt64T | t.CPtr)(mb.alloc(t.CSizeT(_g_pyi_cdef_count) * 8)) + # 第二遍:按声明顺序注册(BinOp 内 Name 引用依赖先前注册项) + pci: t.CSizeT = 0 + cdi: t.CInt = 0 + while pci < pcn: + stmt_pc: ast.AST | t.CPtr = pre_children.get(pci) + if stmt_pc is not None and stmt_pc.kind() == ast.ASTKind.AnnAssign: + aa_pc: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt_pc) + if aa_pc.target is not None and aa_pc.target.kind() == ast.ASTKind.Name \ + and aa_pc.annotation is not None: + tgt_pc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa_pc.target) + if tgt_pc.id is not None and aa_pc.annotation.kind() == ast.ASTKind.Attribute: + ann_pc: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(aa_pc.annotation) + if ann_pc.attr is not None and string.strcmp(ann_pc.attr, "CDefine") == 0: + if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None: + _g_pyi_cdef_names.append(tgt_pc.id) + _g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value) + cdi += 1 + pci += 1 + # 文件头 pos = _PyiEmit(buf, buf_size, pos, "\"\"\"Auto-generated Python stub file\"\"\"\n\n") @@ -645,13 +765,33 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, cv: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value) num_buf: bytes = mb.alloc(32) if cv.const_kind == ast.CONST_INT and num_buf is not None: - viperlib.snprintf(num_buf, 32, " = %d", cv.int_val) + viperlib.snprintf(num_buf, 32, " = %lld", cv.int_val) pos = _PyiEmit(buf, buf_size, pos, num_buf) + elif cv.const_kind == ast.CONST_FLOAT: + # 浮点 CDefine(numpy 的 pi/e 等):stub 必须输出值。 + # 否则读回端"vpi 中任一 CDefine 带值 → 不读源文件" + # 的启发式会被同模块 int CDefine(如 MAX_NDIM=4) + # 挡住,无值的 pi 解析不到 → 退化 @pi 全局指针 → + # float<->ptr 报错(NumpyTest 根因)。%.17g 保证 + # double 全精度往返(与读回端缓存格式一致)。 + fbuf_c: bytes = mb.alloc(64) + if fbuf_c is not None: + viperlib.snprintf(fbuf_c, 64, " = %.17g", cv.float_val) + pos = _PyiEmit(buf, buf_size, pos, fbuf_c) elif cv.const_kind == ast.CONST_BOOL: if cv.int_val != 0: pos = _PyiEmit(buf, buf_size, pos, " = True") else: pos = _PyiEmit(buf, buf_size, pos, " = False") + elif cv.const_kind == ast.CONST_STR: + # 字符串 CDefine(pyzlib.ZLIB_VERSION = "1.3.2" 等): + # stub 必须输出值(同 CONST_FLOAT 追加44 教训——读回端 + # 按目标名判定会回退源文件可工作,但 stub 应自包含) + sbuf_c: bytes = mb.alloc(string.strlen(cv.str_val) + 6) + if sbuf_c is not None: + viperlib.snprintf(sbuf_c, string.strlen(cv.str_val) + 6, + " = \"%s\"", cv.str_val) + pos = _PyiEmit(buf, buf_size, pos, sbuf_c) elif cv.const_kind == ast.CONST_NONE: pos = _PyiEmit(buf, buf_size, pos, " = None") elif aa.value is not None and aa.value.kind() == ast.ASTKind.UnaryOp: @@ -663,8 +803,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, and uop_cv.operand.kind() == ast.ASTKind.Constant: uop_const: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(uop_cv.operand) if uop_const.const_kind == ast.CONST_INT and num_buf_u is not None: - neg_val: int = -uop_const.int_val - viperlib.snprintf(num_buf_u, 32, " = %d", neg_val) + neg_val: t.CInt64T = -uop_const.int_val + viperlib.snprintf(num_buf_u, 32, " = %lld", neg_val) pos = _PyiEmit(buf, buf_size, pos, num_buf_u) elif aa.value is not None and aa.value.kind() == ast.ASTKind.Call: # 处理类型构造函数常量(如 t.CUnsignedLong(-11)) @@ -680,7 +820,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, ca_cv: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(first_arg) num_buf_c: bytes = mb.alloc(32) if ca_cv.const_kind == ast.CONST_INT and num_buf_c is not None: - viperlib.snprintf(num_buf_c, 32, " = %d", ca_cv.int_val) + viperlib.snprintf(num_buf_c, 32, " = %lld", ca_cv.int_val) pos = _PyiEmit(buf, buf_size, pos, num_buf_c) elif arg_kind == ast.ASTKind.UnaryOp: uop_ca: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(first_arg) @@ -689,26 +829,26 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr, and uop_ca.operand.kind() == ast.ASTKind.Constant: uoc: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(uop_ca.operand) if uoc.const_kind == ast.CONST_INT and num_buf_cu is not None: - neg_c_val: int = -uoc.int_val - viperlib.snprintf(num_buf_cu, 32, " = %d", neg_c_val) + neg_c_val: t.CInt64T = -uoc.int_val + viperlib.snprintf(num_buf_cu, 32, " = %lld", neg_c_val) pos = _PyiEmit(buf, buf_size, pos, num_buf_cu) elif arg_kind == ast.ASTKind.BinOp: # Call 内部的 BinOp(如 t.CUnsignedLong(A | B)) # 递归计算 BinOp 值 - binop_val_bo: int = _eval_binop_int(first_arg) + binop_val_bo: t.CInt64T = _eval_binop_int(first_arg) num_buf_bo: bytes = mb.alloc(32) if (binop_val_bo != 0 or _eval_binop_valid(first_arg) != 0) \ and num_buf_bo is not None: - viperlib.snprintf(num_buf_bo, 32, " = %d", binop_val_bo) + viperlib.snprintf(num_buf_bo, 32, " = %lld", binop_val_bo) pos = _PyiEmit(buf, buf_size, pos, num_buf_bo) elif aa.value is not None and aa.value.kind() == ast.ASTKind.BinOp: # 处理 BinOp 常量(如 FOREGROUND_COMBO = FOREGROUND_RED | FOREGROUND_GREEN) # 递归计算 BinOp 值并输出 - binop_val_av: int = _eval_binop_int(aa.value) + binop_val_av: t.CInt64T = _eval_binop_int(aa.value) if binop_val_av != 0 or _eval_binop_valid(aa.value) != 0: num_buf_b: bytes = mb.alloc(32) if num_buf_b is not None: - viperlib.snprintf(num_buf_b, 32, " = %d", binop_val_av) + viperlib.snprintf(num_buf_b, 32, " = %lld", binop_val_av) pos = _PyiEmit(buf, buf_size, pos, num_buf_b) elif aa.value is not None and aa.value.kind() == ast.ASTKind.Name: # 处理 Name 引用常量(如 ALIAS = OTHER_CONST) diff --git a/App/lib/StubGen/VpiReader.vp b/App/lib/StubGen/VpiReader.vp index 6a6bfbc..864402b 100644 --- a/App/lib/StubGen/VpiReader.vp +++ b/App/lib/StubGen/VpiReader.vp @@ -456,6 +456,13 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy | t.CPtr, return llvmlite.Double(pool) if string.strcmp(seg, "void") == 0: return llvmlite.Void(pool) + # vpi 源文本中 `-> None` 被原样缓存为 "None"(_cache_vpi_return_annots_text + # 提取 arrow_pos 后到 ':' 前的内容)。此前无 "None" 分支 → 解析返回 None → + # `-> None` 的跨模块方法(如 AssignGenMixin._register_cdefine_from_value)vpi + # 缓存命中也解析不出 void → 方法返回类型 fail-fast 误报(追加80e 断点 B)。 + # None 与 void 同义,归一到 Void。 + if string.strcmp(seg, "None") == 0: + return llvmlite.Void(pool) # t.XXX / c.XXX 内置类型映射 if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0: attr_nm: str = seg + 2 @@ -591,6 +598,11 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy | t.CPtr, if first_ty is not None: return llvmlite.Ptr(pool, first_ty) return i8_ptr_ty + if has_cvoid != 0: + # t.CVoid | t.State:State 是装饰器标记(编译侧忽略),t.CVoid → void。 + # 此前缺此分支 → first_ty=None → 返回 None → 跨模块函数返回类型 + # fail-fast(CPythonTest Py_Initialize() -> t.CVoid | t.State 根因)。 + return llvmlite.Void(pool) if first_ty is not None: return first_ty return None @@ -1022,8 +1034,17 @@ def _inject_global_variable_declare(pool: memhub.MemBuddy | t.CPtr, match resolved_v: case llvmlite.LLVMType.Struct(_, _, _): gv_ty_v = llvmlite.Ptr(pool, resolved_v) - case llvmlite.LLVMType.Array(aelem_v, _): - gv_ty_v = llvmlite.Ptr(pool, aelem_v) + case llvmlite.LLVMType.Array(aelem_v, acount_v): + # 定长数组(CArray[T, N]):保留完整数组类型 [N x T],与定义端 + # handle_module_level_var 生成的 global [N x T] 一致——此前一律 + # 衰减为元素指针 i32*,链接后引用端 load 把数组前 8 字节当指针 + # 读 → 垃圾地址 → zdeflate_len_to_symbol 读 NULL/垃圾崩溃 + # (zdeflate_len_base 跨模块数组根因)。 + if acount_v > 0: + gv_ty_v = resolved_v + else: + # 不定长数组(CArray[T, None]):元素指针(C 数组衰减) + gv_ty_v = llvmlite.Ptr(pool, aelem_v) case _: gv_ty_v = resolved_v gv_v: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gname_v, gv_ty_v) @@ -1261,6 +1282,16 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr, param_ty = resolved_ty llvmlite.add_param(pool, func, param_ty, pname) + # OOP iter 协议:__next__ 方法 stub 声明带隐藏 __stop_iter_flag__(i1*), + # 与 _translate_method 的 define 签名一致(对齐 TPC HandlesFunctions.py + # L703-705)。for x in obj 循环会以 2 个实参调用 __next__(self, flag)。 + if m_fd.name is not None and string.strcmp(m_fd.name, "__next__") == 0: + i1_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_si is not None: + i1_ptr_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_si) + if i1_ptr_ty_si is not None: + llvmlite.add_param(pool, func, i1_ptr_ty_si, "__stop_iter_flag__") + # 注册到函数表(用裸名 ClassName.method,与调用方 lookup_name 一致) # ParamCount 不含 self(方法首参 self/cls 不计入,与 _translate_method 的 # md_param_count = args.__len__() - 1 保持一致)——默认参数填充按非 self diff --git a/App/lib/core/BuildPipeline.vp b/App/lib/core/BuildPipeline.vp index acd2e98..31c9f76 100644 --- a/App/lib/core/BuildPipeline.vp +++ b/App/lib/core/BuildPipeline.vp @@ -116,8 +116,6 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, ast._lexer_init(lx, src_buf, mb) tokens: ast.Token | t.CPtr = ast.tokenize(lx) tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) - # OOM 诊断:parse 后主池用量(区分 parse/translate/dump 阶段) - stdio.printf("[MEM] TFGT parse后 used=%zu chk_depth=%zu\n", mb.used_bytes(), mb.chk_depth) # parse 完成后 AST 树已独立,释放源代码缓冲区(避免 1MB/文件泄漏) stdlib.free(src_buf) if tree is None: @@ -216,11 +214,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, # 0xffffffffffffffff 崩溃),docstring 提取与翻译状态无关,提前安全。 if gen is not None: gen.extract_module_docstrings(tree, sha1_val) - # OOM 诊断:translate 前主池用量 - stdio.printf("[MEM] TFGT translate前 used=%zu chk_depth=%zu\n", mb.used_bytes(), mb.chk_depth) ret: int = tr.translate(tree) - # OOM 诊断:translate 后主池用量(对比 translate 前 → translate 内部增长量) - stdio.printf("[MEM] TFGT translate后 used=%zu chk_depth=%zu\n", mb.used_bytes(), mb.chk_depth) if ret != 0: fb_tf: str = VLogger.fmt_buf() if fb_tf is not None: @@ -397,12 +391,12 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s asan_path: str = build_sliced_path(output_dir, module_name, "asan.ll") if asan_path is None: return 1 - opt_cmd_len: t.CSizeT = string.strlen(asan_path) + string.strlen(ir_path) + 96 + opt_cmd_len: t.CSizeT = string.strlen(asan_path) + string.strlen(ir_path) + 192 opt_cmd: bytes = _mbuddy.alloc(opt_cmd_len) if opt_cmd is None: return 1 viperlib.snprintf(opt_cmd, opt_cmd_len, - "opt -passes=asan -asan-globals=false -o %s %s", + "opt -passes=forceattrs,asan -force-attribute=sanitize_address -asan-globals=false -o %s %s", asan_path, ir_path) opt_ret: int = stdlib.system(opt_cmd) if opt_ret != 0: diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 7ea0b1b..a1948cf 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -74,8 +74,12 @@ class AssignGenMixin: # 4. Call — 如 t.CUnsignedLong(-11) → 取第一个参数 # 5. UnaryOp(USub/UAdd/Invert) — 如 -11 # ============================================================ - def extract_cdefine_int_value(self, val_node: ast.AST | t.CPtr) -> int: - """从值节点提取整数常量(支持 Constant/BinOp/Name/Call/UnaryOp)""" + def extract_cdefine_int_value(self, val_node: ast.AST | t.CPtr) -> t.CInt64T: + """从值节点提取整数常量(支持 Constant/BinOp/Name/Call/UnaryOp) + + 64 位返回:大整数 CDefine(如 sys.maxsize=9223372036854775807) + 必须全精度提取,否则 int(32 位) 截断成 -1(SysLibTest maxsize FAIL 根因)。 + """ if val_node is None: return 0 k: int = val_node.kind() @@ -83,6 +87,10 @@ class AssignGenMixin: # Case 1: Constant(INT) — 如 0x0002 if k == ast.ASTKind.Constant: cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(val_node) + # True/False 布尔常量 → 1/0(对齐 TPC:bool 在 CDefine 语境按整数处理, + # 如 SHADOW_ENABLED: t.CDefine = False) + if cn.const_kind == ast.CONST_BOOL: + return 1 if cn.int_val != 0 else 0 if cn.const_kind != ast.CONST_INT: # 非 INT 常量:直接报错终止(fail-fast) HandlesType.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError") @@ -91,8 +99,8 @@ class AssignGenMixin: # Case 2: BinOp — 如 FOREGROUND_RED | FOREGROUND_GREEN、(1 << ZDEFLATE_WINDOW_BITS) if k == ast.ASTKind.BinOp: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(val_node) - left_val: int = self.extract_cdefine_int_value(bop.left) - right_val: int = self.extract_cdefine_int_value(bop.right) + left_val: t.CInt64T = self.extract_cdefine_int_value(bop.left) + right_val: t.CInt64T = self.extract_cdefine_int_value(bop.right) if bop.op == ast.OpKind.BitOr: return left_val | right_val if bop.op == ast.OpKind.BitAnd: @@ -109,6 +117,9 @@ class AssignGenMixin: return left_val - right_val if bop.op == ast.OpKind.Mult: return left_val * right_val + if bop.op == ast.OpKind.Div: + # 整数常量除法:与 FloorDiv 一致(对齐 TPC ConstEvaluator L354-355) + return left_val // right_val if bop.op == ast.OpKind.FloorDiv: return left_val // right_val if bop.op == ast.OpKind.Mod: @@ -116,11 +127,16 @@ class AssignGenMixin: # 未知 BinOp 运算符:直接报错终止(fail-fast) HandlesType.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError") - # Case 3: Name — 引用已注册的 CDefine 常量 + # Case 3: Name — 引用已注册的 CDefine 常量,或 True/False 布尔字面量 if k == ast.ASTKind.Name: nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_node) if nm.id is not None: - looked_up: int = HandlesType.lookup_cdefine_constant(nm.id) + # True/False 布尔字面量 → 1/0(对齐 TPC:bool 在 CDefine 语境按整数处理) + if string.strcmp(nm.id, "True") == 0: + return 1 + if string.strcmp(nm.id, "False") == 0: + return 0 + looked_up: t.CInt64T = HandlesType.lookup_cdefine_constant(nm.id) if HandlesType.is_cdefine_found() != 0: return looked_up # 引用未注册:直接报错终止(fail-fast) @@ -134,7 +150,7 @@ class AssignGenMixin: cl_args_list: list[ast.AST | t.CPtr] | t.CPtr = cl.args if cl_args_list is not None and len(cl_args_list) > 0: first_arg: ast.AST | t.CPtr = cl_args_list.get(0) - arg_val: int = self.extract_cdefine_int_value(first_arg) + arg_val: t.CInt64T = self.extract_cdefine_int_value(first_arg) return arg_val # Call 无参数:直接报错终止(fail-fast) HandlesType.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError") @@ -142,7 +158,7 @@ class AssignGenMixin: # Case 5: UnaryOp — 如 -11 if k == ast.ASTKind.UnaryOp: uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(val_node) - operand_val: int = self.extract_cdefine_int_value(uop.operand) + operand_val: t.CInt64T = self.extract_cdefine_int_value(uop.operand) if uop.op == ast.OpKind.USub: return -operand_val if uop.op == ast.OpKind.UAdd: @@ -154,6 +170,9 @@ class AssignGenMixin: # 未知节点类型:直接报错终止(fail-fast) HandlesType.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError") + # 追加89 块设计:fatal_error 调用后补显式 return(运行时不可达, + # 保证函数末尾块被正确 terminated,不做 noreturn 函数名白名单) + return 0 # ============================================================ # _register_cdefine_from_value - 注册 CDefine 常量(浮点/整数自动识别) @@ -167,6 +186,17 @@ class AssignGenMixin: """注册 CDefine 常量(浮点字面量 → register_cdefine_double,否则整数)""" if pool is None or name is None: return + # Call 包装的常量:如 PI: t.CDefine = float(3.14159...) / + # t.CFloat64T(2.5) / t.CDouble(1.0)。取第一个参数作为真实值节点, + # 再按 Constant 分支处理(浮点 → register_cdefine_double)。 + if val_node is not None and val_node.kind() == ast.ASTKind.Call: + cl_cd: ast.Call | t.CPtr = (ast.Call | t.CPtr)(val_node) + if cl_cd is not None and cl_cd.args is not None: + cl_args_cd: list[ast.AST | t.CPtr] | t.CPtr = cl_cd.args + if cl_args_cd.__len__() > 0: + first_arg_cd: ast.AST | t.CPtr = cl_args_cd.get(0) + if first_arg_cd is not None and first_arg_cd.kind() == ast.ASTKind.Constant: + val_node = first_arg_cd if val_node is not None and val_node.kind() == ast.ASTKind.Constant: cn_fp: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(val_node) if cn_fp.const_kind == ast.CONST_STR: @@ -191,7 +221,7 @@ class AssignGenMixin: # CDefine 声明缺少值节点:直接报错终止(fail-fast) HandlesType.fatal_error(None, f"CDefine 值提取失败:'{name}' 缺少初始值", "TypeError") return - val_fp: int = self.extract_cdefine_int_value(val_node) + val_fp: t.CInt64T = self.extract_cdefine_int_value(val_node) HandlesType.register_cdefine_constant(pool, name, val_fp) # ============================================================ diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index 5a38a4c..3d92066 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -61,13 +61,12 @@ ASM_ARGS_BUF_SIZE: t.CDefine = 512 ASM_LINE_BUF_SIZE: t.CDefine = 4096 LLVMIR_MAX_OPS: t.CDefine = 16 LLVMIR_TEMPLATE_BUF: t.CDefine = 512 -# text.ll 读取缓冲区大小(原 HandlesExprCall 模块级常量) -_TEXT_LL_READ_BUF_SIZE: t.CDefine = 262144 # 闭包名表大小(原 HandlesExprCall 模块级常量) MAX_CLOSURE_NAMES: t.CDefine = 128 # 函数指针签名表大小(原 HandlesExprCall 模块级常量) MAX_FUNC_PTR_SIGS: t.CDefine = 64 - +# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32) +ISINSTANCE_CLASS_MAX: t.CDefine = 64 # ============================================================ # CallGenMixin - 调用分派/类型转换/c.*/stdarg/函数表 @@ -148,6 +147,169 @@ class CallGenMixin: """ return StubMerger.LookupSha1ByModNameSuffix(mod_suffix) + def _module_package_from_sha1(self, pool: memhub.MemBuddy | t.CPtr, + mod_sha1: str) -> str: + """从 SHA1 存储器的 rel_path 计算模块所属包名(目录部分,'/' → '.')""" + if pool is None or mod_sha1 is None: + return None + rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() + sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() + count: int = StubMerger.GetSha1StoreCount() + if rel_arr_ptr is None or sha1_arr_ptr is None or count <= 0: + return None + for i in range(count): + sidx: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0: + continue + ridx: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_REL_PATH_LEN + rel: str = rel_arr_ptr + ridx + last_slash: str = string.strrchr(rel, 47) # 47 = ord('/') + if last_slash is None: + return None + pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(last_slash) - t.CUInt64T(rel)) + if pkg_len == 0: + return None + pkg_buf: str = pool.alloc(pkg_len + 1) + if pkg_buf is None: + return None + for pi in range(pkg_len): + if rel[pi] == '/': + pkg_buf[pi] = '.' + else: + pkg_buf[pi] = rel[pi] + pkg_buf[pkg_len] = '\0' + return pkg_buf + return None + + def _lookup_reexport_from_vpi(self, pool: memhub.MemBuddy | t.CPtr, + mod_sha1: str, func_name: str) -> str: + """re-export 表未命中时,从模块 .vpi 按需解析 from-import 定位定义模块 + + includes 模块(如 llvmlite/__init__)的 stub/text 已存在时不会被按需 + 全量翻译(Phase2 3.5 仅在 stub/text 缺失时翻译),而 re-export 表只在 + declare_only=0 的全量翻译中注册(HandleImportFromNames),因此表可能为空。 + 本函数解析该模块 .vpi 的 "from .X import ... func_name ..." 语句, + 返回定义模块名(如 llvmlite/__init__ 的 .__builder → "llvmlite.__builder")。 + """ + if pool is None or mod_sha1 is None or func_name is None: + return None + temp_dir: str = HandlesType.get_temp_dir() + if temp_dir is None: + return None + td_len: t.CSizeT = string.strlen(temp_dir) + vpi_path: str = StubMerger._sliced_path(temp_dir, td_len, mod_sha1, "vpi") + if vpi_path is None: + return None + READ_SZ: t.CSizeT = 1048576 + buf: bytes = stdlib.malloc(READ_SZ) + if buf is None: + stdlib.free(vpi_path) + return None + vf: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R) + stdlib.free(vpi_path) + if vf.closed: + stdlib.free(buf) + return None + nrd: LONG = vf.read_all(buf, READ_SZ) + vf.close() + if nrd <= 0: + stdlib.free(buf) + return None + if nrd < READ_SZ: + buf[nrd] = 0 + else: + buf[READ_SZ - 1] = 0 + name_len: t.CSizeT = string.strlen(func_name) + total: t.CSizeT = string.strlen(buf) + pos: t.CSizeT = 0 + while pos < total: + ls: t.CSizeT = pos + while pos < total and buf[pos] != '\n': + pos += 1 + ll: t.CSizeT = pos - ls + if pos < total: + pos += 1 + # 行必须以 "from " 开头 + if ll < 6: + continue + if buf[ls] != 'f' or buf[ls + 1] != 'r' or buf[ls + 2] != 'o' or buf[ls + 3] != 'm' or buf[ls + 4] != ' ': + continue + # 找 " import " 分隔符 + imp_pos: t.CSizeT = ls + 5 + while imp_pos + 7 <= ls + ll: + if buf[imp_pos] == ' ' and buf[imp_pos + 1] == 'i' and buf[imp_pos + 2] == 'm' and \ + buf[imp_pos + 3] == 'p' and buf[imp_pos + 4] == 'o' and buf[imp_pos + 5] == 'r' and \ + buf[imp_pos + 6] == 't' and buf[imp_pos + 7] == ' ': + break + imp_pos += 1 + if imp_pos + 7 > ls + ll: + continue + mod_start: t.CSizeT = ls + 5 + mod_end: t.CSizeT = imp_pos + # 名称列表从 "import " 之后(逗号分隔,含 "A as B" 取原名 A) + nm_start: t.CSizeT = imp_pos + 8 + scan: t.CSizeT = nm_start + while scan < ls + ll: + while scan < ls + ll and (buf[scan] == ' ' or buf[scan] == ','): + scan += 1 + if scan >= ls + ll: + break + n_end: t.CSizeT = scan + while n_end < ls + ll and buf[n_end] != ',' and buf[n_end] != ' ': + n_end += 1 + cand_start: t.CSizeT = scan + cand_len: t.CSizeT = n_end - scan + as_pos: t.CSizeT = scan + while as_pos + 4 <= n_end: + if buf[as_pos] == ' ' and buf[as_pos + 1] == 'a' and buf[as_pos + 2] == 's' and buf[as_pos + 3] == ' ': + cand_start = as_pos + 4 + cand_len = n_end - cand_start + break + as_pos += 1 + if cand_len == name_len: + m_ok: int = 1 + for ki in range(name_len): + if buf[cand_start + ki] != func_name[ki]: + m_ok = 0 + break + if m_ok == 1: + # 命中:解析模块名(前导点 → 相对导入,补包前缀) + has_dot: int = 0 + if buf[mod_start] == '.': + has_dot = 1 + mod_buf: str = pool.alloc(mod_end - mod_start + 1) + if mod_buf is None: + stdlib.free(buf) + return None + mi: t.CSizeT = 0 + while mod_start + mi < mod_end: + mod_buf[mi] = buf[mod_start + mi] + mi += 1 + mod_buf[mi] = '\0' + while mod_buf[0] == '.': + mod_buf = mod_buf + 1 + if string.strlen(mod_buf) == 0: + stdlib.free(buf) + return None + if has_dot != 0: + pkg: str = self._module_package_from_sha1(pool, mod_sha1) + if pkg is None: + stdlib.free(buf) + return None + rl: t.CSizeT = string.strlen(pkg) + string.strlen(mod_buf) + 2 + res: str = pool.alloc(rl) + if res is None: + stdlib.free(buf) + return None + viperlib.snprintf(res, rl, "%s.%s", pkg, mod_buf) + stdlib.free(buf) + return res + stdlib.free(buf) + return mod_buf + scan = n_end + stdlib.free(buf) + return None + def register_cexport_func(self, sha1: str, func_name: str) -> int: """注册 CExport 函数到全局表(sha1+func_name 二元组)""" if sha1 is None or func_name is None: @@ -185,6 +347,28 @@ class CallGenMixin: return 1 return 0 + def _is_state_func_from_vpi(self, pool: memhub.MemBuddy | t.CPtr, + sha1: str, func_name: str) -> int: + """vpi 返回注解含 t.State → 外部 C 函数(定义端用裸名,跨模块调用无 sha1 头) + + t.State(同 t.CExtern/t.CExport)是外部函数标记:定义端生成裸名 + @PyList_Size(链接外部库 libpython),跨模块调用若生成 @{sha1}.PyList_Size + → ld.lld undefined symbol(CPythonTest PyList_Size 根因)。CExport 走 + is_cexport_func 注册表,State 函数未注册 → 按需解析 vpi 返回注解判断。 + """ + if pool is None or sha1 is None or func_name is None: + return 0 + import lib.StubGen.VpiReader as VpiReader + if self.TempDir is not None and self.TempDirLen > 0: + VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.TempDir) + state_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name) + if state_annot is None: + return 0 + # 返回注解含 "State"(如 "t.CSizeT | t.State")→ 外部 C 函数 + if string.strstr(state_annot, "State") is not None: + return 1 + return 0 + def _lookup_mod_sha1(self, pool: memhub.MemBuddy | t.CPtr, from_imports: str, class_name: str, allow_star_fallback: int = 1) -> str: @@ -486,6 +670,16 @@ class CallGenMixin: return i8_ptr_ty if string.strcmp(func_name, "GetModuleHandleW") == 0: return i8_ptr_ty + # GetCommandLineA/W 返回 LPSTR(i8*):缺则 64 位指针被截成 i32 → + # inttoptr 后 strlen 读到无效地址(SysLibTest argv 段 ASAN 崩溃, + # 非 ASAN 环境随机表现:IDE 0xC0000005 / 部分环境侥幸可读)。 + if string.strcmp(func_name, "GetCommandLineA") == 0: + return i8_ptr_ty + if string.strcmp(func_name, "GetCommandLineW") == 0: + return i8_ptr_ty + # gethostbyname 返回 hostent*(i8*) + if string.strcmp(func_name, "gethostbyname") == 0: + return i8_ptr_ty if string.strcmp(func_name, "LoadLibraryA") == 0: return i8_ptr_ty if string.strcmp(func_name, "LoadLibraryW") == 0: @@ -550,9 +744,11 @@ class CallGenMixin: # 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配 # x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致 # WARNING: 未登记的外部函数返回类型默认推断为 i32,若实际返回指针/i64 会被截断 - VLogger.warning( - f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}", - "HandlesExprCall") + # --nowarn-register 可抑制此 WARN(自举编译中大量出现,刷屏影响日志收敛) + if VLogger.nowarn_register() == 0: + VLogger.warning( + f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}", + "HandlesExprCall") return llvmlite.Int32(pool) def _get_external_func_param_ty(self, pool: memhub.MemBuddy | t.CPtr, @@ -946,7 +1142,30 @@ class CallGenMixin: pass case _: pass - return HandlesVar.lookup_var(trans.SymTab, nm.id) + return_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id) + if return_var is not None and return_var.Ty is not None: + # 对象指针变量(如 lit_tree = zhuff.zhuff_tree(),alloca 是 + # Ptr(Ptr(Struct)),存对象指针):c.Addr(对象变量) 应返回对象 + # 指针本身(load 一次),对齐 self/cls 分支与 c.Addr(self.field) + # (GEP 后对象地址)。此前直接返回 &变量(Ptr(Ptr(Struct)))→ + # 调用端 bitcast 成 Ptr(Struct) 后 ht 指向指针变量存储 → 字段 + # 读到指针值/垃圾 → build_decode_tree 越界崩溃 + # (zinflate inflate_block_fixed 的 c.Addr(lit_tree) 根因)。 + match return_var.Ty: + case llvmlite.LLVMType.Ptr(addr_inner1): + match addr_inner1: + case llvmlite.LLVMType.Ptr(addr_inner2): + match addr_inner2: + case llvmlite.LLVMType.Struct(_, _, _): + addr_obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, addr_inner2) + return llvmlite.build_load(builder, addr_obj_ty, return_var) + case _: + pass + case _: + pass + case _: + pass + return return_var # Attribute 形式: c.Addr(self.field) → 取字段地址(GEP 到字段,不 load) # 用于 atom.__atomic_test_and_set(c.Addr(self.lock_val), ...) 等锁操作 @@ -995,6 +1214,37 @@ class CallGenMixin: return llvmlite.build_gep_struct( builder, struct_ty, fe_addr.Ty, obj_ptr, fe_addr.Index) + # Subscript 形式: c.Addr(arr[i]) → 数组元素地址(GEP 数组,不 load) + # 用于 self.root_directory = c.Addr(directories[directory_count]) 等。 + # 此前只支持 Name/Attribute,Subscript 落到函数末尾返回 None → + # 赋值右侧为 None(devfs.vp:159 根因)。 + if arg0.kind() == ast.ASTKind.Subscript: + if trans is None: + return None + sub_addr: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(arg0) + if sub_addr is None or sub_addr.value is None or sub_addr.slice is None: + return None + arr_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, sub_addr.value, None, 0, trans) + if arr_val is None or arr_val.Ty is None: + return None + idx_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, sub_addr.slice, None, 0, trans) + if idx_val is None: + return None + # 数组基址 pointee 是 Array(elem_ty, N) → GEP 得元素指针 + arr_pointee_ca: llvmlite.LLVMType | t.CPtr = None + if HandlesExpr.is_ptr_type(arr_val.Ty) != 0: + arr_pointee_ca = arr_val.Ty.Pointee + if arr_pointee_ca is not None: + match arr_pointee_ca: + case llvmlite.LLVMType.Array(arr_et_ca, _): + return llvmlite.build_gep_array( + builder, arr_pointee_ca, arr_et_ca, arr_val, idx_val) + case _: + pass + return None + def translate_c_load(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -2018,14 +2268,9 @@ class CallGenMixin: OOP 结构体:alloca → call __before_init__(ptr) → call __init__(ptr, args) 或 store 参数 → load 普通结构体:alloca → zero + defaults → store 参数 → load """ - # OOM 诊断:结构体构造入口 used(定位跨模块类构造语句暴涨段) - stdio.printf("[MEM] struct_ctor %s can=%d used=%zu chk=%zu\n", - class_name, can, pool.used_bytes(), pool.chk_depth) # 创建临时 alloca # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) - stdio.printf("[MEM] struct_ctor %s after_ensure used=%zu chk=%zu\n", - class_name, pool.used_bytes(), pool.chk_depth) tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty) if tmp is None: return llvmlite.ConstZero(pool, struct_ty) @@ -2083,9 +2328,6 @@ class CallGenMixin: new_arg_node: ast.AST | t.CPtr = cargs.get(nai) if new_arg_node is None: continue - # OOM 诊断:__new__ 参数逐个打印 used(定位第几个参数暴涨) - stdio.printf("[MEM] struct_ctor %s newarg %d used=%zu chk=%zu\n", - class_name, nai, pool.used_bytes(), pool.chk_depth) new_av: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, new_arg_node, None, 0, trans) if new_av is not None: @@ -2549,9 +2791,6 @@ class CallGenMixin: node: 触发本次调用的 AST 节点(如 Subscript 下标节点),用于 fatal_error 准确报告出错行号(传 None 时行号显示 0,不利于定位)。""" - # OOM 诊断:方法调用入口 used(定位 __init__/__before_init__ 跨模块调用段) - stdio.printf("[MEM] method_on_ptr %s.%s used=%zu chk=%zu\n", - class_name, method_name, pool.used_bytes(), pool.chk_depth) if pool is None or builder is None or class_name is None or method_name is None: return None if self_ptr is None: @@ -2626,13 +2865,21 @@ class CallGenMixin: HandlesType.fatal_error(node, "方法返回类型无法确定") else: # found_func 为 None(跨模块调用 stub 未注入):无法从 declare 获取返回类型。 + # __before_init__/__init__ 是编译器生成/约定方法(vpi stub 无条目): + # 恒返回 void(对齐 _special_method_ret_ty L3196-3198),直接固定, + # 否则走 text.ll/vpi 解析失败 → fail-fast 误报 + # (CPythonTest PyLongObj() 构造:类未定义 __init__/__before_init__ + # 但实例化仍生成调用 → "PyLongObj.__before_init__ 返回类型无法确定"根因)。 + if string.strcmp(method_name, "__before_init__") == 0 or \ + string.strcmp(method_name, "__init__") == 0: + call_ret_ty = void_ty # __new__ 返回 Ptr(struct_ty)(构造函数返回自身指针,可推断)。 # __getitem__ 返回类型无法从 self 推断(可返回 i8* 如 HashTable.__getitem__ # -> t.CPtr,或 Ptr(JsonValue) 如 JsonValue.__getitem__)——推断 Ptr(cmop_struct_ty) # 会与 define 返回类型不匹配,调用侧按错误类型读取返回值导致指针截断崩溃。 # fail-fast:__getitem__ 无 stub declare 立即报错,暴露依赖模块 vpi 缺失或 # 拓扑顺序错误(依赖模块未先翻译生成 vpi/text.ll)。 - if string.strcmp(method_name, "__new__") == 0: + elif string.strcmp(method_name, "__new__") == 0: if cmop_struct_ty is not None: call_ret_ty = llvmlite.Ptr(pool, cmop_struct_ty) else: @@ -2650,6 +2897,43 @@ class CallGenMixin: class_name) HandlesType.fatal_error(node, err_cm3) HandlesType.fatal_error(node, "__getitem__ stub declare 未注入") + else: + # 其它跨模块方法(泛型特化方法必然无 stub declare):旧实现静默 + # 保持 void → 幽灵 "call void @...__add__" + 调用方(如运算符 + # 重载 try_operator_overload)回退原生运算双重发射 → llc invalid + # operand type for instruction(NumpyTest a+b 根因)。用 text.ll + # → vpi 精确解析返回类型(对齐 _translate_method_call L4641); + # 都失败则 fail-fast,绝不静默 void。 + ret_cm: llvmlite.LLVMType | t.CPtr = None + if cls_sha1 is not None: + ret_cm = self._LookupMethodRetTyFromTextLL( + pool, cls_sha1, class_name, method_name) + if ret_cm is None and cls_sha1 is not None: + ret_cm = self._lookup_method_ret_ty_via_vpi( + pool, cls_sha1, class_name, method_name) + if ret_cm is not None: + call_ret_ty = ret_cm + else: + # Phase A 依赖 text.ll 未生成(方法定义模块尚未翻译)→ 标记重试, + # 保持 void 继续(对齐 _translate_method_call L4760-4773 与跨模块 + # 函数路径 L8167-8195 的 _deps_pending 机制;本函数同样服务 Mixin + # 跨模块方法调用,如 BuildPipeline gen.extract_module_docstrings) + # cls_sha1 为 None 同样视为依赖未就绪(pass1 定义类/祖先未注册 + # 连 sha1 都取不到,对齐 _translate_method_call L4778-4785) + if cls_sha1 is None \ + or (cls_sha1 is not None + and self._dep_text_ll_exists(pool, cls_sha1) == 0): + if trans is not None: + trans._deps_pending = 1 + call_ret_ty = llvmlite.Void(pool) + else: + err_cm4: str = pool.alloc(256) + if err_cm4 is not None: + viperlib.snprintf(err_cm4, 256, + "方法 '%s.%s' 返回类型无法确定(text.ll/vpi 均未命中,跨模块 stub 未注入)", + class_name, method_name) + HandlesType.fatal_error(node, err_cm4) + HandlesType.fatal_error(node, "方法返回类型无法确定") # 构建参数链表: self_ptr → extra_args... llvmlite.value_set_next(self_ptr, None) @@ -2710,8 +2994,11 @@ class CallGenMixin: mdf_val = HandlesExprCall._build_const_from_snapshot( pool, mod, mdf_gd, mdf_di, trans, builder) if mdf_val is None: + # 用 node(本函数报错行号节点,如构造实参/源码 Call)而非 cl—— + # cl 在本函数未定义(无该参数),读取垃圾指针导致 fatal_error + # 显示 "line 0"(FileIoTest File.__init__ 报错行号丢失根因) HandlesType.fatal_error( - cl, + node, f"跨模块默认参数无法重建(快照缺失,默认值需为简单常量): {lookup_name}", "TypeError") continue @@ -2810,6 +3097,20 @@ class CallGenMixin: if cmop_has_vt == 1 and cmop_method_idx >= 0: return self._do_virtual_call(pool, builder, mod, class_name, method_name, self_ptr, head, total_count, found_func, call_ret_ty) + # 防御:返回类型为 i0(0 位整数)时 fail-fast,绝不输出非法 IR。 + # 根因已修(gen_done 成功后不再读未初始化的 looked_up_ty,见 L3970); + # 此兜底防止未来任何路径产出零值 LLVMType 导致 llc + # "bitwidth for integer type out of range"。 + if call_ret_ty is not None: + cd_i0: int = 0 + match call_ret_ty: + case llvmlite.LLVMType.Int(bits): + if bits == 0: + cd_i0 = 1 + case _: + pass + if cd_i0 != 0: + call_ret_ty = self._fail_method_ret_ty(method_name, node) # 跨模块调用(found_func 为 None):创建 declare 声明,避免 llc 报 undefined value if found_func is None: self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) @@ -3178,28 +3479,33 @@ class CallGenMixin: self.TempDir, subdir_buf, class_sha1) stdlib.free(subdir_buf) - # 读取 text.ll - read_buf: bytes = stdlib.malloc(_TEXT_LL_READ_BUF_SIZE) - if read_buf is None: - stdlib.free(path_buf) - return None - + # 读取 text.ll(按文件实际大小分配:固定 _TEXT_LL_READ_BUF_SIZE(256KB) + # 会截断大模块的 text.ll,末尾方法定义查不到 → 误报"方法返回类型无法 + # 精确获取"(追加80b 根因,同追加74 _read_source_line 截断)。64MB 上限 + # 防御异常超大文件整体读取。) tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R) if tf.closed: stdlib.free(path_buf) - stdlib.free(read_buf) return None - text_len: t.CInt64T = tf.read_all(read_buf, _TEXT_LL_READ_BUF_SIZE) + tfsize: LONGLONG = tf.size() + if tfsize <= 0 or tfsize > 67108864: + tf.close() + stdlib.free(path_buf) + return None + read_size: t.CSizeT = t.CSizeT(tfsize) + read_buf: bytes = stdlib.malloc(read_size + 1) + if read_buf is None: + tf.close() + stdlib.free(path_buf) + return None + text_len: t.CInt64T = tf.read_all(read_buf, ULONG(read_size + 1)) tf.close() stdlib.free(path_buf) if text_len <= 0: stdlib.free(read_buf) return None - if text_len < _TEXT_LL_READ_BUF_SIZE: - read_buf[text_len] = '\0' - else: - read_buf[_TEXT_LL_READ_BUF_SIZE - 1] = '\0' + read_buf[text_len] = '\0' # 构造搜索模式: "{sha1}.{class_name}.{method_name}" 或 bare 模式 "{sha1}.{method_name}" cls_len: t.CSizeT = 0 @@ -3385,7 +3691,16 @@ class CallGenMixin: if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty else: - call_ret_ty = self._fail_method_ret_ty(method_name) + # Phase A 依赖未就绪(cls_sha1_rt None / 定义模块 text.ll 未生成): + # 标记 pending 下遍重译(对齐 _translate_method_call L4783-4790), + # 绝不 fail-fast 中断多遍收敛;真实错误由收敛检查兜底。 + if cls_sha1_rt is None \ + or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0: + if trans is not None: + trans._deps_pending = 1 + call_ret_ty = llvmlite.Void(pool) + else: + call_ret_ty = self._fail_method_ret_ty(method_name) # 参数类型对齐:按目标函数声明的参数类型转换实参(与 _translate_method_call # 一致)。例如 from_long(10) 的 i32 字面量 → i64 形参(v: t.CLong), @@ -3662,6 +3977,573 @@ class CallGenMixin: if string.strcmp(method_name, "__setitem__") == 0: return 1 return 0 + # ============================================================ + # _translate_fakeduck_call - FakeDuck: str 变量方法调用 → 扩展类 _Type/_str + # + # 对齐 TPC HandlesExprCall.py L2841-2880: + # ClassName 常规解析失败且对象是 char*(i8*)时,遍历扩展类查找 + # {CN}.{method} 函数。对象是 Name 变量且有影子({var}__meta__,fd2 + # 创建)时 self=影子;否则(字符串字面量/匿名 str)在入口创建临时 + # 影子,写 __data__=obj 值、__mbuddy__=NULL 后作 self,避免 + # bitcast i8* → _str* 读取垃圾指针导致 ACCESS_VIOLATION。 + # ============================================================ + def _translate_fakeduck_call(self, pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + cls_name: str, + method_name: str, + cl: ast.Call | t.CPtr, + trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: + """FakeDuck: str 变量方法调用解析到扩展类 _Type/_str,返回 call 结果或 None""" + if pool is None or builder is None or cl is None or trans is None: + return None + if method_name is None: + return None + # 仅对 str 类型触发(AnnotClassName == "str",对象是 char*); + # cls_name 为 None(表达式接收者解析失败)时也尝试 + if cls_name is not None and string.strcmp(cls_name, "str") != 0: + return None + if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: + return None + fd_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if fd_at is None or fd_at.value is None or fd_at.attr is None: + return None + if string.strcmp(fd_at.attr, method_name) != 0: + return None + # 候选扩展类:先 _Type(a.type_id())后 _str(a.upper() 等) + fd_func: llvmlite.Function | t.CPtr = None + fd_cls: str = None + fd_key: str = pool.alloc(160) + if fd_key is not None: + for fd_ci in range(2): + fd_cn: str = "_Type" + if fd_ci == 1: + fd_cn = "_str" + viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name) + fd_func = self.find_func_in_module(mod, fd_key) + if fd_func is not None: + fd_cls = fd_cn + break + if fd_func is None or fd_cls is None: + return None + # 扩展类结构体必须已注册(GEP/临时影子需要类型) + fd_sty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(fd_cls) + if fd_sty is None: + return None + # 扩展类字段数:有字段(_str)时 self 用影子结构体指针; + # 无字段(_Type)时 self 用对象值 bitcast 到扩展类指针(对齐 TPC L2856-2876) + fd_entry_fd: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(fd_sty) + fd_has_fields: int = 0 + if fd_entry_fd is not None and fd_entry_fd.FieldCount > 0: + fd_has_fields = 1 + # self 参数:优先使用已有影子 {var}__meta__(fd2 创建,仅当扩展类有字段时) + fd_self: llvmlite.Value | t.CPtr = None + if fd_at.value.kind() == ast.ASTKind.Name: + fd_onm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(fd_at.value) + if fd_onm.id is not None: + # pool 持久缓冲(f-string 栈缓冲在函数返回后失效) + fd_mn: str = pool.alloc(256) + if fd_mn is not None: + viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id) + fd_meta: llvmlite.Value | t.CPtr = HandlesVar.lookup_current( + trans.SymTab, fd_mn) + if fd_meta is not None and fd_has_fields != 0: + fd_self = fd_meta + if fd_self is None: + if fd_has_fields != 0: + # 有字段扩展类且无影子(字符串字面量等):入口创建临时影子并初始化 + # 先确保 _str 完整定义(跨模块结构体默认 opaque,alloca 报 unsized) + HandlesStruct.ensure_struct_def_in_module(pool, mod, fd_sty) + fd_self = HandlesVar._alloca_at_entry(builder, fd_sty) + if fd_self is None: + return None + fd_obj_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, fd_at.value, None, 0, trans) + if fd_obj_val is not None: + fd_dfe: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(fd_sty, "__data__") + if fd_dfe is not None and fd_dfe.Ty is not None: + fd_dp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, fd_sty, fd_dfe.Ty, fd_self, fd_dfe.Index) + if fd_dp is not None: + llvmlite.build_store(builder, fd_obj_val, fd_dp) + fd_bfe: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(fd_sty, "__mbuddy__") + if fd_bfe is not None and fd_bfe.Ty is not None: + fd_bp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, fd_sty, fd_bfe.Ty, fd_self, fd_bfe.Index) + fd_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull( + pool, fd_bfe.Ty, "null") + if fd_bp is not None and fd_null is not None: + llvmlite.build_store(builder, fd_null, fd_bp) + # FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__) + self._inject_requires_fields( + trans, pool, builder, mod, fd_self, fd_cls) + else: + # 无字段扩展类(_Type):对象值 bitcast 到扩展类指针 + fd_obj_v: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, fd_at.value, None, 0, trans) + if fd_obj_v is None: + return None + fd_self = llvmlite.build_bitcast( + builder, fd_obj_v, llvmlite.Ptr(pool, fd_sty)) + if fd_self is None: + return None + # 构建参数链表: self → 其余参数 + llvmlite.value_set_next(fd_self, None) + fd_head: llvmlite.Value | t.CPtr = fd_self + fd_tail: llvmlite.Value | t.CPtr = fd_self + fd_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args + fd_can: int = 0 + if fd_args is not None: + fd_can = len(fd_args) + for fd_ai in range(fd_can): + fd_arg: ast.AST | t.CPtr = fd_args.get(fd_ai) + if fd_arg is not None: + fd_av: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, fd_arg, None, 0, trans) + # char 常量参数 → 单字符字符串(char 隐式转 str, + # 如 s1.split(','):',' 是 i32 值 44,传给 str 参数须先字符串化, + # 否则运行时 strlen/strncmp 解引用 44 崩溃) + if fd_arg.kind() == ast.ASTKind.Constant: + fd_ac: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(fd_arg) + if fd_ac.const_kind == ast.CONST_CHAR: + ch_buf: str = pool.alloc(2) + if ch_buf is not None: + ch_buf[0] = t.CChar(fd_ac.int_val) + ch_buf[1] = '\0' + fd_av = HandlesExpr.create_global_string( + builder, pool, mod, ch_buf, trans) + if fd_av is not None: + llvmlite.value_set_next(fd_av, None) + llvmlite.value_set_next(fd_tail, fd_av) + fd_tail = fd_av + # 返回类型与调用名(来自 find_func_in_module 命中的函数定义) + fd_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(fd_func) + if fd_ret_ty is None: + fd_ret_ty = llvmlite.Int32(pool) + fd_fname: str = llvmlite.function_get_name(fd_func) + if fd_fname is None: + return None + # 返回类型精确化:跨模块 declare 的 list[T] 特化返回类型可能被简化 + # 为 Ptr(i8),调用方变量 alloca 类型随之退化 → parts[0] 走字符下标 + # 路径而非 list 内联 __getitem__(printf %s 解引用字符值崩溃)。 + # 从定义模块 text.ll 读取精确返回类型(对齐普通方法调用 _translate_method_call)。 + fd_sha1: str = pool.alloc(17) + if fd_sha1 is not None: + string.strncpy(fd_sha1, fd_fname, 16) + fd_sha1[16] = '\0' + fd_exact: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL( + pool, fd_sha1, fd_cls, method_name) + if fd_exact is not None: + fd_ret_ty = fd_exact + fd_total: int = 1 + fd_can + return llvmlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0) + + # ============================================================ + # _inject_requires_fields - FakeDuck: with 上下文注入 requires 字段 + # + # 对齐 TPC HandlesExprCall.py L4021-4079: + # 对类 __requires__ 元数据(fd1 注册)中的每个字段,运行时调用 + # _find_provider(field) 查找 with 上下文提供的指针;非 NULL 时 + # bitcast 到字段类型写入 meta_ptr 对应槽位(条件注入,避免覆盖 + # 用户已手动设置的值)。_find_provider 未注册(未 import + # _withcontent)时跳过注入,不影响非 with 场景。 + # ============================================================ + def _inject_requires_fields(self, trans: HT.Translator | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + meta_ptr: llvmlite.Value | t.CPtr, + class_name: str) -> int: + """在 with 上下文注入 requires 字段,返回注入字段数""" + if trans is None or pool is None or builder is None or mod is None: + return 0 + if meta_ptr is None or class_name is None: + return 0 + # _withcontent._find_provider(跨模块):仅当本模块可见(_withcontent + # 被 import,vpi stub declare 已注入)时才注入。对齐 TPC L4048-4050 + # (Gen.functions.get('_find_provider') 不存在则跳过)——否则非 with + # 项目(如 HashTableTest 未 import _withcontent)也会为每个 _str(...) + # 构造发射 {sha1}._find_provider 调用 → ld.lld undefined symbol。 + fd_fn_probe: llvmlite.Function | t.CPtr = self.find_func_in_module( + mod, "_find_provider") + if fd_fn_probe is None: + return 0 + fd_fname: str = llvmlite.function_get_name(fd_fn_probe) + if fd_fname is None: + return 0 + cnt: int = HandlesType.class_meta_count( + HandlesType.CLASS_META_REQUIRES, class_name) + if cnt <= 0: + return 0 + sty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(class_name) + if sty is None: + return 0 + i8_ty_j: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + if i8_ty_j is None: + return 0 + i8_ptr_ty_j: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_j) + if i8_ptr_ty_j is None: + return 0 + cur_func: llvmlite.Function | t.CPtr = trans._cur_func + if cur_func is None: + return 0 + injected: int = 0 + for ji in range(cnt): + fname: str = HandlesType.class_meta_field_at( + HandlesType.CLASS_META_REQUIRES, class_name, ji) + if fname is None: + continue + fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field( + sty, fname) + if fe is None or fe.Ty is None: + continue + # 参数:字段名字符串(i8*) + fd_fstr: llvmlite.Value | t.CPtr = HandlesExpr.create_global_string( + builder, pool, mod, fname, trans) + if fd_fstr is None: + continue + llvmlite.value_set_next(fd_fstr, None) + self._ensure_method_declare( + pool, mod, fd_fname, i8_ptr_ty_j, fd_fstr, 1) + fd_ptr: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, fd_fname, fd_fstr, 1, i8_ptr_ty_j, 0) + if fd_ptr is None: + continue + # 条件注入:ptr != NULL 才写入(保留用户手动设置的值) + fd_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull( + pool, i8_ptr_ty_j, "null") + if fd_null is None: + continue + fd_cmp: llvmlite.Value | t.CPtr = llvmlite.build_icmp( + builder, llvmlite.ICMP_NE, fd_ptr, fd_null) + if fd_cmp is None: + continue + # 块名唯一化(create_block 不自动编号,同名 label 在文本 IR 中 + # 冲突 → llc "Terminator found in the middle of a basic block") + vtc2: int = trans._label_counter + trans._label_counter = vtc2 + 1 + fd_bb_buf2: str = pool.alloc(64) + if fd_bb_buf2 is None: + continue + viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2) + fd_skip_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, cur_func, fd_bb_buf2) + viperlib.snprintf(fd_bb_buf2, 64, "fd.inject.%d", vtc2) + fd_inject_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, cur_func, fd_bb_buf2) + if fd_skip_bb is None or fd_inject_bb is None: + continue + llvmlite.build_cond_br(builder, fd_cmp, fd_inject_bb, fd_skip_bb) + # inject 块:bitcast + GEP + store,跳回 skip + llvmlite.position_at_end(builder, fd_inject_bb) + fd_cast: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, fd_ptr, fe.Ty) + if fd_cast is not None: + fd_fp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, sty, fe.Ty, meta_ptr, fe.Index) + if fd_fp is not None: + llvmlite.build_store(builder, fd_cast, fd_fp) + llvmlite.build_br(builder, fd_skip_bb) + # skip 块:恢复 CurBlock,继续后续代码生成 + llvmlite.position_at_end(builder, fd_skip_bb) + injected += 1 + return injected + + # ============================================================ + # _emit_provider_push / _emit_provider_pop - FakeDuck: with 上下文 + # provider push/pop(__provides__ 元数据,fd1 注册) + # + # 对齐 TPC HandlesWith.py L138-174: + # with 进入时对每个提供字段生成 _push_provider(field_str, ctx_i8); + # 退出时生成等量 _pop_provider()。运行时 _withcontent 模块负责 + # 维护 TLS provider 栈,供 _inject_requires_fields 查找。 + # ============================================================ + def _emit_provider_push(self, trans: HT.Translator | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + cls_name: str, + ctx_val: llvmlite.Value | t.CPtr) -> int: + """生成 _push_provider(field, ctx_i8) 调用(每提供字段一次),返回字段数""" + if trans is None or pool is None or builder is None or mod is None: + return 0 + if cls_name is None or ctx_val is None: + return 0 + fd_pc: int = HandlesType.class_meta_count( + HandlesType.CLASS_META_PROVIDES, cls_name) + if fd_pc <= 0: + return 0 + # _withcontent._push_provider(跨模块,同 _find_provider 的可见性守卫: + # 本模块未 import _withcontent 时跳过,避免 undefined symbol) + fd_pfn_probe: llvmlite.Function | t.CPtr = self.find_func_in_module( + mod, "_push_provider") + if fd_pfn_probe is None: + return 0 + fd_pfn: str = llvmlite.function_get_name(fd_pfn_probe) + if fd_pfn is None: + return 0 + i8_ty_p: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + if i8_ty_p is None: + return 0 + i8_ptr_ty_p: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_p) + if i8_ptr_ty_p is None: + return 0 + ret_i32_p: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + if ret_i32_p is None: + return 0 + # ctx 值 bitcast 到 i8*(provider 栈存 void*) + fd_ctx_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, ctx_val, i8_ptr_ty_p) + if fd_ctx_i8 is None: + return 0 + for pi in range(fd_pc): + fname: str = HandlesType.class_meta_field_at( + HandlesType.CLASS_META_PROVIDES, cls_name, pi) + if fname is None: + continue + fd_fstr: llvmlite.Value | t.CPtr = HandlesExpr.create_global_string( + builder, pool, mod, fname, trans) + if fd_fstr is None: + continue + llvmlite.value_set_next(fd_fstr, fd_ctx_i8) + llvmlite.value_set_next(fd_ctx_i8, None) + self._ensure_method_declare( + pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2) + llvmlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0) + return fd_pc + + def _emit_provider_pop(self, trans: HT.Translator | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + cls_name: str) -> int: + """生成 _pop_provider() 调用(每提供字段一次),返回字段数""" + if trans is None or pool is None or builder is None or mod is None: + return 0 + if cls_name is None: + return 0 + fd_pc: int = HandlesType.class_meta_count( + HandlesType.CLASS_META_PROVIDES, cls_name) + if fd_pc <= 0: + return 0 + # _withcontent._pop_provider(跨模块,同 _find_provider 的可见性守卫: + # 本模块未 import _withcontent 时跳过,避免 undefined symbol) + fd_pon_probe: llvmlite.Function | t.CPtr = self.find_func_in_module( + mod, "_pop_provider") + if fd_pon_probe is None: + return 0 + fd_pon: str = llvmlite.function_get_name(fd_pon_probe) + if fd_pon is None: + return 0 + ret_i32_q: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + if ret_i32_q is None: + return 0 + self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0) + for _pi in range(fd_pc): + llvmlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0) + return fd_pc + + # ============================================================ + # _write_vtable_slot / _write_vtable_attr — vtable 方法槽位直接写入 + # + # serial0_file.open = serial0_open(devfs 驱动注册模式):把模块级函数 + # 指针写入对象 vtable 的方法槽位。vtable 方法不是数据字段(FieldEntry), + # get_attribute_ptr 查不到 → 此前 fatal_error "属性写入失败"。 + # 对齐 TPC HandlesAssign.py L905-949 的写时复制: + # 对象 vtable 仍指向类默认 vtable 时,先复制一份(internal 全局 + + # llvm.memcpy)并让对象指向副本,再 GEP method_index 槽位 store + # 函数指针(i8*)。多次写入互不覆盖(第二次起 vtable 已非默认, + # 跳过复制直接写槽位)。 + # ============================================================ + def _write_vtable_slot(self, pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + obj_ptr: llvmlite.Value | t.CPtr, + class_name: str, + method_index: int, + func_val: llvmlite.Value | t.CPtr, + trans: HT.Translator | t.CPtr) -> int: + """生成 vtable 槽位写入(写时复制),成功返回 1""" + if pool is None or builder is None or mod is None or trans is None: + return 0 + if obj_ptr is None or class_name is None or func_val is None: + return 0 + if method_index < 0 or trans._cur_func is None: + return 0 + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + if i8_ty is None: + return 0 + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + if i8_ptr_ty is None: + return 0 + i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty) + if i8_ptr_ptr_ty is None: + return 0 + method_count: int = HandlesStruct.get_vtable_method_count(class_name) + if method_count <= 0: + return 0 + # 构造 vtable 类型 { i8*, i8*, ... } + vt_first: llvmlite.ParamNode | t.CPtr = None + vt_prev: llvmlite.ParamNode | t.CPtr = None + for vi in range(method_count): + pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, i8_ptr_ty) + if pn is None: + continue + if vt_first is None: + vt_first = pn + if vt_prev is not None: + llvmlite.paramnode_set_next(vt_prev, pn) + vt_prev = pn + vtable_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, vt_first, method_count, None) + if vtable_ty is None: + return 0 + # 对象 vtable 槽位(对象头 → i8**,vtable 指针总是第一个字段) + vtable_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, obj_ptr, i8_ptr_ptr_ty) + if vtable_slot is None: + return 0 + vtable_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i8_ptr_ty, vtable_slot) + if vtable_ptr is None: + return 0 + # 类默认 vtable 地址(比较用) + class_vt: llvmlite.Value | t.CPtr = self._make_vtable_ref_i8( + pool, builder, mod, class_name, trans) + if class_vt is None: + return 0 + cmp_vt: llvmlite.Value | t.CPtr = llvmlite.build_icmp( + builder, llvmlite.ICMP_EQ, vtable_ptr, class_vt) + if cmp_vt is None: + return 0 + # 块名唯一化(create_block 不自动编号;同函数多次 vtable 写入(如 + # devfs open/close/read/write 连续赋值)同名 label 冲突) + vtc3: int = trans._label_counter + trans._label_counter = vtc3 + 1 + vt_bb_buf3: str = pool.alloc(64) + if vt_bb_buf3 is None: + return 0 + viperlib.snprintf(vt_bb_buf3, 64, "vt.copy.%d", vtc3) + vt_copy_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, trans._cur_func, vt_bb_buf3) + viperlib.snprintf(vt_bb_buf3, 64, "vt.skip.%d", vtc3) + vt_skip_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, trans._cur_func, vt_bb_buf3) + if vt_copy_bb is None or vt_skip_bb is None: + return 0 + llvmlite.build_cond_br(builder, cmp_vt, vt_copy_bb, vt_skip_bb) + # copy 块:创建 internal 副本全局(zeroinit)+ memcpy + 对象指向副本 + llvmlite.position_at_end(builder, vt_copy_bb) + vtc: int = trans._label_counter + trans._label_counter = vtc + 1 + vcopy_buf: str = pool.alloc(256) + if vcopy_buf is not None: + viperlib.snprintf(vcopy_buf, 256, "%s.vtable.copy.%d", class_name, vtc) + vcopy_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable( + pool, vcopy_buf, vtable_ty) + if vcopy_gv is not None: + llvmlite.module_add_global(mod, vcopy_gv) + vcopy_gv.Linkage = None + vcopy_gv.Initializer = "zeroinitializer" + vcopy_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, llvmlite.Ptr(pool, vtable_ty), vcopy_buf) + if vcopy_ref is not None: + vcopy_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, vcopy_ref, i8_ptr_ty) + if vcopy_i8 is not None: + vt_size: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, llvmlite.Int64(pool), method_count * 8, "vtsz") + if vt_size is not None: + self._emit_llvm_memcpy_intrinsic( + pool, builder, mod, vcopy_i8, class_vt, vt_size) + llvmlite.build_store(builder, vcopy_i8, vtable_slot) + llvmlite.build_br(builder, vt_skip_bb) + # skip 块:reload vtable → GEP 槽位 → store 函数指针(i8*) + llvmlite.position_at_end(builder, vt_skip_bb) + vt_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i8_ptr_ty, vtable_slot) + if vt_ptr2 is None: + return 0 + vt_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, vt_ptr2, llvmlite.Ptr(pool, vtable_ty)) + if vt_typed is None: + return 0 + vt_slotp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, vtable_ty, i8_ptr_ty, vt_typed, method_index) + if vt_slotp is None: + return 0 + fptr_i8: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, func_val, i8_ptr_ty) + if fptr_i8 is not None: + llvmlite.build_store(builder, fptr_i8, vt_slotp) + return 1 + + def _write_vtable_attr(self, builder: llvmlite.IRBuilder | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + node: ast.AST | t.CPtr, + rhs_val: llvmlite.Value | t.CPtr, + trans: HT.Translator | t.CPtr) -> int: + """属性写入 obj.method = func:检测 vtable 方法并生成槽位写入(成功返回 1)""" + if node is None or builder is None or pool is None or mod is None: + return 0 + if rhs_val is None or trans is None: + return 0 + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + if at is None or at.value is None or at.attr is None: + return 0 + # 解析类名(AnnotClassName;去模块前缀) + class_nm_v: str = None + if at.value.kind() == ast.ASTKind.Name: + nm_v: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if nm_v.id is not None: + ve_v: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + trans.SymTab, nm_v.id) + if ve_v is not None: + class_nm_v = ve_v.AnnotClassName + elif at.value.kind() == ast.ASTKind.Attribute: + # 跨 Mixin:_resolve_attr_field_class 定义在 ExprGenMixin,经 gen 单例调用 + import lib.core.Gen.Generator as Generator + g_vta: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g_vta is not None: + class_nm_v = g_vta._resolve_attr_field_class(pool, trans, at.value) + if class_nm_v is None: + return 0 + fd_dot_v: str = string.strrchr(class_nm_v, '.') + if fd_dot_v is not None: + class_nm_v = fd_dot_v + 1 + if HandlesStruct.get_struct_type(class_nm_v) is None: + return 0 + vm_idx_v: int = HandlesStruct.get_vtable_method_index(class_nm_v, at.attr) + if vm_idx_v < 0: + return 0 + # 解析 obj_ptr(对齐 get_attribute_ptr 前半段:Name 直接查 alloca) + obj_ptr_v: llvmlite.Value | t.CPtr = None + if at.value.kind() == ast.ASTKind.Name: + obj_ptr_v = HandlesVar.lookup_var( + trans.SymTab, ((ast.Name | t.CPtr)(at.value)).id) + else: + obj_ptr_v = HandlesExpr.translate_value( + builder, pool, mod, at.value, None, 0, trans) + if obj_ptr_v is None or obj_ptr_v.Ty is None: + return 0 + # X|t.CPtr 变量(Ptr(Ptr))load 解引用取对象指针 + match obj_ptr_v.Ty: + case llvmlite.LLVMType.Ptr(inner_v): + match inner_v: + case llvmlite.LLVMType.Ptr(_): + obj_ptr_v = llvmlite.build_load(builder, inner_v, obj_ptr_v) + case _: + pass + case _: + pass + if obj_ptr_v is None: + return 0 + return self._write_vtable_slot( + pool, builder, mod, obj_ptr_v, class_nm_v, vm_idx_v, rhs_val, trans) + # ============================================================ # _translate_method_call - 翻译方法调用 obj.method(args) # @@ -3750,6 +4632,38 @@ class CallGenMixin: sp_ty2: llvmlite.LLVMType | t.CPtr = self._special_method_ret_ty(pool, class_name, method_name) if sp_ty2 is not None: call_ret_ty = sp_ty2 + # 特殊方法(__before_init__ 等)跨模块调用同样需带 sha1 前缀: + # memhub.alloc_buf 显式调 buf.__before_init__(),buf 是 viperio.Buf*, + # 若用裸名 @Buf.__before_init__ 链接 undefined symbol。用 + # mc_entry.ModuleSha1 / get_struct_sha1 / 类型名提取 sha1 构造 + # {sha1}.Buf.__before_init__。 + sp_anc_name: str = class_name + sp_anc_sha1: str = None + if mc_entry is not None: + sp_anc_sha1 = mc_entry.ModuleSha1 + else: + sp_anc_sha1 = HandlesStruct.get_struct_sha1(class_name) + if sp_anc_sha1 is None and mc_struct_ty is not None: + match mc_struct_ty: + case llvmlite.LLVMType.Struct(_fs_sp, _fc_sp, type_name_sp): + if type_name_sp is not None and type_name_sp[0] != '\0': + tl_sp: t.CSizeT = string.strlen(type_name_sp) + if tl_sp >= 16: + sb_sp: str = pool.alloc(17) + if sb_sp is not None: + string.strncpy(sb_sp, type_name_sp, 16) + sb_sp[16] = '\0' + sp_anc_sha1 = sb_sp + case _: + pass + if sp_anc_sha1 is not None: + ml_sp: t.CSizeT = string.strlen(sp_anc_sha1) + string.strlen(sp_anc_name) \ + + string.strlen(method_name) + 3 + mangled_sp: str = pool.alloc(ml_sp) + if mangled_sp is not None: + viperlib.snprintf(mangled_sp, ml_sp, "%s.%s.%s", + sp_anc_sha1, sp_anc_name, method_name) + call_name = mangled_sp else: # 方法实际定义类(跨模块调用名/返回类型来源;Module.append 定义在 AST) # 默认 class_name 自身,MRO fallback 命中祖先类时更新 @@ -3773,6 +4687,11 @@ class CallGenMixin: # list[AST|t.CPtr].get)。主动触发特化(幂等,MethodsEmitted 防重入), # 特化后重新查找即可命中,返回类型来自特化函数定义(天然精确)。 gen_done: int = 0 + # text.ll 查找结果提升为函数级并显式初始化:gen_done==1 成功时 + # 下方回退段不执行,若在此声明则读到未初始化的池内存垃圾(非 NULL), + # 把已正确解析的 call_ret_ty(特化 define 的 i32)覆盖成垃圾零值 + # LLVMType → build_call 输出 call i0(llc bitwidth out of range)。 + looked_up_ty: llvmlite.LLVMType | t.CPtr = None if class_name is not None: lbkt_g: str = string.strchr(class_name, '[') if lbkt_g is not None: @@ -3824,7 +4743,7 @@ class CallGenMixin: if gen_done == 0: # 尝试从 text.ll 查找实际返回类型 # 避免指针返回值被默认 i32 导致 inttoptr 截断 - looked_up_ty: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL( + looked_up_ty = self._LookupMethodRetTyFromTextLL( pool, ret_anc_sha1, ret_anc_name, method_name) # 沿 MRO 祖先链重试:方法可能定义在基类(如 Module.append → # AST.append,函数名 {ast_sha1}.AST.append,且基类在另一模块文件 @@ -3844,9 +4763,9 @@ class CallGenMixin: ret_anc_name = anc_t2 ret_anc_sha1 = anc_sha1_t2 mi_t2 += 1 - if looked_up_ty is not None: + if gen_done == 0 and looked_up_ty is not None: call_ret_ty = looked_up_ty - else: + elif gen_done == 0: # 精确查找:vpi stub 返回注解缓存(方法定义处,不硬编码白名单) vpi_ret_ty: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi( pool, ret_anc_sha1, ret_anc_name, method_name) @@ -3867,10 +4786,74 @@ class CallGenMixin: if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty else: - call_ret_ty = self._fail_method_ret_ty(method_name, cl) + # Phase A 依赖 text.ll 未生成(方法定义模块尚未翻译)→ 标记 + # 重试,保持 void 继续(下遍依赖生成后可精确解析),对齐跨模块 + # 函数路径 L8167-8195 的 _deps_pending 机制。BuildPipeline.vp + # L216 gen.extract_module_docstrings 根因:方法定义在 + # ExprGenMixin(Mixin 模块),Phase A 顺序中 ExprGenMixin 的 + # text.ll 尚未生成 → MRO 链查找全部未命中 → fail-fast 误报。 + # ret_anc_sha1 为 None 同样视为依赖未就绪:pass1 中 Generator + # 及其 Mixin 全部 defer(依赖 Handles/* 未翻译)→ 结构体未注册 + # → get_struct_sha1/MRO 查找全空 → 连 sha1 都取不到 + # (g._register_cdefine_from_value 根因)。标记 _deps_pending + # 下遍重译,绝不 fail-fast 中断多遍收敛;真实错误由收敛检查兜底。 + if ret_anc_sha1 is None \ + or (ret_anc_sha1 is not None + and self._dep_text_ll_exists(pool, ret_anc_sha1) == 0): + if trans is not None: + trans._deps_pending = 1 + call_ret_ty = llvmlite.Void(pool) + else: + # 追加80e 断点 A:方法可能定义在 MRO 祖先(Mixin)模块。 + # 此时 ret_anc_sha1 仍是调用类自身的 sha1(MRO 双路重试 + # 失败未更新),且调用类(如 Generator)已注册、自身 text.ll + # 存在 → 上方 guard 不成立 → 直接 fail-fast。但 MRO 重试 + # 落空可能只是"祖先定义模块未翻译"(AssignGenMixin.vp 的 + # text.ll 未生成),属依赖未就绪而非方法不存在。遍历祖先链: + # 任一祖先的 sha1 取不到或其 text.ll 未生成 → 结果不确定, + # 标记 pending 下遍重试(届时祖先 text.ll 生成,MRO 重试 + # 可精确解析);全部祖先就绪仍查不到才是真错误。 + mro_p2: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi_p2: int = 1 + anc_missing_p2: int = 0 + while mro_p2 is not None and mro_p2[mi_p2] != 0: + anc_p2: str = (str | t.CPtr)(t.CVoid(mro_p2[mi_p2], t.CPtr)) + if anc_p2 is not None: + anc_sha1_p2: str = HandlesStruct.get_struct_sha1(anc_p2) + if anc_sha1_p2 is None \ + or self._dep_text_ll_exists(pool, anc_sha1_p2) == 0: + anc_missing_p2 = 1 + break + mi_p2 += 1 + if anc_missing_p2 != 0: + if trans is not None: + trans._deps_pending = 1 + call_ret_ty = llvmlite.Void(pool) + else: + call_ret_ty = self._fail_method_ret_ty(method_name, cl) # found_func 未命中但返回类型已解析:用方法定义类构造跨模块调用名 # (Module.append → {AST_sha1}.AST.append,避免链接 undefined reference) + if ret_anc_name is not None and ret_anc_sha1 is None: + # ret_anc_sha1 缺失(如 find_struct 未命中跨模块结构体)时, + # 从对象实际 LLVM 类型名提取 sha1 前缀:类型名形如 + # "2c175afd85852faf.Buf"({sha1}.{ClassName}),前 16 字符即模块 sha1。 + # 例:memhub.alloc_buf 里 buf.__before_init__(),buf 是 viperio.Buf*, + # 其类型名含 viperio 模块 sha1,借此构造 {sha1}.Buf.__before_init__, + # 避免静默生成裸名 @Buf.__before_init__(链接 undefined symbol)。 + if mc_struct_ty is not None: + match mc_struct_ty: + case llvmlite.LLVMType.Struct(_fields_st, _fcount_st, type_name_st): + if type_name_st is not None and type_name_st[0] != '\0': + type_len_st: t.CSizeT = string.strlen(type_name_st) + if type_len_st >= 16: + sha1_from_ty: str = pool.alloc(17) + if sha1_from_ty is not None: + string.strncpy(sha1_from_ty, type_name_st, 16) + sha1_from_ty[16] = '\0' + ret_anc_sha1 = sha1_from_ty + case _: + pass if ret_anc_name is not None and ret_anc_sha1 is not None: ml_anc: t.CSizeT = string.strlen(ret_anc_sha1) + string.strlen(ret_anc_name) \ + string.strlen(method_name) + 3 @@ -3879,6 +4862,16 @@ class CallGenMixin: viperlib.snprintf(mangled_anc, ml_anc, "%s.%s.%s", ret_anc_sha1, ret_anc_name, method_name) call_name = mangled_anc + # 类型名提取的 sha1 无法保证为真实模块 sha1(跨模块 stub 类型名 + # 带前缀但 find_struct 未注册时仍可用),此处不校验格式 + elif ret_anc_name is not None and ret_anc_sha1 is None: + # fail-fast:方法所属类既查不到 SHA1 又从类型名提取失败, + # 禁止静默裸名(否则链接期 undefined symbol 难以定位) + HandlesType.fatal_error( + cl, + f"方法 '{method_name}' 所属类 '{ret_anc_name}' 无法解析模块 SHA1" + f"(text.ll/vpi/类型名均未命中),跨模块调用名构造失败", + "TypeError") # 翻译调用参数 cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args @@ -4142,7 +5135,10 @@ class CallGenMixin: # 检查变长函数 + C ABI 提升(cargs 是实参 AST,self_offset=1 跳过 self) is_variadic_3441: int = HandlesExprCall._check_and_promote_variadic_call( pool, builder, mod, call_name, head, total_count, cargs, 1, trans) - return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, is_variadic_3441) + # try-except:方法含 raise(EH 输出参数)时追加输出实参 + 调用后检测 + return self._emit_eh_guarded_call( + builder, pool, mod, trans, found_func, call_name, head, total_count, + call_ret_ty, is_variadic_3441) # ============================================================ # _translate_super_call — super().method(args) → 静态调用父类方法 @@ -4353,7 +5349,16 @@ class CallGenMixin: else: # 查找链(函数/text.ll/vpi stub)全部未命中:fail-fast, # 绝不静默保持默认 i32(会截断指针/i64 返回值) - self._fail_method_ret_ty(method_name, cl) + # Phase A 依赖未就绪(anc_sha1_sp None / 定义模块 text.ll + # 未生成):标记 pending 下遍重译(对齐 L4783-4790), + # 绝不 fail-fast 中断多遍收敛;真实错误由收敛检查兜底。 + if anc_sha1_sp is None \ + or self._dep_text_ll_exists(pool, anc_sha1_sp) == 0: + if trans is not None: + trans._deps_pending = 1 + ret_ty_sp = llvmlite.Void(pool) + else: + self._fail_method_ret_ty(method_name, cl) # 7. 跨模块:创建 declare;生成 call if found_func is None: self._ensure_method_declare(pool, mod, call_name, ret_ty_sp, head, arg_count_sp) @@ -5331,6 +6336,40 @@ class CallGenMixin: # t.XXX 类型转换已确认但不满足参数条件:返回 None,不跌落到跨模块 FATAL return None + # 模块限定 typedef 转换: posix.mode_t(mode), w32.win32base.HANDLE(v) 等 + # func = Attribute(value=Name(模块), attr=typedef名),此前被误当跨模块函数调用 + # → "跨模块函数 'sha1.mode_t' 返回类型无法精确获取"(SysLibTest os/_posix.vp + # L130 posix.mode_t(mode) 根因)。typedef 名做类型转换(与裸名 BYTEPTR(x) + # 对齐);非 typedef 名继续走模块函数调用。 + if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + mt_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if mt_at.value is not None and mt_at.value.kind() == ast.ASTKind.Name: + mt_mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(mt_at.value) + if mt_mod_nm.id is not None and string.strcmp(mt_mod_nm.id, "t") != 0 \ + and string.strcmp(mt_mod_nm.id, "c") != 0: + mod_td_ty: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type( + pool, func_name) + if mod_td_ty is None: + mod_td_str: str = HandlesType.lookup_ctypedef(func_name) + if mod_td_str is not None and HandlesType.is_ctypedef_found() != 0: + mod_td_ast: ast.AST | t.CPtr = HandlesType._parse_type_string( + pool, mod_td_str, 0) + if mod_td_ast is not None: + mod_imp_td: str = None + mod_fi_td: str = None + if trans is not None: + mod_imp_td = trans._imported_modules + mod_fi_td = trans._from_imports + mod_td_ty = HandlesType.resolve_annotation_type( + pool, mod_td_ast, mod_imp_td, mod_fi_td, trans) + if mod_td_ty is not None and can == 1: + mt_arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans) + if mt_arg_val is not None: + return self._translate_t_type_cast( + pool, builder, mt_arg_val, mod_td_ty, + func_name, cargs.get(0), trans) + # 裸名 typedef 调用: BYTEPTR(ptr1), HANDLE(val), CHARPTR(str) 等 # 用户通过 `from stdint import *` 导入 typedef 后,可直接用裸名做类型转换 # map_t_type 包含所有内置 typedef 名称(BYTEPTR/HANDLE/LPCSTR/size_t 等) @@ -5513,6 +6552,9 @@ class CallGenMixin: sz_resolved_ty = HandlesStruct.get_struct_type(spec_sz) # 回退 2: 实例接收者 self.__sizeof__() / obj.__sizeof__() — 用接收者值的实际类型 # (GenericTest2 Stack.size_of 依赖此路径;self 在方法内是 Ptr(Stack[int])) + # 数组变量接收者 bl_count.__sizeof__()(t.CArray[T, N] 变量)→ alloca + # 指针 Ptr([N x T]),算 N*elem_size(TPC _HandleSizeofLlvm 数组分支; + # zhuff.vp memset(bl_count, 0, bl_count.__sizeof__()) 依赖) if sz_resolved_ty is None: inst_sz_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, sz_at.value, None, 0, trans) @@ -5522,10 +6564,18 @@ class CallGenMixin: match inst_inner_sz: case llvmlite.LLVMType.Struct(_, _, _): sz_resolved_ty = inst_inner_sz + case llvmlite.LLVMType.Array(_, _): + sz_resolved_ty = inst_inner_sz case _: pass case llvmlite.LLVMType.Struct(_, _, _): sz_resolved_ty = inst_sz_val.Ty + case llvmlite.LLVMType.Array(_, _): + # 全局数组变量(模块级 t.CArray[T,N])translate_value + # 返回数组值 [N x T](非指针);局部数组返回 alloca + # 指针 Ptr([N x T]) 走上方 Ptr(Array) 分支。 + # pyzlib_error_msg.__sizeof__() 依赖(全局字符缓冲)。 + sz_resolved_ty = inst_sz_val.Ty case _: pass if sz_resolved_ty is not None: @@ -5609,60 +6659,134 @@ class CallGenMixin: pass if len_is_arr != 0: return llvmlite.const_int64(pool, len_arr_n) - # len(对象):结构体指针且类定义了 __len__ → 调用 - # Class.__len__(obj)(返回类型取方法定义,如 i64)。 - # 此前恒走 strlen:对结构体对象生成 strlen(struct*) - # → 把对象首字段当字符串读 → 垃圾长度(CPythonTest - # PyUnicodeObj len('Hello') / PyListObj len == 3 根因)。 + # len(对象):有 __len__ 就调用,没有就编译期报错 + # (用户要求 2026-08-15,不再静默 strlen 读对象首字段): + # - list 泛型内置容器:__len__ 由 list 内置分支内联 + # (读 __count__,偏移 1),不生成方法函数 + # (_is_list_builtin_method,通用判断非特判) + # - 普通类:调用 {Class}.__len__ 函数 + # - 都没有:fatal_error + # str(Ptr(Int8))保持 strlen(内建语义)。 len_struct_ty: llvmlite.LLVMType | t.CPtr = \ HandlesStruct.get_struct_type_from_value(len_val) if len_struct_ty is not None: len_cls_nm: str = HandlesStruct.get_class_name_by_type( pool, len_struct_ty) - if len_cls_nm is not None: - len_lookup: str = pool.alloc(128) - if len_lookup is not None: - viperlib.snprintf(len_lookup, 128, "%s.__len__", len_cls_nm) - len_fn2: llvmlite.Function | t.CPtr = self.find_func_in_module( - mod, len_lookup) - if len_fn2 is not None: - len_ret_ty2: llvmlite.LLVMType | t.CPtr = \ - llvmlite.function_get_ret_ty(len_fn2) - len_fn_name2: str = \ - llvmlite.function_get_name(len_fn2) - if len_ret_ty2 is not None and len_fn_name2 is not None: - # self 参数类型对齐(按方法首个形参 bitcast) - len_self_arg2: llvmlite.Value | t.CPtr = len_val - len_first_param2: llvmlite.Param | t.CPtr = \ - llvmlite.function_get_param_head(len_fn2) - if len_first_param2 is not None: - len_self_ty2: llvmlite.LLVMType | t.CPtr = \ - llvmlite.param_get_ty(len_first_param2) - if len_self_ty2 is not None and len_val.Ty is not None: - len_self_arg2 = llvmlite.build_bitcast( - builder, len_val, len_self_ty2) - len_self_arg2.Next = None - return llvmlite.build_call( - builder, len_fn_name2, len_self_arg2, - 1, len_ret_ty2, 0) - # 确保 strlen 在模块中有声明 - strlen_fn: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, "strlen") - if strlen_fn is None: - i64_ty_ln: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) - i8_ptr_ty_ln: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) - strlen_fn = llvmlite.create_declare(pool, mod, "strlen", i64_ty_ln) - if strlen_fn is not None: - llvmlite.add_param(pool, strlen_fn, i8_ptr_ty_ln, "s") - # 参数类型转换为 i8* - i8_ptr_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + if len_cls_nm is None: + HandlesType.fatal_error( + len_arg_node, "len() 参数类型推断失败(结构体类名未知)", + "TypeError") + # list 内置 __len__:读 __count__(GEP 偏移 1,i64) + if self._is_list_builtin_method(len_cls_nm, "__len__") != 0: + i64_ty_lb: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + idx_one_lb: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 1) + cnt_ptr_lb: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i64_ty_lb, len_val, idx_one_lb) + if cnt_ptr_lb is not None: + return llvmlite.build_load(builder, i64_ty_lb, cnt_ptr_lb) + len_lookup: str = pool.alloc(128) + if len_lookup is not None: + viperlib.snprintf(len_lookup, 128, "%s.__len__", len_cls_nm) + len_fn2: llvmlite.Function | t.CPtr = self.find_func_in_module( + mod, len_lookup) + if len_fn2 is not None: + len_ret_ty2: llvmlite.LLVMType | t.CPtr = \ + llvmlite.function_get_ret_ty(len_fn2) + len_fn_name2: str = \ + llvmlite.function_get_name(len_fn2) + if len_ret_ty2 is not None and len_fn_name2 is not None: + # self 参数类型对齐(按方法首个形参 bitcast) + len_self_arg2: llvmlite.Value | t.CPtr = len_val + len_first_param2: llvmlite.Param | t.CPtr = \ + llvmlite.function_get_param_head(len_fn2) + if len_first_param2 is not None: + len_self_ty2: llvmlite.LLVMType | t.CPtr = \ + llvmlite.param_get_ty(len_first_param2) + if len_self_ty2 is not None and len_val.Ty is not None: + len_self_arg2 = llvmlite.build_bitcast( + builder, len_val, len_self_ty2) + len_self_arg2.Next = None + return llvmlite.build_call( + builder, len_fn_name2, len_self_arg2, + 1, len_ret_ty2, 0) + # len_fn2 未命中(跨模块方法):如 ndarray[t.CDouble].__len__ + # 定义在 numpy——泛型特化方法无 stub declare,find_func_in_module + # 后缀匹配查不到。用结构体所属模块 sha1 构造 + # {sha1}.{cls}.__len__ 精确调用名(对齐 _translate_method_call + # 跨模块调用名构造),返回类型从 text.ll/vpi 精确解析, + # _ensure_method_declare 建 declare 后调用。 + len_cls_sha1_x: str = HandlesStruct.get_struct_sha1(len_cls_nm) + if len_cls_sha1_x is not None: + # 缓冲区需容纳 "{sha1}.{cls}.__len__": + # 2 个点 + "__len__"(7) + NUL = 10;+8 会截断 + # 末两位 → 符号变 ".__len" 链接 undefined + # (NumpyTest test_len 链接错误根因) + len_ml_x: t.CSizeT = string.strlen(len_cls_sha1_x) \ + + string.strlen(len_cls_nm) + 10 + len_mangled_x: str = pool.alloc(len_ml_x) + if len_mangled_x is not None: + viperlib.snprintf(len_mangled_x, len_ml_x, + "%s.%s.__len__", len_cls_sha1_x, len_cls_nm) + len_ret_x: llvmlite.LLVMType | t.CPtr = \ + self._LookupMethodRetTyFromTextLL( + pool, len_cls_sha1_x, len_cls_nm, "__len__") + if len_ret_x is None: + len_ret_x = self._lookup_method_ret_ty_via_vpi( + pool, len_cls_sha1_x, len_cls_nm, "__len__") + if len_ret_x is not None: + # self 参数按对象实际类型 Ptr(len_struct_ty)(与定义侧 + # 同名类型链接匹配);len_val 已是 load 后的对象指针。 + # Value 链直接作 param_head(同跨模块调用模式)。 + len_self_x: llvmlite.Value | t.CPtr = len_val + len_self_x.Next = None + self._ensure_method_declare( + pool, mod, len_mangled_x, len_ret_x, + len_self_x, 1) + return llvmlite.build_call( + builder, len_mangled_x, len_self_x, + 1, len_ret_x, 0) + # fail-fast:类没有 __len__(用户要求编译期报错) + HandlesType.fatal_error( + len_arg_node, + f"len() 参数类型 '{len_cls_nm}' 没有 __len__ 方法", + "TypeError") + # 非结构体:str(Ptr(Int8))走 strlen;其它类型 fail-fast len_arg_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(len_val) - len_coerced: llvmlite.Value | t.CPtr = len_val - if HandlesExpr.is_ptr_type(len_arg_ty) != 0: - len_coerced = llvmlite.build_bitcast(builder, len_val, i8_ptr_ty_ln2) - # 构建单参数链表并调用 strlen - len_coerced.Next = None - i64_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) - return llvmlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0) + len_is_str: int = 0 + if len_arg_ty is not None and HandlesExpr.is_ptr_type(len_arg_ty) != 0: + match len_arg_ty: + case llvmlite.LLVMType.Ptr(len_pt_ty): + if len_pt_ty is not None: + match len_pt_ty: + case llvmlite.LLVMType.Int(len_pt_bits): + if len_pt_bits == 8: + len_is_str = 1 + case _: + pass + case _: + pass + if len_is_str != 0: + # 确保 strlen 在模块中有声明 + strlen_fn: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, "strlen") + if strlen_fn is None: + i64_ty_ln: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + i8_ptr_ty_ln: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + strlen_fn = llvmlite.create_declare(pool, mod, "strlen", i64_ty_ln) + if strlen_fn is not None: + llvmlite.add_param(pool, strlen_fn, i8_ptr_ty_ln, "s") + # 参数类型转换为 i8* + i8_ptr_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + len_coerced: llvmlite.Value | t.CPtr = len_val + if HandlesExpr.is_ptr_type(len_arg_ty) != 0: + len_coerced = llvmlite.build_bitcast(builder, len_val, i8_ptr_ty_ln2) + # 构建单参数链表并调用 strlen + len_coerced.Next = None + i64_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + return llvmlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0) + # fail-fast:非字符串/数组/对象,无法求长度 + HandlesType.fatal_error( + len_arg_node, "len() 参数类型不支持(非字符串/数组/对象)", + "TypeError") # 检测 isinstance() 内置函数调用 — 运行时 vtable 子类集合比较 # isinstance(obj, Cls):obj 头部 vtable 指针 ∈ {Cls 及全部子类 vtable}; @@ -5685,13 +6809,72 @@ class CallGenMixin: st_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(st_at.value) if st_nm.id is not None: # 仅当该名称不是普通变量且是已注册结构体类名时尝试静态/类方法 + # 追加"非导入模块"条件:st_nm.id 是模块名(import X as Y 把别名 + # Y 加入 _imported_modules,HandlesImports L344-347)时,X.Y(...) + # 是模块限定调用(Module.Class 构造 / Module.func 函数),不是 + # ClassName.method(obj,args)。模块名与类名同名时(CompilerFactory. + # CompilerFactory(...) 构造 / .set_factory(...) 函数,main.vp + # 追加84/84b 根因)get_struct_type(st_nm.id) 会误判为类名 → + # 把首实参当 self 传 _call_method_on_ptr → 返回类型查 "X.Y" 永远 + # 落空 → fail-fast。模块限定调用由尾部 ctor 检测(L7441 模块限定 + # 构造)与跨模块函数路径正确路由。 st_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( trans.SymTab, st_nm.id) - if st_var is None and HandlesStruct.get_struct_type(st_nm.id) is not None: + if st_var is None and HandlesStruct.get_struct_type(st_nm.id) is not None \ + and HandlesImports.is_module_imported( + trans._imported_modules, st_nm.id) == 0: + # REnum 变体构造优先于静态/类方法调用: + # ClassName.VariantName(args)(如 Option.Some(42))若被当 + # "实例方法经类名调用"处理,会把首实参 42 当 self 传 + # _call_method_on_ptr → 查 "Option.Some" 方法返回类型 → + # 跨模块 stub 未命中 → fail-fast(EnumTest "方法 + # 'Option.Some' 返回类型无法确定" 根因)。REnum 变体 + # 构造检测原在 translate_call 尾部(L7493),被本分支 + # 拦截先执行不到,此处提前检测。 + if HandlesClassDef._is_renum_class_name(st_nm.id) != 0: + rc_tag_sc: int = HandlesClassDef._lookup_renum_variant_in_class( + st_nm.id, func_name) + if rc_tag_sc >= 0: + return self._translate_renum_construct( + pool, builder, mod, st_nm.id, func_name, + rc_tag_sc, cargs, can, trans) st_res: llvmlite.Value | t.CPtr = self._translate_static_class_call( pool, builder, mod, st_nm.id, func_name, cl, trans) if st_res is not None: return st_res + # 实例方法经类名调用: ClassName.method(obj, args) → obj.method(args) + # (Python 语义,首参是 self 对象;zdeflate.vp + # zdeflate_stream.find_match(s, data, ...) 根因:_translate_ + # static_class_call 对非静态方法返回 None 后,ClassName 被 + # 通用路径当模块名 → "跨模块调用找不到模块 SHA1") + # st_nm.id == func_name(类名==方法名,如 CompilerFactory. + # CompilerFactory(...))不是实例方法调用而是构造调用: + # 模块名与类名同名时本分支把模块限定类构造误判为 + # ClassName.method(obj,args),首实参当 self 传 → + # _call_method_on_ptr 查 "X.X" 返回类型永远落空 → + # fail-fast(main.vp CompilerFactory 追加84 根因)。 + # 跳过本分支,落到 translate_call 尾部的 ctor 检测 + # (L7426 模块限定类构造 find_struct_by_module 正确路由)。 + if can >= 1 and cargs is not None \ + and string.strcmp(st_nm.id, func_name) != 0: + im_self: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, cargs.get(0), None, 0, trans) + if im_self is not None: + im_rem: t.CSizeT | t.CPtr = None + if can >= 2: + im_rem = pool.alloc(t.CSizeT(can - 1) * 8) + if im_rem is not None: + ri_im: t.CSizeT + for ri_im in range(can - 1): + im_rv: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, cargs.get(ri_im + 1), None, 0, trans) + if im_rv is not None: + im_rem[ri_im] = t.CSizeT(im_rv) + im_res: llvmlite.Value | t.CPtr = self._call_method_on_ptr( + pool, builder, mod, st_nm.id, func_name, + im_self, im_rem, can - 1, trans) + if im_res is not None: + return im_res elif st_at.value.kind() == ast.ASTKind.Attribute: # 模块限定静态调用: module.ClassName.static_method(args) # cl.func = Attribute(value=Attribute(value=Name(module), @@ -5753,6 +6936,14 @@ class CallGenMixin: return self._translate_method_call( pool, builder, mod, inner_nm, func_name, obj_ptr, cl, trans) + # FakeDuck: str/bytes 变量(Ptr(i8),AnnotClassName 为空) + # 方法调用 → 扩展类 _Type/_str(a.type_id() / a.upper())。 + # 前置拦截:常规结构体方法分支全落空时,尝试扩展类方法 + # (find_func_in_module 未命中返回 None,不影响正常路径)。 + fd_name_res: llvmlite.Value | t.CPtr = self._translate_fakeduck_call( + pool, builder, mod, None, func_name, cl, trans) + if fd_name_res is not None: + return fd_name_res # AnnotClassName 回退:联合类型简化为 Ptr(i8) 时, # 通过原始类型注解的类名查找结构体 var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( @@ -5790,6 +6981,14 @@ class CallGenMixin: # list 内置方法跳过(结构体未注册路径同样不能 # 构造 {sha1}.{cls}.append 调用名——list 方法无此函数) if self._is_list_builtin_method(cls_nm2, func_name) == 0: + # FakeDuck: str 变量方法调用 → 扩展类 _Type/_str + # a.type_id() / a.upper() / " ".isspace() 等 + # AnnotClassName=="str" 且对象是 char* 时触发, + # self 用影子结构体 {a}__meta__(或临时影子)。 + fd_res: llvmlite.Value | t.CPtr = self._translate_fakeduck_call( + pool, builder, mod, cls_nm2, func_name, cl, trans) + if fd_res is not None: + return fd_res # 结构体未注册(跨模块依赖未翻译): # 用模块名查 SHA1 构造方法调用名 sha1_fc: str = None @@ -5831,7 +7030,21 @@ class CallGenMixin: ret_ty3 = self._lookup_method_ret_ty_via_vpi( pool, sha1_fc, cls_nm2, func_name) if ret_ty3 is None: - ret_ty3 = self._fail_method_ret_ty(func_name) + # 本分支仅在"注解类结构体未注册"时到达(L6932 else): + # 类未注册 → 无法沿 MRO 解析 Mixin 方法定义模块。 + # 例如 g 注解 Generator.Generator,但 _register_cdefine_from_value + # 定义在 AssignGenMixin.vp——其 text.ll 的 define 名是 + # {AssignGenMixin_sha1}.AssignGenMixin.xxx,查 + # {Generator_sha1}.Generator.xxx 永远落空(追加80d 根因; + # sha1_fc 存在 + text.ll 存在仍查不到,固定 256KB 截断 + # 只是次要因素)。必须标记 pending:等 Generator 注册后 + # 下遍走 L6919 已注册分支 → _translate_method_call 沿 + # MRO 链找到 AssignGenMixin → 精确解析。真正不存在的 + # 类/方法由 Phase A 收敛检查兜底,绝不在此 fail-fast + # 中断多遍收敛(对齐追加77/77b 类 defer 原则)。 + if trans is not None: + trans._deps_pending = 1 + ret_ty3 = llvmlite.Void(pool) self._ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3) return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0) @@ -5846,7 +7059,29 @@ class CallGenMixin: expr_method_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) if expr_method_at is not None and expr_method_at.value is not None and trans is not None: evk: int = expr_method_at.value.kind() - if evk != ast.ASTKind.Name and evk != ast.ASTKind.Attribute: + # Attribute 接收者区分两种形态: + # 1. 模块限定路径(os._win32.exists / w32.fileio.fn)——根名是已导入 + # 模块或 t/c,留给下方跨模块限定路径处理 + # 2. 字段链表达式接收者(self.writer.write_bits / self.pool.alloc) + # ——根名是对象(self/局部变量),必须走本方法调用路径; + # 否则被模块限定路径误判 xmod_path="self.writer" → "找不到模块 + # SHA1"(zdeflate.vp write_fixed_block 根因) + is_mod_path_expr: int = 0 + if evk == ast.ASTKind.Attribute: + expr_root: ast.AST | t.CPtr = expr_method_at.value + while expr_root is not None and expr_root.kind() == ast.ASTKind.Attribute: + expr_root = (ast.Attribute | t.CPtr)(expr_root).value + if expr_root is not None and expr_root.kind() == ast.ASTKind.Name: + expr_root_nm: str = (ast.Name | t.CPtr)(expr_root).id + if expr_root_nm is not None: + if string.strcmp(expr_root_nm, "t") == 0 or \ + string.strcmp(expr_root_nm, "c") == 0: + is_mod_path_expr = 1 + elif trans._imported_modules is not None: + if HandlesImports.is_module_imported( + trans._imported_modules, expr_root_nm) != 0: + is_mod_path_expr = 1 + if evk != ast.ASTKind.Name and is_mod_path_expr == 0: # 翻译接收者表达式(求值结果应为 Ptr(Struct)) expr_obj: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, expr_method_at.value, funcs_ptr, func_count, trans) @@ -5872,9 +7107,33 @@ class CallGenMixin: expr_cls_nm = g3.infer_expr_class( pool, mod, expr_method_at.value, trans) if expr_cls_nm is not None: + # 值类型接收者(内嵌 struct 字段,如 zdeflate s.writer 值字段): + # 方法调用 self 必须传字段地址(GEP)而非 load 出的结构体值, + # 否则结构体值(40B)塞进 zbit_writer* 参数 → ABI 错配 → + # 被调方把值当 this 指针解引用 → GEP 0x18 崩溃 + # (s.writer.write_zlib_header 运行时 access-violation 根因; + # 本路径 evk=Attribute 先于 self.field.method() 分支命中)。 + expr_is_ptr: int = 0 + match expr_obj.Ty: + case llvmlite.LLVMType.Ptr(expr_ptr_inner): + expr_is_ptr = 1 + case _: + expr_is_ptr = 0 + if expr_is_ptr == 0: + expr_addr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr( + builder, pool, mod, expr_method_at.value, trans) + if expr_addr is not None: + expr_obj = expr_addr return self._translate_method_call( pool, builder, mod, expr_cls_nm, func_name, expr_obj, cl, trans) + # FakeDuck: 表达式接收者解析失败且对象是 char* 时 + # (如 " ".isspace()),尝试扩展类 _Type/_str 方法 + # (self 用临时影子,对齐 TPC L2858-2873) + fd_expr_res: llvmlite.Value | t.CPtr = self._translate_fakeduck_call( + pool, builder, mod, None, func_name, cl, trans) + if fd_expr_res is not None: + return fd_expr_res # ============================================================ # 检测 self.field.method() 调用 @@ -5922,6 +7181,22 @@ class CallGenMixin: else: field_cls_nm = field_annot if field_cls_nm is not None: + # 值类型字段(内嵌 struct,如 zdeflate_stream.writer: zbit_writer) + # 方法调用 self 必须传字段地址(GEP),而非 load 出的结构体值: + # 传值给 stub 声明/define 的 zbit_writer* 参数 → ABI 错配, + # 被调方把结构体值前 8 字节当 this 指针解引用 → GEP 0x18 崩溃 + # (zdeflate s.writer.write_zlib_header 运行时 access-violation 根因)。 + is_val_field: int = 0 + match field_obj.Ty: + case llvmlite.LLVMType.Ptr(field_ptr_inner): + is_val_field = 0 + case _: + is_val_field = 1 + if is_val_field != 0: + field_addr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr( + builder, pool, mod, attr_method_at.value, trans) + if field_addr is not None: + field_obj = field_addr return self._translate_method_call( pool, builder, mod, field_cls_nm, func_name, field_obj, cl, trans) @@ -5965,6 +7240,29 @@ class CallGenMixin: i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + # 元素类型 T:从 list 特化类型名解析(list[int]→i32、 + # list[str]→i8*、未知/指针元素→i8*)。此前 get/set/pop + # 固定 i8*(8 字节)读写:set 的 8 字节 store 覆盖相邻元素 + # (inttoptr 值,如 set(1,99) 把 data[8..11] 的 30 清 0), + # pop 读回 0 → ListTest "pop FAILED expect 30" 根因。 + # subscript 路径(list_getitem_inline 调用方)已用 + # _list_elem_type_from_name 修正,方法调用路径漏改。 + lm_esty: llvmlite.LLVMType | t.CPtr = \ + HandlesStruct.get_struct_type_from_value(lm_obj) + lm_ename: str = None + if lm_esty is not None: + lm_ename = llvmlite.get_struct_name(lm_esty) + lm_elem_ty: llvmlite.LLVMType | t.CPtr = None + if lm_ename is not None: + import lib.core.Gen.Generator as Generator + g_le: Generator.Generator | t.CPtr = ( + Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g_le is not None: + lm_elem_ty = g_le._list_elem_type_from_name( + pool, lm_ename) + if lm_elem_ty is None: + lm_elem_ty = i8_ptr_ty # get / __getitem__: 返回 index 处的元素 if string.strcmp(lm_name, "get") == 0 or string.strcmp(lm_name, "__getitem__") == 0: @@ -5987,7 +7285,10 @@ class CallGenMixin: g_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, g_idx_i64, g_esize) g_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, g_data, g_offset) if g_addr is not None: - return llvmlite.build_load(builder, i8_ptr_ty, g_addr) + g_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, g_addr, llvmlite.Ptr(pool, lm_elem_ty)) + if g_ep is not None: + return llvmlite.build_load(builder, lm_elem_ty, g_ep) # set / __setitem__: 设置 index 处的元素 elif string.strcmp(lm_name, "set") == 0 or string.strcmp(lm_name, "__setitem__") == 0: @@ -6010,10 +7311,13 @@ class CallGenMixin: s_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, s_idx_i64, s_esize) s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset) if s_addr is not None: - s_val_ptr: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type( - builder, s_val_val, i8_ptr_ty, s_val_node) - llvmlite.build_store(builder, s_val_ptr, s_addr) - return None + s_val_elem: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type( + builder, s_val_val, lm_elem_ty, s_val_node) + s_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, s_addr, llvmlite.Ptr(pool, lm_elem_ty)) + if s_ep is not None: + llvmlite.build_store(builder, s_val_elem, s_ep) + return None # pop: 返回最后一个元素,count-- elif string.strcmp(lm_name, "pop") == 0: @@ -6033,7 +7337,10 @@ class CallGenMixin: p_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, p_nc, p_esize) p_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, p_data, p_offset) if p_addr is not None: - return llvmlite.build_load(builder, i8_ptr_ty, p_addr) + p_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, p_addr, llvmlite.Ptr(pool, lm_elem_ty)) + if p_ep is not None: + return llvmlite.build_load(builder, lm_elem_ty, p_ep) # clear: count = 0 elif string.strcmp(lm_name, "clear") == 0: @@ -6061,10 +7368,66 @@ class CallGenMixin: cl, "list.append: 找不到 _list 模块 SHA1(依赖模块未编译)", "InternalCompilerError") if ap_sha1 is not None: - ap_mlen: t.CSizeT = string.strlen(ap_sha1) + 13 + # 特化类名提取:list.append 的符号名是 + # {sha1}.list[AST|CPtr].append(带类型参数特化名), + # 而非 {sha1}.list.append。lm_obj 类型即特化结构体 + # Ptr(list[AST|CPtr]),从类型解析特化类名拼接调用名, + # 否则 ld.lld undefined symbol(定义端是特化名)。 + ap_spec_nm: str = None + ap_struct_ty: llvmlite.LLVMType | t.CPtr = \ + HandlesStruct.get_struct_type_from_value(lm_obj) + if ap_struct_ty is not None: + ap_spec_nm = HandlesStruct.get_class_name_by_type( + pool, ap_struct_ty) + if ap_spec_nm is None: + ap_spec_nm = "list" + # 触发泛型特化翻译(幂等,MethodsEmitted 防重入): + # list[AST|CPtr].append 的 define 方法体在特化类翻译时 + # 生成。仅构造调用名不触发特化会导致定义缺失 + # (ld.lld undefined symbol: list[AST|CPtr].append)。 + if trans is not None and ap_spec_nm is not None: + lbkt_ap: str = string.strchr(ap_spec_nm, '[') + if lbkt_ap is not None: + rbkt_ap: str = string.strrchr(ap_spec_nm, ']') + if rbkt_ap is not None and rbkt_ap > lbkt_ap: + base_len_ap: t.CSizeT = t.CSizeT(t.CUInt64T(lbkt_ap) - t.CUInt64T(ap_spec_nm)) + arg_len_ap: t.CSizeT = t.CSizeT(t.CUInt64T(rbkt_ap) - t.CUInt64T(lbkt_ap) - 1) + if base_len_ap > 0 and arg_len_ap > 0: + base_buf_ap: str = pool.alloc(base_len_ap + 1) + arg_buf_ap: str = pool.alloc(arg_len_ap + 1) + if base_buf_ap is not None and arg_buf_ap is not None: + string.strncpy(base_buf_ap, ap_spec_nm, base_len_ap) + base_buf_ap[base_len_ap] = '\0' + string.strncpy(arg_buf_ap, lbkt_ap + 1, arg_len_ap) + arg_buf_ap[arg_len_ap] = '\0' + args_ap: list[str] | t.CPtr = list[str](pool, 8) + if args_ap is not None: + # 逗号分隔多类型实参(list[X] / dict[K, V]) + cur_ap: str = arg_buf_ap + while cur_ap is not None: + comma_ap: str = string.strchr(cur_ap, ',') + if comma_ap is None: + args_ap.append(cur_ap) + break + seg_len_ap: t.CSizeT = t.CSizeT(t.CUInt64T(comma_ap) - t.CUInt64T(cur_ap)) + seg_buf_ap: str = pool.alloc(seg_len_ap + 1) + if seg_buf_ap is not None: + string.strncpy(seg_buf_ap, cur_ap, seg_len_ap) + seg_buf_ap[seg_len_ap] = '\0' + args_ap.append(seg_buf_ap) + cur_ap = comma_ap + 1 + HandlesClassDef._specialize_generic_class( + trans, base_buf_ap, args_ap) + # 调用名 "{sha1}.{spec}.append": + # sha1(16) + '.'(1) + spec + '.append'(7) + NUL(1) = spec + 25。 + # 此前 +8 少 1 字节,snprintf 截断末尾 'd' → + # undefined symbol: ...list[AST|CPtr].appen(缺 d)。 + ap_mlen: t.CSizeT = string.strlen(ap_sha1) + \ + string.strlen(ap_spec_nm) + 26 ap_mangled: str = pool.alloc(ap_mlen) if ap_mangled is not None: - viperlib.snprintf(ap_mangled, ap_mlen, "%s.list.append", ap_sha1) + viperlib.snprintf(ap_mangled, ap_mlen, "%s.%s.append", + ap_sha1, ap_spec_nm) ap_item_node: ast.AST | t.CPtr = cargs.get(0) ap_item_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, ap_item_node, None, 0, trans) @@ -6109,6 +7472,28 @@ class CallGenMixin: gen_ctor_entry = HandlesStruct.find_struct(func_name) if gen_ctor_entry is not None: struct_ty = gen_ctor_entry.Ty + elif at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Attribute: + # 多层模块路径类构造: w32.fileio.File(...)(value 是嵌套 Attribute)。 + # 仅两层(fileio.File)支持时,三层会被跳过 → 退化走跨模块普通函数 + # 调用生成 @File 裸名 → 链接 undefined symbol: File(SysLibTest 根因)。 + # 模块路径经 attr_module_path_str 递归拼接("w32.fileio"), + # import 检查用完整路径(import w32.fileio 注册 "w32.fileio"), + # 结构体查找用该模块自身 SHA1 精确命中(find_struct_by_module)。 + # 注:经 HandlesExpr 委托调用(跨 mixin 不能 self. 直接调, + # @t.NoVTable 静态绑定会 mangle 成 CallGenMixin. 前缀 → undefined reference)。 + mod_path_qc: str = HandlesExpr.attr_module_path_str(pool, at_qc.value) + if mod_path_qc is not None: + imp_ok_qc: int = HandlesImports.is_module_imported( + trans._imported_modules, mod_path_qc) + if imp_ok_qc != 0: + mod_sha1_qc: str = self._lookup_module_sha1(mod_path_qc) + if mod_sha1_qc is not None: + gen_ctor_entry = HandlesStruct.find_struct_by_module( + func_name, mod_sha1_qc) + if gen_ctor_entry is None: + gen_ctor_entry = HandlesStruct.find_struct(func_name) + if gen_ctor_entry is not None: + struct_ty = gen_ctor_entry.Ty # 别名导入的类构造: from X import Class as Alias → Alias(args) # 可见性列表按别名注册(HandleImportFromNames 用 local_name=asname),但结构体 # 按原名注册(如 Keyword as KwNode → 可见 "KwNode"、注册名 "Keyword")。 @@ -6273,7 +7658,47 @@ class CallGenMixin: found_func: llvmlite.Function | t.CPtr = None if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: # 裸名调用:允许精确 + 后缀匹配 - found_func = self.find_func_in_module(mod, func_name) + # 自定义装饰器 wrapper 重写:原函数名 → __decor_wrap_{name} + # (对齐 TPC DecoratorPass.run 的 _redirect_calls;wrapper 在函数 + # 翻译末尾生成,同 pass 内 main 调用点晚于被装饰函数定义,可达) + decor_wrap_nm: str = HandlesClassDef.lookup_decor_wrapper(func_name) + if decor_wrap_nm is not None: + found_func = self.find_func_in_module(mod, decor_wrap_nm) + else: + found_func = self.find_func_in_module(mod, func_name) + # 治本修复(对齐 Attribute 路径 L6579-6613):后缀匹配误命中类方法 + # (SHA1.Class.method,第 2 段是已注册结构体类)必须拒绝——否则方法体内 + # 与 from-import 模块函数同名的裸名调用(如 LLVMModuleCore.SSAValue 内 + # 的 SSAValue(self.Pool, ...))绑定到本类方法 define → 无限递归 + # (栈溢出 0xC0000005)。裸名调用应优先解析为模块级函数 + # (同模块函数表 / from_imports),方法调用需显式 obj.method。 + if found_func is not None: + bn_name: str = llvmlite.function_get_name(found_func) + if bn_name is not None: + bn_dots: int = 0 + bni2: t.CSizeT = 0 + while bn_name[bni2] != '\0': + if bn_name[bni2] == '.': + bn_dots += 1 + bni2 += 1 + if bn_dots >= 2: + # 提取第 2 段(SHA1.Class.method → Class;SHA1.submod.func → submod) + bn_seg2: str = pool.alloc(64) + if bn_seg2 is not None: + bn_seg2[0] = '\0' + bsi2: t.CSizeT = 0 + while bn_name[bsi2] != '\0' and bsi2 < 17: + bsi2 += 1 + bse2: t.CSizeT = bsi2 + while bn_name[bse2] != '\0' and bn_name[bse2] != '.': + bse2 += 1 + bsl2: t.CSizeT = bse2 - bsi2 + if bsl2 > 0 and bsl2 < 63: + string.strncpy(bn_seg2, bn_name + bsi2, bsl2) + bn_seg2[bsl2] = '\0' + # 第 2 段是已注册结构体类 → 是方法,拒绝(走模块 SHA1 路径) + if HandlesStruct.get_struct_type(bn_seg2) is not None: + found_func = None else: # 模块限定/属性调用:精确匹配优先,后缀匹配仅接受模块函数。 # 后缀匹配命中的"方法"(SHA1.Class.method,Class 是已注册类)必须拒绝—— @@ -6284,6 +7709,37 @@ class CallGenMixin: # 模块函数(SHA1.func)或子模块函数(SHA1.submod.func)的后缀匹配保留。 found_func = self.find_func_in_module(mod, func_name) if found_func is not None: + # 模块限定调用(mod.func,value 是已导入模块名):校验 found_func + # 的 SHA1 前缀必须等于该模块。否则 find_func_in_module 第 3 遍对 + # stub declare 的后缀匹配会误命中其他模块的同名函数(如 main 模块 + # 里 subprocess 的 declare 先注入,syslibtest_impl.run() 后缀匹配 + # 误命中 subprocess.run → call 到错误符号 → 参数错位解引用崩溃, + # SysLibTest 0xC0000005 根因)。误命中时拒绝(found_func=None), + # 落到下方跨模块模块限定解析用模块自身 SHA1 构造正确符号。 + xm_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if xm_at is not None and xm_at.value is not None and trans is not None \ + and trans._imported_modules is not None: + # 模块路径:单层 Name(syslibtest_impl.run)或嵌套 Attribute + # (os._win32.exists)。后者此前不校验 → os/__init__.vp 内部 + # 调 os._win32.exists 后缀匹配命中本模块同名 define → + # 无限递归栈溢出(SysLibTest 0xC00000FD 根因)。 + xm_mod_path: str = None + if xm_at.value.kind() == ast.ASTKind.Name: + xm_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(xm_at.value) + if xm_nm.id is not None: + xm_mod_path = xm_nm.id + elif xm_at.value.kind() == ast.ASTKind.Attribute: + xm_mod_path = HandlesExpr.attr_module_path_str( + pool, xm_at.value) + if xm_mod_path is not None: + if HandlesImports.is_module_imported( + trans._imported_modules, xm_mod_path) != 0: + mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path) + if mod_sha1_chk is not None: + ff_nm2: str = llvmlite.function_get_name(found_func) + if ff_nm2 is not None: + if string.strncmp(ff_nm2, mod_sha1_chk, 16) != 0: + found_func = None ff_name: str = llvmlite.function_get_name(found_func) if ff_name is not None: ff_dots: int = 0 @@ -6573,10 +8029,23 @@ class CallGenMixin: # 如 string.strchr(...) → @{sha1_of_string}.strchr if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: xmod_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) - if xmod_at.value is not None and xmod_at.value.kind() == ast.ASTKind.Name: - xmod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(xmod_at.value) - if xmod_nm.id is not None: - mod_sha1: str = self._lookup_module_sha1(xmod_nm.id) + if xmod_at.value is not None and (xmod_at.value.kind() == ast.ASTKind.Name + or xmod_at.value.kind() == ast.ASTKind.Attribute): + # 模块路径:单层 Name(string.strchr)或嵌套 Attribute + # (os._win32.exists)。嵌套路径此前不支持 → os/__init__.vp + # 内部调 os._win32.exists 只能被 find_func_in_module 后缀匹配 + # 命中本模块同名 define(无限递归),或找不到模块路径而走 + # 错误 fallback;此处用完整模块路径解析正确 SHA1(与校验段 + # L7344 对齐,SysLibTest 0xC00000FD 根因)。 + xmod_path: str = None + if xmod_at.value.kind() == ast.ASTKind.Name: + xmod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(xmod_at.value) + if xmod_nm.id is not None: + xmod_path = xmod_nm.id + else: + xmod_path = HandlesExpr.attr_module_path_str(pool, xmod_at.value) + if xmod_path is not None: + mod_sha1: str = self._lookup_module_sha1(xmod_path) if mod_sha1 is not None: # fast-fail: 模块限定调用要求模块已被当前文件 import。 # 全局 SHA1 存储包含所有被扫描的 includes,未导入的模块 @@ -6585,17 +8054,22 @@ class CallGenMixin: is_xmod_imported: int = 1 if trans is not None and trans._imported_modules is not None: is_xmod_imported = HandlesImports.is_module_imported( - trans._imported_modules, xmod_nm.id) + trans._imported_modules, xmod_path) if is_xmod_imported == 0: err_ni: str = pool.alloc(256) if err_ni is not None: viperlib.snprintf(err_ni, 256, "跨模块调用 '%s.%s':模块 '%s' 未导入(请先 import %s)", - xmod_nm.id, func_name, xmod_nm.id, xmod_nm.id) - HandlesType.fatal_error(xmod_nm, err_ni) - HandlesType.fatal_error(xmod_nm, "跨模块调用:模块未导入") + xmod_path, func_name, xmod_path, xmod_path) + HandlesType.fatal_error(xmod_at, err_ni) + HandlesType.fatal_error(xmod_at, "跨模块调用:模块未导入") # 检查是否是 re-export 函数(如 ast._lexer_init 实际定义在 ast.lexer) reexport_src: str = HandlesImports.lookup_reexport(mod_sha1, func_name) + if reexport_src is None: + # re-export 表未命中(includes 模块 stub/text 已存在时 + # 不被按需全量翻译,表只在 declare_only=0 时注册,可能为空): + # 按需解析该模块 .vpi 的 from-import 定位定义模块 + reexport_src = self._lookup_reexport_from_vpi(pool, mod_sha1, func_name) if reexport_src is not None: reexport_sha1: str = self._lookup_module_sha1(reexport_src) if reexport_sha1 is not None: @@ -6603,6 +8077,11 @@ class CallGenMixin: # CExport 函数定义用裸名(@strlen),跨模块调用需用裸名而非 @{sha1}.func if self.is_cexport_func(mod_sha1, func_name) != 0: call_name = func_name + elif self._is_state_func_from_vpi(pool, mod_sha1, func_name) != 0: + # t.State 标记的外部 C 函数(如 CPython PyList_Size): + # 定义端用裸名 @PyList_Size(链接外部库),跨模块调用 + # 必须用裸名——带 sha1 前缀 → ld.lld undefined symbol + call_name = func_name else: mangled_len: t.CSizeT = string.strlen(mod_sha1) + string.strlen(func_name) + 2 mangled: str = pool.alloc(mangled_len) @@ -6611,7 +8090,7 @@ class CallGenMixin: call_name = mangled else: # t 和 c 是内部库,豁免 SHA1 查找:使用裸名调用 - if string.strcmp(xmod_nm.id, "t") == 0 or string.strcmp(xmod_nm.id, "c") == 0: + if string.strcmp(xmod_path, "t") == 0 or string.strcmp(xmod_path, "c") == 0: call_name = func_name else: # Bug 修复: 先检查是否是已导入的模块名(如 BuildPipeline.run_pipeline) @@ -6620,7 +8099,7 @@ class CallGenMixin: is_imported_mod: int = 0 if trans is not None and trans._imported_modules is not None: is_imported_mod = HandlesImports.is_module_imported( - trans._imported_modules, xmod_nm.id) + trans._imported_modules, xmod_path) if is_imported_mod != 0: # 别名导入(import X as Y):Y 在 _imported_modules 中, # 但 _lookup_module_sha1("Y") 精确匹配失败(mod_arr 只存文件名)。 @@ -6628,10 +8107,13 @@ class CallGenMixin: # allow_star_fallback=0 避免被 star import 回退误导。 alias_sha1: str = None if trans is not None and trans._from_imports is not None: - alias_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_nm.id, 0) + alias_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0) if alias_sha1 is not None: if self.is_cexport_func(alias_sha1, func_name) != 0: call_name = func_name + elif self._is_state_func_from_vpi(pool, alias_sha1, func_name) != 0: + # t.State 外部 C 函数:裸名(同主路径 L7918) + call_name = func_name else: AliasMangledLen: t.CSizeT = string.strlen(alias_sha1) + string.strlen(func_name) + 2 alias_mangled: str = pool.alloc(AliasMangledLen) @@ -6642,17 +8124,17 @@ class CallGenMixin: # 已导入模块但 SHA1 未注册:该模块未被编译,立即报错 err_msg_imp: str = pool.alloc(256) if err_msg_imp is not None: - viperlib.snprintf(err_msg_imp, 256, "模块 '%s' 已导入但 SHA1 未注册(该模块未被编译)", xmod_nm.id) - HandlesType.fatal_error(xmod_nm, err_msg_imp) - HandlesType.fatal_error(xmod_nm, "已导入模块 SHA1 未注册") + viperlib.snprintf(err_msg_imp, 256, "模块 '%s' 已导入但 SHA1 未注册(该模块未被编译)", xmod_path) + HandlesType.fatal_error(xmod_at, err_msg_imp) + HandlesType.fatal_error(xmod_at, "已导入模块 SHA1 未注册") else: # 检查是否是通过 from import 导入的名称(如 from .__types import LLVMType) # LLVMType.Ptr(ty) 中 LLVMType 是类名不是模块名, # 但 Ptr 是 __types 模块的模块级函数 - # 禁用 star import 回退:xmod_nm.id 是明确名称,不应回退到 star import 模块 + # 禁用 star import 回退:xmod_path 是明确名称,不应回退到 star import 模块 from_sha1: str = None if trans is not None and trans._from_imports is not None: - from_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_nm.id, 0) + from_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0) if from_sha1 is not None: # CExport 函数定义用裸名,跨模块调用需用裸名 if self.is_cexport_func(from_sha1, func_name) != 0: @@ -6666,9 +8148,9 @@ class CallGenMixin: else: err_msg2: str = pool.alloc(256) if err_msg2 is not None: - viperlib.snprintf(err_msg2, 256, "跨模块调用 '%s.%s' 找不到模块 SHA1", xmod_nm.id, func_name) - HandlesType.fatal_error(xmod_nm, err_msg2) - HandlesType.fatal_error(xmod_nm, "跨模块调用找不到模块 SHA1") + viperlib.snprintf(err_msg2, 256, "跨模块调用 '%s.%s' 找不到模块 SHA1", xmod_path, func_name) + HandlesType.fatal_error(xmod_at, err_msg2) + HandlesType.fatal_error(xmod_at, "跨模块调用找不到模块 SHA1") else: # 裸名调用(非 Attribute):检查 from_imports # 治本修复:函数调用禁用 star import 回退(allow_star_fallback=0)。 @@ -6702,6 +8184,20 @@ class CallGenMixin: # func_name="json_parse", bare_func_name="parse" # _infer_external_func_ret_ty 用 "parse" 能正确匹配,用 "json_parse" 则返回默认 i32 orig_func_name = bare_func_name + # re-export 解析(对齐模块限定路径 L6598): + # 裸名 from import 的函数可能实际定义在子模块(re-export)。 + # 例:from ast import dump → dump 定义在 ast.base(sha1=71b2), + # ast/__init__(sha1=876e)只是 re-export。若沿用包 sha1 构造 + # call_name="876e.dump",text.ll/vpi 返回类型解析均未命中, + # fail-fast 报"返回类型无法精确获取";需重定向到定义模块 SHA1。 + reexport_src2: str = HandlesImports.lookup_reexport(bare_sha1, bare_func_name) + if reexport_src2 is None: + # re-export 表未命中:按需解析该模块 .vpi 的 from-import + reexport_src2 = self._lookup_reexport_from_vpi(pool, bare_sha1, bare_func_name) + if reexport_src2 is not None: + reexport_sha1_2: str = self._lookup_module_sha1(reexport_src2) + if reexport_sha1_2 is not None: + bare_sha1 = reexport_sha1_2 if self.is_cexport_func(bare_sha1, bare_func_name) != 0: call_name = bare_func_name else: @@ -6811,7 +8307,8 @@ class CallGenMixin: cf_dbg, 1024, "跨模块函数 '%s' 依赖模块 text.ll 尚未生成(Phase A 顺序)," "标记重试,本遍保持默认类型", call_name) - VLogger.warning(cf_dbg, "HandlesExprCall") + if VLogger.nowarn_register() == 0: + VLogger.warning(cf_dbg, "HandlesExprCall") stdlib.free(cf_sha1) stdlib.free(cf_bare) # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value @@ -6820,7 +8317,157 @@ class CallGenMixin: # (cargs 为本函数参数 AST 链表,self_offset=0 普通函数调用) is_variadic_call: int = HandlesExprCall._check_and_promote_variadic_call( pool, builder, mod, call_name, head, can, cargs, 0, trans) - return llvmlite.build_call(builder, call_name, head, can, call_ret_ty, is_variadic_call) + # try-except:被调函数带 EH 输出参数时,追加输出实参 + 调用后检测异常 + return self._emit_eh_guarded_call( + builder, pool, mod, trans, found_func, call_name, head, can, + call_ret_ty, is_variadic_call) + + # ============================================================ + # _func_has_eh_params - 被调函数签名是否含 try-except EH 输出参数 + # + # EH 参数(%__eh_msg_out__/__eh_code_out__)由 translate_function_def/ + # _translate_method 在含 raise 的函数签名末尾注入(main 除外)。 + # ============================================================ + def _func_has_eh_params(self, func: llvmlite.Function | t.CPtr) -> int: + """返回 1=函数带 EH 输出参数 / 0=不带""" + if func is None: + return 0 + cur_p: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func) + while cur_p is not None: + pn: str = llvmlite.param_get_name(cur_p) + if pn is not None and string.strcmp(pn, "%__eh_msg_out__") == 0: + return 1 + cur_p = llvmlite.param_get_next(cur_p) + return 0 + + # ============================================================ + # _emit_eh_guarded_call - 调用侧 try-except EH 检测(对齐 TPC + # HandlesExprCall._append_eh_msg_out_arg + _check_eh_return) + # + # 被调函数带 EH 输出参数时: + # 1. 确定 msg/code 输出指针: + # - 当前在 try(trans._eh_dispatch_bb 非空):复用 try 的 + # eh_message/exception_code alloca——被调函数直接写入,检查 + # 路径读同一 alloca 即得异常,无需搬运 + # - 当前函数有 EH 输出参数:复用本函数 out 参数(异常直接写进 + # 本函数输出,检查命中后仅返回,由调用方接管传播) + # - 两者皆无:局部 null alloca(异常被吞) + # 2. 调用前清零输出指针(复用 alloca 必须复位,防上次残留误判) + # 3. 追加 msg/code 两个实参(count+2) + # 4. 调用后检查 msg != null:命中 → err 块(try 内分支 DispatchBB; + # 否则仅返回默认值);未命中 → ok 块继续 + # ============================================================ + def _emit_eh_guarded_call(self, builder: llvmlite.IRBuilder | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + trans: HT.Translator | t.CPtr, + func: llvmlite.Function | t.CPtr, + call_name: str, + head: llvmlite.Value | t.CPtr, + count: int, + ret_ty: llvmlite.LLVMType | t.CPtr, + is_variadic: int) -> llvmlite.Value | t.CPtr: + """带 try-except EH 检测的函数调用,返回 call 结果(检查后位于 ok 块)""" + if trans is None or self._func_has_eh_params(func) == 0: + return llvmlite.build_call(builder, call_name, head, count, ret_ty, is_variadic) + # ---- 1. 确定输出指针 ---- + i8_ty_g: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_g: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_g) + i32_ty_g: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + if i8_ty_g is None or i8_ptr_ty_g is None or i32_ty_g is None: + return llvmlite.build_call(builder, call_name, head, count, ret_ty, is_variadic) + msg_ptr: llvmlite.Value | t.CPtr = None + code_ptr: llvmlite.Value | t.CPtr = None + if trans._eh_msg_ptr is not None and trans._eh_code_ptr is not None: + msg_ptr = trans._eh_msg_ptr + code_ptr = trans._eh_code_ptr + elif trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \ + and trans._eh_code_out_param is not None: + msg_ptr = trans._eh_msg_out_param + code_ptr = trans._eh_code_out_param + if msg_ptr is None: + msg_ptr = llvmlite.build_alloca(builder, i8_ptr_ty_g) + code_ptr = llvmlite.build_alloca(builder, i32_ty_g) + # ---- 2. 调用前清零 ---- + null_msg_g: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty_g, "null") + zero_code_g: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) + if null_msg_g is not None: + llvmlite.build_store(builder, null_msg_g, msg_ptr) + if zero_code_g is not None: + llvmlite.build_store(builder, zero_code_g, code_ptr) + # ---- 3. 追加实参(链表末尾 + count+2)---- + msg_ptr.Next = code_ptr + code_ptr.Next = None + if head is None: + head = msg_ptr + else: + tail_g: llvmlite.Value | t.CPtr = head + while tail_g.Next is not None: + tail_g = tail_g.Next + tail_g.Next = msg_ptr + count = count + 2 + # ---- 4. 调用 + 检查 ---- + result: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, call_name, head, count, ret_ty, is_variadic) + cur_func_g: llvmlite.Function | t.CPtr = trans._cur_func + if cur_func_g is None or null_msg_g is None: + return result + msg_val_g: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i8_ptr_ty_g, msg_ptr) + if msg_val_g is None: + return result + is_err_g: llvmlite.Value | t.CPtr = llvmlite.build_icmp( + builder, llvmlite.ICMP_NE, msg_val_g, null_msg_g) + if is_err_g is None: + return result + lc_g: int = trans._label_counter + trans._label_counter = lc_g + 1 + bb_buf_g: str = pool.alloc(64) + if bb_buf_g is None: + return result + viperlib.snprintf(bb_buf_g, 64, "eh.err.%d", lc_g) + err_bb_g: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, cur_func_g, bb_buf_g) + viperlib.snprintf(bb_buf_g, 64, "eh.ok.%d", lc_g) + ok_bb_g: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, cur_func_g, bb_buf_g) + if err_bb_g is None or ok_bb_g is None: + return result + llvmlite.build_cond_br(builder, is_err_g, err_bb_g, ok_bb_g) + # err 块 + llvmlite.position_at_end(builder, err_bb_g) + if trans._eh_dispatch_bb is not None: + # try 内:异常已写入 try 的 alloca(code_ptr 即 try 的 alloca), + # 分支 DispatchBB 交给 except 链 + llvmlite.build_br(builder, trans._eh_dispatch_bb) + else: + # 非 try:异常已写入本函数 out 参数(复用)或局部 alloca(吞掉), + # 仅返回默认值,由调用方检测本函数 out 参数 + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + ret_ty_g2: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty( + cur_func_g) + if ret_ty_g2 is not None: + match ret_ty_g2: + case llvmlite.LLVMType.Void(): + llvmlite.build_ret_void(builder) + case llvmlite.LLVMType.Ptr(_pg): + nv_g: llvmlite.Value | t.CPtr = llvmlite.ConstNull( + pool, ret_ty_g2, "null") + if nv_g is not None: + llvmlite.build_ret(builder, nv_g) + case llvmlite.LLVMType.Float(_fg): + zf_g: llvmlite.Value | t.CPtr = llvmlite.ConstFloat( + pool, ret_ty_g2, 0.0) + if zf_g is not None: + llvmlite.build_ret(builder, zf_g) + case _: + zv_g: llvmlite.Value | t.CPtr = llvmlite.ConstZero( + pool, ret_ty_g2) + if zv_g is not None: + llvmlite.build_ret(builder, zv_g) + # ok 块:继续 + llvmlite.position_at_end(builder, ok_bb_g) + return result # ============================================================ # isinstance - 运行时 vtable 子类集合比较(2026-08-10) @@ -6833,9 +8480,6 @@ class CallGenMixin: # NoVTable / 未知类回退编译时静态判断(obj 静态类型 MRO 判断)。 # ============================================================ - # isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32) - ISINSTANCE_CLASS_MAX: t.CDefine = 64 - def _translate_isinstance(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -7438,8 +9082,6 @@ class CallGenMixin: builder, pool, mod, arg, None, 0, trans) if arg_val is None or arg_val.Ty is None: # fail-fast:print 参数翻译失败必须终止编译,绝不在格式串写 - # "(null)" 兜底。静默输出 "(null)" 会掩盖表达式翻译失败 - # (变量未定义/类型退化等),运行期 printf 打出 "(null)" 会 # 误导为数据问题而非编译器 bug。 fb_pp: str = VLogger.fmt_buf() if fb_pp is not None: @@ -8089,6 +9731,18 @@ class CallGenMixin: if scopy is not None: string.strcpy(scopy, dcn.str_val) strs[di] = t.CSizeT(scopy) + elif dnode is not None and dnode.kind() == ast.ASTKind.Name: + # CDefine Name 引用默认值(如 share: ULONG = SHARE_READ): + # 注册时求值 CDefine 存快照——否则跨模块重建时快照为空 + # → "跨模块默认参数无法重建"(FileIoTest File.__init__ 根因)。 + # 仅支持已注册的整型 CDefine;未注册保持 kinds=0(重建时 + # 仍 fail-fast,绝不静默回退)。 + dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(dnode) + if dnm.id is not None: + cdef_v: int = HandlesType.lookup_cdefine_constant(dnm.id) + if HandlesType.is_cdefine_found() != 0: + kinds[di] = ast.CONST_INT + ints[di] = cdef_v entry.ConstKinds = kinds entry.ConstInts = ints entry.ConstStrs = strs diff --git a/App/lib/core/Gen/ClassGenMixin.vp b/App/lib/core/Gen/ClassGenMixin.vp index b899896..3488a8e 100644 --- a/App/lib/core/Gen/ClassGenMixin.vp +++ b/App/lib/core/Gen/ClassGenMixin.vp @@ -45,6 +45,10 @@ RENUM_CLASS_MAX: t.CDefine = 64 SCOPE_FUNCTION: t.CDefine = 1 RENUM_VARIANT_PER_CLASS: t.CDefine = 32 RENUM_FIELD_PER_VARIANT: t.CDefine = 16 +# 变体字段别名表上限(与 HandlesClassDef.RENUM_ALIAS_MAX 保持一致; +# 本模块 _register_renum_field_alias 用到,必须在此定义——TPV 不跨模块 +# 解析裸常量名,函数体首次被类型检查时 NameError) +RENUM_ALIAS_MAX: t.CDefine = 512 # 方法元数据位掩码(与 HandlesClassDef.META_* 一致) META_STATIC: t.CDefine = 1 @@ -52,10 +56,31 @@ META_CLASS: t.CDefine = 2 META_PROP_GETTER: t.CDefine = 4 META_PROP_SETTER: t.CDefine = 8 META_PROP_DELETER: t.CDefine = 16 +META_TLS: t.CDefine = 32 META_MAX: t.CDefine = 512 META_KEY_LEN: t.CDefine = 128 +# 类编译期元数据表容量上限(__provides__/__requires__/__require_must__) +CLASS_META_MAX: t.CDefine = 256 + +# 自定义装饰器 wrapper 表容量上限(带 @decor 的函数数) +DECOR_FUNC_MAX: t.CDefine = 64 +# 装饰器表槽内函数名/wrapper 名长度(含 NUL) +DECOR_NAME_LEN: t.CDefine = 128 + +# 异常类注册表容量上限(class X(Exception) 自定义异常类) +EXCEPTION_MAX: t.CDefine = 64 +# 异常类表槽内类名/父类名长度(含 NUL) +EXCEPTION_NAME_LEN: t.CDefine = 128 +# 内置异常编码起始(对齐 TPC HandlesBase.py EXCEPTION_CODE_MAP) +# ValueError=1 TypeError=2 RuntimeError=3 ZeroDivisionError=4 IndexError=5 +# KeyError=6 IOError=7 OSError=8 AssertionError=9 Exception=99;用户类从 100 起 +BUILTIN_EH_VALUE_ERROR: t.CDefine = 1 +BUILTIN_EH_RUNTIME_ERROR: t.CDefine = 3 +BUILTIN_EH_EXCEPTION: t.CDefine = 99 +EH_USER_CODE_START: t.CDefine = 100 + # 泛型特化缓存容量上限(与 HandlesClassDef.SPEC_CACHE_MAX 一致) SPEC_CACHE_MAX: t.CDefine = 512 @@ -96,6 +121,14 @@ class ClassGenMixin: RenumAliasClass: t.CSizeT | t.CPtr RenumAliasField: t.CSizeT | t.CPtr RenumAliasIndex: t.CSizeT | t.CPtr + # 变体成员实际 LLVM 类型(对齐 TPC class_members):注册时按注解解析, + # perm_pool 深拷贝持久化。match 绑定用真实成员类型 bitcast 槽位地址 + # (Struct.Name: i8*,否则按槽位 i64 读 → is/subscript/strlen 失败)。 + RenumAliasTy: t.CSizeT | t.CPtr + # 别名所属变体名(perm_pool 拷贝):同一 payload 位置跨变体类型不同 + # (Int.Bits=i32 vs Struct.Fields=Ptr),match 绑定按 (class, 变体, 位置) + # 唯一定位成员类型。 + RenumAliasVariant: t.CSizeT | t.CPtr RenumAliasCount: int # --- 泛型特化缓存(原 _spec_keys/_spec_names/_spec_count) --- @@ -103,6 +136,40 @@ class ClassGenMixin: SpecNames: t.CSizeT | t.CPtr SpecCount: int + # --- 类编译期元数据表(__provides__/__requires__/__require_must__) --- + # perm_pool 分配(跨 mark/reset 存活):每槽 kind(4B) + cls(64B) + field(64B) + # FakeDuck:_str.__requires__=['__mbuddy__'] 等(FakeDuck 移植点 1) + ClassMetaKinds: t.CInt | t.CPtr + ClassMetaClss: bytes + ClassMetaFields: bytes + ClassMetaCount: int + + # --- 自定义装饰器 wrapper 表(@log/@skip/@repeat 等 → __decor_wrap_{name}) --- + # perm_pool 分配(跨 mark/reset 存活):每槽函数名(128B) + wrapper 名(128B) + # 对齐 TPC HandlesFunctions.py L1621-1631 的 _decorated_funcs + + # DecoratorPass.py 的 wrapper 生成。调用点重写经 + # lookup_decor_wrapper(translate_call 拦截原函数名 → wrapper 名)。 + DecoratorFuncCount: int + DecoratorFuncNames: bytes + DecoratorWrapNames: bytes + # 装饰器首个常量参数(@repeat(3) 的 3),无参数为 0(每槽 8B,i64) + DecoratorArgs: t.CInt64T | t.CPtr + + # --- 异常类注册表(try-except,对齐 TPC HandlesClassDef.py L47-61) --- + # perm_pool 分配(跨 mark/reset 存活):每槽类名(128B) + 父类名(128B) + 编码(4B) + # 用户异常类编码从 100 递增;内置编码(ValueError=1 等)不在此表 + # (_lookup_exception_code 走内置硬编码)。异常类不生成 struct。 + ExceptionCount: int + ExceptionNames: bytes + ExceptionParents: bytes + ExceptionCodes: t.CInt | t.CPtr + # 下一个待分配的异常编码(用户类,从 100 起) + NextExceptionCode: int + # 递归收集子孙编码用的临时计数(collect_descendant_codes 返回后无效)。 + # 必须与其它字段一起声明在类顶:方法中间的字段注解可能被编译器 + # 当作局部声明处理,破坏 Mixin 布局/this_adjust 对齐。 + _exception_codes_tmp_count: int + # --- 泛型特化递归深度(泛型循环套用防护) --- # _specialize_generic_class 进入时 +1、退出时 -1;超过上限立即报错终止 # (防止 F-bounded 等循环特化把编译变成死循环) @@ -356,6 +423,8 @@ class ClassGenMixin: meta |= META_PROP_GETTER elif string.strcmp(nm.id, "classmethod") == 0: meta |= META_CLASS + elif string.strcmp(nm.id, "TLS") == 0: + meta |= META_TLS elif k == ast.ASTKind.Attribute: at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(deco) if at.attr is not None and at.value is not None: @@ -364,6 +433,11 @@ class ClassGenMixin: is_prop_name: int = 0 if vn.id is not None and string.strcmp(vn.id, "property") == 0: is_prop_name = 1 + # @t.TLS 一次性初始化标记(对齐 TPC HandlesFunctions.py + # L1223-1231:value 必须为 Name('t');@TLS 裸名走上面 Name 分支) + if vn.id is not None and string.strcmp(vn.id, "t") == 0 \ + and string.strcmp(at.attr, "TLS") == 0: + meta |= META_TLS # @property.setter 或 @x.setter/getter/deleter 都识别 if is_prop_name != 0 or string.strcmp(at.attr, "setter") == 0 \ or string.strcmp(at.attr, "getter") == 0 or string.strcmp(at.attr, "deleter") == 0: @@ -375,6 +449,336 @@ class ClassGenMixin: meta |= META_PROP_DELETER return meta + def register_class_meta(self, pool: memhub.MemBuddy | t.CPtr, + kind: int, cls: str, field: str) -> int: + """注册类编译期元数据字段(__provides__/__requires__/__require_must__,幂等)""" + if pool is None or cls is None or field is None: + return -1 + if self.ClassMetaKinds is None: + self.ClassMetaKinds = CompilerFactory.get_perm_pool().alloc(CLASS_META_MAX * 4) + self.ClassMetaClss = CompilerFactory.get_perm_pool().alloc(CLASS_META_MAX * 64) + self.ClassMetaFields = CompilerFactory.get_perm_pool().alloc(CLASS_META_MAX * 64) + if self.ClassMetaKinds is None or self.ClassMetaClss is None or self.ClassMetaFields is None: + return -1 + string.memset(self.ClassMetaKinds, 0, CLASS_META_MAX * 4) + string.memset(self.ClassMetaClss, 0, CLASS_META_MAX * 64) + string.memset(self.ClassMetaFields, 0, CLASS_META_MAX * 64) + self.ClassMetaCount = 0 + if self.ClassMetaCount >= CLASS_META_MAX: + return -1 + # 幂等:同 (kind, cls, field) 跳过 + for i in range(self.ClassMetaCount): + if self.ClassMetaKinds[i] == kind: + if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0: + if string.strcmp(self.ClassMetaFields + i * 64, field) == 0: + return 0 + idx_cm: t.CSizeT = t.CSizeT(self.ClassMetaCount) * 64 + self.ClassMetaKinds[self.ClassMetaCount] = kind + string.strncpy(self.ClassMetaClss + idx_cm, cls, 63) + self.ClassMetaClss[idx_cm + 63] = '\0' + string.strncpy(self.ClassMetaFields + idx_cm, field, 63) + self.ClassMetaFields[idx_cm + 63] = '\0' + self.ClassMetaCount += 1 + return 0 + + def class_meta_count(self, kind: int, cls: str) -> int: + """返回某类某元数据类型的字段数""" + if self.ClassMetaKinds is None or cls is None: + return 0 + n_cm: int = 0 + for i in range(self.ClassMetaCount): + if self.ClassMetaKinds[i] == kind: + if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0: + n_cm += 1 + return n_cm + + def class_meta_field_at(self, kind: int, cls: str, idx: int) -> str: + """返回某类某元数据类型的第 idx 个字段名""" + if self.ClassMetaKinds is None or cls is None: + return None + n_cm2: int = 0 + for i in range(self.ClassMetaCount): + if self.ClassMetaKinds[i] == kind: + if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0: + if n_cm2 == idx: + return self.ClassMetaFields + i * 64 + n_cm2 += 1 + return None + + def register_decorator(self, pool: memhub.MemBuddy | t.CPtr, + func_name: str, wrapper_name: str, + arg0: t.CInt64T) -> int: + """注册自定义装饰器函数(@log/@skip/@repeat(3) → __decor_wrap_{name}) + + 对齐 TPC HandlesFunctions.py L1621-1631 的 _decorated_funcs 注册。 + arg0 为装饰器首个常量参数(@repeat(3) 的 3),无参数为 0。 + """ + if pool is None or func_name is None or wrapper_name is None: + return -1 + if self.DecoratorFuncNames is None: + self.DecoratorFuncNames = CompilerFactory.get_perm_pool().alloc( + DECOR_FUNC_MAX * DECOR_NAME_LEN) + self.DecoratorWrapNames = CompilerFactory.get_perm_pool().alloc( + DECOR_FUNC_MAX * DECOR_NAME_LEN) + self.DecoratorArgs = CompilerFactory.get_perm_pool().alloc( + DECOR_FUNC_MAX * 8) + if self.DecoratorFuncNames is None or self.DecoratorWrapNames is None \ + or self.DecoratorArgs is None: + return -1 + string.memset(self.DecoratorFuncNames, 0, DECOR_FUNC_MAX * DECOR_NAME_LEN) + string.memset(self.DecoratorWrapNames, 0, DECOR_FUNC_MAX * DECOR_NAME_LEN) + string.memset(self.DecoratorArgs, 0, DECOR_FUNC_MAX * 8) + self.DecoratorFuncCount = 0 + if self.DecoratorFuncCount >= DECOR_FUNC_MAX: + return -1 + # 幂等:同名函数已注册则跳过(多遍扫描) + for i in range(self.DecoratorFuncCount): + if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: + return 0 + idx_dc: t.CSizeT = t.CSizeT(self.DecoratorFuncCount) * DECOR_NAME_LEN + string.strncpy(self.DecoratorFuncNames + idx_dc, func_name, DECOR_NAME_LEN - 1) + self.DecoratorFuncNames[idx_dc + DECOR_NAME_LEN - 1] = '\0' + string.strncpy(self.DecoratorWrapNames + idx_dc, wrapper_name, DECOR_NAME_LEN - 1) + self.DecoratorWrapNames[idx_dc + DECOR_NAME_LEN - 1] = '\0' + self.DecoratorArgs[self.DecoratorFuncCount] = arg0 + self.DecoratorFuncCount += 1 + return 0 + + def lookup_decor_wrapper(self, func_name: str) -> str: + """按原函数名查 wrapper 名,未注册返回 None""" + if self.DecoratorFuncNames is None or func_name is None: + return None + for i in range(self.DecoratorFuncCount): + if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: + return self.DecoratorWrapNames + i * DECOR_NAME_LEN + return None + + def lookup_decor_arg0(self, func_name: str) -> t.CInt64T: + """按原函数名查装饰器首个常量参数(无参数为 0)""" + if self.DecoratorFuncNames is None or func_name is None: + return 0 + for i in range(self.DecoratorFuncCount): + if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: + return self.DecoratorArgs[i] + return 0 + + # ============================================================ + # 异常类注册表(try-except,对齐 TPC HandlesClassDef.py L47-61) + # ============================================================ + def _ensure_exception_tables(self) -> int: + """懒初始化异常表(perm_pool),返回 0 成功""" + if self.ExceptionNames is not None: + return 0 + pp_e: memhub.MemBuddy | t.CPtr = CompilerFactory.get_perm_pool() + if pp_e is None: + return -1 + self.ExceptionNames = pp_e.alloc(EXCEPTION_MAX * EXCEPTION_NAME_LEN) + self.ExceptionParents = pp_e.alloc(EXCEPTION_MAX * EXCEPTION_NAME_LEN) + self.ExceptionCodes = pp_e.alloc(EXCEPTION_MAX * 4) + if self.ExceptionNames is None or self.ExceptionParents is None \ + or self.ExceptionCodes is None: + return -1 + string.memset(self.ExceptionNames, 0, EXCEPTION_MAX * EXCEPTION_NAME_LEN) + string.memset(self.ExceptionParents, 0, EXCEPTION_MAX * EXCEPTION_NAME_LEN) + string.memset(self.ExceptionCodes, 0, EXCEPTION_MAX * 4) + self.ExceptionCount = 0 + self.NextExceptionCode = EH_USER_CODE_START + return 0 + + def register_exception(self, class_name: str, parent_name: str) -> int: + """注册用户异常类并分配编码(幂等:同名跳过)。返回编码或 -1""" + if class_name is None: + return -1 + if self._ensure_exception_tables() != 0: + return -1 + # 幂等:已注册则返回既有编码(多遍扫描) + for i in range(self.ExceptionCount): + if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, class_name) == 0: + return self.ExceptionCodes[i] + if self.ExceptionCount >= EXCEPTION_MAX: + return -1 + idx_e: t.CSizeT = t.CSizeT(self.ExceptionCount) * EXCEPTION_NAME_LEN + string.strncpy(self.ExceptionNames + idx_e, class_name, EXCEPTION_NAME_LEN - 1) + self.ExceptionNames[idx_e + EXCEPTION_NAME_LEN - 1] = '\0' + if parent_name is not None: + string.strncpy(self.ExceptionParents + idx_e, parent_name, EXCEPTION_NAME_LEN - 1) + self.ExceptionParents[idx_e + EXCEPTION_NAME_LEN - 1] = '\0' + code_e: int = self.NextExceptionCode + self.NextExceptionCode += 1 + self.ExceptionCodes[self.ExceptionCount] = code_e + self.ExceptionCount += 1 + return code_e + + def _builtin_exception_code(self, name: str) -> int: + """内置异常编码(对齐 TPC HandlesBase.py EXCEPTION_CODE_MAP),非内置返回 0""" + if name is None: + return 0 + if string.strcmp(name, "ValueError") == 0: return 1 + if string.strcmp(name, "TypeError") == 0: return 2 + if string.strcmp(name, "RuntimeError") == 0: return 3 + if string.strcmp(name, "ZeroDivisionError") == 0: return 4 + if string.strcmp(name, "IndexError") == 0: return 5 + if string.strcmp(name, "KeyError") == 0: return 6 + if string.strcmp(name, "IOError") == 0: return 7 + if string.strcmp(name, "OSError") == 0: return 8 + if string.strcmp(name, "AssertionError") == 0: return 9 + if string.strcmp(name, "Exception") == 0: return 99 + return 0 + + def lookup_exception_code(self, name: str) -> int: + """查异常编码:内置表优先,其次用户异常表,都未命中返回 0""" + if name is None: + return 0 + bi: int = self._builtin_exception_code(name) + if bi != 0: + return bi + if self.ExceptionNames is None: + return 0 + for i in range(self.ExceptionCount): + if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: + return self.ExceptionCodes[i] + return 0 + + def lookup_exception_parent(self, name: str) -> str: + """查用户异常类的父类名,非用户异常返回 None""" + if name is None or self.ExceptionNames is None: + return None + for i in range(self.ExceptionCount): + if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: + p_e: str = self.ExceptionParents + i * EXCEPTION_NAME_LEN + if p_e is not None and p_e[0] != '\0': + return p_e + return None + return None + + def is_exception_name(self, name: str) -> int: + """name 是否是内置或用户异常类名(1=是 / 0=否)""" + if name is None: + return 0 + if self._builtin_exception_code(name) != 0: + return 1 + if self.ExceptionNames is None: + return 0 + for i in range(self.ExceptionCount): + if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: + return 1 + return 0 + + def collect_descendant_codes(self, name: str, out_codes: t.CInt | t.CPtr, + max_codes: int) -> int: + """把 name 及其所有子孙异常类的编码收集到 out_codes,返回数量 + (对齐 TPC HandlesTry.py _get_all_match_codes/_collect_descendant_codes: + 编译期静态展开继承树,except X 匹配 X 及全部子孙)。""" + if name is None or out_codes is None: + return 0 + self._exception_codes_tmp_count = 0 + self._collect_desc_codes_rec(name, out_codes, max_codes, 0) + return self._exception_codes_tmp_count + + def _collect_desc_codes_rec(self, name: str, out_codes: t.CInt | t.CPtr, + max_codes: int, depth: int) -> None: + """递归收集 name 编码 + 全部子孙编码(BFS 语义对齐 TPC)""" + if depth > 32: + return + code_e: int = self.lookup_exception_code(name) + if code_e != 0 and self._exception_codes_tmp_count < max_codes: + # 去重(继承环防护) + dup: int = 0 + for i in range(self._exception_codes_tmp_count): + if out_codes[i] == code_e: + dup = 1 + break + if dup == 0: + out_codes[self._exception_codes_tmp_count] = code_e + self._exception_codes_tmp_count += 1 + # 遍历全部用户异常,找以 name 为父类的子类递归收集 + if self.ExceptionNames is None: + return + for i in range(self.ExceptionCount): + cname_e: str = self.ExceptionNames + i * EXCEPTION_NAME_LEN + pname_e: str = self.ExceptionParents + i * EXCEPTION_NAME_LEN + if pname_e is not None and pname_e[0] != '\0': + if string.strcmp(pname_e, name) == 0: + self._collect_desc_codes_rec(cname_e, out_codes, max_codes, depth + 1) + + def _is_exception_class_def(self, cd: ast.ClassDef | t.CPtr) -> int: + """类定义是否是异常类:基类是 Exception 或已注册异常类(1=是)""" + if cd is None or cd.bases is None: + return 0 + bases_e: list[ast.AST | t.CPtr] | t.CPtr = cd.bases + bn_e: t.CSizeT = len(bases_e) + for bi_e in range(bn_e): + base_e: ast.AST | t.CPtr = bases_e.get(bi_e) + if base_e is None: + continue + base_name_e: str = None + if base_e.kind() == ast.ASTKind.Name: + base_name_e = ((ast.Name | t.CPtr)(base_e)).id + elif base_e.kind() == ast.ASTKind.Attribute: + base_at_e: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(base_e) + if base_at_e.attr is not None: + base_name_e = base_at_e.attr + if base_name_e is None: + continue + if string.strcmp(base_name_e, "Exception") == 0: + return 1 + if self.is_exception_name(base_name_e) != 0: + return 1 + return 0 + + def _get_exception_parent_name(self, cd: ast.ClassDef | t.CPtr) -> str: + """返回异常类定义的真实父类名(第一个非 Exception 的已注册异常类),无则 None""" + if cd is None or cd.bases is None: + return None + bases_e2: list[ast.AST | t.CPtr] | t.CPtr = cd.bases + bn_e2: t.CSizeT = len(bases_e2) + for bi_e2 in range(bn_e2): + base_e2: ast.AST | t.CPtr = bases_e2.get(bi_e2) + if base_e2 is None: + continue + base_name_e2: str = None + if base_e2.kind() == ast.ASTKind.Name: + base_name_e2 = ((ast.Name | t.CPtr)(base_e2)).id + elif base_e2.kind() == ast.ASTKind.Attribute: + base_at_e2: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(base_e2) + if base_at_e2.attr is not None: + base_name_e2 = base_at_e2.attr + if base_name_e2 is None: + continue + if string.strcmp(base_name_e2, "Exception") != 0 \ + and self.is_exception_name(base_name_e2) != 0: + return base_name_e2 + return None + + def _register_exception_class_def(self, trans: HT.Translator | t.CPtr, + cd: ast.ClassDef | t.CPtr) -> int: + """注册异常类(分配编码 + 记录父类),不生成 struct,返回 0""" + if cd is None or cd.name is None: + return 0 + parent_e: str = self._get_exception_parent_name(cd) + self.register_exception(cd.name, parent_e) + return 0 + + def _register_class_meta_list(self, pool: memhub.MemBuddy | t.CPtr, + kind: int, cls: str, value_node: ast.AST | t.CPtr) -> None: + """解析类体 __provides__/__requires__ 的 list[str] 字面量并注册""" + if value_node is None or cls is None: + return + if value_node.kind() != ast.ASTKind.List: + return + lst_rcm: ast.List | t.CPtr = (ast.List | t.CPtr)(value_node) + if lst_rcm.elts is None: + return + elts_rcm: list[ast.AST | t.CPtr] | t.CPtr = lst_rcm.elts + en_rcm: t.CSizeT = len(elts_rcm) + for ei_rcm in range(en_rcm): + el_rcm: ast.AST | t.CPtr = elts_rcm.get(ei_rcm) + if el_rcm is None or el_rcm.kind() != ast.ASTKind.Constant: + continue + ec_rcm: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(el_rcm) + if ec_rcm.str_val is not None: + self.register_class_meta(pool, kind, cls, ec_rcm.str_val) + def register_method_meta(self, class_name: str, method_name: str, meta: int) -> None: """注册方法元数据(幂等)""" @@ -871,9 +1275,10 @@ class ClassGenMixin: pool: memhub.MemBuddy | t.CPtr = trans.Pool class_name: str = cd.name - # 已注册则跳过 - if HandlesEnum.is_enum_class(class_name) == 1: - return 0 + # 不按全局同名跳过:跨模块可能同名枚举(includes/w32/fileio.vp 的 + # FRESULT 与 fat32_types.vp 的 FRESULT),跳过会导致后注册模块的 + # 成员缺失(FR_OK 找不到)。register_enum 幂等返回已有 entry, + # add_enum_member 成员名去重,重复处理安全合并。 children: list[ast.AST | t.CPtr] | t.CPtr = cd.children if children is None: @@ -1148,12 +1553,12 @@ class ClassGenMixin: cn: t.CSizeT = len(children) # 临时数组存储字段信息 - field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 64) - field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 64) + field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * HandlesStruct.FIELD_MAX) + field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * HandlesStruct.FIELD_MAX) if field_names_buf is None or field_types_buf is None: return 0 - string.memset(field_names_buf, 0, 8 * 64) - string.memset(field_types_buf, 0, 8 * 64) + string.memset(field_names_buf, 0, 8 * HandlesStruct.FIELD_MAX) + string.memset(field_types_buf, 0, 8 * HandlesStruct.FIELD_MAX) field_count: int = 0 max_size: int = 0 @@ -1198,7 +1603,7 @@ class ClassGenMixin: max_size = fsize # 存储到数组 - if field_count < 64: + if field_count < HandlesStruct.FIELD_MAX: field_names_buf[field_count] = t.CSizeT(tgt.id) field_types_buf[field_count] = t.CSizeT(field_ty) field_count += 1 @@ -1427,8 +1832,21 @@ class ClassGenMixin: offset: t.CSizeT = variant_count * 16 + field_idx variant_field_sizes[offset] = t.CSizeT(fsize) # 直接注册变体字段名别名(避免 CSizeT→str 转换问题) + # 同时解析成员实际类型(对齐 TPC class_members,供 match + # 绑定 bitcast 槽位地址用)。自引用注解(如 + # LLVMType|t.CPtr 引用自身类)此时解析失败返回 None → + # 不存类型,绑定回退槽位类型。variant_cd.name 在 1a reset + # 后悬空,注册内部会 strcpy 到 perm_pool。 if vnm is not None and vnm.id is not None: - self._register_renum_field_alias(pool, class_name, vnm.id, field_idx + 1) + mem_ty_l: llvmlite.LLVMType | t.CPtr = None + if trans is not None: + mem_ty_l = HandlesType.resolve_annotation_type( + pool, vaa.annotation, + trans._imported_modules, + trans._from_imports, trans) + self._register_renum_field_alias( + pool, class_name, vnm.id, field_idx + 1, + mem_ty_l, variant_cd.name) field_idx += 1 if field_idx > max_field_count: max_field_count = field_idx @@ -1706,8 +2124,15 @@ class ClassGenMixin: def _register_renum_field_alias(self, pool: memhub.MemBuddy | t.CPtr, class_name: str, field_name: str, - payload_idx: int): - """注册 REnum 变体字段名别名到全局表""" + payload_idx: int, + field_ty: llvmlite.LLVMType | t.CPtr = None, + variant_name: str = None): + """注册 REnum 变体字段名别名到全局表 + + field_ty: 变体成员的实际 LLVM 类型(可选;自引用类型如 + LLVMType|t.CPtr 在注册期解析失败时为 None,match 绑定回退槽位类型)。 + variant_name: 所属变体名(可选;match 绑定按 (class, 变体, 位置) 定位成员) + """ if pool is None or class_name is None or field_name is None: return if payload_idx < 1: @@ -1719,13 +2144,18 @@ class ClassGenMixin: self.RenumAliasClass = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) self.RenumAliasField = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) self.RenumAliasIndex = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) + self.RenumAliasTy = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) + self.RenumAliasVariant = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX) if (self.RenumAliasClass is None or self.RenumAliasField is None - or self.RenumAliasIndex is None): + or self.RenumAliasIndex is None or self.RenumAliasTy is None + or self.RenumAliasVariant is None): self.RenumAliasClass = None return string.memset(self.RenumAliasClass, 0, 8 * RENUM_ALIAS_MAX) string.memset(self.RenumAliasField, 0, 8 * RENUM_ALIAS_MAX) string.memset(self.RenumAliasIndex, 0, 8 * RENUM_ALIAS_MAX) + string.memset(self.RenumAliasTy, 0, 8 * RENUM_ALIAS_MAX) + string.memset(self.RenumAliasVariant, 0, 8 * RENUM_ALIAS_MAX) self.RenumAliasCount = 0 if self.RenumAliasCount >= RENUM_ALIAS_MAX: @@ -1755,6 +2185,22 @@ class ClassGenMixin: string.strcpy(fn_buf_rfa, field_name) self.RenumAliasField[idx] = t.CSizeT(fn_buf_rfa) self.RenumAliasIndex[idx] = t.CSizeT(payload_idx) + # 成员类型深拷贝到 perm_pool(1a 每文件 reset 后 pool 类型悬空; + # 未解析成功(自引用等)存 0,绑定回退槽位类型) + self.RenumAliasTy[idx] = 0 + if field_ty is not None: + ty_copy_rfa: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType( + CompilerFactory.get_perm_pool(), field_ty) + if ty_copy_rfa is not None: + self.RenumAliasTy[idx] = t.CSizeT(ty_copy_rfa) + # 变体名同样 strcpy 到 perm_pool(1a reset 后 variant_cd.name 悬空) + self.RenumAliasVariant[idx] = 0 + if variant_name is not None: + vn_len_rfa: t.CSizeT = string.strlen(variant_name) + vn_buf_rfa: str = CompilerFactory.get_perm_pool().alloc(vn_len_rfa + 1) + if vn_buf_rfa is not None: + string.strcpy(vn_buf_rfa, variant_name) + self.RenumAliasVariant[idx] = t.CSizeT(vn_buf_rfa) self.RenumAliasCount = idx + 1 def _update_renum_alias_class_ptr(self, old_ptr: t.CSizeT, new_ptr: t.CSizeT): @@ -1786,6 +2232,60 @@ class ClassGenMixin: return self.RenumAliasIndex[i] return -1 + def _lookup_renum_field_alias_ty(self, class_name: str, + field_name: str) -> llvmlite.LLVMType | t.CPtr: + """查找 REnum 变体成员的实际 LLVM 类型,返回类型或 None + + 与 _lookup_renum_field_alias 同表同键(class 指针值比较 + field strcmp)。 + 注册期未解析成功(自引用类型等)槽位为 0 → 返回 None,绑定回退槽位类型。 + """ + if class_name is None or field_name is None or self.RenumAliasTy is None: + return None + cls_key: t.CSizeT = t.CSizeT(class_name) + for i in range(self.RenumAliasCount): + cls_existing: t.CSizeT = self.RenumAliasClass[i] + if cls_existing == cls_key: + fld_existing_ptr: str = (str | t.CPtr)( + t.CVoid(self.RenumAliasField[i], t.CPtr)) + if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0: + if self.RenumAliasTy[i] == 0: + return None + return (llvmlite.LLVMType | t.CPtr)( + t.CVoid(self.RenumAliasTy[i], t.CPtr)) + return None + + def _lookup_renum_member_ty(self, class_name: str, + variant_name: str, + payload_idx: int) -> llvmlite.LLVMType | t.CPtr: + """按 (class, 变体名, payload 位置) 查找变体成员的实际 LLVM 类型 + + match 绑定用:同一 payload 位置跨变体类型不同(Int.Bits=i32 vs + Struct.Fields=Ptr),必须带变体名定位。class 指针值比较 + 变体/位置 + 内容比较。返回类型或 None(未注册/未解析成功 → 绑定回退槽位类型)。 + """ + if class_name is None or variant_name is None or self.RenumAliasVariant is None: + return None + if payload_idx < 1: + return None + cls_key: t.CSizeT = t.CSizeT(class_name) + for i in range(self.RenumAliasCount): + cls_existing: t.CSizeT = self.RenumAliasClass[i] + if cls_existing != cls_key: + continue + if self.RenumAliasVariant[i] == 0: + continue + vnt_ptr: str = (str | t.CPtr)( + t.CVoid(self.RenumAliasVariant[i], t.CPtr)) + if vnt_ptr is None or string.strcmp(vnt_ptr, variant_name) != 0: + continue + if self.RenumAliasIndex[i] != payload_idx: + continue + if self.RenumAliasTy[i] == 0: + return None + return (llvmlite.LLVMType | t.CPtr)( + t.CVoid(self.RenumAliasTy[i], t.CPtr)) + return None + # ============================================================ # 簇 C:泛型模板注册/特化(P5-2 第三阶段迁移,函数体来自 # HandlesClassDef.vp,逻辑零改动) @@ -2459,7 +2959,6 @@ class ClassGenMixin: if trans.ModuleSha1 is None: # fail-fast:翻译类时模块 SHA1 缺失(命名空间隔离依赖它)—— - # 静默打印 "(null)" 会让类注册进错误命名空间。 VLogger.error("翻译类时 ModuleSha1 缺失", "CLASS") HandlesType.fatal_error(cd, "翻译类时 ModuleSha1 缺失", "TypeError") @@ -2486,6 +2985,12 @@ class ClassGenMixin: self._register_generic_template(pool, cd, trans.ModuleSha1) return 0 + # 异常类(try-except):注册编码+继承树,不生成 struct + # (对齐 TPC HandlesClassDef.py L509-511:_is_exception_class → + # _RegisterExceptionClass → return,异常类不编译为结构体) + if self._is_exception_class_def(cd) == 1: + return self._register_exception_class_def(trans, cd) + # 检查是否已注册(用 SHA1 区分跨模块同名类) existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( class_name, trans.ModuleSha1) @@ -2585,18 +3090,18 @@ class ClassGenMixin: # 1. 遍历 class body,收集字段信息 # ============================================================ # 用 CSizeT 数组存储指针值(64 位系统上 8 字节) - field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 64) - field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 64) - field_defaults_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 64) - field_annot_buf: t.CSizeT | t.CPtr = pool.alloc(8 * 64) + field_names_buf: t.CSizeT | t.CPtr = pool.alloc(8 * HandlesStruct.FIELD_MAX) + field_types_buf: t.CSizeT | t.CPtr = pool.alloc(8 * HandlesStruct.FIELD_MAX) + field_defaults_buf: t.CSizeT | t.CPtr = pool.alloc(8 * HandlesStruct.FIELD_MAX) + field_annot_buf: t.CSizeT | t.CPtr = pool.alloc(8 * HandlesStruct.FIELD_MAX) if field_names_buf is None or field_types_buf is None or field_defaults_buf is None: return 0 if field_annot_buf is None: return 0 - string.memset(field_names_buf, 0, 8 * 64) - string.memset(field_types_buf, 0, 8 * 64) - string.memset(field_defaults_buf, 0, 8 * 64) - string.memset(field_annot_buf, 0, 8 * 64) + string.memset(field_names_buf, 0, 8 * HandlesStruct.FIELD_MAX) + string.memset(field_types_buf, 0, 8 * HandlesStruct.FIELD_MAX) + string.memset(field_defaults_buf, 0, 8 * HandlesStruct.FIELD_MAX) + string.memset(field_annot_buf, 0, 8 * HandlesStruct.FIELD_MAX) field_count: int = 0 @@ -2626,7 +3131,18 @@ class ClassGenMixin: # 否则子类无法继承父类字段 → field_count==0 → __sizeof__ 失败 parent_entry = HandlesStruct.find_struct(pname) if parent_entry is None: - continue + # 父类尚未注册(依赖模块未翻译:Phase A 按文件序,子类模块可能 + # 先于父类模块,如 Generator.vp G < TypeGenMixin.vp T)。此时 + # **不能**继续注册——StructTable 在 StdlibAlloc=1 时用 perm_pool + # 分配(init_struct_table L136-140),残缺条目跨 pass 存活, + # 后续 pass 的 existing 早退(L2995-3006)永远无法修复 → 子类 + # __init__ 写父类字段(self.TraceFrames)每遍永久失败。 + # 整类 defer(不注册)+ 标记 _deps_pending 下遍重译;模块级 + # 变量/函数参数解析失败静默降级 i32(handle_module_level_var + # L422-426 / FuncGenMixin L482-483),不致命,本遍文件不写盘。 + if trans is not None: + trans._deps_pending = 1 + return 0 pfc: int = parent_entry.FieldCount for pfi in range(pfc): pfe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry( @@ -2642,7 +3158,7 @@ class ClassGenMixin: # 多继承去重:字段名已存在跳过(先声明父类优先) if self._field_name_exists(field_names_buf, field_count, pfn) != 0: continue - if field_count < 64: + if field_count < HandlesStruct.FIELD_MAX: pft: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_field_type_ptr(pfe) pfd: ast.AST | t.CPtr = HandlesStruct.get_field_default_ptr(pfe) pfa: str = HandlesStruct.get_field_annot_class_name(pfe) @@ -2688,19 +3204,38 @@ class ClassGenMixin: # 方法内裸名(如 ISINSTANCE_CLASS_MAX)经 translate_name_value 查 # CDefine 表解析为常量值;类体开头的常量(MRO_MAX 等)由早期预注册 # 路径注册,但方法之后的 AnnAssign 不经过那些路径 → 此处统一兜底注册。 - # 不 continue:CDefine 字段仍按普通字段收集(i32,与 MRO_MAX 一致)。 if HandlesAnnAssign.is_cdefine_annotation(aa.annotation) != 0: if aa.value is not None: HandlesAnnAssign._register_cdefine_from_value(pool, tgt.id, aa.value) + # CDefine 字段类型固定 i32,直接收集后跳过通用注解解析: + # resolve_annotation_type 不识别 "CDefine"(map_t_type/CTypedef + # 表均无此名),类体尾部常量(ISINSTANCE_CLASS_MAX 是首个触发 + # 该路径的实例)Phase B 会 NameError: 't.CDefine' is not defined。 + # 与类头常量(MRO_MAX 等)的 i32 字段语义保持一致。 + if field_count < HandlesStruct.FIELD_MAX: + cdef_ty_l: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + if cdef_ty_l is not None: + field_names_buf[field_count] = t.CSizeT(tgt.id) + field_types_buf[field_count] = t.CSizeT(cdef_ty_l) + if aa.value is not None: + field_defaults_buf[field_count] = t.CSizeT(aa.value) + else: + field_defaults_buf[field_count] = 0 + field_count += 1 + continue # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) # 这些字段只用于 with 上下文的静态可达性检查,不生成运行时代码 - # 避免类体中 list 字面量翻译失败(类体无 pool 变量上下文) + # (FakeDuck:_str.__requires__=['__mbuddy__'])。此处解析 list[str] + # 字面量注册到持久元数据表,供 requires 注入 / with provider 查询。 if string.strcmp(tgt.id, "__provides__") == 0: + self._register_class_meta_list(pool, HandlesType.CLASS_META_PROVIDES, class_name, aa.value) continue if string.strcmp(tgt.id, "__requires__") == 0: + self._register_class_meta_list(pool, HandlesType.CLASS_META_REQUIRES, class_name, aa.value) continue if string.strcmp(tgt.id, "__require_must__") == 0: + self._register_class_meta_list(pool, HandlesType.CLASS_META_REQUIRE_MUST, class_name, aa.value) continue # 解析字段类型 @@ -2733,16 +3268,20 @@ class ClassGenMixin: # 静默 return 0 让多遍收敛机制生效(下一遍依赖类已注册即可成功) if trans._declare_only == 1: return 0 - # Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止 - type_name2: str = HandlesType.annotation_to_str(aa.annotation) - HandlesType.fatal_error( - aa, - f"'{type_name2}' is not defined", - "NameError") + # Phase A(declare_only=0,Phase2.vp L573 多遍):依赖模块按 + # 文件序可能晚于本模块翻译(如 TypeGenMixin.vp G < HandlesType.vp + # H,TraceFrames 注解的 HandlesType.TraceFrame 未注册)→ 标记 + # _deps_pending 下遍重译,不在本遍以残缺字段注册结构体。 + # 真正的类型错误由 Phase A 收敛检查暴露("Phase A 无法完成 + # 翻译: ,请检查依赖模块是否已编译/注册"),不静默掩盖。 + if trans is not None: + trans._deps_pending = 1 return 0 + # 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时 + # 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底) # 存储到数组(指针转 CSizeT) - if field_count < 64: + if field_count < HandlesStruct.FIELD_MAX: diag_name_val: t.CSizeT = t.CSizeT(tgt.id) field_names_buf[field_count] = diag_name_val field_types_buf[field_count] = t.CSizeT(field_ty) @@ -3601,7 +4140,6 @@ class ClassGenMixin: method_name: str = fd.name if method_name is None: # fail-fast:方法名缺失(解析器不可能产出无名 def)——静默打印 - # "(null)" 会掩盖 AST/解析器上游异常。 VLogger.error("方法名缺失(fd.name is None)", "OOP") HandlesType.fatal_error(fd, "方法名缺失(fd.name is None)", "TypeError") @@ -3625,10 +4163,13 @@ class ClassGenMixin: is_static_m: int = 0 is_class_m: int = 0 + is_tls_m: int = 0 if func_meta & META_STATIC != 0: is_static_m = 1 if func_meta & META_CLASS != 0: is_class_m = 1 + if func_meta & META_TLS != 0: + is_tls_m = 1 # 构建方法全名: ClassName.method_name full_name_buf: str = pool.alloc(128) @@ -3743,6 +4284,16 @@ class ClassGenMixin: viperlib.snprintf(pname, 32, "%%%s", arg.arg) llvmlite.add_param(pool, func, param_ty, pname) + # OOP iter 协议:__next__ 方法注入隐藏参数 __stop_iter_flag__(i1*)。 + # raise StopIteration 时置 1(对齐 TPC HandlesFunctions.py L703-705)。 + # 该参数不计入 md_param_count(逻辑参数),调用侧(for 循环)显式传。 + if string.strcmp(method_name, "__next__") == 0: + i1_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_si is not None: + i1_ptr_ty_si: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_si) + if i1_ptr_ty_si is not None: + llvmlite.add_param(pool, func, i1_ptr_ty_si, "%__stop_iter_flag__") + # 参数添加完成,补全全局函数表的 TypeStr(供 vtable 在 func 被 reset 后使用) HandlesExprCall.update_global_func_typestr(pool, full_name, func) @@ -3757,6 +4308,65 @@ class ClassGenMixin: return 0 llvmlite.position_at_end(func_builder, entry_blk) + # @t.TLS 一次性初始化守卫(对齐 TPC HandlesFunctions.py L1763-1792; + # 与 FuncGenMixin.translate_function_def 同一实现,类方法同样支持) + if is_tls_m != 0: + tls_m_flag_name: str = pool.alloc(128) + if tls_m_flag_name is not None: + viperlib.snprintf(tls_m_flag_name, 128, "__tls_init_done_%s", method_name) + tls_m_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.create_global_int( + pool, mod, tls_m_flag_name, 1, 0) + if tls_m_gv is not None: + i1_ty_m: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_m is not None: + # GlobalVariable 非 Value 子类:构造 Value 视图后再 load/store + tls_m_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_m) + tls_m_rn: str = pool.alloc(64) + if tls_m_rn is not None: + viperlib.snprintf(tls_m_rn, 64, "@%s", tls_m_flag_name) + tls_m_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, tls_m_ptr_ty, tls_m_rn) + if tls_m_ref is not None: + tls_m_ld: llvmlite.Value | t.CPtr = llvmlite.build_load( + func_builder, i1_ty_m, tls_m_ref) + lc_m: int = trans._label_counter + trans._label_counter = lc_m + 1 + tls_m_buf: str = pool.alloc(64) + if tls_m_buf is not None: + viperlib.snprintf(tls_m_buf, 64, "tls.skip.%d", lc_m) + tls_m_skip: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, tls_m_buf) + viperlib.snprintf(tls_m_buf, 64, "tls.cont.%d", lc_m) + tls_m_cont: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, tls_m_buf) + if tls_m_skip is not None and tls_m_cont is not None: + llvmlite.build_cond_br( + func_builder, tls_m_ld, tls_m_skip, tls_m_cont) + llvmlite.position_at_end(func_builder, tls_m_skip) + match ret_ty: + case llvmlite.LLVMType.Void(): + llvmlite.build_ret_void(func_builder) + case llvmlite.LLVMType.Int(_): + tlm0: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, ret_ty, 0, "0") + if tlm0 is not None: + llvmlite.build_ret(func_builder, tlm0) + case llvmlite.LLVMType.Float(_): + tlmf: llvmlite.Value | t.CPtr = llvmlite.ConstFloat( + pool, ret_ty, 0.0) + if tlmf is not None: + llvmlite.build_ret(func_builder, tlmf) + case _: + tlmn: llvmlite.Value | t.CPtr = llvmlite.ConstNull( + pool, ret_ty, "null") + if tlmn is not None: + llvmlite.build_ret(func_builder, tlmn) + llvmlite.position_at_end(func_builder, tls_m_cont) + tlmo: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, i1_ty_m, 1, "1") + if tlmo is not None: + llvmlite.build_store(func_builder, tlmo, tls_m_ref) + # 进入函数作用域 HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION) @@ -3821,9 +4431,10 @@ class ClassGenMixin: if alloca is not None: HandlesVar.define_var(trans.SymTab, arg2.arg, alloca) # 存储原始类型注解的类名(方法调用检测时,Ptr(i8) 回退到类名查找结构体) + # 传 pool:泛型 Subscript 返回 mangled name(对齐 HandlesAnnAssign 修复) if arg2.annotation is not None: cls_nm_oc: str = HandlesType.extract_class_name_from_annotation( - arg2.annotation, imported_modules) + arg2.annotation, imported_modules, pool) if cls_nm_oc is not None: HandlesVar.set_var_annot_class_name( trans.SymTab, arg2.arg, cls_nm_oc) @@ -3834,12 +4445,30 @@ class ClassGenMixin: pool, param_ty2, pname2) llvmlite.build_store(func_builder, param_val, alloca) + # OOP iter 协议:__next__ 的 stop-flag 参数初始化(store 0)并保存 + # 指针到 trans,供 translate_raise 的 raise StopIteration 写 1。 + # 参数名必须与上面 add_param 的 "%__stop_iter_flag__" 一致。 + if string.strcmp(method_name, "__next__") == 0: + i1_ty_sf: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_sf is not None: + i1_ptr_ty_sf: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_sf) + if i1_ptr_ty_sf is not None: + flag_ref_sf: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, i1_ptr_ty_sf, "%__stop_iter_flag__") + if flag_ref_sf is not None: + trans._stop_iter_flag_ptr = flag_ref_sf + zero_i1_sf: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, i1_ty_sf, 0, "0") + if zero_i1_sf is not None: + llvmlite.build_store(func_builder, zero_i1_sf, flag_ref_sf) + # 保存模块级作用域状态 old_func: llvmlite.Function | t.CPtr = trans._cur_func old_builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder old_global_count: int = trans._global_name_count old_nonlocal_count: int = trans._nonlocal_name_count old_env_count: int = trans._closure_env_count + old_stop_iter_flag: llvmlite.Value | t.CPtr = trans._stop_iter_flag_ptr trans._cur_func = func trans._cur_builder = func_builder @@ -3862,43 +4491,80 @@ class ClassGenMixin: if stmt2 is not None: HandlesBody.translate_stmt(trans, stmt2) - # 如果返回类型为 void,添加 ret void;否则添加隐式 ret 0 - is_void: int = 0 - if ret_ty is not None: - match ret_ty: - case llvmlite.LLVMType.Void(): - is_void = 1 - - if is_void != 0: + # 追加89:方法末尾收尾与模块级函数(FuncGenMixin.translate_function_def) + # 同语义——编译器不替代码发明返回值,三种合法收尾: + # ① void 方法掉出末尾 → ret void(无值可发明,Python 语义) + # ② 末句是控制流语句(match/if 全分支 return)→ merge 块结构性 + # 不可达 → build_unreachable(不可能抵达,GCC/Clang fall-through + # 视为 UB 同语义) + # ③ 无返回类型注解的方法 → 视为无返回值过程,ConstZero 兜底 + # 有 -> 注解且末句普通语句掉出末尾 → fail-fast。 + # 不做 noreturn 函数名白名单:错误终止路径(fatal_error 等)也必须在 + # 源码层面保证块收尾(如显式 return 样板),每个函数都符合块设计。 + # 原实现 L4529 const_int32 硬编码 ret i32 0,对 i64 返回方法报 llc + # "value doesn't match function result type 'i64'"(追加88 只修了 + # FuncGenMixin 模块级函数路径,方法路径此处追加89 一并同步) + last_is_return: int = 0 + # 追加89:bn3 提前初始化,避免 body is None 时下方引用未定义变量 + bn3: t.CSizeT = 0 + if body is not None: + bn3 = len(body) + if bn3 > 0: + last_stmt_m: ast.AST | t.CPtr = body.get(bn3 - 1) + if last_stmt_m is not None and last_stmt_m.kind() == ast.ASTKind.Return: + last_is_return = 1 + if last_is_return == 0: if llvmlite.builder_cur_block_is_terminated(func_builder) == 0: - llvmlite.build_ret_void(func_builder) - else: - last_is_return: int = 0 - if body is not None: - bn3: t.CSizeT = len(body) + last_is_ctrl: int = 0 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: - if llvmlite.builder_cur_block_is_terminated(func_builder) == 0: - # 根据返回类型生成正确的零值返回 - _m_is_void: int = 0 - _m_is_ptr: int = 0 + last_stmt3_m: ast.AST | t.CPtr = body.get(bn3 - 1) + if last_stmt3_m is not None: + k3m: int = last_stmt3_m.kind() + if k3m == ast.ASTKind.If or k3m == ast.ASTKind.While or \ + k3m == ast.ASTKind.For or k3m == ast.ASTKind.Try or \ + k3m == ast.ASTKind.With or k3m == ast.ASTKind.Match: + last_is_ctrl = 1 + _is_void: int = 0 + if ret_ty is not None: + match ret_ty: + case llvmlite.LLVMType.Void(): + _is_void = 1 + case _: + pass + if _is_void != 0: + llvmlite.build_ret_void(func_builder) + elif last_is_ctrl != 0: + # 非 void 方法末句是控制流且块未 terminated:merge 块 + # 结构性不可达(不可能抵达),LLVM 语义 unreachable + llvmlite.build_unreachable(func_builder) + elif fd.returns is None: + # 无返回类型注解 → 无返回值过程方法,默认零值返回 if ret_ty is not None: - match ret_ty: - case llvmlite.LLVMType.Void(): - _m_is_void = 1 - case llvmlite.LLVMType.Ptr(_m_pe): - _m_is_ptr = 1 - if _m_is_void != 0: - llvmlite.build_ret_void(func_builder) - elif _m_is_ptr != 0: - _m_null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null") - llvmlite.build_ret(func_builder, _m_null_val) + _m_zero_val: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, ret_ty) + if _m_zero_val is not None: + llvmlite.build_ret(func_builder, _m_zero_val) + else: + HandlesType.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError") else: - _m_zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) - llvmlite.build_ret(func_builder, _m_zero_val) + HandlesType.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError") + else: + # fail-fast:有 -> 注解但流程真实掉出末尾(traceback 风格) + if ret_ty is not None: + rt_name_m: str = pool.alloc(64) + if rt_name_m is not None: + llvmlite.TypePrint(rt_name_m, 64, ret_ty, pool) + if rt_name_m[0] == '\0': + rt_name_m = "?" + err_fn_m: str = pool.alloc(256) + if err_fn_m is not None: + viperlib.snprintf( + err_fn_m, 256, + "方法 '%s' 流程掉出末尾但未显式 return(返回类型 %s)," + "请补显式 return(如 return 0 / return null)", method_name, rt_name_m) + HandlesType.fatal_error(fd, err_fn_m, "TypeError") + HandlesType.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError") + else: + HandlesType.fatal_error(fd, "方法流程掉出末尾但未显式 return", "TypeError") # 恢复模块级作用域 HandlesVar.exit_scope(trans.SymTab) @@ -3907,6 +4573,7 @@ class ClassGenMixin: trans._global_name_count = old_global_count trans._nonlocal_name_count = old_nonlocal_count trans._closure_env_count = old_env_count + trans._stop_iter_flag_ptr = old_stop_iter_flag return 0 diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index 490b4ef..df4eddd 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -926,12 +926,6 @@ class ExprGenMixin: """翻译值表达式为 LLVM Value""" if node is None: return None - # OOM 诊断:translate_value 调用计数(每 4096 次打印 kind+行号,定位 CONST_INT 循环调用源) - self.DiagTranslateValueCount += 1 - if (self.DiagTranslateValueCount % 4096) == 1: - stdio.printf("[MEM] trans_val %d kind=%d line=%d used=%zu chk=%zu\n", - self.DiagTranslateValueCount, node.kind(), node.lineno, - pool.used_bytes(), pool.chk_depth) k: int = node.kind() if k == ast.ASTKind.Constant: rv: llvmlite.Value | t.CPtr = self.translate_constant(builder, pool, mod, node, trans) @@ -1020,7 +1014,6 @@ class ExprGenMixin: builder, pool, mod, fv.value, None, 0, trans) if val is None: # fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在 - # 格式串写 "(null)" 兜底(静默输出掩盖表达式翻译失败根因)。 HandlesType.fatal_error( fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError") continue @@ -1073,7 +1066,6 @@ class ExprGenMixin: builder, pool, mod, fv.value, None, 0, trans) if val is None: # fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在 - # 格式串写 "(null)" 兜底(静默输出掩盖表达式翻译失败根因)。 HandlesType.fatal_error( fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError") else: @@ -1809,6 +1801,65 @@ class ExprGenMixin: if lhs is None: return None + # === in / not in:容器右操作数调用 __contains__(对齐 TPC + # HandlesExprOps.py L261-281)。x in y → y.__contains__(x); + # x not in y → !y.__contains__(x)。容器无 __contains__ → + # 返回 i1 0(TPC 同:恒 False)。 + if op == ast.OpKind.In or op == ast.OpKind.NotIn: + cn_st: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(rhs) + if cn_st is not None: + cn_cls: str = HandlesStruct.get_class_name_by_type(pool, cn_st) + if cn_cls is not None: + cn_lookup: str = pool.alloc(128) + if cn_lookup is not None: + viperlib.snprintf(cn_lookup, 128, "%s.__contains__", cn_cls) + cn_fn: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module( + mod, cn_lookup) + if cn_fn is not None: + cn_ret: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(cn_fn) + cn_fn_name: str = llvmlite.function_get_name(cn_fn) + if cn_ret is not None and cn_fn_name is not None: + # self 参数(容器)按 __contains__ 首参类型对齐 + cn_self: llvmlite.Value | t.CPtr = rhs + cn_first: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(cn_fn) + if cn_first is not None: + cn_self_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cn_first) + if cn_self_ty is not None: + cn_self = llvmlite.build_bitcast(builder, rhs, cn_self_ty) + if cn_self is not None: + # 参数链表: self → key(独立 null 结尾) + cn_self.Next = lhs + lhs.Next = None + cn_res: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, cn_fn_name, cn_self, 2, cn_ret, 0) + if cn_res is not None: + # 归一化到 i1(__contains__ 返回 t.CInt/i32 等) + cn_i1: llvmlite.Value | t.CPtr = cn_res + match cn_ret: + case llvmlite.LLVMType.Int(cn_bits): + if cn_bits > 1: + cn_zero: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, cn_ret, 0, "0") + if cn_zero is not None: + cn_i1 = llvmlite.build_icmp( + builder, llvmlite.ICMP_NE, cn_res, cn_zero) + case _: + pass + if op == ast.OpKind.NotIn: + i1_ty_ni: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_ni is not None and cn_i1 is not None: + one_i1_ni: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, i1_ty_ni, 1, "1") + if one_i1_ni is not None: + cn_i1 = llvmlite.build_xor(builder, cn_i1, one_i1_ni) + if cn_i1 is not None: + return cn_i1 + # 容器无 __contains__:TPC 语义恒 False(i1 0) + i1_ty_in: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_in is not None: + return llvmlite.ConstInt(pool, i1_ty_in, 0, "0") + return None + # === 比较运算符重载路径 2: lhs 是 Ptr(Struct) === # Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较 # None 比较跳过重载(走身份比较);指针判空(cmp_zero + 指针)同样跳过 @@ -1865,13 +1916,19 @@ class ExprGenMixin: else: lhs = llvmlite.ConstNull(pool, rhs.Ty, "null") else: - # 类型不匹配:数值vs指针 或 不同级指针 - # fail-fast:比较类型不匹配立即终止编译(暴露上游根因), - # 不再 VLogger.error + 返回 None 静默跳过 - HandlesType.fatal_error( - cmp, - f"比较类型不匹配: 数值与指针/不同级指针比较 (lhs 层级={lhs_ptr_level}, rhs 层级={rhs_ptr_level})", - "TypeError") + # 指针 vs 非零整数(C 哨兵值模式,如 w32.win32base.INVALID_HANDLE_VALUE = -1): + # 对齐 TPC HandlesExprOps.py L388-398 —— 指针 ptrtoint 成整数做地址值比较。 + # (指针 vs 整数字面量 0 已在上方转 null 处理;此处处理非零整数哨兵) + if lhs_ptr_level != 0 and rhs_ptr_level == 0: + lhs = llvmlite.build_ptrtoint(builder, lhs, rhs.Ty) + elif rhs_ptr_level != 0 and lhs_ptr_level == 0: + rhs = llvmlite.build_ptrtoint(builder, rhs, lhs.Ty) + else: + # 不同级指针(1 vs 2 等)→ 禁止(fast-fail) + HandlesType.fatal_error( + cmp, + f"比较类型不匹配: 不同级指针比较 (lhs 层级={lhs_ptr_level}, rhs 层级={rhs_ptr_level})", + "TypeError") else: # None 比较: None 被翻译为 i8* null(一级指针) # 如果 lhs 是指针,将 rhs (i8* null) bitcast 为 lhs 的指针类型 @@ -2216,29 +2273,58 @@ class ExprGenMixin: if rel_path[0] == '\0': return None # 选择基础目录:rel_path 以 "lib/" 开头 → App 源文件(SourceDir); - # 以 "includes/" 开头 → includes 文件(去掉 "includes/" 前缀, - # 因 IncludesDir 已包含 includes 目录本身,避免双前缀导致文件不存在) - base_dir: str = Config.IncludesDir - is_app: int = 0 + # 以 "includes/" 开头 → includes 文件(去掉 "includes/" 前缀)。 + # 其他 rel_path(如 VKernel 的 "drivers/core/cpu/cpu.vp")无前缀, + # 无法从 rel_path 判断归属:先试 SourceDir(App 文件),文件不存在 + # 再试 IncludesDir(includes 文件的 RelPath 同样无前缀存储)。 + # 避免路径构建错误导致跨模块 CDefine 查找静默失败。 + base_dir: str = None + rel_used: str = rel_path if string.strlen(rel_path) >= 4: if (rel_path[0] == 'l' and rel_path[1] == 'i' and rel_path[2] == 'b' and rel_path[3] == '/'): - is_app = 1 - if is_app != 0 and Config.SourceDir is not None: - base_dir = Config.SourceDir - elif string.strlen(rel_path) >= 9: - if (rel_path[0] == 'i' and rel_path[1] == 'n' and rel_path[2] == 'c' and - rel_path[3] == 'l' and rel_path[4] == 'u' and rel_path[5] == 'd' and - rel_path[6] == 'e' and rel_path[7] == 's' and rel_path[8] == '/'): - rel_path = rel_path + 9 - # 构建完整路径: {base_dir}/{rel_path} + base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir + if base_dir is None and string.strlen(rel_path) >= 9: + if (rel_path[0] == 'i' and rel_path[1] == 'n' and + rel_path[2] == 'c' and rel_path[3] == 'l' and + rel_path[4] == 'u' and rel_path[5] == 'd' and + rel_path[6] == 'e' and rel_path[7] == 's' and + rel_path[8] == '/'): + rel_used = rel_path + 9 + base_dir = Config.IncludesDir + if base_dir is None: + base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir + # 构建完整路径: {base_dir}/{rel_used} base_len: t.CSizeT = string.strlen(base_dir) - rel_len: t.CSizeT = string.strlen(rel_path) + rel_len: t.CSizeT = string.strlen(rel_used) path_len: t.CSizeT = base_len + rel_len + 2 buf: str = stdlib.malloc(path_len) if buf is None: return None - viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_path) + viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_used) + # 存在性检查:SourceDir 下不存在且 IncludesDir 不同 → 重试 IncludesDir + pf_chk: fileio.File | t.CPtr = fileio.File(buf, fileio.MODE.R) + if pf_chk.closed: + pf_chk.close() + if Config.IncludesDir is not None and base_dir != Config.IncludesDir: + stdlib.free(buf) + base_len2: t.CSizeT = string.strlen(Config.IncludesDir) + path_len2: t.CSizeT = base_len2 + rel_len + 2 + buf = stdlib.malloc(path_len2) + if buf is None: + return None + viperlib.snprintf(buf, path_len2, "%s/%s", Config.IncludesDir, rel_used) + pf_chk2: fileio.File | t.CPtr = fileio.File(buf, fileio.MODE.R) + if pf_chk2.closed: + pf_chk2.close() + stdlib.free(buf) + return None + pf_chk2.close() + else: + stdlib.free(buf) + return None + else: + pf_chk.close() return buf # ============================================================ @@ -2255,8 +2341,8 @@ class ExprGenMixin: from_imports: str, imported_modules: str = None, depth: int = 0, - node: ast.AST | t.CPtr = None) -> int: - """跨模块查找 CDefine 常量,返回值或 -1(未找到) + node: ast.AST | t.CPtr = None) -> t.CInt64T: + """跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到) 查找顺序: 1. from_imports 精确匹配(from X import NAME) @@ -2292,14 +2378,6 @@ class ExprGenMixin: if from_imports is None: from_imports = "" - # OOM 诊断:跨模块 CDefine 查找调用计数(每 256 次打印 used) - self.DiagCDefineCount += 1 - if (self.DiagCDefineCount % 256) == 1: - stdio.printf("[MEM] cdefine_lookup %d name=%s fi=%.16s used=%zu chk=%zu\n", - self.DiagCDefineCount, name, - from_imports if from_imports is not None else "(nil)", - pool.used_bytes(), pool.chk_depth) - # 1. 从 from_imports 查找名称对应的模块名 # 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导 mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0) @@ -2350,9 +2428,9 @@ class ExprGenMixin: xd_v: double = string.atof(xval) HandlesType.register_cdefine_double(pool, name, xd_v) else: - xv_i: int = string.atoi(xval) + xv_i: t.CInt64T = string.atoll(xval) HandlesType.register_cdefine_constant(pool, name, xv_i) - return string.atoi(xval) + return string.atoll(xval) # 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6) # 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中, @@ -2395,7 +2473,7 @@ class ExprGenMixin: sub_fi[2 + mk2] = im_buf[mk2] mk2 += 1 sub_fi[2 + im_nlen] = '\0' - sub_val: int = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node) + sub_val: t.CInt64T = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node) if HandlesType.is_cdefine_found() != 0: return sub_val return -1 @@ -2439,11 +2517,19 @@ class ExprGenMixin: pyi_buf[bytes_read] = 0 else: pyi_buf[PYI_READ_BUF_SIZE - 1] = 0 - # vpi(存根签名文件)中的 CDefine 只声明名字不带值 - # (如 "U_M_PI: t.CDefine",无 '='),真正的值定义在源 - # .vp/.py 文件中。若 vpi 中所有 "t.CDefine" 声明后均无 - # '='(即 vpi 不含 CDefine 值),必须回退读源文件才能找到。 + # vpi(存根签名文件)中的 CDefine 声明可能只带名字不带值 + # (Converter 旧版只对 CONST_INT/BOOL 等输出 "= value",CONST_FLOAT + # 等不输出 → numpy 的 pi/e 在 stub 中无值),真正的值在源 + # .vp/.py 文件中。旧启发式"vpi 中任一 CDefine 带值 → 不读源"是错的: + # 模块同时含 int CDefine(带值,如 numpy.MAX_NDIM=4)与 float + # CDefine(无值,如 pi)时,int 行让 cd_sig=1 而漏回退源 → pi + # 解析失败 → 退化 @pi 全局指针 → float<->ptr 报错(NumpyTest 根因)。 + # 改为按目标名判定:仅当 vpi 中 name 的 CDefine 行带值时才信任 + # vpi,否则(name 行无值 / name 不在 vpi / 整个 vpi 无带值 CDefine) + # 一律回退读源文件。 cd_sig: int = 0 + name_has_val: int = 0 + cs_name_len: t.CSizeT = string.strlen(name) cs_total: t.CSizeT = string.strlen(pyi_buf) cs_i: t.CSizeT = 0 while cs_i + 9 < cs_total: @@ -2462,9 +2548,20 @@ class ExprGenMixin: cs_j += 1 if cd_eq != 0: cd_sig = 1 - break + # 定位该行行首,检查是否以 "name:" 开头(目标名带值) + cs_line_start: t.CSizeT = cs_i + while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n': + cs_line_start -= 1 + if cs_i - cs_line_start >= cs_name_len + 1: + nm_ok: int = 1 + for ki2 in range(cs_name_len): + if pyi_buf[cs_line_start + ki2] != name[ki2]: + nm_ok = 0 + break + if nm_ok == 1 and pyi_buf[cs_line_start + cs_name_len] == ':': + name_has_val = 1 cs_i += 1 - if cd_sig == 0: + if cd_sig == 0 or name_has_val == 0: need_py_fallback = 1 stdlib.free(pyi_path) @@ -2503,9 +2600,10 @@ class ExprGenMixin: name_len: t.CSizeT = string.strlen(name) total_len: t.CSizeT = string.strlen(pyi_buf) pos: t.CSizeT = 0 - result_val: int = 0 + result_val: t.CInt64T = 0 result_found: int = 0 res_is_double: int = 0 + res_is_str: int = 0 while pos < total_len: # 找到行首 line_start: t.CSizeT = pos @@ -2574,7 +2672,15 @@ class ExprGenMixin: while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1 - # 解析整数值(支持十六进制 0x 前缀、负号、浮点小数点) + # 括号包裹标记:(1 << 23) / (512) / (A | B) + is_paren_expr: int = 0 + if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '(': + is_paren_expr = 1 + eq_pos += 1 + while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': + eq_pos += 1 + + # === 解析左操作数 token(数字 / hex / 负号 / 名称) === # 负号标记 is_neg: int = 0 if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-': @@ -2584,12 +2690,39 @@ class ExprGenMixin: val_str_len: t.CSizeT = 0 is_hex: int = 0 is_float_val: int = 0 + is_name_val: int = 0 + is_str_val: int = 0 + # 字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2"):读引号内内容。 + # 跨模块读回此前不支持引号 → val_str_len=0 被跳过 → "跨模块 CDefine + # 未找到"(test_pyzlib.vp pyzlib.ZLIB_VERSION 根因) + if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"': + is_str_val = 1 + eq_pos += 1 # 跳过开引号 + val_str_start = eq_pos + while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '"': + val_str_len += 1 + eq_pos += 1 + if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"': + eq_pos += 1 # 跳过闭引号 if eq_pos + 1 < line_start + line_len: if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'): is_hex = 1 val_str_len = 2 eq_pos += 2 - while eq_pos < line_start + line_len: + elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \ + (pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \ + pyi_buf[eq_pos] == '_': + is_name_val = 1 + while eq_pos < line_start + line_len: + c_tk: t.CChar = pyi_buf[eq_pos] + if (c_tk >= 'A' and c_tk <= 'Z') or (c_tk >= 'a' and c_tk <= 'z') or \ + (c_tk >= '0' and c_tk <= '9') or c_tk == '_': + val_str_len += 1 + eq_pos += 1 + else: + break + # 数字/hex 字符续读(字符串值已整体读取,跳过) + while eq_pos < line_start + line_len and is_name_val == 0 and is_str_val == 0: ch: t.CChar = pyi_buf[eq_pos] if is_hex == 1: if ((ch >= '0' and ch <= '9') or @@ -2611,10 +2744,10 @@ class ExprGenMixin: else: break - if val_str_len == 0: + if val_str_len == 0 and is_str_val == 0: continue - # 复制值字符串到新缓冲区并解析 + # 复制左 token 到缓冲区 val_buf: str = pool.alloc(val_str_len + 1) if val_buf is None: continue @@ -2622,9 +2755,34 @@ class ExprGenMixin: val_buf[vi] = pyi_buf[val_str_start + vi] val_buf[val_str_len] = '\0' - if is_hex == 1: - # 手动解析十六进制(跳过 0x 前缀) - hex_result: int = 0 + # 左 token 求值 + lval: t.CInt64T = 0 + l_is_double: int = 0 + ldbl: double = 0.0 + if is_str_val != 0: + # 字符串 CDefine(如 ZLIB_VERSION = "1.3.2"):注册字符串值 + # (含空串 ""),result_found 标记 + res_is_str 供尾部跳过整数 + # 注册/缓存(否则 register_cdefine_constant 把字符串当 0 整数) + HandlesType.register_cdefine_string(pool, name, val_buf) + result_val = 0 + result_found = 1 + res_is_str = 1 + elif is_name_val != 0: + # True/False 布尔字面量 → 1/0(对齐 TPC:bool 在 CDefine 语境按整数处理) + if string.strcmp(val_buf, "True") == 0: + lval = 1 + elif string.strcmp(val_buf, "False") == 0: + lval = 0 + else: + # 名称引用:查当前 CDefine 表(如 FOREGROUND_COMBO = FOREGROUND_RED) + lval = HandlesType.lookup_cdefine_constant(val_buf) + if HandlesType.is_cdefine_found() == 0: + # 名称未注册:该行无法求值,跳过 + continue + elif is_hex == 1: + # 手动解析十六进制(跳过 0x 前缀)——64 位累加, + # 0xFFFFFFFF=4294967295 保持全精度(常量生成端 i32 截断回 -1) + hex_result: t.CInt64T = 0 hex_buf: str = val_buf + 2 # 跳过 "0x" for hi in range(val_str_len - 2): hc: t.CChar = hex_buf[hi] @@ -2638,29 +2796,182 @@ class ExprGenMixin: else: break hex_result = hex_result * 16 + hd - result_val = hex_result - # 十六进制负值(如 0xFFFFFFFF)保持原样,由 32 位截断处理 - result_found = 1 + lval = hex_result elif is_float_val != 0: - # 浮点 CDefine(如 U_M_PI = 3.14159...): - # 值缓存为 double(register_cdefine_double),供调用处生成 - # double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。 - dval_f: double = string.atof(val_buf) - if is_neg != 0: - dval_f = -dval_f - HandlesType.register_cdefine_double(pool, name, dval_f) - result_val = string.atoi(val_buf) - if is_neg != 0: - result_val = -result_val - result_found = 1 - res_is_double = 1 + l_is_double = 1 + ldbl = string.atof(val_buf) + lval = string.atoll(val_buf) else: - result_val = string.atoi(val_buf) - # 应用负号(如 t.CUnsignedLong(-11) → -11) - if is_neg != 0: - result_val = -result_val + lval = string.atoll(val_buf) + if is_neg != 0: + lval = -lval + if l_is_double != 0: + ldbl = -ldbl + + # 跳过空格,检查是否有二元运算符 + while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': + eq_pos += 1 + + # 解析运算符(<< >> | & ^ + - * /) + cd_op: int = 0 + if eq_pos + 1 < line_start + line_len: + if pyi_buf[eq_pos] == '<' and pyi_buf[eq_pos + 1] == '<': + cd_op = 1 # LShift + eq_pos += 2 + elif pyi_buf[eq_pos] == '>' and pyi_buf[eq_pos + 1] == '>': + cd_op = 2 # RShift + eq_pos += 2 + if cd_op == 0 and eq_pos < line_start + line_len: + if pyi_buf[eq_pos] == '|': + cd_op = 3 # BitOr + eq_pos += 1 + elif pyi_buf[eq_pos] == '&': + cd_op = 4 # BitAnd + eq_pos += 1 + elif pyi_buf[eq_pos] == '^': + cd_op = 5 # BitXor + eq_pos += 1 + elif pyi_buf[eq_pos] == '+': + cd_op = 6 # Add + eq_pos += 1 + elif pyi_buf[eq_pos] == '-': + cd_op = 7 # Sub + eq_pos += 1 + elif pyi_buf[eq_pos] == '*': + cd_op = 8 # Mult + eq_pos += 1 + elif pyi_buf[eq_pos] == '/': + cd_op = 9 # Div + eq_pos += 1 + + if cd_op != 0: + # === 解析右操作数 token === + while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': + eq_pos += 1 + r_neg: int = 0 + if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-': + r_neg = 1 + eq_pos += 1 + r_start: t.CSizeT = eq_pos + r_len: t.CSizeT = 0 + r_hex: int = 0 + r_name: int = 0 + if eq_pos + 1 < line_start + line_len: + if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'): + r_hex = 1 + r_len = 2 + eq_pos += 2 + elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \ + (pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \ + pyi_buf[eq_pos] == '_': + r_name = 1 + while eq_pos < line_start + line_len: + c_r: t.CChar = pyi_buf[eq_pos] + if (c_r >= 'A' and c_r <= 'Z') or (c_r >= 'a' and c_r <= 'z') or \ + (c_r >= '0' and c_r <= '9') or c_r == '_': + r_len += 1 + eq_pos += 1 + else: + break + while eq_pos < line_start + line_len and r_name == 0: + c_r2: t.CChar = pyi_buf[eq_pos] + if r_hex == 1: + if ((c_r2 >= '0' and c_r2 <= '9') or + (c_r2 >= 'a' and c_r2 <= 'f') or + (c_r2 >= 'A' and c_r2 <= 'F')): + r_len += 1 + eq_pos += 1 + else: + break + else: + if c_r2 >= '0' and c_r2 <= '9': + r_len += 1 + eq_pos += 1 + else: + break + if r_len == 0: + # 运算符后无右操作数:无法求值,跳过该行 + continue + r_buf: str = pool.alloc(r_len + 1) + if r_buf is None: + continue + for r_i in range(r_len): + r_buf[r_i] = pyi_buf[r_start + r_i] + r_buf[r_len] = '\0' + rval: t.CInt64T = 0 + if r_name != 0: + # True/False 布尔字面量 → 1/0 + if string.strcmp(r_buf, "True") == 0: + rval = 1 + elif string.strcmp(r_buf, "False") == 0: + rval = 0 + else: + rval = HandlesType.lookup_cdefine_constant(r_buf) + if HandlesType.is_cdefine_found() == 0: + # 名称未注册:无法求值,跳过该行 + continue + elif r_hex != 0: + rhex_v: t.CInt64T = 0 + rh_buf: str = r_buf + 2 + for rh_i in range(r_len - 2): + rc: t.CChar = rh_buf[rh_i] + rd: int = 0 + if rc >= '0' and rc <= '9': + rd = rc - '0' + elif rc >= 'a' and rc <= 'f': + rd = rc - 'a' + 10 + elif rc >= 'A' and rc <= 'F': + rd = rc - 'A' + 10 + else: + break + rhex_v = rhex_v * 16 + rd + rval = rhex_v + else: + rval = string.atoll(r_buf) + if r_neg != 0: + rval = -rval + # 计算二元表达式结果(对齐 TPC ConstEvaluator) + if cd_op == 1: + result_val = lval << rval + elif cd_op == 2: + result_val = lval >> rval + elif cd_op == 3: + result_val = lval | rval + elif cd_op == 4: + result_val = lval & rval + elif cd_op == 5: + result_val = lval ^ rval + elif cd_op == 6: + result_val = lval + rval + elif cd_op == 7: + result_val = lval - rval + elif cd_op == 8: + result_val = lval * rval + else: + if rval == 0: + continue + result_val = lval // rval result_found = 1 - break + break + else: + # 单值(无运算符):括号内单值如 (512) / (-11) 也走这里 + if is_paren_expr != 0: + while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': + eq_pos += 1 + if eq_pos < line_start + line_len and pyi_buf[eq_pos] == ')': + eq_pos += 1 + if is_float_val != 0: + # 浮点 CDefine(如 U_M_PI = 3.14159...): + # 值缓存为 double(register_cdefine_double),供调用处生成 + # double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。 + HandlesType.register_cdefine_double(pool, name, ldbl) + result_val = lval + result_found = 1 + res_is_double = 1 + else: + result_val = lval + result_found = 1 + break # 9.5 如果当前模块文件中没找到,递归查找子模块 # 处理 from .X import NAME 和 from .X import * 的情况 @@ -2822,8 +3133,6 @@ class ExprGenMixin: viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod) # 递归调用查找子模块 - stdio.printf("[MEM] cdef_rec d=%d name=%s mod=%s\n", - depth, name, rc_full_mod) rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node) if HandlesType.is_cdefine_found() != 0: result_val = rc_sub_val @@ -2865,7 +3174,7 @@ class ExprGenMixin: sub_fi2[2 + mk4] = im2_buf[mk4] mk4 += 1 sub_fi2[2 + im2_nlen] = '\0' - sub_val2: int = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node) + sub_val2: t.CInt64T = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node) if HandlesType.is_cdefine_found() != 0: result_val = sub_val2 result_found = 1 @@ -2874,7 +3183,16 @@ class ExprGenMixin: # 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存 # (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。 if result_found != 0: - if res_is_double == 0: + if res_is_str != 0: + # 字符串 CDefine:值已在匹配循环 register_cdefine_string(val_buf + # 是命中行的字符串内容,pool 分配跨函数有效),此处只写跨模块缓存 + if base_mod is not None: + ck_len_s: t.CSizeT = string.strlen(name) + string.strlen(base_mod) + 2 + ckey_s: str = pool.alloc(ck_len_s) + if ckey_s is not None: + viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod) + HandlesType.register_xmod_cdefine(ckey_s, val_buf, 0, 1) + elif res_is_double == 0: HandlesType.register_cdefine_constant(pool, name, result_val) # 跨模块查找缓存(perm_pool 永久,跨文件复用):整数 # key 与入口查询一致:"name@源模块"(用截断后的 base_mod, @@ -2886,7 +3204,7 @@ class ExprGenMixin: viperlib.snprintf(ckey_buf, ck_len, "%s@%s", name, base_mod) cbuf_i: str = pool.alloc(32) if cbuf_i is not None: - viperlib.snprintf(cbuf_i, 32, "%d", result_val) + viperlib.snprintf(cbuf_i, 32, "%lld", result_val) HandlesType.register_xmod_cdefine(ckey_buf, cbuf_i, 0, 0) else: # 浮点:从本地表取回 %.17g 文本,写入跨模块缓存(保精度) @@ -2937,24 +3255,44 @@ class ExprGenMixin: # ============================================================ # 翻译变量引用(Name 节点)→ load # ============================================================ + def _const_int_for_cdefine(self, pool: memhub.MemBuddy | t.CPtr, + value: t.CInt64T) -> llvmlite.Value | t.CPtr: + """CDefine 整数常量 → LLVM 常量(对齐 TPC TypeConvert 的 64 位升级) + + 规则(与 translate_constant 的 i32 溢出升级一致,但保留 0xFFFFFFFF 语义): + 值超 32 位无符号范围(> 0xFFFFFFFF)→ i64 全精度常量 + (如 sys.maxsize = 9223372036854775807); + 否则 → i32 常量,内部 t.CInt 截断(0xFFFFFFFF → -1,保持既有 + INVALID_HANDLE_VALUE / WAIT_FAILED = -1 语义不变,避免 i32 4294967295 + 生成非法 LLVM IR 文本 "i32 4294967295")。 + + 注意:不能用 `value > 4294967295` 直接比较——TPV 编译自身源码时大数字面量 + (> 0x7FFFFFFF)在字面量层回绕成 i32(sext 后等于 -1),阈值恒成立导致所有 + 正数 CDefine 都被升 i64(INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 从 i32 -1 变 + i64 4294967295 → 与 _get_attrs 的 sext(-1) 比较失败,SysLibTest exists FAIL 根因)。 + 改用截断回比较:t.CUInt32T(value) 回绕后还原,与 value 不等即超 u32 范围。 + """ + if value >= 0 and t.CUInt64T(t.CUInt32T(value)) != value: + return llvmlite.const_int64(pool, value) + return llvmlite.const_int32(pool, t.CInt(value)) + def translate_name_value(self, builder: llvmlite.IRBuilder | t.CPtr, pool: memhub.MemBuddy | t.CPtr, node: ast.AST | t.CPtr, trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: """翻译变量引用(Name 节点)→ load""" + # mod 必须取自 trans.Module:函数签名无 mod 参数,若用裸名 mod 会被 + # 解析为 None → create_global_string 的 module_add_global(None, gv) 被 + # add_global 的 `if self is None: return` 静默丢弃 → 字符串 CDefine 的 + # global 定义丢失但 _label_counter/IR 引用已生成 → llc 报 + # "use of undefined value '@.str.{sha1}.N'(SysLibTest _cleanup 根因)" + mod: llvmlite.LLVMModule | t.CPtr = trans.Module nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) if nm is None: return None nm_id: str = nm.id if nm_id is None: return None - # OOM 诊断:translate_name_value 调用计数(每 2048 次打印,定位 CONST_INT 循环调用源) - self.DiagNameValueCount += 1 - if (self.DiagNameValueCount % 2048) == 1: - stdio.printf("[MEM] name_value %d name=%s used=%zu chk=%zu\n", - self.DiagNameValueCount, nm_id, - pool.used_bytes(), pool.chk_depth) - # CDefine 编译期常量: 返回常量值(浮点 CDefine → double 常量,整数 → i32) # NAME: t.CDefine = value 形式定义的常量在编译期已注册到本地表 # 浮点 CDefine(如 vipermath 的 U_M_PI=3.14...)必须返回浮点常量, @@ -2970,15 +3308,17 @@ class ExprGenMixin: HandlesType.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError") if HandlesType.is_cdefine_double() != 0: return llvmlite.ConstFloat(pool, llvmlite.Double(pool), cdef_val_d) - cdef_val_i: int = t.CInt(cdef_val_d) - return llvmlite.const_int32(pool, cdef_val_i) + # 整数 CDefine:重新查表取 64 位全精度值(double 往返会丢精度, + # 9223372036854775807 → 9.223372036854776e18 舍入错位,生成错常量) + cdef_val_c: t.CInt64T = HandlesType.lookup_cdefine_constant(nm_id) + return self._const_int_for_cdefine(pool, cdef_val_c) # 本地表未找到:尝试跨模块 .vpi 查找(保证多模块查表存表一致) # 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找, # 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗 if self._is_cdefine_name_pattern(nm_id) != 0: if trans is not None: - cdef_val = self._lookup_cross_module_cdefine( + cdef_val: t.CInt64T = self._lookup_cross_module_cdefine( pool, nm_id, trans._from_imports, trans._imported_modules, 0, node) if HandlesType.is_cdefine_found() != 0: if HandlesType.is_cdefine_str() != 0: @@ -2991,7 +3331,7 @@ class ExprGenMixin: dchk_nm: double = HandlesType.lookup_cdefine_double(nm_id) if HandlesType.is_cdefine_double() != 0: return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nm) - return llvmlite.const_int32(pool, cdef_val) + return self._const_int_for_cdefine(pool, cdef_val) # global 变量:从模块作用域查找(global 声明优先级最高) if trans is not None: @@ -3006,6 +3346,16 @@ class ExprGenMixin: # fail-fast:global 变量存储类型缺失,立即终止编译(暴露上游根因) HandlesType.fatal_error( node, f"变量 '{nm_id}' 存储类型缺失", "TypeError") + # 全局数组(如 pyzlib.pyzlib_error_msg: CArray[t.CChar, 512]): + # 按 C 语义衰减为基址返回(对齐下方局部数组分支 L3383-3388)。 + # 否则 load [512 x i8] 数组值当 i8* 传 → set_error 的 strncpy + # dest 是数组值(栈拷贝)而非数组基址 → 写入丢失 → get_error + # 为空(test_pyzlib "error message is empty" 根因)。 + match load_ty: + case llvmlite.LLVMType.Array(_, _): + return mod_alloca + case _: + pass return llvmlite.build_load(builder, load_ty, mod_alloca) # nonlocal 变量:通过闭包 env 访问 @@ -3315,10 +3665,25 @@ class ExprGenMixin: s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset) if s_addr is None: return 0 - s_val_ptr: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, rhs_val, i8_ptr_ty, node) + # 元素类型 T:从 list 特化类型名解析(list[int]→i32、list[str]→i8*)。 + # 此前固定 i8*(8 字节)store(inttoptr 值)覆盖相邻元素——与 + # CallGenMixin list 内置分支同根因(ListTest pop FAILED expect 30)。 + s_sty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(lm_obj) + s_name: str = None + if s_sty is not None: + s_name = llvmlite.get_struct_name(s_sty) + s_elem_ty: llvmlite.LLVMType | t.CPtr = None + if s_name is not None: + s_elem_ty = self._list_elem_type_from_name(pool, s_name) + if s_elem_ty is None: + s_elem_ty = i8_ptr_ty + s_val_ptr: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, rhs_val, s_elem_ty, node) if s_val_ptr is None: return 0 - llvmlite.build_store(builder, s_val_ptr, s_addr) + s_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, s_addr, llvmlite.Ptr(pool, s_elem_ty)) + if s_ep is not None: + llvmlite.build_store(builder, s_val_ptr, s_ep) return 0 # ============================================================ @@ -3473,6 +3838,30 @@ class ExprGenMixin: if elem_ptr is not None: return llvmlite.build_load(builder, elem_ty, elem_ptr) return None + case llvmlite.LLVMType.Struct(_fs_v, _fc_v, lsn_v): + # list[T] 值变量(无注解赋值 rhs 返回 list 值, + # 如 parts = s1.split(','):split 的 define 返回 + # %list[str] 结构体值,alloca 直接存 list 对象)。 + # alloca 即 list*,直接走 list 内联 __getitem__ + #(对齐 Ptr(Ptr(list)) 变量路径,修复 parts[0] 崩溃) + if lsn_v is not None: + if string.strstr(lsn_v, "list[") is not None: + elem_addr_v: llvmlite.Value | t.CPtr = \ + self.list_getitem_inline( + builder, pool, alloca, idx_val, node) + if elem_addr_v is not None: + elem_ty_v: llvmlite.LLVMType | t.CPtr = \ + self._list_elem_type_from_name(pool, lsn_v) + if elem_ty_v is not None: + elem_ptr_ty_v: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, elem_ty_v) + casted_v: llvmlite.Value | t.CPtr = \ + llvmlite.build_bitcast( + builder, elem_addr_v, elem_ptr_ty_v) + if casted_v is not None: + return llvmlite.build_load( + builder, elem_ty_v, casted_v) + return None case llvmlite.LLVMType.Ptr(inner_ty): # 检查 inner_ty 是否是 list[T] 类型(泛型类不注册 struct) # list 的 subscript 应该走 __getitem__ 内联路径,而非指针遍历 @@ -3831,6 +4220,35 @@ class ExprGenMixin: return 1 return 0 + def _attr_module_path_str(self, pool: memhub.MemBuddy | t.CPtr, + node: ast.AST | t.CPtr) -> str: + """把模块引用 AST 转成模块路径字符串(递归拼接 Attribute 链): + Name('sys') → "sys";Attribute(Attribute(Name('os'),'_win32')) → "os._win32"。 + 非模块引用形态(Call/Subscript 等)返回 None。""" + if node is None: + return None + k: int = node.kind() + if k == ast.ASTKind.Name: + nm_m: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm_m.id is not None: + return nm_m.id + return None + if k == ast.ASTKind.Attribute: + at_m: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + if at_m.attr is None: + return None + base_m: str = self._attr_module_path_str(pool, at_m.value) + if base_m is None: + return None + blen: t.CSizeT = string.strlen(base_m) + alen: t.CSizeT = string.strlen(at_m.attr) + buf_m: str = pool.alloc(blen + alen + 2) + if buf_m is None: + return None + viperlib.snprintf(buf_m, blen + alen + 2, "%s.%s", base_m, at_m.attr) + return buf_m + return None + def _resolve_module_attribute_global(self, pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, trans: HT.Translator | t.CPtr, @@ -3839,15 +4257,16 @@ class ExprGenMixin: """解析模块属性访问,返回全局变量引用 (用于跨模块 global 访问)""" if at is None or at.value is None or at.attr is None: return None - if at.value.kind() != ast.ASTKind.Name: - return None if trans is None or trans._imported_modules is None: return None - mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) - if mod_nm.id is None: + # 提取模块路径名:Name → "sys";嵌套 Attribute → "os._win32" + # (此前只支持 Name 模块;SysLibTest main.vp L32 `os._win32._mbuddy = v` + # 的 at.value 是 Attribute → 被跳过 → "属性写入失败" 根因) + mod_path_w: str = self._attr_module_path_str(pool, at.value) + if mod_path_w is None: return None # 检查是否是已导入模块 - if HandlesImports.is_module_imported(trans._imported_modules, mod_nm.id) == 0: + if HandlesImports.is_module_imported(trans._imported_modules, mod_path_w) == 0: return None # 在当前模块查找全局变量 (stub 可能已注入) gv: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, at.attr) @@ -3865,7 +4284,7 @@ class ExprGenMixin: # 创建带 null 初值的 linkonce_odr 全局变量定义,避免链接时 undefined reference。 # LLVM 22+ 不允许 external linkage 全局变量带初值,使用 linkonce_odr 替代。 is_t_type_obj: int = 0 - if string.strcmp(mod_nm.id, "t") == 0: + if string.strcmp(mod_path_w, "t") == 0: is_t_type_obj = self._is_t_type_object_name(at.attr) ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty) if ext_gv is not None: @@ -4277,29 +4696,61 @@ class ExprGenMixin: return None # 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值 - # 当 value 是 Attribute(如 fileio.MODE)且 attr 部分是已注册枚举时 + # 当 value 是 Attribute(如 fileio.MODE / w32.fileio.MODE,含三级以上 + # 模块路径)且 attr 部分是已注册枚举时。此前限制 inner_at_e.value 必须是 + # Name(仅支持两层 Module.EnumName),w32.fileio.MODE.W(三层,value 是 + # Attribute)被跳过 → "属性访问失败: .W"(SysLibTest L190 根因)。 + # 枚举类名只取最内层 attr(MODE),模块路径形态不影响。 if at.value.kind() == ast.ASTKind.Attribute: inner_at_e: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value) - if inner_at_e.value is not None and inner_at_e.value.kind() == ast.ASTKind.Name: - inner_nm_e: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at_e.value) - if inner_nm_e.id is not None and inner_at_e.attr is not None: - if HandlesEnum.is_enum_class(inner_at_e.attr) == 1: - member_e: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member( - inner_at_e.attr, at.attr) - if member_e is not None: - base_ty_e: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type( - inner_at_e.attr) - if base_ty_e is not None: - name_buf_e: str = pool.alloc(32) - if name_buf_e is not None: - viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value) - return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e) - return llvmlite.const_int32(pool, member_e.Value) - # fast-fail: 嵌套枚举成员未找到 - VLogger.error( - f"嵌套枚举成员未找到: {inner_at_e.attr}.{at.attr}", - "ATTR") - return None + if inner_at_e.attr is not None: + if HandlesEnum.is_enum_class(inner_at_e.attr) == 1: + member_e: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member( + inner_at_e.attr, at.attr) + if member_e is not None: + base_ty_e: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type( + inner_at_e.attr) + if base_ty_e is not None: + name_buf_e: str = pool.alloc(32) + if name_buf_e is not None: + viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value) + return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e) + return llvmlite.const_int32(pool, member_e.Value) + # fast-fail: 嵌套枚举成员未找到 + dbg_ee: HandlesEnum.EnumEntry | t.CPtr = HandlesEnum.find_enum( + inner_at_e.attr) + dbg_mc: int = -1 + if dbg_ee is not None: + dbg_mc = dbg_ee.MemberCount + VLogger.error( + f"嵌套枚举成员未找到: {inner_at_e.attr}.{at.attr} " + f"(enum_members={dbg_mc})", "ATTR") + return None + + # FakeDuck: 影子结构体属性读取 (str 变量的 per-instance 字段) + # a.__data__ / a.__mbuddy__ → GEP {a}__meta__[field] 并加载 + # 对齐 TPC HandlesExprAttr.py L433-442:对象是 Name 变量且存在 + # {var}__meta__ 影子(fd2 创建)时,属性访问改走影子结构体。 + if at.value.kind() == ast.ASTKind.Name and trans is not None: + fd_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if fd_nm.id is not None: + # pool 持久缓冲(f-string 栈缓冲在函数返回后失效) + fd_meta_buf: str = pool.alloc(256) + if fd_meta_buf is not None: + viperlib.snprintf(fd_meta_buf, 256, "%s__meta__", fd_nm.id) + fd_meta_v: llvmlite.Value | t.CPtr = HandlesVar.lookup_current( + trans.SymTab, fd_meta_buf) + if fd_meta_v is not None: + fd_sty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type( + "_str") + if fd_sty is not None: + fd_fe: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(fd_sty, at.attr) + if fd_fe is not None and fd_fe.Ty is not None: + fd_fp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, fd_sty, fd_fe.Ty, fd_meta_v, fd_fe.Index) + if fd_fp is not None: + return llvmlite.build_load(builder, fd_fe.Ty, fd_fp) # 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy # 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用并加载 @@ -4312,7 +4763,7 @@ class ExprGenMixin: if ma_is_var is None: # 先检查是否是 CDefine 编译期常量 (如 ast.CONST_INT / win32base.INVALID_HANDLE_VALUE) # 模块属性访问的 CDefine 常量需要跨模块查找源 .vp / .vpi 文件 - cdef_ma_val: int = HandlesType.lookup_cdefine_constant(at.attr) + cdef_ma_val: t.CInt64T = HandlesType.lookup_cdefine_constant(at.attr) cd_local: int = HandlesType.is_cdefine_found() if cd_local == 0: # 确定真实模块名:优先从 _from_imports 解析别名 @@ -4344,11 +4795,18 @@ class ExprGenMixin: viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name) cdef_ma_val = self._lookup_cross_module_cdefine(pool, at.attr, ma_fi) if HandlesType.is_cdefine_found() != 0: + # 跨模块字符串 CDefine(如 pyzlib.ZLIB_VERSION = "1.3.2")→ + # 字符串全局常量(test_pyzlib.vp L49 ver = pyzlib.ZLIB_VERSION) + if HandlesType.is_cdefine_str() != 0: + cdef_str_v: str = HandlesType.lookup_cdefine_string(at.attr) + if cdef_str_v is not None: + return self.create_global_string( + builder, pool, mod, cdef_str_v, trans) # 跨模块浮点 CDefine(如 vipermath.U_M_PI)→ 浮点常量 dchk_ma: double = HandlesType.lookup_cdefine_double(at.attr) if HandlesType.is_cdefine_double() != 0: return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_ma) - return llvmlite.const_int32(pool, cdef_ma_val) + return self._const_int_for_cdefine(pool, cdef_ma_val) # 检查 nm_ma.id.at.attr 是否是已导入子模块 (如 w32.win32file) # 如果是子模块,不应创建全局变量,让嵌套属性访问路径处理 is_submod: int = 0 @@ -4405,6 +4863,14 @@ class ExprGenMixin: if mod_gv_ref is not None and mod_gv_ref.Ty is not None: load_ty_mod: llvmlite.LLVMType | t.CPtr = mod_gv_ref.Ty.Pointee if load_ty_mod is not None: + # 模块级全局数组(如 zdef.zdeflate_len_base: CArray[T, N]): + # 按 C 语义衰减为基址返回(对齐 translate_name_value 局部 + # 数组分支 L3383-3388),load [N x T] 数组值无法作 GEP 基址。 + match load_ty_mod: + case llvmlite.LLVMType.Array(_, _): + return mod_gv_ref + case _: + pass return llvmlite.build_load(builder, load_ty_mod, mod_gv_ref) # 嵌套模块属性访问 (w32.win32file.GENERIC_READ → 查找全局 @GENERIC_READ) @@ -4433,7 +4899,7 @@ class ExprGenMixin: # 未找到全局: 尝试 CDefine 常量查找 # 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问, # 先从限定模块的 .vpi 文件中查找 CDefine 常量值并内联 - cdef_val_nested: int = HandlesType.lookup_cdefine_constant(at.attr) + cdef_val_nested: t.CInt64T = HandlesType.lookup_cdefine_constant(at.attr) if HandlesType.is_cdefine_found() == 0: # 构建临时 from_imports "attr:module" 用于跨模块查找 tmp_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(qual_buf) + 2 @@ -4446,7 +4912,7 @@ class ExprGenMixin: dchk_nst: double = HandlesType.lookup_cdefine_double(at.attr) if HandlesType.is_cdefine_double() != 0: return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nst) - return llvmlite.const_int32(pool, cdef_val_nested) + return self._const_int_for_cdefine(pool, cdef_val_nested) # CDefine 未找到: fail-fast 终止编译(不再创建未定义的全局变量引用) # 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义, # 导致 llc 报 "use of undefined value" 错误。 @@ -4577,6 +5043,23 @@ class ExprGenMixin: cur_sha1: str = trans.ModuleSha1 field_info = HandlesStruct.lookup_field_by_class( base_class, at.attr, cur_sha1) + # REnum 变体字段别名回退:base_class 是 REnum 类时,变体 + # 字段名(如 LLVMType.Pointee → _p2 槽位)不是普通字段, + # lookup_field_by_class 查不到(LLVMType"字段 Pointee 未找到" + # 根因;主路径 L4989-5004 的 REnum 别名查找因 struct_ty 简化 + # 为 i8、find_struct_by_type 失败被跳过) + if field_info is None \ + and HandlesClassDef._is_renum_class_name(base_class) != 0: + rn_alias: int = HandlesClassDef._lookup_renum_field_alias( + base_class, at.attr) + if rn_alias >= 1: + rn_se: HandlesStruct.StructEntry | t.CPtr = \ + HandlesStruct.find_struct(base_class) + if rn_se is not None: + rn_fe: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct._get_field_entry(rn_se, rn_alias) + if rn_fe is not None and rn_fe.Ty is not None: + field_info = rn_fe # 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 # 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)), # 需用实际结构体类型做 GEP,否则 GEP i8 失败 @@ -4630,6 +5113,11 @@ class ExprGenMixin: return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty) return loaded_val # fast-fail: 字段未找到,立即终止编译(不再打印诊断后返回 None) + # Phase A 依赖未就绪(_deps_pending,父类/依赖类未注册致字段缺失): + # 跳过本属性访问交给下遍重译(对齐 get_attribute_ptr L5541-5547), + # 避免 fail-fast 中断多遍收敛;依赖就绪后仍缺失则走下方真实报错。 + if trans is not None and trans._deps_pending != 0: + return None if trans is not None: diag_annot: str = None diag_var_nm: str = None @@ -4928,20 +5416,25 @@ class ExprGenMixin: else: obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id) - # 模块属性访问 (写路径): sys._mbuddy = mb → store 到全局变量 @_mbuddy - # 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用 - if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None: - nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) - if nm_wma.id is not None: + # 模块属性访问 (写路径): sys._mbuddy = mb / os._win32._mbuddy = v + # → store 到模块全局变量。当 obj 是已导入模块名(Name 或嵌套 Attribute + # 模块路径)且不是普通变量时,查找/创建全局变量引用。 + if obj_ptr is None and trans is not None: + mod_path_c: str = self._attr_module_path_str(pool, at.value) + if mod_path_c is not None: # CDefine 常量不可写: 报 FATAL 错误,不创建未定义全局 - if self._is_cdefine_name_pattern(at.attr) != 0: - err_buf_wma: str = pool.alloc(256) - if err_buf_wma is not None: - viperlib.snprintf(err_buf_wma, 256, - "[CD] FATAL: Cannot assign to CDefine constant '%s.%s'\n", - nm_wma.id, at.attr) - VLogger.error(err_buf_wma, "CD") - return None + # (仅单层 Name 模块可精确判 CDefine) + if at.value.kind() == ast.ASTKind.Name: + nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if nm_wma.id is not None: + if self._is_cdefine_name_pattern(at.attr) != 0: + err_buf_wma: str = pool.alloc(256) + if err_buf_wma is not None: + viperlib.snprintf(err_buf_wma, 256, + "[CD] FATAL: Cannot assign to CDefine constant '%s.%s'\n", + nm_wma.id, at.attr) + VLogger.error(err_buf_wma, "CD") + return None # 使用 i8* 作为类型提示 (模块级变量通常存储指针) i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) wma_ref: llvmlite.Value | t.CPtr = self._resolve_module_attribute_global( @@ -5009,6 +5502,21 @@ class ExprGenMixin: cur_sha1 = trans.ModuleSha1 field_info = HandlesStruct.lookup_field_by_class( base_class, at.attr, cur_sha1) + # REnum 变体字段别名回退(同 translate_attribute L5046-5062): + # get_attribute_ptr 赋值路径的按需过滤同样需要——struct_ty 简化 + # i8 时主路径 REnum 别名查找被跳过(LLVMType.Pointee 报错根因) + if field_info is None \ + and HandlesClassDef._is_renum_class_name(base_class) != 0: + rn_alias_g: int = HandlesClassDef._lookup_renum_field_alias( + base_class, at.attr) + if rn_alias_g >= 1: + rn_se_g: HandlesStruct.StructEntry | t.CPtr = \ + HandlesStruct.find_struct(base_class) + if rn_se_g is not None: + rn_fe_g: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct._get_field_entry(rn_se_g, rn_alias_g) + if rn_fe_g is not None and rn_fe_g.Ty is not None: + field_info = rn_fe_g # 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 # 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)), # 需用实际结构体类型做 GEP,否则 GEP i8 失败 @@ -5036,6 +5544,12 @@ class ExprGenMixin: return llvmlite.build_gep_struct( builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index) # fast-fail: 字段未找到,立即终止编译(不再打印诊断后返回 None) + # Phase A 依赖未就绪(_deps_pending,父类/依赖类未注册致字段缺失, + # 如 Generator.vp 先于 TypeGenMixin.vp 编译时 self.TraceFrames): + # 跳过本属性写入交给下遍重译(对齐 HandlesAssign L69-78), + # 避免 fail-fast 中断多遍收敛;依赖就绪后仍缺失则走下方真实报错。 + if trans is not None and trans._deps_pending != 0: + return None if trans is not None: diag_annot_g: str = None diag_var_nm_g: str = None @@ -5156,7 +5670,18 @@ class ExprGenMixin: trans.SymTab, nm.id) if ve is None: return None - return ve.AnnotClassName + acn_n: str = ve.AnnotClassName + if acn_n is None: + return None + # 去模块前缀:json.JsonValue → JsonValue。AnnotClassName 可能带 + # 模块限定(extract_class_name_from_annotation 的 Attribute 分支返回 + # "memhub.MemBuddy" 形式),而 find_struct/_cls_has_dunder/ + # get_struct_sha1 查找都用裸类名(struct 注册名)。泛型名 + # (list[str] 含 '[')不截断(方括号内可能有点号)。 + acn_dot_n: str = string.strrchr(acn_n, 46) + if acn_dot_n is not None and string.strchr(acn_n, 91) is None: + acn_n = acn_dot_n + 1 + return acn_n # BinOp/UnaryOp: 运算符重载返回同类对象 if k == ast.ASTKind.BinOp: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) @@ -5411,6 +5936,14 @@ class ExprGenMixin: if found_func is None and trans is not None and trans.ModuleSha1 is not None: if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0: return None + # 跨模块类:stub 可能未注入,方法存在性以依赖模块 vpi stub 为准。 + # vpi 未命中(依赖模块确实无此 dunder)→ 回退原生比较(icmp), + # 绝不发幽灵 call——`call @{cls_sha1}.{Class}.{dunder}` 链接 undefined + # (MemBuddy 无 __eq__ 时 os._mbuddy == sys._mbuddy,SysLibTest 根因)。 + vpi_dup: llvmlite.LLVMType | t.CPtr = HandlesExprCall._lookup_method_ret_ty_via_vpi( + pool, cls_sha1, search_class, dunder) + if vpi_dup is None: + return None # 构建 extra_args 数组(仅含 rhs 一个参数) extra_args: t.CSizeT | t.CPtr = pool.alloc(8) diff --git a/App/lib/core/Gen/FuncGenMixin.vp b/App/lib/core/Gen/FuncGenMixin.vp index 4b18f8f..0ff8f25 100644 --- a/App/lib/core/Gen/FuncGenMixin.vp +++ b/App/lib/core/Gen/FuncGenMixin.vp @@ -285,6 +285,44 @@ class FuncGenMixin: return name return self._mangle_name(trans, name) + # ============================================================ + # _body_has_raise - 函数体是否含 raise(非 StopIteration) + # + # 对齐 TPC HandlesFunctions.py L216-243 _body_contains_raise:函数签名 + # 追加 __eh_msg_out__/__eh_code_out__ 输出参数的条件。v1 只扫函数体 + # 顶层 raise(嵌套 if/for/try 内的 raise 未覆盖,后续扩展)。 + # ============================================================ + def _body_has_raise(self, fd: ast.FunctionDef | t.CPtr) -> int: + """函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否""" + if fd is None or fd.children is None: + return 0 + body_f: list[ast.AST | t.CPtr] | t.CPtr = fd.children + bn_f: t.CSizeT = len(body_f) + for bi_f in range(bn_f): + stmt_f: ast.AST | t.CPtr = body_f.get(bi_f) + if stmt_f is None: + continue + if stmt_f.kind() != ast.ASTKind.Raise: + continue + rn_f: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(stmt_f) + if rn_f is None or rn_f.exc is None: + continue + ek_f: int = rn_f.exc.kind() + is_stop_f: int = 0 + if ek_f == ast.ASTKind.Name: + enm_f: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn_f.exc) + if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0: + is_stop_f = 1 + elif ek_f == ast.ASTKind.Call: + ecl_f: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn_f.exc) + if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name: + ecn_f: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl_f.func) + if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: + is_stop_f = 1 + if is_stop_f == 0: + return 1 + return 0 + # ============================================================ # forward_declare_functions - 预扫描所有顶层 FunctionDef,创建前向声明 # @@ -539,6 +577,69 @@ class FuncGenMixin: has_export = 1 if (has_extern != 0 or has_state != 0) and is_extern_decl == 0: is_extern_decl = 1 + # @t.TLS 一次性初始化标记(对齐 TPC HandlesFunctions.py L1223-1231) + has_tls: int = 0 + if fd.decorator_list is not None: + has_tls = HandlesClassDef._has_decorator(fd.decorator_list, "TLS") + + # 自定义行为装饰器检测(@log/@skip/@repeat(3) 等):非 t 存储标记、 + # 非内置(staticmethod/property/classmethod)的 Name/Name(args) 装饰器。 + # 对齐 TPC HandlesFunctions.py L1161-1189 + DecoratorPass.py。首版取 + # decorator_list 中第一个自定义装饰器(Python 最外层),链式嵌套后续扩展。 + has_custom_decor: int = 0 + decor_handler: str = None + decor_arg0: t.CInt64T = 0 + if fd.decorator_list is not None: + ddn: t.CSizeT = len(fd.decorator_list) + for ddi in range(ddn): + deco: ast.AST | t.CPtr = fd.decorator_list.get(ddi) + if deco is None: + continue + dk: int = deco.kind() + dname: str = None + if dk == ast.ASTKind.Name: + dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(deco) + dname = dnm.id + elif dk == ast.ASTKind.Call: + dcl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco) + if dcl.func is not None and dcl.func.kind() == ast.ASTKind.Name: + dnm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(dcl.func) + dname = dnm2.id + # @repeat(3):取首个常量 int 参数(对齐 TPC L1182-1184) + if dcl.args is not None and len(dcl.args) >= 1: + da0: ast.AST | t.CPtr = dcl.args.get(0) + if da0 is not None and da0.kind() == ast.ASTKind.Constant: + dc0: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(da0) + if dc0.const_kind == ast.CONST_INT: + decor_arg0 = dc0.int_val + if dname is None: + continue + # 跳过内置装饰器 + if string.strcmp(dname, "staticmethod") == 0 \ + or string.strcmp(dname, "property") == 0 \ + or string.strcmp(dname, "classmethod") == 0: + continue + # 跳过 t 模块存储类标记(@t.CExport/@t.CExtern/@t.State/@t.TLS/ + # @t.CStatic/@t.CInline/@t.CVTable/@t.NoVTable/@t.Object/@t.CEnum + # /@t.CUnion/@t.REnum/@t.CDefine 等裸名形式——Attribute 形式 + # @t.xxx 的 kind 是 Attribute,不进入本检测) + if string.strcmp(dname, "CExport") == 0 \ + or string.strcmp(dname, "CExtern") == 0 \ + or string.strcmp(dname, "State") == 0 \ + or string.strcmp(dname, "TLS") == 0 \ + or string.strcmp(dname, "CStatic") == 0 \ + or string.strcmp(dname, "CInline") == 0 \ + or string.strcmp(dname, "CVTable") == 0 \ + or string.strcmp(dname, "NoVTable") == 0 \ + or string.strcmp(dname, "Object") == 0 \ + or string.strcmp(dname, "CEnum") == 0 \ + or string.strcmp(dname, "CUnion") == 0 \ + or string.strcmp(dname, "REnum") == 0 \ + or string.strcmp(dname, "CDefine") == 0: + continue + has_custom_decor = 1 + decor_handler = dname + break # SHA1 命名空间:t.CExtern/t.State/t.CExport 不加前缀,直接用裸名 # (裸名映射到 C 标准库符号,带 sha1 前缀会导致 undefined reference) @@ -707,6 +808,24 @@ class FuncGenMixin: if ags.vararg is not None: func.IsVarArg = 1 + # try-except:函数体含 raise → 追加 __eh_msg_out__(i8**)/ + # __eh_code_out__(i32*)输出参数(对齐 TPC HandlesFunctions.py + # L706-711;main 除外)。被调函数把异常编码/消息写入调用方传入 + # 的指针,调用方 _check_eh_return 检测。 + # 注意:必须在参数添加块(if args_node is not None:)外——无参函数 + # (def f())的 args_node 为 None,若放在其内会被跳过,导致函数体 + # store %__eh_code_out__ 但签名无此参数(llc: use of undefined value)。 + if string.strcmp(fd.name, "main") != 0: + if self._body_has_raise(fd) != 0: + i8_ty_eh: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_eh: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_eh) + i8_ptr_ptr_ty_eh: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_eh) + i32_ty_eh: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i32_ptr_ty_eh: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i32_ty_eh) + if i8_ptr_ptr_ty_eh is not None and i32_ptr_ty_eh is not None: + llvmlite.add_param(pool, func, i8_ptr_ptr_ty_eh, "%__eh_msg_out__") + llvmlite.add_param(pool, func, i32_ptr_ty_eh, "%__eh_code_out__") + # 参数添加完成,补全全局函数表的 TypeStr HandlesExprCall.update_global_func_typestr(trans.Pool, fd.name, func) @@ -721,6 +840,72 @@ class FuncGenMixin: return 0 llvmlite.position_at_end(func_builder, entry_blk) + # @t.TLS 一次性初始化守卫(对齐 TPC HandlesFunctions.py L1763-1792): + # 首次调用执行函数体并置全局标志 __tls_init_done_{name}=1,后续调用 + # 直接返回默认值。TPV 此前未实现 @t.TLS,_init_tls 每次 TlsAlloc 导致 + # provider 栈跨调用丢失(FakeDuckTest Test 9 注入失败根因)。 + if has_tls != 0: + tls_flag_name: str = pool.alloc(128) + if tls_flag_name is not None: + viperlib.snprintf(tls_flag_name, 128, "__tls_init_done_%s", fd.name) + tls_flag_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.create_global_int( + pool, mod, tls_flag_name, 1, 0) + if tls_flag_gv is not None: + i1_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_t is not None: + # GlobalVariable 不是 Value 子类(字段布局不同),build_load/store + # 按 Value 读 ptr.Ty/ptr.Name 会错位(Ty 读到 Name → void,Name 为空)。 + # 与 create_global_string 一致:构造 Value 视图(Ty=Ptr(i1), Name="@name")。 + tls_gv_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i1_ty_t) + tls_ref_name: str = pool.alloc(64) + if tls_ref_name is not None: + viperlib.snprintf(tls_ref_name, 64, "@%s", tls_flag_name) + tls_flag_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, tls_gv_ptr_ty, tls_ref_name) + if tls_flag_ref is not None: + tls_flag_ld: llvmlite.Value | t.CPtr = llvmlite.build_load( + func_builder, i1_ty_t, tls_flag_ref) + # 块名唯一化(create_block 不自动编号,同函数同名 label 冲突) + lc_t: int = trans._label_counter + trans._label_counter = lc_t + 1 + tls_bb_buf: str = pool.alloc(64) + if tls_bb_buf is not None: + viperlib.snprintf(tls_bb_buf, 64, "tls.skip.%d", lc_t) + tls_skip_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, tls_bb_buf) + viperlib.snprintf(tls_bb_buf, 64, "tls.cont.%d", lc_t) + tls_cont_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( + pool, func, tls_bb_buf) + if tls_skip_bb is not None and tls_cont_bb is not None: + llvmlite.build_cond_br( + func_builder, tls_flag_ld, tls_skip_bb, tls_cont_bb) + # skip 块:按返回类型生成默认值直接返回 + llvmlite.position_at_end(func_builder, tls_skip_bb) + match ret_ty: + case llvmlite.LLVMType.Void(): + llvmlite.build_ret_void(func_builder) + case llvmlite.LLVMType.Int(_): + tlz0: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, ret_ty, 0, "0") + if tlz0 is not None: + llvmlite.build_ret(func_builder, tlz0) + case llvmlite.LLVMType.Float(_): + tlf0: llvmlite.Value | t.CPtr = llvmlite.ConstFloat( + pool, ret_ty, 0.0) + if tlf0 is not None: + llvmlite.build_ret(func_builder, tlf0) + case _: + tln0: llvmlite.Value | t.CPtr = llvmlite.ConstNull( + pool, ret_ty, "null") + if tln0 is not None: + llvmlite.build_ret(func_builder, tln0) + # cont 块:置标志=1,继续函数体 + llvmlite.position_at_end(func_builder, tls_cont_bb) + tlone: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, i1_ty_t, 1, "1") + if tlone is not None: + llvmlite.build_store(func_builder, tlone, tls_flag_ref) + # 进入函数作用域(嵌套符号表) HandlesVar.enter_scope(trans.SymTab, SCOPE_FUNCTION) @@ -777,10 +962,11 @@ class FuncGenMixin: if alloca is not None: HandlesVar.define_var(trans.SymTab, arg2.arg, alloca) # 存储原始类型注解的类名(方法调用检测时,Ptr(i8) 回退到类名查找结构体) + # 传 pool:泛型 Subscript 返回 mangled name(对齐 HandlesAnnAssign 修复) cls_nm: str = None if arg2.annotation is not None: cls_nm = HandlesType.extract_class_name_from_annotation( - arg2.annotation, imported_modules) + arg2.annotation, imported_modules, pool) if cls_nm is not None: HandlesVar.set_var_annot_class_name( trans.SymTab, arg2.arg, cls_nm) @@ -791,37 +977,68 @@ class FuncGenMixin: pool, param_ty2, pname2) llvmlite.build_store(func_builder, param_val, alloca) - # *args 变长参数:创建 va_list 缓冲区 + 调用 va_start + 注册到符号表 - # 生成 IR: - # %args_va = alloca [24 x i8], align 16 ; va_list 缓冲区(Win64 __va_list_tag) - # %args = bitcast [24 x i8]* %args_va to i8* - # call void @llvm.va_start(i8* %args) ; 初始化 va_list - # 用户函数体内可通过 args 名字读取 va_list 地址(i8*),或用 arg(type) 糖函数 - if ags2.vararg is not None: - vararg_node: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(ags2.vararg) - if vararg_node is not None and vararg_node.arg is not None: - vararg_name_str: str = vararg_node.arg - # va_list 缓冲区 24 字节(Win64 的 __va_list_tag = {i32,i32,i8*,i8*})+ 16 对齐 - # (对齐 TPC HandlesFunctions.py:1739-1741) - i8_ty_va: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) - i8_ptr_ty_va: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_va) - va_buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty_va, 24) - va_list_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca_aligned( - func_builder, va_buf_ty, 16) - if va_list_alloca is not None: - # i8* 地址(指向 24 字节 va_list 缓冲区)——vararg 名的值语义 - va_list_addr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( - func_builder, va_list_alloca, i8_ptr_ty_va) - if va_list_addr is not None: - # 注册到符号表(translate_name_value 对 vararg 名特殊处理返回地址) - HandlesVar.define_var(trans.SymTab, vararg_name_str, va_list_alloca) - # 调用 @llvm.va_start(i8* %va_list_addr) - void_ty_va: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool) - llvmlite.build_call(func_builder, "llvm.va_start", - va_list_addr, 1, void_ty_va, 0) - # 保存到 trans 供 arg(type) 糖函数与 vararg 名翻译使用 - trans._va_list_ptr = va_list_addr - trans._vararg_name = vararg_name_str + # *args 变长参数:创建 va_list 缓冲区 + 调用 va_start + 注册到符号表 + # 生成 IR: + # %args_va = alloca [24 x i8], align 16 ; va_list 缓冲区(Win64 __va_list_tag) + # %args = bitcast [24 x i8]* %args_va to i8* + # call void @llvm.va_start(i8* %args) ; 初始化 va_list + # 用户函数体内可通过 args 名字读取 va_list 地址(i8*),或用 arg(type) 糖函数 + # 注意:必须在参数 alloca 段(if args_node is not None:)内但 EH 块外—— + # 无 raise 的函数(viperlib.sprintf 等)同样有 *args,若放在 EH 块内 + # 会被跳过,args 未注册到符号表 → NameError: 未定义名称 'args'。 + if ags2.vararg is not None: + vararg_node: ast.Arg | t.CPtr = (ast.Arg | t.CPtr)(ags2.vararg) + if vararg_node is not None and vararg_node.arg is not None: + vararg_name_str: str = vararg_node.arg + # va_list 缓冲区 24 字节(Win64 的 __va_list_tag = {i32,i32,i8*,i8*})+ 16 对齐 + # (对齐 TPC HandlesFunctions.py:1739-1741) + i8_ty_va: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_va: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_va) + va_buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty_va, 24) + va_list_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca_aligned( + func_builder, va_buf_ty, 16) + if va_list_alloca is not None: + # i8* 地址(指向 24 字节 va_list 缓冲区)——vararg 名的值语义 + va_list_addr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + func_builder, va_list_alloca, i8_ptr_ty_va) + if va_list_addr is not None: + # 注册到符号表(translate_name_value 对 vararg 名特殊处理返回地址) + HandlesVar.define_var(trans.SymTab, vararg_name_str, va_list_alloca) + # 调用 @llvm.va_start(i8* %va_list_addr) + void_ty_va: llvmlite.LLVMType | t.CPtr = llvmlite.Void(pool) + llvmlite.build_call(func_builder, "llvm.va_start", + va_list_addr, 1, void_ty_va, 0) + # 保存到 trans 供 arg(type) 糖函数与 vararg 名翻译使用 + trans._va_list_ptr = va_list_addr + trans._vararg_name = vararg_name_str + + # try-except:新函数作用域 EH 状态从零开始。必须先保存外层函数 + # (调用方/包围函数)的 EH 输出参数,翻译完本函数后再恢复——否则 + # 下方设置本函数 EH 参数会覆盖外层值,恢复时 old_eh_* 拿到的是 + # 本函数自身参数(嵌套函数/多函数链 EH 状态错乱)。 + old_eh_msg_out: llvmlite.Value | t.CPtr = trans._eh_msg_out_param + old_eh_code_out: llvmlite.Value | t.CPtr = trans._eh_code_out_param + old_cur_func_has_eh: int = trans._cur_func_has_eh + trans._eh_msg_out_param = None + trans._eh_code_out_param = None + trans._cur_func_has_eh = 0 + + # try-except:保存当前函数的 EH 输出参数(__eh_msg_out__/__eh_code_out__) + # 供 translate_raise / UnhandledBB 写入并向上传播。无 EH 参数的函数置空。 + if string.strcmp(fd.name, "main") != 0 and self._body_has_raise(fd) != 0: + i8_ty_eh2: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_eh2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_eh2) + i8_ptr_ptr_ty_eh2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_eh2) + i32_ty_eh2: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i32_ptr_ty_eh2: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i32_ty_eh2) + if i8_ptr_ptr_ty_eh2 is not None and i32_ptr_ty_eh2 is not None: + msg_out_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, i8_ptr_ptr_ty_eh2, "%__eh_msg_out__") + code_out_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, i32_ptr_ty_eh2, "%__eh_code_out__") + trans._eh_msg_out_param = msg_out_ref + trans._eh_code_out_param = code_out_ref + trans._cur_func_has_eh = 1 # 保存模块级作用域状态(仅非变量表相关) old_func: llvmlite.Function | t.CPtr = trans._cur_func @@ -872,39 +1089,89 @@ class FuncGenMixin: # 如果函数体最后一条语句不是 Return,添加隐式 ret last_is_return: int = 0 + # 追加89:bn3 提前初始化,避免 body is None 时下方 fail-fast 分支 + # 引用未定义的 bn3(追加81 同款 NameError 风险) + bn3: t.CSizeT = 0 if body is not None: - bn3: t.CSizeT = len(body) + bn3 = 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: if llvmlite.builder_cur_block_is_terminated(func_builder) == 0: - # 根据返回类型生成正确的零值返回 - # 必须覆盖浮点返回类型:double/float 函数生成 ConstFloat(0.0), - # 否则默认 ret i32 0 类型不匹配(llc: value doesn't match function result type) + # 追加89:编译器不替代码发明返回值——函数流程真实掉出末尾而 + # 未显式 return 时 fail-fast(要求补 return,Python traceback + # 风格定位到函数)。三种合法收尾: + # ① void 函数掉出末尾 → ret void(无值可发明,Python 语义) + # ② 末句是控制流语句(match/if 全分支 return)且当前块 + # 未 terminated → merge 块结构性不可达 → unreachable + # ③ 无返回类型注解的函数(fd.returns is None,如 + # ast/base.vp _set_pos 过程风格赋值函数)→ 视为无返回值 + # 函数,默认生成零值返回(用户决策:没有返回值默认不用 + # return;ConstZero 按推断出的 ret_ty 构造,类型匹配) + # 有 -> 注解且末句是普通语句掉出末尾 → fail-fast(不得替代码 + # 发明返回值,也不做 noreturn 函数名白名单——错误终止路径 + # 也必须在源码层面保证块收尾,如显式 return 样板) + # 末句是否为控制流语句(If/Match/While/For/Try/With): + # 控制流语句翻译后当前块未 terminated 表示 merge 块结构性 + # 不可达(所有分支均已 return/terminated)——对非 void 函数 + # 生成 unreachable 而非 fail-fast(GCC/Clang 对 fall-through + # 视为 UB 同语义;追加89 修正 get_struct_name 全 case return + # 被错判"不可能抵达") + last_is_ctrl: int = 0 + if bn3 > 0: + last_stmt3: ast.AST | t.CPtr = body.get(bn3 - 1) + if last_stmt3 is not None: + k3: int = last_stmt3.kind() + if k3 == ast.ASTKind.If or k3 == ast.ASTKind.While or \ + k3 == ast.ASTKind.For or k3 == ast.ASTKind.Try or \ + k3 == ast.ASTKind.With or k3 == ast.ASTKind.Match: + last_is_ctrl = 1 _is_void: int = 0 - _is_ptr: int = 0 - _is_float: int = 0 if ret_ty is not None: match ret_ty: case llvmlite.LLVMType.Void(): _is_void = 1 - case llvmlite.LLVMType.Ptr(_pe): - _is_ptr = 1 - case llvmlite.LLVMType.Float(_fb): - _is_float = 1 + case _: + pass if _is_void != 0: llvmlite.build_ret_void(func_builder) - elif _is_ptr != 0: - _null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, ret_ty, "null") - llvmlite.build_ret(func_builder, _null_val) - elif _is_float != 0: - _zero_f: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, ret_ty, 0.0) - llvmlite.build_ret(func_builder, _zero_f) + elif last_is_ctrl != 0: + # 非 void 函数末句是控制流且块未 terminated:merge 块 + # 结构性不可达(不可能抵达),LLVM 语义 unreachable + llvmlite.build_unreachable(func_builder) + elif fd.returns is None: + # 无返回类型注解 → 无返回值过程函数,默认零值返回 + if ret_ty is not None: + zero_val: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, ret_ty) + if zero_val is not None: + llvmlite.build_ret(func_builder, zero_val) + else: + HandlesType.fatal_error(node, "函数流程掉出末尾但未显式 return", "TypeError") + else: + HandlesType.fatal_error(node, "函数流程掉出末尾但未显式 return", "TypeError") else: - _zero_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) - llvmlite.build_ret(func_builder, _zero_val) + # fail-fast:有 -> 注解但流程真实掉出末尾(定位到函数定义 + # 节点,fatal_error 输出 traceback 风格错误) + if ret_ty is not None: + # llvmlite 无 type_get_name API,类型序列化用真实接口 + # TypePrint(buf, size, ty, pool)(追加89 修正) + rt_name: str = pool.alloc(64) + if rt_name is not None: + llvmlite.TypePrint(rt_name, 64, ret_ty, pool) + if rt_name[0] == '\0': + rt_name = "?" + err_fn: str = pool.alloc(256) + if err_fn is not None: + viperlib.snprintf( + err_fn, 256, + "函数 '%s' 流程掉出函数末尾但未显式 return(返回类型 %s)," + "请补显式 return(如 return 0 / return null)", fd.name, rt_name) + HandlesType.fatal_error(node, err_fn, "TypeError") + HandlesType.fatal_error(node, "函数流程掉出末尾但未显式 return", "TypeError") + else: + HandlesType.fatal_error(node, "函数流程掉出末尾但未显式 return", "TypeError") # 恢复模块级作用域(退出函数作用域) if tp_names_fn is not None: @@ -917,9 +1184,348 @@ class FuncGenMixin: trans._closure_env_count = old_env_count trans._va_list_ptr = old_va_list_ptr trans._vararg_name = old_vararg_name + # 恢复外层函数(调用方)的 EH 输出参数——本函数体内的 raise 传播 + # 使用本函数参数,翻译结束必须交还外层作用域,否则嵌套函数/链式 + # 调用后 EH 状态残留导致 raise 写入错误的 out 指针。 + trans._eh_msg_out_param = old_eh_msg_out + trans._eh_code_out_param = old_eh_code_out + trans._cur_func_has_eh = old_cur_func_has_eh + + # 自定义装饰器:注册 + 生成 wrapper(对齐 TPC DecoratorPass.run)。 + # 仅模块级函数(fd 是顶层定义);wrapper 名 __decor_wrap_{裸函数名}。 + # 调用点重写经 lookup_decor_wrapper(translate_call 拦截原函数名)。 + if has_custom_decor != 0: + wb_name: str = pool.alloc(128) + if wb_name is not None: + viperlib.snprintf(wb_name, 128, "__decor_wrap_%s", fd.name) + HandlesClassDef.register_decorator(pool, fd.name, wb_name, decor_arg0) + self._gen_decor_wrapper( + trans, pool, mod, func, fd.name, wb_name, + decor_handler, decor_arg0) return 0 + # ============================================================ + # _gen_decor_wrapper - 生成装饰器 wrapper 函数(对齐 TPC DecoratorPass.py + # _generate_single_wrapper v4) + # + # v4 Wrapper 结构(最外层含递归保护): + # entry → 递归保护检查(最外层) + # recursive_call → 递归时直接调用原函数,跳过所有装饰 + # decorated_entry → 置递归标志,分配 ctx/args/ret,调用 handler 前置 + # loop_header → 计数器 i < n_calls(TPV 用 alloca 计数器替代 phi) + # loop.body → 从 args_struct 读回参数,调用原函数 + # post → 调用 handler 后置,清递归标志,返回结果 + # + # handler 调用约定:i32 decor(i8* ctx, i8* func_name, i32 phase, + # i8* args_ptr, i8* ret_ptr, i8* decor_args_ptr) + # 前置返回:0=跳过原函数,1=调用一次,N=循环 N 次 + # ============================================================ + def _gen_decor_wrapper(self, trans: HT.Translator | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + func: llvmlite.Function | t.CPtr, + func_name: str, + wrapper_name: str, + handler_name: str, + decor_arg0: t.CInt64T) -> llvmlite.Function | t.CPtr: + """生成装饰器 wrapper 函数,返回 wrapper 或 None""" + if trans is None or pool is None or mod is None or func is None: + return None + if func_name is None or wrapper_name is None or handler_name is None: + return None + ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(func) + if ret_ty is None: + return None + orig_call_name: str = llvmlite.function_get_name(func) + if orig_call_name 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) + + # ---- 收集原函数参数类型(按位置,最多 16 个)---- + n_params: int = 0 + param_tys: t.CSizeT | t.CPtr = pool.alloc(16 * 8) + if param_tys is None: + return None + string.memset(param_tys, 0, 16 * 8) + pp: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(func) + while pp is not None: + pty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(pp) + if pty is not None and n_params < 16: + param_tys[n_params] = t.CSizeT(pty) + n_params += 1 + pp = llvmlite.param_get_next(pp) + + # ---- 创建 wrapper 函数并添加参数(名字 %a0..%aN)---- + wrapper: llvmlite.Function | t.CPtr = llvmlite.create_function( + pool, mod, wrapper_name, ret_ty) + if wrapper is None: + return None + pi: int = 0 + for pi in range(n_params): + pname_w: str = pool.alloc(16) + if pname_w is not None: + viperlib.snprintf(pname_w, 16, "%%a%d", pi) + llvmlite.add_param(pool, wrapper, (llvmlite.LLVMType | t.CPtr)( + t.CVoid(param_tys[pi], t.CPtr)), pname_w) + + # ---- 创建块 ---- + entry_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, wrapper, "entry") + loop_h_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, wrapper, "loop.header") + loop_b_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, wrapper, "loop.body") + post_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, wrapper, "post") + if entry_bb is None or loop_h_bb is None or loop_b_bb is None or post_bb is None: + return None + + # 递归保护标志(最外层):@__decor_rec_{wrapper} = global i8 0 + rec_flag_ref: llvmlite.Value | t.CPtr = None + rec_flag_name: str = pool.alloc(128) + if rec_flag_name is not None: + viperlib.snprintf(rec_flag_name, 128, "__decor_rec_%s", wrapper_name) + llvmlite.create_global_int(pool, mod, rec_flag_name, 8, 0) + rec_ref_nm: str = pool.alloc(96) + if rec_ref_nm is not None: + viperlib.snprintf(rec_ref_nm, 96, "@%s", rec_flag_name) + rec_flag_ref = llvmlite.SSAValue(pool, llvmlite.Ptr(pool, i8_ty), rec_ref_nm) + + wb: llvmlite.IRBuilder | t.CPtr = llvmlite.new_builder(pool, wrapper) + if wb is None: + return None + + # 返回类型分类 + ret_is_void: int = 0 + match ret_ty: + case llvmlite.LLVMType.Void(): + ret_is_void = 1 + case _: + pass + + # ---- entry:递归保护检查(最外层)---- + llvmlite.position_at_end(wb, entry_bb) + loop_pred_bb: llvmlite.BasicBlock | t.CPtr = entry_bb + if rec_flag_ref is not None: + rec_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, wrapper, "recursive_call") + dec_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, wrapper, "decorated_entry") + if rec_bb is None or dec_bb is None: + return None + rec_val: llvmlite.Value | t.CPtr = llvmlite.build_load(wb, i8_ty, rec_flag_ref) + rec_zero: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i8_ty, 0, "0") + rec_is: llvmlite.Value | t.CPtr = llvmlite.build_icmp( + wb, llvmlite.ICMP_NE, rec_val, rec_zero) + llvmlite.build_cond_br(wb, rec_is, rec_bb, dec_bb) + # recursive_call:直接调用原函数返回(跳过所有装饰逻辑) + llvmlite.position_at_end(wb, rec_bb) + rec_args: llvmlite.Value | t.CPtr = None + ri: int = 0 + for ri in range(n_params): + an_r: str = pool.alloc(16) + if an_r is None: + continue + viperlib.snprintf(an_r, 16, "%%a%d", ri) + av_r: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, (llvmlite.LLVMType | t.CPtr)(t.CVoid(param_tys[ri], t.CPtr)), an_r) + if av_r is not None: + llvmlite.value_set_next(av_r, None) + if rec_args is None: + rec_args = av_r + else: + # 链尾追加 + rt: llvmlite.Value | t.CPtr = rec_args + while rt.Next is not None: + rt = rt.Next + llvmlite.value_set_next(rt, av_r) + if ret_is_void != 0: + llvmlite.build_call(wb, orig_call_name, rec_args, n_params, ret_ty, 0) + llvmlite.build_ret_void(wb) + else: + rc_res: llvmlite.Value | t.CPtr = llvmlite.build_call( + wb, orig_call_name, rec_args, n_params, ret_ty, 0) + if rc_res is not None: + llvmlite.build_ret(wb, rc_res) + else: + llvmlite.build_ret_void(wb) + # decorated_entry:置递归标志,继续装饰路径 + llvmlite.position_at_end(wb, dec_bb) + rec_one: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i8_ty, 1, "1") + if rec_one is not None: + llvmlite.build_store(wb, rec_one, rec_flag_ref) + loop_pred_bb = dec_bb + + # ---- 公共装饰路径(entry 或 decorated_entry)---- + # 1. ctx = alloca [32 x i8]; ctx_ptr = bitcast i8* + ctx_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, 32) + ctx_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(wb, ctx_ty) + ctx_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(wb, ctx_alloca, i8_ptr_ty) + + # 2. 打包函数参数到结构体 {param_types}(装饰器可读写 args_ptr) + args_alloca: llvmlite.Value | t.CPtr = None + args_ptr: llvmlite.Value | t.CPtr = None + if n_params > 0: + fields_head: llvmlite.ParamNode | t.CPtr = None + fk: int = 0 + for fk in range(n_params - 1, -1, -1): + pn_n: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node( + pool, (llvmlite.LLVMType | t.CPtr)(t.CVoid(param_tys[fk], t.CPtr))) + if pn_n is not None: + pn_n.Next = fields_head + fields_head = pn_n + args_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct( + pool, fields_head, n_params, None) + if args_ty is not None: + args_alloca = llvmlite.build_alloca(wb, args_ty) + fi2: int = 0 + for fi2 in range(n_params): + fld_ty: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)( + t.CVoid(param_tys[fi2], t.CPtr)) + fld_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + wb, args_ty, fld_ty, args_alloca, fi2) + if fld_ptr is not None: + fld_nm: str = pool.alloc(16) + if fld_nm is not None: + viperlib.snprintf(fld_nm, 16, "%%a%d", fi2) + fld_v: llvmlite.Value | t.CPtr = llvmlite.SSAValue( + pool, fld_ty, fld_nm) + if fld_v is not None: + llvmlite.build_store(wb, fld_v, fld_ptr) + args_ptr = llvmlite.build_bitcast(wb, args_alloca, i8_ptr_ty) + else: + args_ptr = llvmlite.ConstNull(pool, i8_ptr_ty, "null") + + # 3. 返回值存储(零初始化;跳过原函数时返回安全默认值) + ret_alloca: llvmlite.Value | t.CPtr = None + if ret_is_void == 0: + ret_alloca = llvmlite.build_alloca(wb, ret_ty) + ret_zero: llvmlite.Value | t.CPtr = None + match ret_ty: + case llvmlite.LLVMType.Int(_): + ret_zero = llvmlite.ConstInt(pool, ret_ty, 0, "0") + case llvmlite.LLVMType.Float(_): + ret_zero = llvmlite.ConstFloat(pool, ret_ty, 0.0) + case _: + ret_zero = llvmlite.ConstNull(pool, ret_ty, "null") + if ret_zero is not None and ret_alloca is not None: + llvmlite.build_store(wb, ret_zero, ret_alloca) + + # 4. 函数名字符串常量 + fn_str: llvmlite.Value | t.CPtr = HandlesExpr.create_global_string( + wb, pool, mod, func_name, trans) + if fn_str is None: + fn_str = llvmlite.ConstNull(pool, i8_ptr_ty, "null") + + # 5. handler 声明(未翻译时 declare,固定签名 i32 (i8*,i8*,i32,i8*,i8*,i8*)) + if HandlesExprCall.find_func_in_module(mod, handler_name) is None: + handler_d: llvmlite.Function | t.CPtr = llvmlite.create_declare( + pool, mod, handler_name, i32_ty) + if handler_d is not None: + llvmlite.add_param(pool, handler_d, i8_ptr_ty, "ctx") + llvmlite.add_param(pool, handler_d, i8_ptr_ty, "func_name") + llvmlite.add_param(pool, handler_d, i32_ty, "phase") + llvmlite.add_param(pool, handler_d, i8_ptr_ty, "args_ptr") + llvmlite.add_param(pool, handler_d, i8_ptr_ty, "ret_ptr") + llvmlite.add_param(pool, handler_d, i8_ptr_ty, "decor_args_ptr") + + # 6. 前置阶段:handler 返回调用次数 n_calls + ph0: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i32_ty, 0, "0") + null_ret: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null") + null_dec: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null") + # 参数链表(6 个独立值):ctx_ptr → fn_str → 0 → args_ptr → null → null + # 注意:不能 self 链接(null.Next = null 会让 build_call 遍历死循环) + if ctx_ptr is not None: + llvmlite.value_set_next(ctx_ptr, fn_str) + if fn_str is not None: + llvmlite.value_set_next(fn_str, ph0) + if ph0 is not None: + llvmlite.value_set_next(ph0, args_ptr) + if args_ptr is not None: + llvmlite.value_set_next(args_ptr, null_ret) + if null_ret is not None: + llvmlite.value_set_next(null_ret, null_dec) + n_calls: llvmlite.Value | t.CPtr = llvmlite.build_call( + wb, handler_name, ctx_ptr, 6, i32_ty, 0) + + # 7. 计数器 alloca(TPV 用 alloca 计数器替代 phi) + cnt_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(wb, i32_ty) + cnt_zero: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i32_ty, 0, "0") + if cnt_alloca is not None and cnt_zero is not None: + llvmlite.build_store(wb, cnt_zero, cnt_alloca) + llvmlite.build_br(wb, loop_h_bb) + + # 8. loop.header:cur = load cnt; cur < n_calls → body / post + llvmlite.position_at_end(wb, loop_h_bb) + cur_i: llvmlite.Value | t.CPtr = llvmlite.build_load(wb, i32_ty, cnt_alloca) + loop_cond: llvmlite.Value | t.CPtr = llvmlite.build_icmp( + wb, llvmlite.ICMP_SLT, cur_i, n_calls) + llvmlite.build_cond_br(wb, loop_cond, loop_b_bb, post_bb) + + # 9. loop.body:读回参数(装饰器可能已修改)→ 调用原函数 → store ret + llvmlite.position_at_end(wb, loop_b_bb) + call_args: llvmlite.Value | t.CPtr = None + if n_params > 0: + k: int = 0 + for k in range(n_params): + fld_ty_k: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)( + t.CVoid(param_tys[k], t.CPtr)) + fld_pk: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + wb, args_ty, fld_ty_k, args_alloca, k) + if fld_pk is None: + continue + fld_vk: llvmlite.Value | t.CPtr = llvmlite.build_load(wb, fld_ty_k, fld_pk) + if fld_vk is not None: + llvmlite.value_set_next(fld_vk, None) + if call_args is None: + call_args = fld_vk + else: + ct: llvmlite.Value | t.CPtr = call_args + while ct.Next is not None: + ct = ct.Next + llvmlite.value_set_next(ct, fld_vk) + res_v: llvmlite.Value | t.CPtr = llvmlite.build_call( + wb, orig_call_name, call_args, n_params, ret_ty, 0) + if ret_alloca is not None and res_v is not None: + llvmlite.build_store(wb, res_v, ret_alloca) + nxt_i: llvmlite.Value | t.CPtr = llvmlite.build_add( + wb, cur_i, llvmlite.ConstInt(pool, i32_ty, 1, "1")) + if nxt_i is not None and cnt_alloca is not None: + llvmlite.build_store(wb, nxt_i, cnt_alloca) + llvmlite.build_br(wb, loop_h_bb) + + # 10. post:handler 后置 → 清递归标志 → 返回结果 + llvmlite.position_at_end(wb, post_bb) + ret_ptr: llvmlite.Value | t.CPtr = null_ret + if ret_alloca is not None: + ret_ptr = llvmlite.build_bitcast(wb, ret_alloca, i8_ptr_ty) + ph1: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i32_ty, 1, "1") + # 后置参数链:ctx_ptr → fn_str → 1 → args_ptr → ret_ptr → null + if ctx_ptr is not None: + llvmlite.value_set_next(ctx_ptr, fn_str) + if fn_str is not None: + llvmlite.value_set_next(fn_str, ph1) + if ph1 is not None: + llvmlite.value_set_next(ph1, args_ptr) + if args_ptr is not None: + llvmlite.value_set_next(args_ptr, ret_ptr) + if ret_ptr is not None: + llvmlite.value_set_next(ret_ptr, null_dec) + llvmlite.build_call(wb, handler_name, ctx_ptr, 6, i32_ty, 0) + if rec_flag_ref is not None: + rec_zero2: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i8_ty, 0, "0") + if rec_zero2 is not None: + llvmlite.build_store(wb, rec_zero2, rec_flag_ref) + if ret_is_void != 0: + llvmlite.build_ret_void(wb) + else: + final_v: llvmlite.Value | t.CPtr = llvmlite.build_load(wb, ret_ty, ret_alloca) + if final_v is not None: + llvmlite.build_ret(wb, final_v) + else: + llvmlite.build_ret_void(wb) + + return wrapper + + # ============================================================ # translate_nested_function_def - 嵌套函数提升 + 闭包创建 # @@ -1048,7 +1654,7 @@ class FuncGenMixin: HandlesVar.define_var(trans.SymTab, arg_p.arg, p_alloca) if arg_p.annotation is not None: cls_nm_p: str = HandlesType.extract_class_name_from_annotation( - arg_p.annotation, imported_modules) + arg_p.annotation, imported_modules, pool) if cls_nm_p is not None: HandlesVar.set_var_annot_class_name( trans.SymTab, arg_p.arg, cls_nm_p) diff --git a/App/lib/core/Gen/StmtGenMixin.vp b/App/lib/core/Gen/StmtGenMixin.vp index b4038b3..4d9def5 100644 --- a/App/lib/core/Gen/StmtGenMixin.vp +++ b/App/lib/core/Gen/StmtGenMixin.vp @@ -13,6 +13,7 @@ import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesExpr as HandlesExpr import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesFunctions as HandlesFunctions import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.Handles.HandlesImports as HandlesImports @@ -91,14 +92,6 @@ class StmtGenMixin: """翻译单条语句,返回新增的变量数""" if node is None: return 0 - # OOM 语句级诊断:每 10 语句打印主池用量+行号——定位单文件翻译 - # (NewAstTest main.vp)从 6.4MB 暴涨到 1.6GB 发生在哪一行 - self.DiagStmtCount += 1 - if (self.DiagStmtCount % 10) == 1: - pool_diag: memhub.MemBuddy | t.CPtr = CompilerFactory.get_pool() - stdio.printf("[MEM] stmt %d line %d used=%zu chk_depth=%zu\n", - self.DiagStmtCount, node.lineno, - pool_diag.used_bytes(), pool_diag.chk_depth) k: int = node.kind() if k == ast.ASTKind.Expr: @@ -143,6 +136,8 @@ class StmtGenMixin: return self.translate_delete(trans, node) elif k == ast.ASTKind.Raise: return self.translate_raise(trans, node) + elif k == ast.ASTKind.Try: + return self.translate_try(trans, node) elif k == ast.ASTKind.With: return self.translate_with(trans, node) # 未知语句类型:直接报错终止(fail-fast),暴露上游解析问题 @@ -173,22 +168,421 @@ class StmtGenMixin: if del_at is not None: HandlesExprCall._try_property_deleter( pool, builder, mod, del_at, trans) + # del obj[key] → __delitem__(obj, key) + # (对齐 TPC HandlesDelete.py L14-25;此前 Subscript 目标被静默 + # 忽略,HashTableTest Test 11 的 del ht3[del_buf] 未执行 → len 不变) + elif tgt.kind() == ast.ASTKind.Subscript: + dl_sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(tgt) + if dl_sub is not None and dl_sub.value is not None and dl_sub.slice is not None: + dl_obj: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, dl_sub.value, None, 0, trans) + dl_idx: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, dl_sub.slice, None, 0, trans) + if dl_obj is not None and dl_idx is not None: + dl_st: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value( + dl_obj) + if dl_st is not None: + dl_cls: str = HandlesStruct.get_class_name_by_type(pool, dl_st) + if dl_cls is not None: + dl_lookup: str = pool.alloc(128) + if dl_lookup is not None: + viperlib.snprintf(dl_lookup, 128, "%s.__delitem__", dl_cls) + dl_fn: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module( + mod, dl_lookup) + if dl_fn is not None: + dl_ret: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(dl_fn) + dl_fn_name: str = llvmlite.function_get_name(dl_fn) + if dl_ret is not None and dl_fn_name is not None: + # self 参数(容器)按 __delitem__ 首参类型对齐 + dl_self: llvmlite.Value | t.CPtr = dl_obj + dl_first: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(dl_fn) + if dl_first is not None: + dl_self_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(dl_first) + if dl_self_ty is not None: + dl_self = llvmlite.build_bitcast(builder, dl_obj, dl_self_ty) + if dl_self is not None: + # 参数链表: self → key(独立 null 结尾) + dl_self.Next = dl_idx + dl_idx.Next = None + llvmlite.build_call( + builder, dl_fn_name, dl_self, 2, dl_ret, 0) + return 0 + + # ============================================================ + # translate_try - try/except 语句翻译 + # + # 对齐 TPC HandlesTry.py L59-214(v1 迁移)。异常 = (i32 code, i8* msg) + # 二元组;except X 编译期静态展开 X 及全部子孙异常类编码。布局: + # pre → TryBB → DispatchBB(编码检查链)→ MatchBB → ExceptBB → AfterBB + # try 体正常结束 → store 0 →(else 若有)→ AfterBB + # DispatchBB 无匹配 → UnhandledBB:函数有 EH 输出参数则写入并返回 + # (向上传播),否则 store 0 → AfterBB(吞掉) + # try 上下文(DispatchBB + 两个 alloca)写入 trans._eh_*,translate_raise + # 与调用侧 EH 检测(CallGenMixin._emit_eh_guarded_call)据此重定向; + # 退出时恢复外层上下文(嵌套 try 支持)。 + # v1 未实现 finalbody(TestFBProject 未使用;finally+return 交互复杂, + # 后续按 TPC HandlesTry.py L188-214 的 pending_ret 方案扩展)。 + # ============================================================ + def translate_try(self, trans: HT.Translator | t.CPtr, + node: ast.AST | t.CPtr) -> int: + """翻译 try/except 语句,返回 0""" + if trans is None or node is None: + return 0 + tr: ast.Try | t.CPtr = (ast.Try | t.CPtr)(node) + if tr is None: + return 0 + pool: memhub.MemBuddy | t.CPtr = trans.Pool + builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder + func: llvmlite.Function | t.CPtr = trans._cur_func + if pool is None or builder is None or func is None: + return 0 + if llvmlite.builder_cur_block_is_terminated(builder) != 0: + return 0 + + # ---- 1. 异常编码/消息 alloca(当前 try 上下文)---- + i32_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + i8_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty_t: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_t) + if i32_ty_t is None or i8_ptr_ty_t is None: + return 0 + exception_code: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i32_ty_t) + eh_message: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i8_ptr_ty_t) + if exception_code is None or eh_message is None: + return 0 + init_code_t: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) + init_msg_t: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty_t, "null") + if init_code_t is not None: + llvmlite.build_store(builder, init_code_t, exception_code) + if init_msg_t is not None: + llvmlite.build_store(builder, init_msg_t, eh_message) + + # ---- 2. 块布局(块名唯一化,create_block 不自动编号)---- + lc_t: int = trans._label_counter + trans._label_counter = lc_t + 1 + bb_buf_t: str = pool.alloc(64) + if bb_buf_t is None: + return 0 + viperlib.snprintf(bb_buf_t, 64, "try.dispatch.%d", lc_t) + dispatch_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t) + viperlib.snprintf(bb_buf_t, 64, "try.after.%d", lc_t) + after_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t) + viperlib.snprintf(bb_buf_t, 64, "try.unhandled.%d", lc_t) + unhandled_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t) + viperlib.snprintf(bb_buf_t, 64, "try.body.%d", lc_t) + try_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t) + if dispatch_bb is None or after_bb is None or unhandled_bb is None or try_bb is None: + return 0 + else_bb: llvmlite.BasicBlock | t.CPtr = None + if tr.orelse is not None and len(tr.orelse) > 0: + viperlib.snprintf(bb_buf_t, 64, "try.else.%d", lc_t) + else_bb = llvmlite.create_block(pool, func, bb_buf_t) + n_handlers_t: t.CSizeT = 0 + if tr.handlers is not None: + n_handlers_t = len(tr.handlers) + # 注意:list[ptr](pool, N) 第二参数是 elem_size 不是容量! + # 指针元素必须显式传 8(T.__sizeof__ 对联合类型不可靠), + # 否则 append 按 N 字节步长错位存取 → 读出垃圾指针(崩溃)。 + except_bbs: list[llvmlite.BasicBlock | t.CPtr] | t.CPtr = \ + list[llvmlite.BasicBlock | t.CPtr](pool, 8) + match_bbs: list[llvmlite.BasicBlock | t.CPtr] | t.CPtr = \ + list[llvmlite.BasicBlock | t.CPtr](pool, 8) + if except_bbs is None or match_bbs is None: + return 0 + for i_t in range(n_handlers_t): + viperlib.snprintf(bb_buf_t, 64, "try.except.%d.%d", lc_t, i_t) + eb_t: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t) + viperlib.snprintf(bb_buf_t, 64, "try.match.%d.%d", lc_t, i_t) + mb_t: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, bb_buf_t) + except_bbs.append(eb_t) + match_bbs.append(mb_t) + + # ---- 3. 保存外层 try 上下文,设置本 try 上下文 ---- + old_dispatch_t: llvmlite.BasicBlock | t.CPtr = trans._eh_dispatch_bb + old_code_t: llvmlite.Value | t.CPtr = trans._eh_code_ptr + old_msg_t: llvmlite.Value | t.CPtr = trans._eh_msg_ptr + trans._eh_dispatch_bb = dispatch_bb + trans._eh_code_ptr = exception_code + trans._eh_msg_ptr = eh_message + + # ---- 4. Try 体 ---- + llvmlite.build_br(builder, try_bb) + llvmlite.position_at_end(builder, try_bb) + if tr.children is not None: + tb_n: t.CSizeT = len(tr.children) + for tbi in range(tb_n): + stmt_t: ast.AST | t.CPtr = tr.children.get(tbi) + if stmt_t is not None: + self.translate_stmt(trans, stmt_t) + # 正常走完 try 体:清异常码,进 else(若有)或 After + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + if init_code_t is not None: + llvmlite.build_store(builder, init_code_t, exception_code) + if else_bb is not None: + llvmlite.build_br(builder, else_bb) + else: + llvmlite.build_br(builder, after_bb) + + # ---- 5. DispatchBB:编码检查链 ---- + llvmlite.position_at_end(builder, dispatch_bb) + if n_handlers_t == 0: + # 无 except(非法 try,防御):直接吞掉 + llvmlite.build_br(builder, unhandled_bb) + else: + eh_code_val: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i32_ty_t, exception_code) + next_check_t: llvmlite.BasicBlock | t.CPtr = None + for i_t in range(n_handlers_t): + hdr_t: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)( + tr.handlers.get(i_t)) + match_bb_t: llvmlite.BasicBlock | t.CPtr = match_bbs.get(i_t) + if i_t > 0 and next_check_t is not None: + llvmlite.position_at_end(builder, next_check_t) + eh_code_val = llvmlite.build_load( + builder, i32_ty_t, exception_code) + if hdr_t is None or hdr_t.type is None: + # 裸 except:无条件匹配 + llvmlite.build_br(builder, match_bb_t) + else: + # 展开匹配编码集(handler.type 自身 + 全部子孙异常类) + hname_t: str = None + htype_t: ast.AST | t.CPtr = hdr_t.type + if htype_t.kind() == ast.ASTKind.Name: + hname_t = ((ast.Name | t.CPtr)(htype_t)).id + elif htype_t.kind() == ast.ASTKind.Attribute: + hname_t = ((ast.Attribute | t.CPtr)(htype_t)).attr + # 匹配编码缓冲:必须是 t.CInt*(i32 步长)——collect_descendant_codes + # 按 out_codes[i](4 字节)写入;若用 t.CSizeT*(8 字节)读, + # 多码时读出 (code[i+1]<<32)|code[i] 的 64 位拼接垃圾(单码时 + # 高位恰为 0 掩盖;Test 2/3/8 父类多码捕获失败的根因)。 + codes_buf_t: t.CInt | t.CPtr = pool.alloc(64 * 4) + if codes_buf_t is None: + return 0 + n_codes_t: int = 0 + if hname_t is not None and HandlesClassDef.is_exception_name(hname_t) != 0: + n_codes_t = HandlesClassDef.collect_descendant_codes( + hname_t, codes_buf_t, 64) + else: + c0_t: int = 1 + if hname_t is not None: + c0_t = HandlesClassDef.lookup_exception_code(hname_t) + if c0_t == 0: + c0_t = 1 + codes_buf_t[0] = c0_t + n_codes_t = 1 + match_cond_t: llvmlite.Value | t.CPtr = None + for ci_t in range(n_codes_t): + code_val_t: llvmlite.Value | t.CPtr = llvmlite.const_int32( + pool, codes_buf_t[ci_t]) + is_match_t: llvmlite.Value | t.CPtr = llvmlite.build_icmp( + builder, llvmlite.ICMP_EQ, eh_code_val, code_val_t) + if match_cond_t is None: + match_cond_t = is_match_t + elif is_match_t is not None: + match_cond_t = llvmlite.build_or( + builder, match_cond_t, is_match_t) + if i_t < n_handlers_t - 1: + viperlib.snprintf(bb_buf_t, 64, "try.check.%d.%d", lc_t, i_t) + next_check_t = llvmlite.create_block(pool, func, bb_buf_t) + else: + next_check_t = unhandled_bb + llvmlite.build_cond_br( + builder, match_cond_t, match_bb_t, next_check_t) + # MatchBB:绑定 handler 变量(e = 消息指针),跳 ExceptBB + llvmlite.position_at_end(builder, match_bb_t) + if hdr_t is not None and hdr_t.name is not None and hdr_t.name[0] != '\0': + # 同名 handler 变量复用函数入口 alloca(find_var 首条命中、 + # 重复 define_var 不 shadow——多 try 共用 `as e` 时各 MatchBB + # 各自 store 消息,各 ExceptBB 读同一入口 alloca,避免 + # 非支配 alloca 触发 "does not dominate all uses")。 + h_ent_t: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + trans.SymTab, hdr_t.name) + if h_ent_t is not None and h_ent_t.Alloca is not None: + h_alloca_t: llvmlite.Value | t.CPtr = h_ent_t.Alloca + else: + h_alloca_t = HandlesVar._alloca_at_entry(builder, i8_ptr_ty_t) + if h_alloca_t is not None: + HandlesVar.define_var(trans.SymTab, hdr_t.name, h_alloca_t) + if h_alloca_t is not None: + h_msg_t: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i8_ptr_ty_t, eh_message) + if h_msg_t is not None: + llvmlite.build_store(builder, h_msg_t, h_alloca_t) + except_bb_t: llvmlite.BasicBlock | t.CPtr = except_bbs.get(i_t) + llvmlite.build_br(builder, except_bb_t) + + # ---- 6. UnhandledBB:无 except 匹配 → 向上传播或吞掉 ---- + llvmlite.position_at_end(builder, unhandled_bb) + if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \ + and trans._eh_code_out_param is not None: + un_msg_t: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i8_ptr_ty_t, eh_message) + un_code_t: llvmlite.Value | t.CPtr = llvmlite.build_load( + builder, i32_ty_t, exception_code) + if un_msg_t is not None: + llvmlite.build_store(builder, un_msg_t, trans._eh_msg_out_param) + if un_code_t is not None: + llvmlite.build_store(builder, un_code_t, trans._eh_code_out_param) + self._eh_ret_default(trans, pool, builder) + else: + if init_code_t is not None: + llvmlite.build_store(builder, init_code_t, exception_code) + llvmlite.build_br(builder, after_bb) + + # ---- 7. Except 体 ---- + for i_t in range(n_handlers_t): + hdr2_t: ast.ExceptHandler | t.CPtr = (ast.ExceptHandler | t.CPtr)( + tr.handlers.get(i_t)) + except_bb2_t: llvmlite.BasicBlock | t.CPtr = except_bbs.get(i_t) + llvmlite.position_at_end(builder, except_bb2_t) + if hdr2_t is not None and hdr2_t.children is not None: + hn2_t: t.CSizeT = len(hdr2_t.children) + for hi2 in range(hn2_t): + stmt2_t: ast.AST | t.CPtr = hdr2_t.children.get(hi2) + if stmt2_t is not None: + self.translate_stmt(trans, stmt2_t) + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + llvmlite.build_br(builder, after_bb) + + # ---- 8. Else 体 ---- + if else_bb is not None: + llvmlite.position_at_end(builder, else_bb) + if tr.orelse is not None: + en_t: t.CSizeT = len(tr.orelse) + for ei in range(en_t): + stmt3_t: ast.AST | t.CPtr = tr.orelse.get(ei) + if stmt3_t is not None: + self.translate_stmt(trans, stmt3_t) + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + llvmlite.build_br(builder, after_bb) + + # ---- 9. AfterBB + 恢复外层 try 上下文 ---- + llvmlite.position_at_end(builder, after_bb) + trans._eh_dispatch_bb = old_dispatch_t + trans._eh_code_ptr = old_code_t + trans._eh_msg_ptr = old_msg_t return 0 def translate_raise(self, trans: HT.Translator | t.CPtr, node: ast.AST | t.CPtr) -> int: """翻译 raise 语句,返回 0 - Viper 无异常传播机制:raise 统一按"终止当前函数并返回零值"处理, - 对齐 TPC HandlesRaise 的 StopIteration 语义——迭代器 __next__ 用 - null/0 表示迭代结束(如 dict.__next__ 的 `raise StopIteration`)。 - 非 StopIteration 的 raise 同样返回零值(异常消息忽略)。 + 对齐 TPC HandlesRaise.py 的 try-except 语义(v1 迁移): + - StopIteration:置 __stop_iter_flag__ = 1 后返回默认值(迭代结束协议) + - 非 StopIteration: + 1. 处于 try(trans._eh_dispatch_bb 非空):异常编码/消息写入当前 + try 的 eh_code/eh_message alloca,分支到 DispatchBB 由 except 链裁决 + 2. 函数有 EH 输出参数(_cur_func_has_eh):编码/消息写入 + __eh_code_out__/__eh_msg_out__,返回默认值(异常向调用方传播) + 3. 两者皆无:仅返回默认值(v1 语义:异常被吞掉) """ if trans is None or node is None: return 0 pool: memhub.MemBuddy | t.CPtr = trans.Pool builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder - if pool is None or builder is None: + mod: llvmlite.LLVMModule | t.CPtr = trans.Module + if pool is None or builder is None or mod is None: + return 0 + rn: ast.Raise | t.CPtr = (ast.Raise | t.CPtr)(node) + if rn is None or rn.exc is None: + # bare raise(无 exc):v1 无活动异常可再抛,仅返回默认值 + self._eh_ret_default(trans, pool, builder) + return 0 + + # OOP iter 协议:raise StopIteration → 置 __stop_iter_flag__ = 1 + # (对齐 TPC HandlesRaise.py L76-100),随后按迭代结束返回默认值。 + ek: int = rn.exc.kind() + is_stop_iter: int = 0 + if ek == ast.ASTKind.Name: + enm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn.exc) + if enm.id is not None and string.strcmp(enm.id, "StopIteration") == 0: + is_stop_iter = 1 + elif ek == ast.ASTKind.Call: + ecl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn.exc) + if ecl.func is not None and ecl.func.kind() == ast.ASTKind.Name: + ecn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl.func) + if ecn.id is not None and string.strcmp(ecn.id, "StopIteration") == 0: + is_stop_iter = 1 + if is_stop_iter != 0: + if trans._stop_iter_flag_ptr is not None: + i1_ty_r: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + if i1_ty_r is not None: + one_i1_r: llvmlite.Value | t.CPtr = llvmlite.ConstInt( + pool, i1_ty_r, 1, "1") + if one_i1_r is not None: + llvmlite.build_store( + builder, one_i1_r, trans._stop_iter_flag_ptr) + self._eh_ret_default(trans, pool, builder) + return 0 + + # ---- 非 StopIteration:计算异常编码与消息 ---- + exc_code: int = 1 + exc_msg: llvmlite.Value | t.CPtr = None + if ek == ast.ASTKind.Constant: + cst_r: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rn.exc) + if cst_r is not None and cst_r.const_kind == ast.CONST_INT: + exc_code = cst_r.int_val + elif ek == ast.ASTKind.Name: + enm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rn.exc) + if enm2.id is not None: + exc_code = HandlesClassDef.lookup_exception_code(enm2.id) + if exc_code == 0: + exc_code = 1 + elif ek == ast.ASTKind.Call: + ecl2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(rn.exc) + if ecl2.func is not None and ecl2.func.kind() == ast.ASTKind.Name: + ecn2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ecl2.func) + if ecn2.id is not None: + exc_code = HandlesClassDef.lookup_exception_code(ecn2.id) + if exc_code == 0: + exc_code = 1 + if ecl2.args is not None and len(ecl2.args) > 0: + exc_msg = HandlesExpr.translate_value( + builder, pool, mod, ecl2.args.get(0), None, 0, trans) + + # ---- 异常分发 ---- + # 1. try 内:写入 try 的 eh_code/eh_message alloca,分支 DispatchBB + if trans._eh_dispatch_bb is not None and trans._eh_code_ptr is not None: + code_val_r: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, exc_code) + if code_val_r is not None: + llvmlite.build_store(builder, code_val_r, trans._eh_code_ptr) + if exc_msg is None: + exc_msg = llvmlite.ConstNull( + pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null") + if exc_msg is not None and trans._eh_msg_ptr is not None: + llvmlite.build_store(builder, exc_msg, trans._eh_msg_ptr) + llvmlite.build_br(builder, trans._eh_dispatch_bb) + return 0 + # 2. 函数有 EH 输出参数:写入 out 参数(异常向调用方传播) + if trans._cur_func_has_eh != 0 and trans._eh_msg_out_param is not None \ + and trans._eh_code_out_param is not None: + code_val_r2: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, exc_code) + if code_val_r2 is not None: + llvmlite.build_store(builder, code_val_r2, trans._eh_code_out_param) + if exc_msg is None: + exc_msg = llvmlite.ConstNull( + pool, llvmlite.Ptr(pool, llvmlite.Int8(pool)), "null") + if exc_msg is not None: + llvmlite.build_store(builder, exc_msg, trans._eh_msg_out_param) + self._eh_ret_default(trans, pool, builder) + return 0 + # 3. 无 try 无 EH 参数:仅返回默认值 + self._eh_ret_default(trans, pool, builder) + return 0 + + # ============================================================ + # _eh_ret_default - 异常路径返回当前函数的默认零值 + # + # 对齐 TPC HandlesTry.py UnhandledBB / HandlesRaise.py 的返回生成 + # (void→ret_void,ptr→null,float→0.0,其余→零值)。当前块已 + # 终结(前一条是 raise/return)时跳过,避免在终止块后插指令。 + # ============================================================ + def _eh_ret_default(self, trans: HT.Translator | t.CPtr, + pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr) -> int: + """异常路径返回当前函数默认零值,返回 0""" + if trans is None or builder is None: + return 0 + if llvmlite.builder_cur_block_is_terminated(builder) != 0: return 0 cur_func: llvmlite.Function | t.CPtr = trans._cur_func if cur_func is None: @@ -327,6 +721,20 @@ class StmtGenMixin: if cls_name is not None: HandlesVar.set_var_annot_class_name( trans.SymTab, asname, cls_name) + # FakeDuck: __provides__ 元数据 → 生成 _push_provider 调用 + # (对齐 TPC HandlesWith.py L138-159;经 gen 单例委托 CallGenMixin。 + # 类名去模块前缀后查 __provides__ 表,如 memhub.MemBuddy → MemBuddy) + fd_cls_bare: str = cls_name + if cls_name is not None: + fd_dot: str = string.strrchr(cls_name, '.') + if fd_dot is not None: + fd_cls_bare = fd_dot + 1 + import lib.core.Gen.Generator as Generator + g_fd6: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g_fd6 is not None and fd_cls_bare is not None: + g_fd6._emit_provider_push( + trans, pool, builder, mod, fd_cls_bare, ctx_val) # 2. 翻译 body body: list[ast.AST | t.CPtr] | t.CPtr = wt.children @@ -337,6 +745,24 @@ class StmtGenMixin: if st is not None: self.translate_stmt(trans, st) + # 2.5 FakeDuck: __provides__ 元数据 → 生成 _pop_provider 调用 + # (对齐 TPC HandlesWith.py L167-174;逆序弹栈,经 gen 单例委托) + import lib.core.Gen.Generator as Generator + g_fd6b: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g_fd6b is not None: + for pi_pop in range(n): + if ctx_class_buf[pi_pop] != 0: + cls_pop: str = (str | t.CPtr)(t.CVoid(ctx_class_buf[pi_pop], t.CPtr)) + fd_cls_bare_p: str = cls_pop + if cls_pop is not None: + fd_dot_p: str = string.strrchr(cls_pop, '.') + if fd_dot_p is not None: + fd_cls_bare_p = fd_dot_p + 1 + if fd_cls_bare_p is not None: + g_fd6b._emit_provider_pop( + trans, pool, builder, mod, fd_cls_bare_p) + # 3. 逆序调用 __exit__()(Python with 语义:后进先出) for i in range(n): ci: int = n - 1 - i @@ -789,6 +1215,12 @@ class StmtGenMixin: if bop.op == ast.OpKind.Add: return lv + rv if bop.op == ast.OpKind.Sub: return lv - rv if bop.op == ast.OpKind.Mult: return lv * rv + if bop.op == ast.OpKind.Div: + # 整数常量除法:与 FloorDiv 一致(对齐 TPC ConstEvaluator L354-355) + if rv == 0: + self.CifEvalOk = 0 + return 0 + return lv // rv if bop.op == ast.OpKind.FloorDiv: if rv == 0: self.CifEvalOk = 0 @@ -1207,6 +1639,15 @@ class StmtGenMixin: if enum_entry is None or member_name is None or member_ty is None: return -1 + # 去重:同名成员已存在则跳过(跨模块同名枚举合并注册 / 同模块 + # 重复处理幂等——如 w32.fileio 与 fat32_types 都有 FRESULT) + for ei_d in range(enum_entry.MemberCount): + ex_d: HandlesEnum.EnumMember | t.CPtr = self._get_enum_member( + enum_entry, ei_d) + if ex_d is not None and ex_d.Name is not None: + if string.strcmp(ex_d.Name, member_name) == 0: + return ei_d + if enum_entry.MemberCount >= ENUM_MEMBER_MAX: fb_em: str = VLogger.fmt_buf() if fb_em is not None: diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 7cb79a2..4ddb51e 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -35,10 +35,12 @@ import lib.core.Handles.HandlesStruct as HandlesStruct # 常量(与 HandlesStruct 保持一致) # ============================================================ STRUCT_MAX: t.CDefine = 512 -# 字段槽位上限制(与 HandlesStruct 一致:对象化后 Generator 50+ 字段) -FIELD_MAX: t.CDefine = 64 +# 字段槽位上限制(与 HandlesStruct 保持一致;Generator 12 Mixin 展平 ~97 字段, +# 2026-08-16 追加78:64 截断导致第 9+ 父类字段(StmtGenMixin.DiagStmtCount 等) +# 丢失 → __init__ 写 self.DiagStmtCount "属性写入失败") +FIELD_MAX: t.CDefine = 128 FIELD_NAME_MAX: t.CDefine = 64 -PARENT_MAX: t.CDefine = 8 +PARENT_MAX: t.CDefine = 16 MRO_MAX: t.CDefine = 16 @@ -498,6 +500,33 @@ class StructGenMixin: # 无 SHA1:按类名查找第一个 return self.find_struct(name) + def reset_module_methods_emitted(self, sha1: str) -> int: + """回滚指定模块注册类的 MethodsEmitted 标志(Phase A 重试前调用) + + 根因(追加87):Phase A 第 1 遍翻译类方法体时 _translate_oop_methods + 先置 MethodsEmitted=1(防重入)再发射方法;方法体内跨模块调用因依赖 + text.ll 未生成触发 trans._deps_pending → 整文件不写盘下遍重译。但 + MethodsEmitted 存于 perm_pool 分配的 StructEntry,跨 mb.reset_to(mark2) + 存活 → 第 2 遍 existing 分支被 _translate_oop_methods 的 + MethodsEmitted==1 防重复检查拦截 → 类方法(含 __before_init__)永不 + 发射 → 链接 undefined symbol(模块级函数不受此标志保护所以正常, + AssignGenMixin text.ll 空 / HandlesTranslator 有模块函数缺类方法)。 + 按本文件 ModuleSha1 精确回滚,不影响其他已写盘文件的 MethodsEmitted。 + """ + if sha1 is None or self.StructTable is None: + return 0 + if self.StructCount < 0 or self.StructCount > 200000: + VLogger.error(f"StructCount 异常: {self.StructCount} (reset_module_methods_emitted)", "STRUCT") + return 0 + reset_count: int = 0 + for i in range(self.StructCount): + entry: HandlesStruct.StructEntry | t.CPtr = self._get_struct_entry(i) + if entry is not None and entry.ModuleSha1 is not None: + if string.strcmp(entry.ModuleSha1, sha1) == 0: + entry.MethodsEmitted = 0 + reset_count += 1 + return reset_count + def find_struct_by_type(self, struct_ty: llvmlite.LLVMType | t.CPtr) -> HandlesStruct.StructEntry | t.CPtr: """按类型指针查找结构体(指针值比较),返回 StructEntry 或 None @@ -641,6 +670,37 @@ class StructGenMixin: if fe is not None and fe.Name is not None: if string.strcmp(fe.Name, field_name) == 0: return fe + # REnum 变体字段别名回退:REnum 类(如 LLVMType)的变体内部字段 + # (Ptr.Pointee 等)不是普通字段,而是 _p1/_p2 槽位别名。此前在调用方 + # (ExprGenMixin 按需过滤 L5046/L5500)回退,但本函数字段未找到的 + # VLogger.error 立即 sys.exit(1),调用方回退永远执行不到("LLVMType 已注册 + # 但字段 Pointee 未找到"四次依旧的根因)。此处直接查变体别名,所有调用方 + # 统一受益。注意:_lookup_renum_field_alias 用 class_name 指针值比较, + # 必须传 entry.Name(register_struct 复制 + _update_renum_alias_class_ptr + # 统一后的池指针),传调用方 class_name(AnnotClassName 等不同指针) + # 恒不匹配 → 返回 -1 → 回退失效。 + import lib.core.Handles.HandlesClassDef as HandlesClassDef + rn_cls_l: str = entry.Name + if rn_cls_l is None: + rn_cls_l = class_name + rn_is_renum_l: int = HandlesClassDef._is_renum_class_name(class_name) + if rn_is_renum_l == 0 and rn_cls_l is not None: + # 类名可能是模块限定名("mod.LLVMType"),REnum 表存裸名,双保险 + rn_is_renum_l = HandlesClassDef._is_renum_class_name(rn_cls_l) + rn_alias_l: int = -1 + if rn_is_renum_l != 0: + rn_alias_l = HandlesClassDef._lookup_renum_field_alias( + rn_cls_l, field_name) + if rn_alias_l < 1 and class_name is not rn_cls_l: + # 双保险:别名表可能仍持有旧 AST 指针(_update_renum_alias_class_ptr + # 未执行/多模块重复注册场景),用调用方 class_name 再试一次 + rn_alias_l = HandlesClassDef._lookup_renum_field_alias( + class_name, field_name) + if rn_alias_l >= 1: + rn_fe_l: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct._get_field_entry(entry, rn_alias_l) + if rn_fe_l is not None and rn_fe_l.Ty is not None: + return rn_fe_l # 诊断:找到结构体但字段确实未找到,打印字段表 # 注意:VLogger.error 会立即退出进程,字段表须在 error 之前用 warning 打印 for fi in range(entry.FieldCount): @@ -655,9 +715,19 @@ class StructGenMixin: VLogger.warning(fb_fn, "STRUCT") fb_fields: str = VLogger.fmt_buf() if fb_fields is not None: - viperlib.snprintf(fb_fields, 1024, - "lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d) 但字段 '%s' 未找到", - class_name, entry.FieldCount, field_name) + if rn_is_renum_l != 0: + # 报错友好化:REnum 类直接说明是"变体字段别名查找失败",并附 + # entry.Name 与别名槽位结果,一眼可辨"别名未注册"(槽位=-1) + # 还是"class_name 指针未统一"(已注册但传错指针查不到) + viperlib.snprintf(fb_fields, 1024, + "lookup_field_by_class: 类 '%s' 是 REnum 类(FieldCount=%d)," + "变体字段 '%s' 别名查找失败(entry.Name='%s',别名槽位=%d," + "请检查该变体字段是否已注册别名,或 class_name 指针是否统一)", + class_name, entry.FieldCount, field_name, rn_cls_l, rn_alias_l) + else: + viperlib.snprintf(fb_fields, 1024, + "lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d) 但字段 '%s' 未找到", + class_name, entry.FieldCount, field_name) VLogger.error(fb_fields, "STRUCT") # 诊断:找到结构体但字段未找到 return None @@ -1204,7 +1274,15 @@ class StructGenMixin: llvmlite.module_ensure_opaque_for_type(mod, pool, ty) case llvmlite.LLVMType.Array(elem_ty, acount): # 数组类型:元素如果是值类型结构体,需要完整定义 + # (如 zhuff_decode_tree = {MemManager*, [576 x zhuff_decode_node], + # i32, i32}——Array 元素 node 只递归字段不发射定义 → llc "use of + # undefined type named '...zhuff_decode_node'",ZlibTest 根因) if elem_ty is not None: + match elem_ty: + case llvmlite.LLVMType.Struct(_, _, _): + self.ensure_struct_def_in_module(pool, mod, elem_ty) + case _: + pass self._ensure_field_types_in_module(pool, mod, elem_ty) case _: pass diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 5d81696..b73090b 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -44,7 +44,7 @@ import lib.core.Handles.HandlesAnnAssign as HandlesAnnAssign # ============================================================ # 常量(与 HandlesTranslator 保持一致) # ============================================================ -MAX_VARS: t.CDefine = 256 +MAX_VARS: t.CDefine = 1024 MAX_FUNCS: t.CDefine = 1024 MAX_GLOBAL_NAMES: t.CDefine = 32 @@ -282,6 +282,50 @@ class TranslatorGenMixin: viperlib.snprintf(buf + pos, buf_size - pos, "]") return buf + # ============================================================ + # _build_string_array_initializer_text - 字符串字面量初始化数组全局 + # + # t.CArray[t.CChar, N] = "..." → [i8 b0, i8 b1, ..., i8 0, ...] + # (字符串字节 + 零填充到数组长度;空串返回 zeroinitializer)。 + # 此前统一走字符串指针 GEP 初始化器 → @g = global [N x i8] + # 类型不匹配(llc constant expression type mismatch, + # pyzlib_error_msg = global [512 x i8] 根因)。 + # ============================================================ + def _build_string_array_initializer_text(self, pool: memhub.MemBuddy | t.CPtr, + var_ty: llvmlite.LLVMType | t.CPtr, + str_val: str) -> str: + """字符串字面量初始化数组全局,返回初始化器文本或 None""" + if var_ty is None or str_val is None: + return None + arr_count: t.CInt = 0 + match var_ty: + case llvmlite.LLVMType.Array(_, cnt): + arr_count = cnt + if arr_count <= 0: + return None + slen: t.CSizeT = string.strlen(str_val) + if slen == 0: + return "zeroinitializer" + buf_size: t.CSizeT = 64 + t.CSizeT(arr_count) * 16 + buf: str = pool.alloc(buf_size) + if buf is None: + return None + written: t.CInt = viperlib.snprintf(buf, buf_size, "[") + pos: t.CSizeT = t.CSizeT(written) + i: t.CSizeT = 0 + while i < t.CSizeT(arr_count): + if i > 0: + written = viperlib.snprintf(buf + pos, buf_size - pos, ", ") + pos += t.CSizeT(written) + bv: t.CInt64T = 0 + if i < slen: + bv = t.CInt64T(str_val[i]) + written = viperlib.snprintf(buf + pos, buf_size - pos, "i8 %lld", bv) + pos += t.CSizeT(written) + i += 1 + viperlib.snprintf(buf + pos, buf_size - pos, "]") + return buf + # ============================================================ # _build_string_ptr_initializer - 为字符串字面量初始化指针类型全局变量 # @@ -423,6 +467,16 @@ class TranslatorGenMixin: if var_name is None: return 0 + # __all__ 是 Python 模块元数据(公开名列表),无运行时语义: + # StubGen 的 .vpi 不为其生成 stub 声明(无注解模块级赋值不产出 vpi + # 条目),此处同样跳过全局创建——否则无注解 list-of-strings 全局 + # 类型推断缺失(var_ty 默认 i32)→ _build_array_initializer_text + # 返回 None → fail-fast "初始化器构建失败" 误报 + # (Projectrans/__init__.vp 追加83 根因)。与 CDefine/CTypedef + # 同类:编译期/元数据实体不落入 LLVM 全局。 + if string.strcmp(var_name, "__all__") == 0: + return 0 + # 检查是否已注册 existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var( trans.SymTab, var_name) @@ -457,13 +511,33 @@ class TranslatorGenMixin: # 初始化器构建失败:直接报错终止(fail-fast),不再回退 zeroinitializer HandlesType.fatal_error(node, f"模块级变量 '{var_name}' 初始化器构建失败", "TypeError") elif init_kind == 3: - # 字符串字面量 → 创建字符串常量全局 + GEP 引用 - str_init: str = self._build_string_ptr_initializer(pool, mod, var_name, init_str_val) - if str_init is not None: - gv.Initializer = str_init + # 字符串字面量初始化 + # 数组类型(t.CArray[t.CChar, N]):字节数组初始化器(字符串内容 + 零填充) + # ——此前统一走字符串指针 GEP 初始化器 → @g = global [N x i8] 类型 + # 不匹配(llc constant expression type mismatch,pyzlib_error_msg 根因) + is_arr_g: int = 0 + match var_ty: + case llvmlite.LLVMType.Array(_, _): + is_arr_g = 1 + case _: + pass + if is_arr_g != 0: + arr_str_init: str = self._build_string_array_initializer_text( + pool, var_ty, init_str_val) + if arr_str_init is not None: + gv.Initializer = arr_str_init + else: + HandlesType.fatal_error( + node, f"模块级数组变量 '{var_name}' 字符串初始化器构建失败", "TypeError") else: - # 初始化器构建失败:直接报错终止(fail-fast),不再回退 zeroinitializer - HandlesType.fatal_error(node, f"模块级变量 '{var_name}' 初始化器构建失败", "TypeError") + # 指针类型(如 b64_tab: t.CArray[t.CChar, None] = "ABC..."): + # 创建字符串常量全局 + GEP 引用 + str_init: str = self._build_string_ptr_initializer(pool, mod, var_name, init_str_val) + if str_init is not None: + gv.Initializer = str_init + else: + HandlesType.fatal_error( + node, f"模块级变量 '{var_name}' 初始化器构建失败", "TypeError") else: # 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer, # 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type" @@ -484,7 +558,7 @@ class TranslatorGenMixin: aa_ml: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) if aa_ml is not None and aa_ml.annotation is not None: cls_nm_ml: str = HandlesType.extract_class_name_from_annotation( - aa_ml.annotation, trans._imported_modules) + aa_ml.annotation, trans._imported_modules, pool) if cls_nm_ml is not None: HandlesVar.set_var_annot_class_name( trans.SymTab, var_name, cls_nm_ml) diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index 230a95f..90b1be9 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -14,6 +14,7 @@ import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HT import lib.core.Handles.HandlesImports as HandlesImports import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.Handles.HandlesEnum as HandlesEnum import lib.core.Handles.HandlesType as HandlesType @@ -38,7 +39,6 @@ import lib.core.Handles.HandlesType as HandlesType # ============================================================ TRACE_MAX: t.CDefine = 32 MAX_CTYPEDEF_DEPTH: t.CDefine = 10 -_SRC_BUF_SIZE: t.CDefine = 262144 # ============================================================ @@ -212,11 +212,13 @@ class TypeGenMixin: pass def register_cdefine_constant(self, pool: memhub.MemBuddy | t.CPtr, - name: str, value: int) -> None: + name: str, value: t.CInt64T) -> None: """注册 CDefine 编译期常量到本地表 - 值以十进制字符串存储。注意:value 是 32 位有符号整数, - 0xFFFFFFFF 会存储为 "-1",lookup 时用 found 标志区分"未找到"和值为 -1。 + 值以十进制字符串存储(64 位 %lld)。大整数 CDefine(如 + sys.maxsize = 9223372036854775807)必须全精度保存,否则 %d 32 位截断 + 成 -1(0xFFFFFFFF)→ 读回 4294967295(SysLibTest maxsize FAIL 根因)。 + 0xFFFFFFFF 仍存为 "4294967295",常量生成端 const_int32 内部截断回 -1。 """ if self.CDefineNames is None: self.CDefineNames = list[str](pool, 64) @@ -226,7 +228,7 @@ class TypeGenMixin: self.CDefineNames.append(name) val_buf: str = pool.alloc(32) if val_buf is not None: - viperlib.snprintf(val_buf, 32, "%d", value) + viperlib.snprintf(val_buf, 32, "%lld", value) self.CDefineValues.append(val_buf) else: self.CDefineValues.append("0") @@ -261,7 +263,6 @@ class TypeGenMixin: if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0) # fail-fast:CDefine 值缓冲区分配失败必须终止编译, - # 绝不以 "(null)" 掩盖内存分配异常。 if val_buf is None: VLogger.error("CDefine 浮点值缓冲区分配失败", "CDEF-DBG") HandlesType.fatal_error(None, "CDefine 浮点值缓冲区分配失败", "MemoryError") @@ -295,6 +296,14 @@ class TypeGenMixin: self.CDefineDoubleFlags.append(0) if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(1) + # 同步设置"最近注册/查表结果为字符串"标志(is_cdefine_str() 读取)。 + # 跨模块字符串 CDefine 读回(_lookup_cross_module_cdefine 字符串分支只调 + # 本函数 + 尾部 set_cdefine_found,无 lookup_cdefine_string 回查)→ 若此处 + # 不设标志,属性路径 is_cdefine_str()==0 → 退化为整数分支返回 0 → + # IR 生成 inttoptr i32 0(NULL)→ 运行时报 (null-) + strlen(NULL-) 崩溃 + # (test_pyzlib.vp pyzlib.ZLIB_VERSION 根因)。 + self.CDefineIsStr = 1 + self.CDefineFound = 1 # ============================================================ # 跨模块 CDefine 查找缓存(perm_pool 永久分配,跨文件复用) @@ -370,10 +379,11 @@ class TypeGenMixin: return val_str return None - def lookup_cdefine_constant(self, name: str) -> int: + def lookup_cdefine_constant(self, name: str) -> t.CInt64T: """查找 CDefine 编译期常量 - 返回找到的值。由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1), + 返回找到的值(64 位,大整数 CDefine 如 sys.maxsize 全精度)。 + 由于 -1 是合法值(如 INVALID_HANDLE_VALUE=0xFFFFFFFF=-1), 用全局 _g_cdefine_found 标志区分"未找到"和值为 -1。 调用方应在调用后检查 _g_cdefine_found。 浮点 CDefine 项返回整数部分(与 _g_cdefine_is_double 标志配合)。 @@ -398,8 +408,8 @@ class TypeGenMixin: return 0 if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1: self.CDefineIsDouble = 1 - return string.atoi(val_str) - return string.atoi(val_str) + return string.atoll(val_str) + return string.atoll(val_str) return 0 def lookup_cdefine_double(self, name: str) -> double: @@ -428,7 +438,7 @@ class TypeGenMixin: self.CDefineIsDouble = 1 ret_d: double = string.atof(val_str) return ret_d - ret_i2: double = double(string.atoi(val_str)) + ret_i2: double = double(string.atoll(val_str)) return ret_i2 return 0.0 @@ -754,6 +764,13 @@ class TypeGenMixin: if bop.op == ast.OpKind.Mult: self.ConstEvalOk = 1 return lv * rv + if bop.op == ast.OpKind.Div: + # 整数常量除法:与 FloorDiv 一致(对齐 TPC ConstEvaluator L354-355) + if rv == 0: + self.ConstEvalOk = 0 + return 0 + self.ConstEvalOk = 1 + return lv // rv if bop.op == ast.OpKind.FloorDiv: if rv == 0: self.ConstEvalOk = 0 @@ -986,11 +1003,19 @@ class TypeGenMixin: f: fileio.File | t.CPtr = fileio.File(file_path, fileio.MODE.R) if f.closed: return None - buf: bytes = stdlib.malloc(_SRC_BUF_SIZE) + # 按文件实际大小分配缓冲区:固定 256KB 会截断大源文件,末尾行号读不到 → + # 误报"文件不存在或行号超出范围"(如 CallGenMixin.vp 555KB/9236 行的第 + # 8390 行)。64MB 上限防御异常超大文件整体读取。 + fsize: LONGLONG = f.size() + if fsize <= 0 or fsize > 67108864: + f.close() + return None + src_size: t.CSizeT = t.CSizeT(fsize) + buf: bytes = stdlib.malloc(src_size + 1) if buf is None: f.close() return None - bytes_read: LONG = f.read_all(buf, _SRC_BUF_SIZE) + bytes_read: LONG = f.read_all(buf, ULONG(src_size + 1)) f.close() if bytes_read <= 0: stdlib.free(buf) @@ -1077,6 +1102,18 @@ class TypeGenMixin: file_name: str = self.CurrentFile if file_name is None: file_name = "" + # 行号兜底:克隆/构造节点 lineno 可能为 0(如泛型特化 _clone_arguments 的 + # ast.Arg 构造器硬编码 0,astaux.vp L139)。此时回退到 trace 栈最后一个 + # 有效帧(文件+行号)作为出错位置,避免输出误导性的 "line 0"。 + if lineno <= 0 and self.TraceCount > 0: + ti_b: int = self.TraceCount - 1 + while ti_b >= 0: + tfb: HandlesType.TraceFrame | t.CPtr = self._get_trace_frame(ti_b) + if tfb is not None and tfb.file_name is not None and tfb.lineno > 0: + file_name = tfb.file_name + lineno = tfb.lineno + break + ti_b -= 1 # 行号合理性防御:先读取该行源码,验证 node.lineno 是否落在当前文件内。 # 若读不到(文件不存在或行号超出范围),说明文件/行号归属不可信 # (如 AST 树被内存复用/串扰后跨文件报错),打印警告而非误导性行号。 @@ -1473,6 +1510,28 @@ class TypeGenMixin: return 1 return 0 + def _is_cptr_subscript(self, node: ast.AST | t.CPtr) -> int: + """检测节点是否为 t.CPtr[T] 下标指针类型(t.CPtr[t.CPtr] / t.CPtr[t.CInt] 等) + + 用于联合类型 X | t.CPtr[T] 的指针层级合并(TPC 联合合并规则: + CPtr 侧贡献 PtrCount,另一侧提供 BaseType)。t.CPtr[...] 不是 + 普通 CPtr 标记(_is_cptr_marker 只认 t.CPtr/CPtr 裸形式)。 + """ + if node is None or node.kind() != ast.ASTKind.Subscript: + return 0 + sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) + if sub.value is None or sub.value.kind() != ast.ASTKind.Attribute: + return 0 + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub.value) + if at.attr is None or string.strcmp(at.attr, "CPtr") != 0: + return 0 + if at.value is None or at.value.kind() != ast.ASTKind.Name: + return 0 + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if nm.id is None or string.strcmp(nm.id, "t") != 0: + return 0 + return 1 + def _is_cvoid_marker(self, node: ast.AST | t.CPtr, imported_modules: str) -> int: """检测节点是否为 CVoid 指针标记,返回 1=是 / 0=否""" @@ -1818,7 +1877,13 @@ class TypeGenMixin: import lib.core.Handles.HandlesClassDef as HandlesClassDef gen_spec_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(gen_mangled) if gen_spec_entry is not None and gen_spec_entry.MethodsEmitted == 0: + # 错误定位:压入触发特化的调用方帧(注解所在文件+行号), + # 使特化方法体报错(如参数类型解析失败)能追溯到用户代码 + # (如 devfs copy.vp 的 list[devfs_directory,MAX_DIRECTORIES] 注解) + gen_cur_f: str = HandlesType.get_current_file() + HandlesType.push_trace(gen_cur_f, sub.lineno) HandlesClassDef._specialize_generic_class(trans, gen_cls_name, gen_arg_names) + HandlesType.pop_trace() return gen_spec_ty # 未特化:尝试主动触发泛型特化(与 TPC _HandleSubscript 对齐) # 当 trans 可用时,检查是否是泛型模板并触发特化 @@ -1830,8 +1895,12 @@ class TypeGenMixin: type_args_chk: list[str] | t.CPtr = HandlesExprCall._extract_type_args_from_slice( pool, sub.slice) if type_args_chk is not None and len(type_args_chk) > 0: + # 错误定位:压入触发特化的调用方帧(注解所在文件+行号) + gen_cur_f2: str = HandlesType.get_current_file() + HandlesType.push_trace(gen_cur_f2, sub.lineno) spec_name_chk: str = HandlesClassDef._specialize_generic_class( trans, gen_cls_name, type_args_chk) + HandlesType.pop_trace() if spec_name_chk is not None: gen_spec_ty = HandlesStruct.get_struct_type(gen_mangled) if gen_spec_ty is not None: @@ -1861,6 +1930,12 @@ class TypeGenMixin: # Constant 节点: 字符串注解(如 'MemManager'),当作无引号的 Name/Attribute/BinOp 处理 if k == ast.ASTKind.Constant: cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node) + # -> None 无返回值注解 → Void(追加89:-> None 被 parser 解析为 + # CONST_NONE 常量而非 Name('None'),此前直接 return None → ret_ty + # 回退 i32 → 掉出末尾被 fail-fast 误杀,BuildPipeline.set_asan_mode + # "依旧"根因;Name 分支的 "None" 归一为字符串注解形式的双保险) + if cn.const_kind == ast.CONST_NONE: + return llvmlite.Void(pool) if cn.const_kind == ast.CONST_STR and cn.str_val is not None: sub_node: ast.AST | t.CPtr = self._parse_type_string(pool, cn.str_val, ast.ASTCtx.Load) if sub_node is not None: @@ -1908,6 +1983,11 @@ class TypeGenMixin: # str = bytes = str = i8* if string.strcmp(nm.id, "str") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + # -> None 无返回值注解 → Void(追加89:与 VpiReader "None"→Void + # 归一对齐;缺失时 -> None 解析失败回退 i32,函数掉出末尾被 + # fail-fast 误杀,BuildPipeline.set_asan_mode 根因) + elif string.strcmp(nm.id, "None") == 0: + return llvmlite.Void(pool) elif string.strcmp(nm.id, "int") == 0: return llvmlite.Int32(pool) elif string.strcmp(nm.id, "bytes") == 0: @@ -1949,6 +2029,10 @@ class TypeGenMixin: if nm_has_new_s != 0: return llvmlite.Ptr(pool, struct_ty) return struct_ty + # 枚举类(t.CEnum)裸名注解 → 底层整数类型 + # 如 fat32_types.vp 内部 `res: FRESULT = ...`(FRESULT 是枚举非结构体) + if HandlesEnum.is_enum_class(nm.id) == 1: + return HandlesEnum.get_enum_base_type(nm.id) # CTypedef 动态查表:查动态 CTypedef 表,递归解析类型表达式 # 用户自定义 CTypedef(如 MyHandle: t.CTypedef = VOIDPTR)在此解析 # 递归深度限制 MAX_CTYPEDEF_DEPTH 防止无限递归,循环检测防止 A→B→A @@ -2004,6 +2088,31 @@ class TypeGenMixin: mod_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(at.attr) if mod_struct_ty is not None: return mod_struct_ty + # 模块限定枚举类型:Module.EnumClass(如 fat32_types.FRESULT, + # t.CEnum 枚举类非结构体,get_struct_type 查不到) + if HandlesEnum.is_enum_class(at.attr) == 1: + return HandlesEnum.get_enum_base_type(at.attr) + # 模块限定 typedef(posix.DIR = t.CTypedef = t.CVoid|t.CPtr 等): + # 与 Name 分支(L1986-2009)同一套 lookup_ctypedef + 递归解析。 + # 追加20只补了嵌套 Attribute(mod.sub.Typedef);单层 + # Module.Typedef(value=Name 非 t/c)同样缺失——shutil.vp + # L190 `dirp: posix.DIR` 变量注解解析失败根因。 + if self.CTypedefResolveDepth < MAX_CTYPEDEF_DEPTH: + mod_td_str: str = self.lookup_ctypedef(at.attr) + if mod_td_str is not None and self.CTypedefFound != 0: + if self._ctypedef_push_visiting(pool, at.attr) == 0: + mod_td_inner: ast.AST | t.CPtr = self._parse_type_string( + pool, mod_td_str, at.ctx) + if mod_td_inner is not None: + self.CTypedefResolveDepth += 1 + mod_td_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, mod_td_inner, imported_modules, from_imports, trans) + self.CTypedefResolveDepth -= 1 + self._ctypedef_pop_visiting() + if mod_td_ty is not None: + return mod_td_ty + else: + self._ctypedef_pop_visiting() return None # 嵌套 Attribute: w32.win32base.SECURITY_ATTRIBUTES 等三级及以上限定名 # at.value 是 Attribute 而非 Name,递归提取最终类名(at.attr)查结构体表 @@ -2012,6 +2121,32 @@ class TypeGenMixin: nested_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(at.attr) if nested_struct_ty is not None: return nested_struct_ty + # 嵌套模块限定的枚举类(fat32_types.FRESULT 三级名):查枚举基类型 + if HandlesEnum.is_enum_class(at.attr) == 1: + return HandlesEnum.get_enum_base_type(at.attr) + # 嵌套模块限定的 typedef(w32.win32base.HANDLE = t.CTypedef = VOIDPTR): + # 与 Name 分支(L1986-2009)同一套 lookup_ctypedef + 递归 + map_t_type 兜底。 + # 此前只查 struct/enum → w32.win32base.HANDLE 解析失败(fail-fast 报 + # NameError: 'w32.win32base.HANDLE' is not defined,FileIoTest 根因)。 + if self.CTypedefResolveDepth < MAX_CTYPEDEF_DEPTH: + nested_td_str: str = self.lookup_ctypedef(at.attr) + if nested_td_str is not None and self.CTypedefFound != 0: + if self._ctypedef_push_visiting(pool, at.attr) == 0: + nested_inner: ast.AST | t.CPtr = self._parse_type_string( + pool, nested_td_str, at.ctx) + if nested_inner is not None: + self.CTypedefResolveDepth += 1 + nested_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, nested_inner, imported_modules, from_imports, trans) + self.CTypedefResolveDepth -= 1 + self._ctypedef_pop_visiting() + if nested_ty is not None: + return nested_ty + else: + self._ctypedef_pop_visiting() + mapped_ty_na: llvmlite.LLVMType | t.CPtr = self.map_t_type(pool, at.attr) + if mapped_ty_na is not None: + return mapped_ty_na return None # BinOp 节点: 联合类型注解 A | B @@ -2124,6 +2259,44 @@ class TypeGenMixin: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) return llvmlite.Ptr(pool, other_ty) + # t.CPtr[...] 下标指针标记(t.CPtr[T] 非普通 CPtr 标记,_is_cptr_marker + # 不识别 Subscript): X | t.CPtr[T] → 指针层级合并(对齐 TPC 联合合并 + # 规则 HandlesTypeMerge.py L864-884:CPtr 侧贡献 PtrCount,另一侧提供 + # BaseType)。如 t.CUInt8T | t.CPtr[t.CPtr] → u8**。此前左右都不是 + # CPtr 标记 → 落下方"无 CPtr 标记"分支返回左值 i8 → c.Deref(out) 报 + # "参数不是指针类型"(zdeflate.vp compress out 根因)。 + cptr_sub_side: ast.AST | t.CPtr = None + cptr_other: ast.AST | t.CPtr = None + if self._is_cptr_subscript(bop.left) != 0: + cptr_sub_side = bop.left + cptr_other = bop.right + elif self._is_cptr_subscript(bop.right) != 0: + cptr_sub_side = bop.right + cptr_other = bop.left + if cptr_sub_side is not None and cptr_other is not None: + cptr_sub_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, cptr_sub_side, imported_modules, from_imports, trans) + cptr_other_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, cptr_other, imported_modules, from_imports, trans) + if cptr_sub_ty is not None and cptr_other_ty is not None: + # 计算 CPtr 侧外层 Ptr 嵌套数(如 t.CPtr[t.CPtr]=i8** → 2 层) + cptr_level: int = 0 + cur_lvl: llvmlite.LLVMType | t.CPtr = cptr_sub_ty + while cur_lvl is not None: + match cur_lvl: + case llvmlite.LLVMType.Ptr(_): + cptr_level += 1 + cur_lvl = cur_lvl.Pointee + case _: + break + # 结果 = 另一侧类型外包 N 层 Ptr + res_ty: llvmlite.LLVMType | t.CPtr = cptr_other_ty + li2: int = 0 + while li2 < cptr_level: + res_ty = llvmlite.Ptr(pool, res_ty) + li2 += 1 + return res_ty + # 无 CPtr 标记:返回第一个非 None(装饰器标记如 CExport/CExtern 被忽略) left_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( pool, bop.left, imported_modules, from_imports, trans) @@ -2210,6 +2383,19 @@ class TypeGenMixin: if HandlesImports.is_module_imported(imported_modules, arr_val_name.id) == 0: self.fatal_type_error(node, "t.Callable 模块未导入") return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + # 指针类型: t.CPtr[T] → Ptr(T)(指向 T 的指针,T 自身可含指针层级) + # t.CPtr[t.CPtr] → Ptr(Ptr(CPtr=i8*)) = i8**(双指针) + # t.CPtr[t.CInt] → i32*(DeclarationGenerator.py L1014-1016 文档化语法)。 + # 此前缺失 → t.CUInt8T | t.CPtr[t.CPtr] 联合退化为左值 i8 → + # c.Deref(out) 报"参数不是指针类型"(zdeflate.vp compress out 根因) + if string.strcmp(arr_at.attr, "CPtr") == 0: + if HandlesImports.is_module_imported(imported_modules, arr_val_name.id) == 0: + self.fatal_type_error(node, "t.CPtr 模块未导入") + inner_cptr_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, sub.slice, imported_modules, from_imports, trans) + if inner_cptr_ty is None: + self.fatal_type_error(sub.slice, "t.CPtr[?] 元素类型解析失败") + return llvmlite.Ptr(pool, inner_cptr_ty) if string.strcmp(arr_at.attr, "CArray") != 0: self.fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)") if HandlesImports.is_module_imported(imported_modules, arr_val_name.id) == 0: @@ -2263,6 +2449,9 @@ class TypeGenMixin: # 未知 AST kind:fail-fast,不宽容返回 None self.fatal_type_error(node, "不支持的类型注解语法") + # 追加89 块设计:fatal_type_error 调用后补显式 return(运行时不可达, + # 保证方法末尾块被正确 terminated,不做 noreturn 函数名白名单) + return None def has_decorator_marker(self, node: ast.AST | t.CPtr, marker_name: str) -> int: diff --git a/App/lib/core/Gen/VarGenMixin.vp b/App/lib/core/Gen/VarGenMixin.vp index 878525f..1befeac 100644 --- a/App/lib/core/Gen/VarGenMixin.vp +++ b/App/lib/core/Gen/VarGenMixin.vp @@ -4,6 +4,7 @@ import llvmlite import memhub import string import lib.core.Handles.HandlesVar as HandlesVar +import lib.core.Handles.HandlesType as HandlesType # ============================================================ @@ -40,7 +41,9 @@ SCOPE_FUNCTION: t.CDefine = 1 SCOPE_BLOCK: t.CDefine = 2 # 每个作用域最大变量数 -MAX_VARS: t.CDefine = 256 +# 256 太小:NewAstTest main() 有 289 个局部变量(几十个测试用例), +# add_var 超限静默失败导致变量未注册,后续使用报"未定义名称"(NameError)。 +MAX_VARS: t.CDefine = 1024 # ============================================================ @@ -388,5 +391,13 @@ class VarGenMixin: alloca: llvmlite.Value | t.CPtr = self._alloca_at_entry(builder, ty) if alloca is None: return None - self.define_var(symtab, name, alloca) + if self.define_var(symtab, name, alloca) != 0: + # fail-fast: 变量注册失败(如超过 MAX_VARS)必须立即终止。 + # 此前静默忽略返回值:alloca 已创建但未入符号表,后续读取该变量 + # 报"未定义名称"(NewAstTest main() 289 个局部变量 > MAX_VARS=256 + # 的 NameError 根因),误导排查。变量表超限暴露上游规模问题。 + HandlesType.fatal_error( + None, + f"变量 '{name}' 注册失败(作用域变量数超过上限 MAX_VARS)", + "InternalCompilerError") return alloca diff --git a/App/lib/core/Handles/HandlesAnnAssign.vp b/App/lib/core/Handles/HandlesAnnAssign.vp index fca9f03..1e34553 100644 --- a/App/lib/core/Handles/HandlesAnnAssign.vp +++ b/App/lib/core/Handles/HandlesAnnAssign.vp @@ -78,7 +78,7 @@ def is_ctypedef_annotation(annot: ast.AST | t.CPtr) -> int: # 4. Call — 如 t.CUnsignedLong(-11) → 取第一个参数 # 5. UnaryOp(USub/UAdd/Invert) — 如 -11 # ============================================================ -def extract_cdefine_int_value(val_node: ast.AST | t.CPtr) -> int: +def extract_cdefine_int_value(val_node: ast.AST | t.CPtr) -> t.CInt64T: """从值节点提取整数常量(支持 Constant/BinOp/Name/Call/UnaryOp,委托到 gen.extract_cdefine_int_value)""" import lib.core.Gen.Generator as Generator g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) @@ -397,9 +397,13 @@ class AnnAssignHandle(HandlesBase.Mixin): if HandlesVar.define_var( self.Trans.SymTab, nm.id, alloca) == 0: # 存储原始类型注解的类名(方法调用检测时,Ptr(i8) 回退到类名查找结构体) + # 传 pool:泛型 Subscript 返回 mangled name(GSList[BasicBlock] → 特化 + # struct 名),否则 pool=None 只返回裸类名(GSList → 泛型模板未注册 + # → 属性访问 lookup_field_by_class("GSList") 报"类未注册",LLVMLiteTest + # __function.vp blocks.Tail 根因;对齐 ClassGenMixin L3159 BUG 1 方案3) if annot is not None: cls_nm_ps: str = HandlesType.extract_class_name_from_annotation( - annot, self.Trans._imported_modules) + annot, self.Trans._imported_modules, pool) if cls_nm_ps is not None: HandlesVar.set_var_annot_class_name( self.Trans.SymTab, nm.id, cls_nm_ps) @@ -420,9 +424,6 @@ class AnnAssignHandle(HandlesBase.Mixin): aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) if aa is None: return 0 - # OOM 诊断:AnnAssign 入口 used(定位 Constant 构造语句的注解/值翻译段) - stdio.printf("[MEM] AnnAssign.Handle line %d used=%zu chk=%zu\n", - node.lineno, self.Trans.Pool.used_bytes(), self.Trans.Pool.chk_depth) target: ast.AST | t.CPtr = aa.target if target is None or target.kind() != ast.ASTKind.Name: @@ -556,10 +557,40 @@ class AnnAssignHandle(HandlesBase.Mixin): aa, f"无法为变量 {nm.id} 创建存储", "TypeError") + # FakeDuck: str 变量创建影子结构体 {var}__meta__(per-instance 字段存储) + # 仅裸 str 注解(Name("str"))且 _str 已注册时创建;bytes / t.CChar|t.CPtr 不创建。 + # a 保持为 i8*(可 printf/len),a__meta__ 存 __data__/__mbuddy__ 等字段, + # 属性读写(fd3)与方法调用(fd5)均通过该影子解析。 + fd_str_ty: llvmlite.LLVMType | t.CPtr = None + fd_meta_alloca: llvmlite.Value | t.CPtr = None + fd_meta_name: str = None + if annot is not None and annot.kind() == ast.ASTKind.Name: + fd_annot: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot) + if fd_annot is not None and fd_annot.id is not None: + if string.strcmp(fd_annot.id, "str") == 0: + fd_str_ty = HandlesStruct.get_struct_type("_str") + if fd_str_ty is not None: + # 确保 _str 完整类型定义在当前模块可用:跨模块结构体默认 opaque, + # 直接 alloca opaque 类型 → llc "Cannot allocate unsized type" + HandlesStruct.ensure_struct_def_in_module(pool, mod, fd_str_ty) + # 影子变量名必须用 pool 持久缓冲:f-string 生成栈缓冲,在 AnnAssign + # 返回后被覆盖,符号表注册的名字内容损坏 → 后续 lookup 找不到。 + fd_meta_name = pool.alloc(256) + if fd_meta_name is not None: + viperlib.snprintf(fd_meta_name, 256, "%s__meta__", nm.id) + fd_meta_alloca = HandlesVar.get_or_create_sym( + self.Trans.SymTab, pool, builder, fd_meta_name, fd_str_ty) + if fd_meta_alloca is None: + # fail-fast: 影子存储创建失败,立即终止 + HandlesType.fatal_error( + aa, + f"无法为 str 影子变量 {fd_meta_name} 创建存储", + "TypeError") # 存储原始类型注解的类名(方法调用检测时,Ptr(i8) 回退到类名查找结构体) + # 传 pool:泛型 Subscript 返回 mangled name(对齐 L401 同根因修复) if annot is not None: cls_nm_hd: str = HandlesType.extract_class_name_from_annotation( - annot, self.Trans._imported_modules) + annot, self.Trans._imported_modules, pool) if cls_nm_hd is not None: HandlesVar.set_var_annot_class_name( self.Trans.SymTab, nm.id, cls_nm_hd) @@ -644,12 +675,48 @@ class AnnAssignHandle(HandlesBase.Mixin): if target_ty is not None: rhs_val = HandlesExpr.coerce_to_type(builder, rhs_val, target_ty, node) llvmlite.build_store(builder, rhs_val, alloca) + # FakeDuck: 初始化影子结构体 __data__ 字段 = 初始字符串指针 + # (对齐 TPC HandlesAnnAssign.py: 仅当有初值时写入 __data__) + if fd_str_ty is not None and fd_meta_alloca is not None: + fd_data_fe: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(fd_str_ty, "__data__") + if fd_data_fe is not None: + fd_data_ptr: llvmlite.Value | t.CPtr = \ + llvmlite.build_gep_struct( + builder, fd_str_ty, fd_data_fe.Ty, + fd_meta_alloca, fd_data_fe.Index) + if fd_data_ptr is not None: + llvmlite.build_store(builder, rhs_val, fd_data_ptr) # 清除赋值上下文(防残留):CurAssignAnnot/CurAssignNode 只在该 # AnnAssign 的 RHS 翻译期间有效,否则后续 while 条件/比较等处的 # c.Deref 会被残留注解误判读取宽度(i8* 被 bitcast i32* 读 4 字节)。 self.Trans.CurAssignAnnot = None self.Trans.CurAssignNode = None + # FakeDuck: __mbuddy__ 字段初始化为 NULL。 + # with 上下文提供的 __mbuddy__ 注入由 _inject_requires_fields(fd4)完成, + # 此处仅确保影子结构体初始为空指针(对齐 TPC HandlesAnnAssign.py L667-670)。 + if fd_str_ty is not None and fd_meta_alloca is not None: + fd_buddy_fe: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(fd_str_ty, "__mbuddy__") + if fd_buddy_fe is not None and fd_buddy_fe.Ty is not None: + fd_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull( + pool, fd_buddy_fe.Ty, "null") + fd_buddy_ptr: llvmlite.Value | t.CPtr = \ + llvmlite.build_gep_struct( + builder, fd_str_ty, fd_buddy_fe.Ty, + fd_meta_alloca, fd_buddy_fe.Index) + if fd_buddy_ptr is not None and fd_null is not None: + llvmlite.build_store(builder, fd_null, fd_buddy_ptr) + # FakeDuck: with 上下文自动注入 requires 字段(如 __mbuddy__)。 + # 经全局 gen 单例委托 CallGenMixin._inject_requires_fields。 + import lib.core.Gen.Generator as Generator + g_fda: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)( + t.CVoid(_g_gen, t.CPtr)) + if g_fda is not None: + g_fda._inject_requires_fields( + self.Trans, pool, builder, mod, fd_meta_alloca, "_str") + return 0 diff --git a/App/lib/core/Handles/HandlesAssign.vp b/App/lib/core/Handles/HandlesAssign.vp index 2a9ec5b..6d262b4 100644 --- a/App/lib/core/Handles/HandlesAssign.vp +++ b/App/lib/core/Handles/HandlesAssign.vp @@ -67,6 +67,15 @@ class AssignHandle(HandlesBase.Mixin): rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, asgn.value, None, 0, self.Trans) if rhs_val is None: + # Phase A 依赖未就绪(_deps_pending=1):跨模块函数/方法因 text.ll + # 未生成返回类型退化为 void/None(CallGenMixin L4493 + # g_vta._resolve_attr_field_class 根因)。此时整文件不写盘、下遍重译 + # (Phase2.vp L590-599),跳过本语句不报错——否则 fatal 使多遍 + # 重试机制永远无法收敛(本语句只是"本遍保持默认类型"的退化结果, + # 下遍依赖已生成即可精确解析)。依赖就绪时 _deps_pending=0, + # 此处仍 fail-fast,不掩盖真实错误。 + if self.Trans is not None and self.Trans._deps_pending != 0: + return 0 # 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位 fb: str = VLogger.fmt_buf() if fb is not None and asgn.value is not None: @@ -357,6 +366,49 @@ class AssignHandle(HandlesBase.Mixin): if HandlesExprCall._try_property_setter( pool, builder, mod, target, rhs_val, self.Trans) == 1: continue + # FakeDuck: 影子结构体属性写入 (str 变量的 per-instance 字段) + # a.__data__ = v → GEP {a}__meta__[field] 并 store + # 对齐 TPC HandlesAssign.py L67-76:对象是 Name 变量且存在 + # {var}__meta__ 影子(fd2 创建)时,属性写入改走影子结构体。 + fd_an: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(target) + if fd_an is not None and fd_an.value is not None and fd_an.attr is not None: + if fd_an.value.kind() == ast.ASTKind.Name: + fd_obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(fd_an.value) + if fd_obj_nm.id is not None: + # pool 持久缓冲(f-string 栈缓冲在函数返回后失效) + fd_meta_w: str = pool.alloc(256) + if fd_meta_w is not None: + viperlib.snprintf(fd_meta_w, 256, "%s__meta__", fd_obj_nm.id) + fd_meta_vw: llvmlite.Value | t.CPtr = HandlesVar.lookup_current( + self.Trans.SymTab, fd_meta_w) + if fd_meta_vw is not None: + fd_sty_w: llvmlite.LLVMType | t.CPtr = \ + HandlesStruct.get_struct_type("_str") + if fd_sty_w is not None: + fd_fe_w: HandlesStruct.FieldEntry | t.CPtr = \ + HandlesStruct.lookup_field(fd_sty_w, fd_an.attr) + if fd_fe_w is not None and fd_fe_w.Ty is not None: + fd_fp_w: llvmlite.Value | t.CPtr = \ + llvmlite.build_gep_struct( + builder, fd_sty_w, fd_fe_w.Ty, + fd_meta_vw, fd_fe_w.Index) + if fd_fp_w is not None: + sv_fd: llvmlite.Value | t.CPtr = \ + HandlesExpr.coerce_to_type( + builder, rhs_val, fd_fe_w.Ty, node) + llvmlite.build_store(builder, sv_fd, fd_fp_w) + continue + # VTABLE 直接写入:obj.method = func(serial0_file.open = serial0_open) + # vtable 方法不是数据字段(FieldEntry),get_attribute_ptr 查不到会 + # fatal_error;此处对齐 TPC HandlesAssign.py L905-949 的写时复制, + # 检测到 vtable 方法时直接生成槽位写入并跳过字段路径。 + import lib.core.Gen.Generator as Generator + g_vtw: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g_vtw is not None: + if g_vtw._write_vtable_attr( + builder, pool, mod, target, rhs_val, self.Trans) == 1: + continue field_ptr: llvmlite.Value | t.CPtr = HandlesExpr.get_attribute_ptr( builder, pool, mod, target, self.Trans) if field_ptr is not None: @@ -368,6 +420,11 @@ class AssignHandle(HandlesBase.Mixin): llvmlite.build_store(builder, store_val, field_ptr) continue # 守卫:field_ptr 为 None → 构造详细错误信息 + # Phase A 依赖未就绪(_deps_pending):get_attribute_ptr 已因 + # 父类/依赖类未注册跳过字段,此处同样跳过不报错,整文件下遍重译 + # (对齐 HandlesAssign L69-78 与 get_attribute_ptr 的 pending 容忍) + if self.Trans is not None and self.Trans._deps_pending != 0: + continue attr_node: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(target) attr_name: str = "(unknown)" obj_name: str = "(unknown)" @@ -535,6 +592,38 @@ class AssignHandle(HandlesBase.Mixin): return None obj_cls: str = self._infer_rhs_annot(pool, mod, ca.value, trans) if obj_cls is None: + # 模块级函数调用(requests.get):ca.value 是已导入模块名 + # 而非对象变量(模块名 lookup_var_entry 返回 None → 上面 + # 对象类名推断失败)。从该模块 vpi 的返回注解缓存解析返回 + # 类名(键 {sha1}.{func},cls_name=None 即模块级函数,与 + # CallGenMixin 跨模块返回类型解析一致)。 + # WebTest resp = requests.get(...) 根因:无注解赋值变量 + # AnnotClassName 推断失败(annot=None)→ 属性访问按需过滤 + # (依赖 AnnotClassName)失效 → "属性访问失败: .status_code"。 + if ca.value.kind() == ast.ASTKind.Name: + mod_nm_rhs: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ca.value) + if mod_nm_rhs.id is not None: + mod_sha1_rhs: str = HandlesExprCall._lookup_module_sha1( + mod_nm_rhs.id) + if mod_sha1_rhs is not None: + import lib.StubGen.VpiReader as VpiReader + temp_dir_rhs: str = None + import lib.core.Gen.Generator as Generator + g_rhs: Generator.Generator | t.CPtr = ( + Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g_rhs is not None and g_rhs.TempDir is not None: + temp_dir_rhs = g_rhs.TempDir + if temp_dir_rhs is not None: + VpiReader._ensure_vpi_ret_annot_loaded( + mod_sha1_rhs, temp_dir_rhs) + annot_rhs: str = VpiReader._lookup_vpi_ret_annot( + mod_sha1_rhs, None, ca.attr) + if annot_rhs is not None: + ret_cls_rhs: str = self._extract_annot_class_name( + pool, annot_rhs) + if ret_cls_rhs is not None: + return ret_cls_rhs return None if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0: return None diff --git a/App/lib/core/Handles/HandlesClassDef.vp b/App/lib/core/Handles/HandlesClassDef.vp index cdc934d..1f318ef 100644 --- a/App/lib/core/Handles/HandlesClassDef.vp +++ b/App/lib/core/Handles/HandlesClassDef.vp @@ -163,6 +163,85 @@ def _has_decorator(decorator_list: list[ast.AST | t.CPtr] | t.CPtr, return 0 +def register_decorator(pool: memhub.MemBuddy | t.CPtr, + func_name: str, wrapper_name: str, + arg0: t.CInt64T) -> int: + """注册自定义装饰器函数(@log/@skip/@repeat(3) → __decor_wrap_{name},委托到 gen)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.register_decorator(pool, func_name, wrapper_name, arg0) + return -1 + + +def lookup_decor_wrapper(func_name: str) -> str: + """按原函数名查装饰器 wrapper 名,未注册返回 None(委托到 gen)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.lookup_decor_wrapper(func_name) + return None + + +def lookup_decor_arg0(func_name: str) -> t.CInt64T: + """按原函数名查装饰器首个常量参数(无参数为 0,委托到 gen)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.lookup_decor_arg0(func_name) + return 0 + + +# ============================================================ +# 异常类注册表委托(try-except,对齐 TPC HandlesClassDef.py L47-61) +# ============================================================ +def register_exception(class_name: str, parent_name: str) -> int: + """注册用户异常类并分配编码(幂等,委托到 gen)。返回编码或 -1""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.register_exception(class_name, parent_name) + return -1 + + +def lookup_exception_code(name: str) -> int: + """查异常编码:内置表优先,其次用户异常表(委托到 gen)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.lookup_exception_code(name) + return 0 + + +def is_exception_name(name: str) -> int: + """name 是否是内置或用户异常类名(委托到 gen)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.is_exception_name(name) + return 0 + + +def collect_descendant_codes(name: str, out_codes: t.CInt | t.CPtr, + max_codes: int) -> int: + """收集 name 及全部子孙异常编码(委托到 gen)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.collect_descendant_codes(name, out_codes, max_codes) + return 0 + + +def register_exception_class_def(trans: HT.Translator | t.CPtr, + cd: ast.ClassDef | t.CPtr) -> int: + """注册异常类定义(分配编码 + 记录父类,不生成 struct,委托到 gen)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g._register_exception_class_def(trans, cd) + return 0 + + # ============================================================ # 方法元数据(装饰器)支持 # @@ -699,12 +778,19 @@ def _lookup_renum_variant_in_class(class_name: str, variant_name: str) -> int: def _register_renum_field_alias(pool: memhub.MemBuddy | t.CPtr, class_name: str, field_name: str, - payload_idx: int): - """注册 REnum 变体字段名别名到全局表(委托到 gen._register_renum_field_alias)""" + payload_idx: int, + field_ty: llvmlite.LLVMType | t.CPtr = None, + variant_name: str = None): + """注册 REnum 变体字段名别名到全局表(委托到 gen._register_renum_field_alias) + + field_ty: 变体成员实际 LLVM 类型(可选,供 match 绑定 bitcast) + variant_name: 所属变体名(可选,match 绑定按 (class, 变体, 位置) 定位成员) + """ import lib.core.Gen.Generator as Generator g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) if g is not None: - g._register_renum_field_alias(pool, class_name, field_name, payload_idx) + g._register_renum_field_alias(pool, class_name, field_name, payload_idx, + field_ty, variant_name) # ============================================================ @@ -752,6 +838,46 @@ def _lookup_renum_field_alias(class_name: str, field_name: str) -> int: return 0 +# ============================================================ +# _lookup_renum_field_alias_ty — 查找 REnum 变体成员的实际 LLVM 类型 +# +# 与 _lookup_renum_field_alias 同表同键。供 match 绑定把槽位地址 +# bitcast 成成员真实类型(Struct.Name: i8* 而非槽位 i64)。 +# +# 返回: +# 成功: LLVMType(perm_pool 深拷贝,跨文件存活) +# 失败: None +# ============================================================ +def _lookup_renum_field_alias_ty(class_name: str, + field_name: str) -> llvmlite.LLVMType | t.CPtr: + """查找 REnum 变体成员的实际 LLVM 类型,返回类型或 None(委托到 gen._lookup_renum_field_alias_ty)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g._lookup_renum_field_alias_ty(class_name, field_name) + return None + + +# ============================================================ +# _lookup_renum_member_ty — 按 (class, 变体名, payload 位置) 查找成员类型 +# +# match 绑定用:同一 payload 位置跨变体类型不同,必须带变体名定位。 +# +# 返回: +# 成功: LLVMType(perm_pool 深拷贝,跨文件存活) +# 失败: None +# ============================================================ +def _lookup_renum_member_ty(class_name: str, + variant_name: str, + payload_idx: int) -> llvmlite.LLVMType | t.CPtr: + """按 (class, 变体名, payload 位置) 查找变体成员实际类型,返回类型或 None(委托到 gen._lookup_renum_member_ty)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g._lookup_renum_member_ty(class_name, variant_name, payload_idx) + return None + + # ============================================================ # translate_class_def — 翻译 class 定义 # diff --git a/App/lib/core/Handles/HandlesExpr.vp b/App/lib/core/Handles/HandlesExpr.vp index 595dfa3..1a01884 100644 --- a/App/lib/core/Handles/HandlesExpr.vp +++ b/App/lib/core/Handles/HandlesExpr.vp @@ -295,6 +295,24 @@ def create_global_string(builder: llvmlite.IRBuilder | t.CPtr, return None +# ============================================================ +# 模块引用 AST → 模块路径字符串(递归拼接 Attribute 链) +# ============================================================ +def attr_module_path_str(pool: memhub.MemBuddy | t.CPtr, + node: ast.AST | t.CPtr) -> str: + """把模块引用 AST 转成模块路径字符串(Name('sys')→"sys"、 + Attribute(Attribute(Name('os'),'_win32'))→"os._win32";非模块引用形态返回 None) + + 跨 mixin 委托:CallGenMixin 等不能 self. 直接调用 ExprGenMixin 的方法 + (@t.NoVTable 静态绑定会 mangle 成当前类名 → undefined reference)。 + """ + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g._attr_module_path_str(pool, node) + return None + + # ============================================================ # 翻译常量表达式 # ============================================================ @@ -511,8 +529,8 @@ def _build_source_path_from_sha1(sha1: str) -> str: def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr, name: str, from_imports: str, - imported_modules: str = None) -> int: - """跨模块查找 CDefine 常量,返回值或 -1(未找到)(委托到 gen._lookup_cross_module_cdefine)""" + imported_modules: str = None) -> t.CInt64T: + """跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到)(委托到 gen._lookup_cross_module_cdefine)""" import lib.core.Gen.Generator as Generator g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) if g is not None: diff --git a/App/lib/core/Handles/HandlesExprCall.vp b/App/lib/core/Handles/HandlesExprCall.vp index ef9ec3d..7994083 100644 --- a/App/lib/core/Handles/HandlesExprCall.vp +++ b/App/lib/core/Handles/HandlesExprCall.vp @@ -770,10 +770,6 @@ def _fail_method_ret_ty(method_name: str, # 行提取实际返回类型,避免指针截断(未命中时回退 vpi stub 缓存)。 # ============================================================ -# text.ll 读取缓冲区大小 -_TEXT_LL_READ_BUF_SIZE: t.CDefine = 262144 - - # ============================================================ # SetTempDir - 设置 temp 目录路径 # diff --git a/App/lib/core/Handles/HandlesFor.vp b/App/lib/core/Handles/HandlesFor.vp index 5be7e2d..8a01718 100644 --- a/App/lib/core/Handles/HandlesFor.vp +++ b/App/lib/core/Handles/HandlesFor.vp @@ -12,6 +12,8 @@ import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesExpr as HandlesExpr import lib.core.Handles.HandlesBody as HandlesBody import lib.core.Handles.HandlesType as HandlesType +import lib.core.Handles.HandlesExprCall as HandlesExprCall +import lib.core.Handles.HandlesStruct as HandlesStruct # ============================================================ @@ -186,6 +188,33 @@ class ForHandle(HandlesBase.Mixin): if lst_val is not None and lst_val.Ty is not None \ and HandlesExpr.is_ptr_type(lst_val.Ty) != 0: return self._handle_list_iter(for_node, var_name, lst_val) + # OOP iter 协议:迭代源是类对象且类有 __iter__/__next__ 方法 → + # 走 __iter__/__next__ 调用迭代(对齐 TPC HandlesFor.py L142-166 + # _HandleForIterLlvm)。dict/list 是内置泛型容器已在上方内联处理。 + ov_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, trans.Module, iter_node, + trans._funcs, trans._func_count, trans) + if ov_val is not None and ov_val.Ty is not None \ + and HandlesExpr.is_ptr_type(ov_val.Ty) != 0: + ov_st: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value( + ov_val) + if ov_st is not None: + ov_cn: str = HandlesStruct.get_class_name_by_type(pool, ov_st) + if ov_cn is not None: + oi_lookup: str = pool.alloc(128) + if oi_lookup is not None: + viperlib.snprintf(oi_lookup, 128, "%s.__iter__", ov_cn) + oi_fn: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module( + trans.Module, oi_lookup) + if oi_fn is not None: + on_lookup: str = pool.alloc(128) + if on_lookup is not None: + viperlib.snprintf(on_lookup, 128, "%s.__next__", ov_cn) + on_fn: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module( + trans.Module, on_lookup) + if on_fn is not None: + return self._handle_oop_iter( + for_node, var_name, ov_cn, ov_val) # 其它不支持的字面量:fast-fail 显式报错, # 避免 translate_value 触发 list[T] 特化崩溃或语义歧义。 ik: int = iter_node.kind() @@ -245,6 +274,26 @@ class ForHandle(HandlesBase.Mixin): if step_val is None: # fail-fast: range 参数翻译失败 HandlesType.fatal_error(args.get(2), "range 参数翻译失败", "TypeError") + # step 为负数字面量时循环条件反转(对齐 TPC HandlesFor.py L86-94): + # range(a, b, -1) → i > stop;range(a, b, +1) → i < stop。 + # TPC 仅识别负数字面量(UnaryOp USub 常量 / 负常量);变量 step 保守按正。 + # 修复 _find_provider 的 range(top-1, -1, -1) 循环体不执行(i < -1 恒假)。 + step_neg_const: int = 0 + if arg_count >= 3: + step_arg_node: ast.AST | t.CPtr = args.get(2) + if step_arg_node is not None: + skn: int = step_arg_node.kind() + if skn == ast.ASTKind.UnaryOp: + uop_r: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(step_arg_node) + if uop_r.op == ast.OpKind.USub and uop_r.operand is not None: + if uop_r.operand.kind() == ast.ASTKind.Constant: + uoc_r: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(uop_r.operand) + if uoc_r.int_val > 0: + step_neg_const = 1 + elif skn == ast.ASTKind.Constant: + skc_r: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(step_arg_node) + if skc_r.int_val < 0: + step_neg_const = 1 # 4. 创建/查找循环变量 alloca var_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( @@ -311,9 +360,19 @@ class ForHandle(HandlesBase.Mixin): if cur_bits < stop_bits: cmp_lhs = llvmlite.build_sext(builder, cur_i, stop_val.Ty) else: - cmp_rhs = llvmlite.build_trunc(builder, stop_val, loop_var_ty) + # 循环变量位宽 > stop 位宽(如 t.CSizeT 循环变量 + range(i32)): + # 应把 cur_i 截断到 stop_val 类型(低向高才 sext/zext,高向低用 + # trunc)。原实现反了——把小的 stop_val trunc 到大的 loop_var_ty, + # 生成 `trunc i32 to i64` 扩宽非法 IR,llc 报 + # "invalid cast opcode for cast from 'i32' to 'i64'" + # (VpiReader.vp range(arg_count) 追加86 根因)。 + cmp_lhs = llvmlite.build_trunc(builder, cur_i, stop_val.Ty) + # step 为负时条件反转:i > stop(ICMP_SGT);正/缺省 step 保持 i < stop + range_cmp_op: int = llvmlite.ICMP_SLT + if step_neg_const != 0: + range_cmp_op = llvmlite.ICMP_SGT cond_i1: llvmlite.Value | t.CPtr = llvmlite.build_icmp( - builder, llvmlite.ICMP_SLT, cmp_lhs, cmp_rhs) + builder, range_cmp_op, cmp_lhs, cmp_rhs) llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb) # 9. body 块: 翻译循环体,跳到 incr @@ -943,6 +1002,189 @@ class ForHandle(HandlesBase.Mixin): llvmlite.position_at_end(builder, end_bb) return new_vars + # ============================================================ + # _handle_oop_iter - OOP 迭代协议: for x in obj: + # obj 的类有 __iter__/__next__ 方法时调用协议迭代 + # (对齐 TPC HandlesFor.py _HandleForIterLlvm L599-663) + # + # 生成 IR 结构: + # %it = call {Class}.__iter__(obj) + # %flag = alloca i1 ; store 0 + # br %cond + # cond: + # store 0, %flag + # %v = call {Class}.__next__(%it, %flag) + # %stop = load i1, %flag + # br i1 %stop==0, %body, %end + # body: + # store %v, %var + # ... 循环体 ... + # br %step + # step: + # br %cond + # end: + # + # __next__ 的隐藏参数 __stop_iter_flag__(i1*)由 ClassGenMixin + # _translate_method 注入,raise StopIteration 时置 1(translate_raise); + # VpiReader stub 声明同步该参数(_inject_method_declare)。 + # ============================================================ + def _handle_oop_iter(self, for_node: ast.For | t.CPtr, + var_name: str, + class_name: str, + obj_val: llvmlite.Value | t.CPtr) -> int: + """OOP iter 协议: for x in obj: 调用 __iter__/__next__ 迭代""" + trans: HT.Translator | t.CPtr = self.Trans + pool: memhub.MemBuddy | t.CPtr = trans.Pool + builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder + func: llvmlite.Function | t.CPtr = trans._cur_func + mod: llvmlite.LLVMModule | t.CPtr = trans.Module + + if builder is None or func is None or mod is None: + return 0 + if obj_val is None or obj_val.Ty is None: + HandlesType.fatal_error(for_node, "OOP 迭代: 迭代源对象类型未知") + if HandlesExpr.is_ptr_type(obj_val.Ty) == 0: + HandlesType.fatal_error(for_node, "OOP 迭代: 迭代源对象不是指针类型") + + # 1. 查找 {Class}.__iter__ 并调用,得到迭代器 + iter_lookup: str = pool.alloc(128) + if iter_lookup is None: + return 0 + viperlib.snprintf(iter_lookup, 128, "%s.__iter__", class_name) + iter_fn: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module( + mod, iter_lookup) + if iter_fn is None: + HandlesType.fatal_error( + for_node, f"OOP 迭代: 类 '{class_name}' 没有 __iter__ 方法") + iter_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(iter_fn) + iter_fn_name: str = llvmlite.function_get_name(iter_fn) + if iter_ret_ty is None or iter_fn_name is None: + return 0 + # self 参数按 __iter__ 首参类型对齐(stub 声明 self 可能是 i8*) + iter_self: llvmlite.Value | t.CPtr = obj_val + iter_first: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(iter_fn) + if iter_first is not None: + iter_self_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(iter_first) + if iter_self_ty is not None: + iter_self = llvmlite.build_bitcast(builder, obj_val, iter_self_ty) + if iter_self is None: + return 0 + iter_self.Next = None + iter_result: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, iter_fn_name, iter_self, 1, iter_ret_ty, 0) + if iter_result is None: + return 0 + + # 2. 查找 {Class}.__next__(带隐藏 __stop_iter_flag__ i1* 参数) + next_lookup: str = pool.alloc(128) + if next_lookup is None: + return 0 + viperlib.snprintf(next_lookup, 128, "%s.__next__", class_name) + next_fn: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module( + mod, next_lookup) + if next_fn is None: + HandlesType.fatal_error( + for_node, f"OOP 迭代: 类 '{class_name}' 没有 __next__ 方法") + next_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(next_fn) + next_fn_name: str = llvmlite.function_get_name(next_fn) + if next_ret_ty is None or next_fn_name is None: + return 0 + + # 3. 循环变量 alloca(元素类型 = __next__ 返回类型) + var_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( + trans.SymTab, var_name) + new_vars: int = 0 + if var_alloca is None: + var_alloca = HandlesVar._alloca_at_entry(builder, next_ret_ty) + if var_alloca is not None: + if HandlesVar.define_var( + trans.SymTab, var_name, var_alloca) == 0: + new_vars = 1 + + # 4. stop flag alloca(i1,初始 0) + i1_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool) + flag_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, i1_ty) + if flag_alloca is None: + return 0 + flag_zero: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i1_ty, 0, "0") + if flag_zero is not None: + llvmlite.build_store(builder, flag_zero, flag_alloca) + + # 5. 创建基本块: cond / body / step / end + cnt: int = trans._label_counter + trans._label_counter = cnt + 1 + name_buf: str = pool.alloc(32) + viperlib.snprintf(name_buf, 32, "oop.cond.%d", cnt) + cond_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "oop.body.%d", cnt) + body_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "oop.step.%d", cnt) + step_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "oop.end.%d", cnt) + end_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + if cond_bb is None or body_bb is None or step_bb is None or end_bb is None: + return 0 + + llvmlite.build_br(builder, cond_bb) + + # 6. cond 块: 重置 flag → 调 __next__(iter, flag) → 检查 flag 判结束 + llvmlite.position_at_end(builder, cond_bb) + if flag_zero is not None: + llvmlite.build_store(builder, flag_zero, flag_alloca) + # __next__ 参数 1:迭代器(按首参类型 bitcast) + next_self: llvmlite.Value | t.CPtr = iter_result + next_first: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(next_fn) + if next_first is not None: + next_self_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(next_first) + if next_self_ty is not None and iter_result.Ty is not None: + next_self = llvmlite.build_bitcast(builder, iter_result, next_self_ty) + if next_self is None: + return 0 + # 参数链表: arg1(iter).Next = arg2(flag);flag 独立 null 结尾防自链接 + next_self.Next = flag_alloca + flag_alloca.Next = None + next_val: llvmlite.Value | t.CPtr = llvmlite.build_call( + builder, next_fn_name, next_self, 2, next_ret_ty, 0) + if next_val is None: + return 0 + stop_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i1_ty, flag_alloca) + if stop_val is None: + return 0 + flag_zero2: llvmlite.Value | t.CPtr = llvmlite.ConstInt(pool, i1_ty, 0, "0") + continue_i1: llvmlite.Value | t.CPtr = None + if flag_zero2 is not None: + continue_i1 = llvmlite.build_icmp( + builder, llvmlite.ICMP_EQ, stop_val, flag_zero2) + if continue_i1 is None: + return 0 + llvmlite.build_cond_br(builder, continue_i1, body_bb, end_bb) + + # 7. body 块: store 元素 → 翻译循环体 → br step + llvmlite.position_at_end(builder, body_bb) + if var_alloca is not None: + llvmlite.build_store(builder, next_val, var_alloca) + old_break: llvmlite.BasicBlock | t.CPtr = trans._break_bb + old_continue: llvmlite.BasicBlock | t.CPtr = trans._continue_bb + trans._break_bb = end_bb + trans._continue_bb = step_bb + body: list[ast.AST | t.CPtr] | t.CPtr = for_node.children + if body is not None: + body_count: t.CSizeT = len(body) + for bi in range(body_count): + stmt: ast.AST | t.CPtr = body.get(bi) + if stmt is not None: + HandlesBody.translate_stmt(trans, stmt) + trans._break_bb = old_break + trans._continue_bb = old_continue + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + llvmlite.build_br(builder, step_bb) + + # 8. step 块 → cond;定位到 end 块 + llvmlite.position_at_end(builder, step_bb) + llvmlite.build_br(builder, cond_bb) + llvmlite.position_at_end(builder, end_bb) + return new_vars + # ============================================================ # 多变量迭代(enumerate / zip) # diff --git a/App/lib/core/Handles/HandlesMatch.vp b/App/lib/core/Handles/HandlesMatch.vp index 4453bac..ed01ab3 100644 --- a/App/lib/core/Handles/HandlesMatch.vp +++ b/App/lib/core/Handles/HandlesMatch.vp @@ -585,14 +585,21 @@ class MatchHandle(HandlesBase.Mixin): payload_idx: int = j + 1 # 字段名查找作为回退(若字段名与 MatchAs 名一致可直接命中) if payload_idx >= renum_entry.FieldCount: + # class_name 用 CSizeT 指针值比较:必须传 renum_entry.Name + # (注册时 _update_renum_alias_class_ptr 统一后的 perm_pool + # 指针);renum_name(AnnotClassName 等)是不同指针恒不匹配 + # → 恒 -1 → fail-fast 误报(追加73d 同款指针陷阱) alt_idx: int = HandlesClassDef._lookup_renum_field_alias( - renum_name, bind_name) + renum_entry.Name, bind_name) if alt_idx >= 1: payload_idx = alt_idx else: # fail-fast: payload 越界且别名查找失败 + # (node 在本函数不存在——参数是 pat/sub_pat,用 sub_pat + # 定位到具体 MatchAs 模式;TPC Python 语义下未定义名只在 + # 运行时求值才报错,TPV 惰性编译方法体时即 NameError) HandlesType.fatal_error( - node, + sub_pat, f"REnum 变体字段 '{bind_name}' 在结构体 '{renum_name}' 中越界且别名查找失败", "TypeError") # 获取字段类型 @@ -619,6 +626,33 @@ class MatchHandle(HandlesBase.Mixin): sub_pat, f"REnum 变体字段 '{bind_name}' 地址计算失败", "TypeError") + # 变体成员实际类型(对齐 TPC class_members L300-307/L376-382): + # 注册时别名表存了成员注解解析出的类型(perm_pool 深拷贝)。 + # 用它把槽位地址 bitcast 成 Ptr(成员类型),使绑定变量按成员 + # 类型读 payload——Struct.Name: t.CChar|t.CPtr = i8*,否则按 + # 槽位 i64 读 → is 比较/下标/strlen 等字符串操作翻译失败 + # (CallGenMixin L4631 type_name_sp is not None 根因)。 + # 注意:必须按 (class, 变体名, 位置) 定位——MatchAs 名是局部 + # 变量名(type_name_sp),不是变体字段名(Name),按字段名 + # 查恒不匹配(追加76 第一次修复失效根因);同一 payload 位置 + # 跨变体类型也不同(Int.Bits=i32 vs Struct.Fields=Ptr)。 + # 自引用类型(如 Pointee: LLVMType|t.CPtr)注册期解析失败 → + # 查不到类型 → 回退槽位类型(原始位宽在嵌套 match 中经 + # subject bitcast 仍可用)。 + mem_ty_m: llvmlite.LLVMType | t.CPtr = None + vnt_m: str = self._extract_variant_name_from_cls(pat) + if vnt_m is not None and renum_entry is not None \ + and renum_entry.Name is not None: + mem_ty_m = HandlesClassDef._lookup_renum_member_ty( + renum_entry.Name, vnt_m, payload_idx) + if mem_ty_m is not None: + mem_ptr_m: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, mem_ty_m) + if mem_ptr_m is not None: + cast_m: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, elem_ptr, mem_ptr_m) + if cast_m is not None: + elem_ptr = cast_m HandlesVar.define_var( trans.SymTab, bind_name, elem_ptr) j = j + 1 diff --git a/App/lib/core/Handles/HandlesStruct.vp b/App/lib/core/Handles/HandlesStruct.vp index aea8c4f..06a64f0 100644 --- a/App/lib/core/Handles/HandlesStruct.vp +++ b/App/lib/core/Handles/HandlesStruct.vp @@ -28,11 +28,12 @@ import lib.core.VLogger as VLogger # ============================================================ STRUCT_MAX: t.CDefine = 512 -# 字段槽位上限制(对象化后 Generator 多继承组合可达 50+ 字段,48 会截断丢失) -FIELD_MAX: t.CDefine = 64 +# 字段槽位上限制(Generator 12 Mixin 展平 ~97 字段;2026-08-16 追加78:64 +# 截断导致第 9+ 父类字段丢失 → __init__ 写 self.DiagStmtCount "属性写入失败") +FIELD_MAX: t.CDefine = 128 FIELD_NAME_MAX: t.CDefine = 64 -# 多继承(MRO)支持:单类最大父类数 / MRO 链最大长度 -PARENT_MAX: t.CDefine = 8 +# 多继承(MRO)支持:单类最大父类数 / MRO 链最大长度(Generator 12 父类) +PARENT_MAX: t.CDefine = 16 MRO_MAX: t.CDefine = 16 @@ -219,6 +220,16 @@ def get_struct_count() -> int: return 0 +def reset_module_methods_emitted(sha1: str) -> int: + """回滚指定模块注册类的 MethodsEmitted 标志(Phase A _deps_pending 重试 + 前调用,防 MethodsEmitted 跨遍残留导致类方法缺失——追加87,委托到 gen.reset_module_methods_emitted)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.reset_module_methods_emitted(sha1) + return 0 + + def find_struct(name: str) -> StructEntry | t.CPtr: """按类名查找结构体,返回 StructEntry 或 None(委托到 gen.find_struct)""" import lib.core.Gen.Generator as Generator diff --git a/App/lib/core/Handles/HandlesTranslator.vp b/App/lib/core/Handles/HandlesTranslator.vp index cb54e9d..ff74fce 100644 --- a/App/lib/core/Handles/HandlesTranslator.vp +++ b/App/lib/core/Handles/HandlesTranslator.vp @@ -47,7 +47,7 @@ import lib.Projectrans.Config as Config # ============================================================ # 常量 -MAX_VARS: t.CDefine = 256 +MAX_VARS: t.CDefine = 1024 MAX_FUNCS: t.CDefine = 1024 MAX_GLOBAL_NAMES: t.CDefine = 32 @@ -129,6 +129,25 @@ class Translator: # *args 参数名(如 "args"),用于符号表注册 _vararg_name: str + # OOP iter 协议:当前 __next__ 方法的隐藏 stop-flag 参数(i1*)。 + # _translate_method 注入 __stop_iter_flag__ 参数时设置;translate_raise + # 检测 raise StopIteration 时对该指针 store 1(对齐 TPC HandlesRaise.py)。 + # 非 __next__ 函数中为 None。 + _stop_iter_flag_ptr: llvmlite.Value | t.CPtr + + # try-except 机制(对齐 TPC HandlesTry.py):当前最内层 try 的 + # 分发块 + 异常编码/消息 alloca。translate_try 设置,translate_raise + # 与调用侧 EH 检测写入/跳转。try 外为 None。 + _eh_dispatch_bb: llvmlite.BasicBlock | t.CPtr + _eh_code_ptr: llvmlite.Value | t.CPtr + _eh_msg_ptr: llvmlite.Value | t.CPtr + # 当前函数的 __eh_msg_out__(i8**)/__eh_code_out__(i32*)输出参数 + # (函数签名含 raise 时由 translate_function_def/_translate_method 注入)。 + # 无 EH 参数时 _cur_func_has_eh == 0。 + _eh_msg_out_param: llvmlite.Value | t.CPtr + _eh_code_out_param: llvmlite.Value | t.CPtr + _cur_func_has_eh: int + # 模块 SHA1 前缀(16 字符),用于函数名混淆(SHA1 命名空间) ModuleSha1: str # 当前文件所属包名(如 "llvmlite"),用于解析相对导入(from . import ...) @@ -203,6 +222,13 @@ class Translator: self._cur_func_name = None self._va_list_ptr = None self._vararg_name = None + self._stop_iter_flag_ptr = None + self._eh_dispatch_bb = None + self._eh_code_ptr = None + self._eh_msg_ptr = None + self._eh_msg_out_param = None + self._eh_code_out_param = None + self._cur_func_has_eh = 0 self.ModuleSha1 = None self.CurrentPackage = None self._closure_env_offsets = 0 diff --git a/App/lib/core/Handles/HandlesType.vp b/App/lib/core/Handles/HandlesType.vp index 74d6cc7..600b2ed 100644 --- a/App/lib/core/Handles/HandlesType.vp +++ b/App/lib/core/Handles/HandlesType.vp @@ -66,6 +66,46 @@ def set_global_gen(gen: t.CVoid | t.CPtr) -> None: # ============================================================ +# ============================================================ +# 类编译期元数据(__provides__/__requires__/__require_must__) +# +# _fakeduck.vp 的 _str 用 __requires__ = ['__mbuddy__'] 声明 with 上下文 +# 依赖字段;with 提供者用 __provides__ 声明注入字段。这些是编译期 +# 元数据,不生成运行字段(FakeDuck 移植点 1)。 +# ============================================================ +CLASS_META_PROVIDES: t.CDefine = 1 +CLASS_META_REQUIRES: t.CDefine = 2 +CLASS_META_REQUIRE_MUST: t.CDefine = 3 + + +def register_class_meta(pool: memhub.MemBuddy | t.CPtr, + kind: int, cls: str, field: str) -> int: + """注册类编译期元数据字段(委托到 gen.register_class_meta),0=成功""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.register_class_meta(pool, kind, cls, field) + return 1 + + +def class_meta_count(kind: int, cls: str) -> int: + """返回某类某元数据类型的字段数(委托到 gen.class_meta_count)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.class_meta_count(kind, cls) + return 0 + + +def class_meta_field_at(kind: int, cls: str, idx: int) -> str: + """返回某类某元数据类型的第 idx 个字段名(委托到 gen.class_meta_field_at)""" + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) + if g is not None: + return g.class_meta_field_at(kind, cls, idx) + return None + + def _ensure_trace_init() -> int: """确保调用栈已初始化(委托到 gen._ensure_trace_init)""" import lib.core.Gen.Generator as Generator @@ -140,7 +180,7 @@ def _dbg_check_ast_floats(mb: memhub.MemBuddy | t.CPtr, def register_cdefine_constant(pool: memhub.MemBuddy | t.CPtr, - name: str, value: int) -> None: + name: str, value: t.CInt64T) -> None: """注册 CDefine 编译期常量到本地表(委托到 gen.register_cdefine_constant)""" import lib.core.Gen.Generator as Generator g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) @@ -166,7 +206,7 @@ def register_cdefine_string(pool: memhub.MemBuddy | t.CPtr, g.register_cdefine_string(pool, name, value) -def lookup_cdefine_constant(name: str) -> int: +def lookup_cdefine_constant(name: str) -> t.CInt64T: """查找 CDefine 编译期常量(委托到 gen.lookup_cdefine_constant)""" import lib.core.Gen.Generator as Generator g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) diff --git a/App/lib/core/Handles/HandlesVar.vp b/App/lib/core/Handles/HandlesVar.vp index ee3d6dc..e09c9f4 100644 --- a/App/lib/core/Handles/HandlesVar.vp +++ b/App/lib/core/Handles/HandlesVar.vp @@ -31,7 +31,9 @@ SCOPE_FUNCTION: t.CDefine = 1 SCOPE_BLOCK: t.CDefine = 2 # 每个作用域最大变量数 -MAX_VARS: t.CDefine = 256 +# 256 太小:NewAstTest main() 有 289 个局部变量,add_var 超限静默失败 +# 导致变量未注册,后续使用报"未定义名称"(与 Gen/VarGenMixin 保持一致)。 +MAX_VARS: t.CDefine = 1024 # ============================================================ diff --git a/App/lib/core/Phase2.vp b/App/lib/core/Phase2.vp index 6e23c08..7395259 100644 --- a/App/lib/core/Phase2.vp +++ b/App/lib/core/Phase2.vp @@ -388,7 +388,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔) # 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist,实现按需编译 # 声明在 if 块外,确保 Phase B+ 和清理代码能访问 - APP_DEPS_BUF_SIZE: t.CSizeT = 8192 + # 65536 与 StubMerger.REACH_WORKLIST_BUF 对齐:全量 App imports 拼接 + # 超 8192(追加82 同源溢出),8192 会静默丢依赖致 Phase B+ 可达集合不全 + APP_DEPS_BUF_SIZE: t.CSizeT = 65536 app_deps_buf: bytes = None app_deps_len: t.CSizeT = 0 if do_phase1 != 0: @@ -552,10 +554,6 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, stdlib.free(entries) return 1 string.memset(done_flags, 0, file_count) - # 二分诊断:Phase A 开始前主池用量——若已接近 1GB 说明 - # pre-inc/pre 阶段的 reset_to(mark2) 未回收(记录栈/realloc 失效); - # 若用量低则泄漏发生在 Phase A 自身。 - stdio.printf("[MEM] Phase A 前主池 used=%zu\n", mb.used_bytes()) PHASE_A_MAX_PASSES: t.CInt = 32 for pass_a in range(PHASE_A_MAX_PASSES): completed_this_pass: int = 0 @@ -564,9 +562,6 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, continue ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) - # OOM 二分诊断:翻译前 used —— 若逐文件递增 → reset_to(mark2) 未回收; - # 若单文件翻译中暴涨 → 单文件瞬时峰值/泄漏(定位 Phase A OOM) - stdio.printf("[MEM] PhaseA 翻译前 i=%d/%d used=%zu\n", i, file_count, mb.used_bytes()) if ent is None or ent.Path is None: done_flags[i] = 1 completed_this_pass += 1 @@ -600,7 +595,17 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if fb_dp is not None: viperlib.snprintf(fb_dp, 1024, "Phase A 依赖未就绪,下遍重试: %s", ent.Path) - VLogger.warning(fb_dp, "PhaseA-DBG") + # --nowarn-register 亦抑制此类 Phase A 依赖重试 WARN(刷屏收敛) + if VLogger.nowarn_register() == 0: + VLogger.warning(fb_dp, "PhaseA-DBG") + # 回滚本文件已注册类的 MethodsEmitted:第 1 遍 _translate_oop_methods + # 发射方法体前已置 MethodsEmitted=1(防重入),但方法体内跨模块调用 + # 依赖 text.ll 未生成触发 _deps_pending → 本遍作废;若不回滚, + # 下遍重译 existing 分支被 MethodsEmitted==1 防重复检查拦截 → 类方法 + # 永不发射 → 链接 undefined symbol(追加87 根因)。 + # 必须先于 mb.reset_to(mark2)(MethodsEmitted 在 perm_pool 上跨 reset 存活)。 + if ent.Sha1 is not None: + HandlesStruct.reset_module_methods_emitted(ent.Sha1) # OOM 清理:本文件翻译已产生瞬时数据(下遍会重新翻译),立即回收 mb.reset_to(mark2) continue @@ -655,9 +660,6 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, # OOM 清理:stub/text/deps 已写盘,回收本文件翻译瞬时数据 # (AST/tokens/lexer/IR/Handler);app_deps_buf 已深拷贝依赖,不受影响 mb.reset_to(mark2) - # OOM 二分诊断:reset 后 used —— 应回落到 mark2 水位(3MB 级), - # 若仍高 → reset 失效(记录栈缺失/coalesce 未归还) - stdio.printf("[MEM] PhaseA 翻译后 reset i=%d/%d used=%zu\n", i, file_count, mb.used_bytes()) # 收敛检查:所有文件完成则结束 all_done: int = 1 for i_ck in range(file_count): @@ -1128,6 +1130,22 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, string.strcpy(final_obj_paths, main_obj_path) fop_pos = mlen + # 合并 Phase B/B+ 编译的 App 非 main 模块 .obj(obj_paths,空格分隔)。 + # 此前 final_obj_paths 只含 main_obj + reachable includes,App 依赖文件 + # (main import 的 syslibtest_impl/os_backend 等)的 .obj 从未入链——追加28 + # 前靠 reachable 误含 App 时从 includes.binary 的重复 obj 兜底(undefined + # symbol: ae2038ced7b4634f.run 根因)。obj_paths 同时含 Phase B+ 本次 + # 新编译的 includes obj(与下方 reachable 收集重复,--allow-multiple- + # definition 容忍,仅本次编译的少量文件)。 + if obj_pos > 0: + if fop_pos + obj_pos + 1 < OBJ_PATHS_SIZE + 512: + if fop_pos > 0: + final_obj_paths[fop_pos] = ' ' + fop_pos += 1 + string.strcpy(final_obj_paths + fop_pos, obj_paths) + fop_pos += obj_pos + final_obj_paths[fop_pos] = '\0' + # 按图链接:遍历 reachable_set,只收集可达 includes 的 .obj # 不再全量收集 includes.binary 下的所有 .obj if reachable_set is not None and reachable_count > 0: diff --git a/App/lib/core/StubMerger.vp b/App/lib/core/StubMerger.vp index 09b9ebf..e6c06fd 100644 --- a/App/lib/core/StubMerger.vp +++ b/App/lib/core/StubMerger.vp @@ -44,6 +44,9 @@ MAX_INCLUDES_SHA1: t.CDefine = 1024 STUB_READ_BUF_SIZE: t.CDefine = 1048576 # 最大 includes 条目数 MAX_INCLUDES: t.CDefine = 1024 +# 可达性分析 worklist/processed 缓冲区大小(8192 不够:递归扫描 source_dir +# 后 worklist 含全部 App 文件 imports 的拼接 + BFS 依赖展开,追加82 溢出) +REACH_WORKLIST_BUF: t.CDefine = 65536 # 全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件) # Phase1 扫描完成后填充,供所有函数使用 @@ -1015,6 +1018,29 @@ def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int: return commented +def _HasGlobalInBuf(out_buf: bytes, global_name: str) -> int: + """检测 out_buf 中是否已有同名全局变量行(行首匹配,未注释) + + 用于跳过重复追加依赖全局强定义:stub 注入已提供 external 声明, + 依赖 text.ll 的强定义只需在定义模块存在一份,复制进每个依赖方 + 会导致 ld.lld duplicate symbol(如 _op_head、MemBuddy.vtable)。 + """ + if out_buf is None or global_name is None: + return 0 + name_len: t.CSizeT = string.strlen(global_name) + out_len: t.CSizeT = string.strlen(out_buf) + if name_len == 0 or out_len == 0 or name_len > out_len: + return 0 + pos: t.CSizeT = 0 + while pos + name_len <= out_len: + if string.strncmp(out_buf + pos, global_name, name_len) == 0: + # 确认是行首(前一个是换行或 pos==0) + if pos == 0 or out_buf[pos - 1] == '\n': + return 1 + pos += 1 + return 0 + + # ============================================================ # _CommentOutOpaqueTypeInBuf - 将 out_buf 中匹配类型名的 opaque 声明行注释掉 # @@ -1501,16 +1527,24 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, # 截断到 '=' 之前(不含 '='),得到全局变量名 @name saved_gv: t.CChar = dep_buf[eq_gv] dep_buf[eq_gv] = '\0' - # 注释掉 out_buf 中的同名行(external 声明或旧定义) - _CommentOutGlobalInBuf(out_buf, dep_buf + ls) - dep_buf[eq_gv] = saved_gv - # 追加完整定义 - if out_pos + ll + 2 < out_size: - string.strncpy(out_buf + out_pos, dep_buf + ls, ll) - out_pos += ll - out_buf[out_pos] = '\n' - out_pos += 1 - out_buf[out_pos] = '\0' + # 依赖方已有同名全局(stub 注入的 external 声明或先前依赖的定义) + # 时跳过:跨模块全局(如 _op_head、MemBuddy.vtable)只需一份 + # 强定义(在定义模块的 text.ll 中),复制进每个依赖方会导致 + # ld.lld duplicate symbol(NewAstTest _op_head/MemBuddy.vtable 根因)。 + # 仅当依赖方完全没有该全局(既无 external 也无定义)时才需要补充。 + if _HasGlobalInBuf(out_buf, dep_buf + ls) == 0: + # 注释掉 out_buf 中的同名行(external 声明或旧定义) + _CommentOutGlobalInBuf(out_buf, dep_buf + ls) + dep_buf[eq_gv] = saved_gv + # 追加完整定义 + if out_pos + ll + 2 < out_size: + string.strncpy(out_buf + out_pos, dep_buf + ls, ll) + out_pos += ll + out_buf[out_pos] = '\n' + out_pos += 1 + out_buf[out_pos] = '\0' + else: + dep_buf[eq_gv] = saved_gv continue # 跳过其他行 @@ -1679,7 +1713,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, VLogger.debug(fb, "Reachable") # 工作列表(空格分隔的模块名) - worklist: bytes = stdlib.malloc(8192) + worklist: bytes = stdlib.malloc(REACH_WORKLIST_BUF) if worklist is None: stdlib.free(sha1_arr) stdlib.free(mod_arr) @@ -1693,67 +1727,33 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, # 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层) if app_deps is not None and app_deps[0] != '\0': ad_len: t.CSizeT = string.strlen(app_deps) - if ad_len > 0 and ad_len < 8192: + if ad_len > 0 and ad_len < REACH_WORKLIST_BUF: string.strcpy(worklist, app_deps) wl_len = ad_len else: VLogger.error("app_deps 过长,无法构建可达集合", "StubMerger") else: # app_deps 缺失:Phase1 场景(未翻译 App 源文件)为设计性回退, - # 显式 warning 暴露回退,不再静默 - VLogger.warning("app_deps 缺失,回退非递归扫描 source_dir", "Reachable") - # 回退到自己的非递归扫描 - dir_len: t.CSizeT = string.strlen(source_dir) - pattern: bytes = stdlib.malloc(dir_len + 8) - if pattern is None: - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - stdlib.free(worklist) - return -1 - viperlib.snprintf(pattern, dir_len + 8, "%s/*", source_dir) - - find_data_size: t.CSizeT = win32file.WIN32_FIND_DATAA.__sizeof__() - find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16) - if find_data is None: - stdlib.free(pattern) - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - stdlib.free(worklist) - return -1 - string.memset(find_data, 0, find_data_size + 16) - - handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) - if handle == win32base.INVALID_HANDLE_VALUE: - fb_fd: str = VLogger.fmt_buf() - if fb_fd is not None: - viperlib.snprintf(fb_fd, 1024, "未找到源文件(.vp/.py): %s", pattern) - VLogger.warning(fb_fd, "Reachable") - stdlib.free(pattern) - stdlib.free(find_data) - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - stdlib.free(worklist) - return -1 - - while True: - fname: str = find_data.cFileName - if fname is None: - if win32file.FindNextFileA(handle, find_data) == 0: - break - continue - fname_len: t.CSizeT = string.strlen(fname) - if Utils.is_vp_or_py_at(fname, 0, fname_len) == 0: - if win32file.FindNextFileA(handle, find_data) == 0: - break - continue - full_path: bytes = Utils.JoinPath(source_dir, fname) - if full_path is None: - if win32file.FindNextFileA(handle, find_data) == 0: - break - continue - sbuf: bytes = Utils.read_file_to_buf(full_path, SRC_BUF_SIZE_R) - if sbuf is not None: - # 解析 AST 获取 _imported_modules + # 显式 warning 暴露回退,不再静默。 + # 原实现仅非递归扫描 source_dir 顶层("%s/*"),漏掉 lib/ 等子目录的 + # App 文件 → 其依赖(如 lib/Projectrans/Config.vp 的 json)不进入 + # worklist → json 不可达 → Phase1 不 stub 不翻译 → Phase2 Phase A + # 翻译 Config.vp 时 root.is_object() 跨模块调用无 stub 可查 → 静默 + # translate_value 返回 None → "if 条件表达式翻译失败"(追加82 根因)。 + # 改为递归扫描(复用 IncludesScanner.scan_includes),覆盖全部子目录。 + VLogger.warning("app_deps 缺失,递归扫描 source_dir", "Reachable") + scan_res_r: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, source_dir) + if scan_res_r is not None: + es_r: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() + for e_i_r in range(scan_res_r.Count): + e_r: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)( + t.CVoid(t.CUInt64T(scan_res_r.Entries) + t.CUInt64T(e_i_r) * es_r, t.CPtr)) + if e_r is None or e_r.Path is None: + continue + sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R) + if sbuf is None: + continue + # 解析 AST 获取 _imported_modules(与原顶层扫描一致) lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) if lx is not None: ast._lexer_init(lx, sbuf, mb) @@ -1763,21 +1763,18 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() if tr is not None: tr._declare_only = 2 - # 源文件包名:fname 是顶级文件名(无目录分隔符),包为 None - tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname) + # 源文件包名:用 RelPath(相对 source_dir,支持子目录), + # 保证 from .xxx import 相对导入解析正确 + tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, e_r.RelPath) HandlesStruct.reset_visible_structs(mb, 0) tr.translate(tree) # 容器使用检测:App 源文件用 list/dict(裸名注解/构造/[] - # /{} 字面量)时注入 _list/_dict 依赖(与 Phase1 对 includes - # 层的 _ScanTreeForContainers 对齐)。否则 dict/list 类未被 - # 纳入可达集合 → 不编译 → 跨模块方法(如 dict.set_int)返回 - # 类型函数/text.ll/vpi 均未命中 → _fail_method_ret_ty 误杀 - # (DictTest 8/13 重现根因)。 + # /{} 字面量)时注入 _list/_dict 依赖(与原实现一致) tr._inject_auto_container_visibility(mb, tree) # 获取 _imported_modules if tr._imported_modules is not None: im_len: t.CSizeT = string.strlen(tr._imported_modules) - if im_len > 0 and wl_len + im_len + 1 < 8192: + if im_len > 0 and wl_len + im_len + 1 < REACH_WORKLIST_BUF: string.strcpy(worklist + wl_len, tr._imported_modules) wl_len += im_len worklist[wl_len] = ' ' @@ -1785,23 +1782,14 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, worklist[wl_len] = '\0' # 计算 App 源文件 SHA1,填充依赖到内存存储器 # worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖 - app_sha1_r: str = IncludesScanner.compute_file_sha1(mb, full_path) - if app_sha1_r is not None: - PopulateIncludesDeps(app_sha1_r, tr._imported_modules) - stdlib.free(app_sha1_r) + if e_r.Sha1 is not None: + PopulateIncludesDeps(e_r.Sha1, tr._imported_modules) # 释放 Translator 资源 if tr._global_names is not None: stdlib.free(tr._global_names) if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names) stdlib.free(sbuf) - stdlib.free(full_path) - - if win32file.FindNextFileA(handle, find_data) == 0: - break - win32base.FindClose(handle) - stdlib.free(pattern) - stdlib.free(find_data) # 去掉末尾多余空格 if wl_len > 0 and worklist[wl_len - 1] == ' ': @@ -1822,7 +1810,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, if HandlesImports.is_module_imported(worklist, "builtins") == 0: bsha1_b: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, "builtins") if bsha1_b is not None: - if wl_len + 9 < 8192: + if wl_len + 9 < REACH_WORKLIST_BUF: if wl_len > 0: worklist[wl_len] = ' ' wl_len += 1 @@ -1837,7 +1825,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, VLogger.debug(fb, "Reachable") # 3. 工作列表算法:递归收集可达 SHA1 - processed: bytes = stdlib.malloc(8192) + processed: bytes = stdlib.malloc(REACH_WORKLIST_BUF) if processed is None: stdlib.free(sha1_arr) stdlib.free(mod_arr) @@ -1874,7 +1862,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, if already == 0: # 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug) if processed is not None: - if proc_len + name_len + 1 < 8192: + if proc_len + name_len + 1 < REACH_WORKLIST_BUF: string.strcpy(processed + proc_len, mod_buf) proc_len = proc_len + name_len processed[proc_len] = ' ' @@ -1886,8 +1874,22 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, # 查找 SHA1 found_sha1: str = _FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf) if found_sha1 is not None: - # 加入 reachable_set - reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1) + # App 源文件(Sha1Store 中 IncDir 为空,AppendToSha1MapStore 规则) + # 不加入 reachable_set:Phase B 已编译其 .obj,若加入会导致 Phase B+ + # 把 App 文件重复编译进 includes.binary,Phase C 链接时与 Phase B 的 + # App obj 重复符号(SysLibTest ld.lld duplicate symbol: _mbuddy 根因)。 + # 依赖仍继续展开(App 文件是依赖图中间节点,其 includes 依赖必须可达)。 + is_app_r: int = 0 + if _sha1_store_incdir is not None: + for ai_r in range(_sha1_store_count): + asi_r: t.CSizeT = t.CSizeT(ai_r) * 17 + if string.strcmp(_sha1_store_arr + asi_r, found_sha1) == 0: + aii_r: t.CSizeT = t.CSizeT(ai_r) * MAX_INC_DIR_LEN + if _sha1_store_incdir[aii_r] == '\0': + is_app_r = 1 + break + if is_app_r == 0: + reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1) # 从内存查找依赖(不读 deps.txt 过程文件) # Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存 @@ -1895,7 +1897,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, if deps_str is not None: dl: t.CSizeT = string.strlen(deps_str) if dl > 0: - if wl_len + dl + 2 < 8192: + if wl_len + dl + 2 < REACH_WORKLIST_BUF: if wl_len > 0 and worklist[wl_len - 1] != ' ': worklist[wl_len] = ' ' wl_len = wl_len + 1 @@ -2052,6 +2054,14 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, else: deps_buf[4095] = '\0' deps_loaded = 1 + elif deps_br == 0: + # 空 deps.txt:无 import 的包标记文件(如 lib/core/Gen/__init__.vp、 + # lib/StubGen/__init__.vp 纯注释)合法——deps_loaded 置 1 视为 + # "已加载且无依赖",避免下方"deps.txt 缺失"误报(追加85 根因)。 + # deps_buf 是 malloc 未初始化内存,必须显式置空串, + # 否则后续 strlen(deps_buf) 读到垃圾长度遍历越界。 + deps_buf[0] = '\0' + deps_loaded = 1 df_deps.close() stdlib.free(deps_path) diff --git a/App/lib/core/VLogger.vp b/App/lib/core/VLogger.vp index 43078b3..7c7f71a 100644 --- a/App/lib/core/VLogger.vp +++ b/App/lib/core/VLogger.vp @@ -234,6 +234,26 @@ def set_logger(logger: Logger | t.CPtr) -> int: return 0 +# ============================================================ +# --nowarn-register 开关(追加):抑制 Phase A 依赖类 WARN 输出—— +# ①"外部函数返回类型未登记"(HandlesExprCall)②"Phase A 依赖未就绪, +# 下遍重试"(PhaseA-DBG)。自举编译中大量出现,刷屏影响日志收敛。 +# 仅抑制输出,不影响内部逻辑(默认 i32 推断、标记重试等照常执行)。 +# ============================================================ +_nowarn_register_flag: t.CInt = 0 + + +def set_nowarn_register(flag: int) -> None: + """设置 --nowarn-register(1=抑制 Phase A 依赖类 WARN 输出)""" + global _nowarn_register_flag + _nowarn_register_flag = flag + + +def nowarn_register() -> int: + """查询 --nowarn-register 开关状态(1=抑制)""" + return _nowarn_register_flag + + # ============================================================ # 模块级便捷函数(与 TPC 的 lib.core.VLogger 对齐) # diff --git a/App/main.vp b/App/main.vp index cd2cc74..fc417e5 100644 --- a/App/main.vp +++ b/App/main.vp @@ -1,17 +1,3 @@ -import t, c -from stdint import * -import stdio -import string -import stdlib -import memhub -import argparse -import w32.win32console as w32cmd -import w32.fileio as fileio -import w32.win32file -import w32.win32base -import sys -import ast -import lib import lib.core.VLogger as VLogger import lib.core.Handles.HandlesTranslator as HandlesTranslator import lib.core.Handles.HandlesStruct as HandlesStruct @@ -43,37 +29,41 @@ import lib.core.CompilerFactory as CompilerFactory import lib.core.Gen.Generator as GenGenerator import lib.Projectrans.Config as Config import lib.Projectrans.Utils as Utils +import lib +from stdint import * +import stdio +import string +import stdlib +import memhub +import argparse +import w32.win32console as w32cmd +import w32.fileio as fileio +import w32.win32file +import w32.win32base +import sys +import ast import llvmlite import subprocess import viperlib import hashlib +import t, c -# 内存大小: fl_bytes=264 (33*8)。 -# 主池 1.5GB(1.5×2^30 + 264 = 1610613000):memhub 二进制分解为 2^30+2^29 两块。 -# 曾试 2GB(2^31+264):当前机器物理内存可用不足 1GB(已提交 76.6GB/81.3GB), -# 主池 2GB + perm_pool 512MB = 2.5GB 提交导致 malloc 失败 → TPV 启动即退出。 -# 1.5GB + 512MB = 2GB 整提交,留 2.7GB 提交余量,稳妥且比 1.2GB 多 0.3GB。 -# Phase1 翻译 87 个 includes 文件 + Phase2 翻译 30 个测试文件,512MB 不足导致 Phase2 崩溃 -# 2026-08-13: NewAstTest 依赖 ast 模块(98 includes),Phase2 的 Phase A-pre-inc/Phase A -# 无 mark/reset 回收,98 个 includes 的 AST/IR 全量累积 pool 超 1GB → 曾增大到 2GB。 -# → 已在 Phase2 对齐 Phase1 的 mark1/reset_to 清理机制(每文件翻译后回收瞬时数据), -# 峰值回落至单文件水平;1.5GB 主池给单文件峰值与 Phase A 高并发翻译留足余量。 -POOL_SIZE: t.CDefine = 1610613000 -# 永久内存池大小:2^29 + 264 = 512MB usable(存跨 mark 持久数据:泛型模板 -# 表/节点、struct/union/REnum 类型树、枚举 BaseTy、spec 缓存等)。 -# 与主 pool 独立(MemBuddy 第二实例),永不 reset_to,不占主 pool 配额。 +# Main Pool 1 GiB +POOL_SIZE: t.CDefine = 1073741824 +# Permanent memory pool size: 2^29 + 264 = 512MB usable PERM_POOL_SIZE: t.CDefine = 536871176 -# 语言编码页 +# Code page: UTF-8 (Windows 65001) CODE_PAGE: t.CDefine = 65001 -# 源代码缓冲区大小(1MB) +# Source code buffer size (1MB) SRC_BUF_SIZE: t.CDefine = 1048576 - +# Version (dev/beta/rc) +VIPER_VERSION: t.CDefine = "1.0.3-dev.27 2026-05-27 Non-LTS" +TPV_VERSION: t.CDefine = "0.0.2-dev.173" +RUNTIME_VERSION: t.CDefine = "0.2.2+3-Non-LTS" # ============================================================ -# main: 命令行入口 -# -# 解析参数 → 加载配置 → AST 解析 → LLVM IR 翻译 → 编译管线 → 可选执行 +# main: Command-line entry # ============================================================ def main() -> int: w32cmd.SetConsoleOutputCP(CODE_PAGE) @@ -89,9 +79,6 @@ def main() -> int: return 1 # 创建全局永久内存池(perm_pool,512MB):存放跨 mark/reset 持久数据 - # (泛型模板表/节点、struct/union/REnum 类型树、枚举 BaseTy、spec 缓存 - # 等),替代 stdlib.malloc 深拷贝(buddy 分配更快),永不 reset,不占 - # 主 pool 配额。经 CompilerFactory 单例分发(不改通用库 memhub)。 perm_arena: bytes = stdlib.malloc(PERM_POOL_SIZE) if perm_arena is None: stdio.printf("FAIL: malloc for perm_pool arena failed\n") @@ -155,8 +142,8 @@ def main() -> int: # 初始化 VLogger 并打印启动日志 log: VLogger.Logger | t.CPtr = VLogger.get_logger() - if log is not None: - log.info("TransPyV 启动") + if log is None: + log.error("TransPyV Failed to parse parameters") # 初始化命令行参数(Windows: GetCommandLineA, POSIX: /proc/self/cmdline) sys._init_argv() @@ -188,6 +175,10 @@ def main() -> int: argparse.STORE_TRUE, "清除 .transpyc_cache 全局缓存") parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan 模式:.ll 先 opt -passes=asan 插桩,链接加 -fsanitize=address") + parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, + argparse.STORE_TRUE, "抑制 Phase A 依赖类 WARN(外部函数返回类型未登记 / 依赖未就绪重试)输出(保留逻辑,仅不打印)") + parser.add_argument("--version", None, argparse.BOOL, 0, None, False, + argparse.STORE_TRUE, "显示版本,指定此参数后忽略其它所有参数") # 解析命令行参数 args: argparse.ParsedArgs | t.CPtr = parser.parse_args(sys._argc, sys._argv) @@ -197,69 +188,75 @@ def main() -> int: return 1 # 打印解析结果 - if log is not None: - log.banner("TransPyV 参数解析结果") + #if log is not None: + # log.banner("TransPyV 参数解析结果") # 字符串参数 project: str = args.get_str("project") - if project is not None: - stdio.printf(" --project: %s\n", project) - else: - stdio.printf(" --project: (未指定)\n") + #if project is not None: + # stdio.printf(" --project: %s\n", project) + #else: + # stdio.printf(" --project: (未指定)\n") src: str = args.get_str("src") - if src is not None: - stdio.printf(" --src: %s\n", src) - else: - stdio.printf(" --src: (未指定)\n") + #if src is not None: + # stdio.printf(" --src: %s\n", src) + #else: + # stdio.printf(" --src: (未指定)\n") temp: str = args.get_str("temp") - if temp is not None: - stdio.printf(" --temp: %s\n", temp) - else: - stdio.printf(" --temp: (未指定)\n") + #if temp is not None: + # stdio.printf(" --temp: %s\n", temp) + #else: + # stdio.printf(" --temp: (未指定)\n") output: str = args.get_str("output") - if output is not None: - stdio.printf(" --output: %s\n", output) - else: - stdio.printf(" --output: (未指定)\n") + #if output is not None: + # stdio.printf(" --output: %s\n", output) + #else: + # stdio.printf(" --output: (未指定)\n") phase: str = args.get_str("phase") - if phase is not None: - stdio.printf(" --phase: %s\n", phase) - else: - stdio.printf(" --phase: (未指定,默认 all)\n") + #if phase is not None: + # stdio.printf(" --phase: %s\n", phase) + #else: + # stdio.printf(" --phase: (未指定,默认 all)\n") cc: str = args.get_str("cc") - if cc is not None: - stdio.printf(" --cc: %s\n", cc) - else: - stdio.printf(" --cc: (未指定)\n") + #if cc is not None: + # stdio.printf(" --cc: %s\n", cc) + #else: + # stdio.printf(" --cc: (未指定)\n") # 布尔参数 _gb_clean: INT = args.get_bool("clean") - if _gb_clean: - stdio.printf(" --clean: True\n") - else: - stdio.printf(" --clean: False\n") + #if _gb_clean: + # stdio.printf(" --clean: True\n") + #else: + # stdio.printf(" --clean: False\n") - if args.get_bool("run"): - stdio.printf(" --run: True\n") - else: - stdio.printf(" --run: False\n") + #if args.get_bool("run"): + # stdio.printf(" --run: True\n") + #else: + # stdio.printf(" --run: False\n") - if args.get_bool("rebuild-includes"): - stdio.printf(" --rebuild-includes: True\n") - else: - stdio.printf(" --rebuild-includes: False\n") + #if args.get_bool("rebuild-includes"): + # stdio.printf(" --rebuild-includes: True\n") + #else: + # stdio.printf(" --rebuild-includes: False\n") - if args.get_bool("clear-cache"): - stdio.printf(" --clear-cache: True\n") - else: - stdio.printf(" --clear-cache: False\n") + #if args.get_bool("clear-cache"): + # stdio.printf(" --clear-cache: True\n") + #else: + # stdio.printf(" --clear-cache: False\n") - stdio.printf("\n参数解析完成。\n") + #stdio.printf("\n参数解析完成。\n") + + if args.get_bool("version"): # 疑问,\n? + stdio.printf("""Viper %s +GVSDS(TM) SE Runtime Environment and Library %s +Viper Frontend Compiler TransPyV %s\n\n""", VIPER_VERSION, RUNTIME_VERSION, TPV_VERSION) + return 0 # ASan 模式:设置 BuildPipeline 全局标志(compile_ll_to_obj 先 opt 插桩,链接 asan 库) # includes 缓存用独立目录 .binary_asan 隔离 asan 插桩 obj,避免与非 asan obj 混链 @@ -282,6 +279,15 @@ def main() -> int: BuildPipeline.set_asan_mode(0) Config.set_inc_bin_suffix(".binary") + # --nowarn-register:抑制"外部函数返回类型未登记"类 WARN 输出 + # (自举编译中大量出现刷屏,仅抑制打印,默认 i32 推断/标记重试逻辑保留) + # 注意:argparse 的 dest 保留短横线("--nowarn-register" → "nowarn-register"), + # 读取时必须用破折号 key,否则 get_bool 查不到 → 开关永不置位 → WARN 照打。 + if args.get_bool("nowarn-register"): + VLogger.set_nowarn_register(1) + if log is not None: + log.info("--nowarn-register 已启用:抑制注册类 WARN 输出", "vlogger") + # 加载 project.vpj 配置 proj_loaded: int = 0 proj_path: str = project