全部整理上传

This commit is contained in:
TermiNexus committed 2026-08-17 04:27:29 +08:00
1 parent f2b33f5e9b
commit 593332636f
28 files changed
+5828 -675

No files matched your search

+159 -19
View File
@@ -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)
+33 -2
View File
@@ -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
+2 -8
View File
@@ -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:
+39 -9
View File
@@ -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)
# ============================================================
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+657 -51
View File
@@ -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)
+454 -13
View File
@@ -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:
+84 -6
View File
@@ -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
+82 -8
View File
@@ -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] <ptr>
# 类型不匹配(llc constant expression type mismatch,
# pyzlib_error_msg = global [512 x i8] <GEP ptr> 根因)。
# ============================================================
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] <ptr> 类型
# 不匹配(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)
+202 -13
View File
@@ -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 = "<unknown>"
# 行号兜底:克隆/构造节点 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:
+13 -2
View File
@@ -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
+73 -6
View File
@@ -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
+89
View File
@@ -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
+129 -3
View File
@@ -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 定义
#
+20 -2
View File
@@ -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:
-4
View File
@@ -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 目录路径
#
+244 -2
View File
@@ -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)
#
+36 -2
View File
@@ -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
+15 -4
View File
@@ -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
+27 -1
View File
@@ -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
+42 -2
View File
@@ -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))
+3 -1
View File
@@ -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
# ============================================================
+30 -12
View File
@@ -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:
+101 -91
View File
@@ -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)
+20
View File
@@ -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 对齐)
#
+88 -82
View File
@@ -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