Files
ViperC/App/lib/core/Gen/AssignGenMixin.vp
T
TermiNexus 7cba4cbc95 chore: 清理超长历史注释,并一并提交并行会话的代码改动
注释清理(65 文件 / 约 -2578 行 / 约 516 处):删除/压缩叙述式历史注释——多行根因叙事、工作代号段(D25/D30/N11/B5/S4/阶段E 等)、改前改后与先例叙述、逐条推导块,压成一行讲清作用信息;契约类注释(用途、参数与返回、关键约束与不变量、IR 结构说明)与被注释掉的代码一律保留

存疑 标记 6 处一字未动:StubMergerStore.vp、VpiReader.vp、VLogger.vp、AssignGenMixin.vp、CallGenLib.vp、CallGenLeafKit.vp

本次一并带入并行会话的代码改动(同批文件,按文件粒度无法拆分):fileio.File -> rowptr[fileio.File] 等

已知副作用/残留:① CallGenDispatch.vp 有 2 行非注释行的行尾被 LF->CRLF 规范化(纯字节变化,无文本改动);② core/Gen 区约 24 处压缩因并行会话整文件覆写被写回原样,尚未生效(StmtGenCifMixin/ClassGenVTable/FuncGenDef/ClassGenEnum/StructGenMixin/TypeSpec 等)

清单与复跑判据见 Debugger/comment_cleanup_summary.md(该目录不受版本控制)
2026-09-16 11:27:05 +08:00

2659 lines
157 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.core.Gen.GenApi as GenApi
# ============================================================
# AssignGenMixin - 赋值翻译 Mixin(P6 方法化)
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
@t.NoVTable
class AssignGenMixin:
Gen: Generator.Generator | t.CPtr
"""赋值翻译 Mixin(P6 方法化)"""
RecoverFound: int
# ============================================================
# 检测注解是否为 t.CDefine(模块级函数)
#
# 支持两种形式:
# 1. Attribute(Name('t'), 'CDefine')
# 2. BinOp(... | Attribute(Name('t'), 'CDefine'))(联合注解)
# ============================================================
def is_cdefine_annotation(self, annot: ast.AST | t.CPtr) -> int:
"""检测注解是否为 t.CDefine,返回 1 表示是,0 表示否"""
if annot is None: return 0
k: int = annot.kind()
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
if at.attr is not None and string.strcmp(at.attr, "CDefine") == 0:
return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if self.is_cdefine_annotation(bop.left) != 0:
return 1
if self.is_cdefine_annotation(bop.right) != 0:
return 1
return 0
# ============================================================
# extract_cdefine_int_value - 从 AnnAssign.value 提取整数常量(模块级函数)
#
# 支持的表达式形式:
# 1. Constant(INT) — 如 0x0002, 42
# 2. BinOp(BitOr/BitAnd) — 如 FOREGROUND_RED | FOREGROUND_GREEN
# 3. Name — 引用已注册的 CDefine 常量
# 4. Call — 如 t.CUnsignedLong(-11) → 取第一个参数
# 5. UnaryOp(USub/UAdd/Invert) — 如 -11
# ============================================================
def extract_cdefine_int_value(self, val_node: ast.AST | t.CPtr) -> int64:
"""从值节点提取整数常量(支持 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()
# 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)
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError")
return cn.int_val
# 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: int64 = self.extract_cdefine_int_value(bop.left)
right_val: int64 = self.extract_cdefine_int_value(bop.right)
if bop.op == ast.OpKind.BitOr: return left_val | right_val
if bop.op == ast.OpKind.BitAnd: return left_val & right_val
if bop.op == ast.OpKind.BitXor: return left_val ^ right_val
if bop.op == ast.OpKind.LShift: return left_val << right_val
if bop.op == ast.OpKind.RShift: return left_val >> right_val
if bop.op == ast.OpKind.Add: return left_val + right_val
if bop.op == ast.OpKind.Sub: return left_val - right_val
if bop.op == ast.OpKind.Mult: return left_val * right_val
if bop.op == ast.OpKind.Div: return left_val // right_val
if bop.op == ast.OpKind.FloorDiv: return left_val // right_val
if bop.op == ast.OpKind.Mod: return left_val % right_val
# 未知 BinOp 运算符:直接报错终止(fail-fast)
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError")
# 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:
# True/False 布尔字面量 → 1/0(bool 在 CDefine 语境按整数处理)
if string.strcmp(nm.id, "True") == 0:
return 1
if string.strcmp(nm.id, "False") == 0:
return 0
looked_up: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
return looked_up
# 引用未注册:直接报错终止(fail-fast)
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError")
# Case 4: Call — 如 t.CUnsignedLong(-11)
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(val_node)
# 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
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: int64 = self.extract_cdefine_int_value(first_arg)
return arg_val
# Call 无参数:直接报错终止(fail-fast)
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError")
# Case 5: UnaryOp — 如 -11
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(val_node)
operand_val: int64 = self.extract_cdefine_int_value(uop.operand)
if uop.op == ast.OpKind.USub: return -operand_val
if uop.op == ast.OpKind.UAdd: return operand_val
if uop.op == ast.OpKind.Invert: return ~operand_val
# 未知 UnaryOp 运算符:直接报错终止(fail-fast)
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError")
# 未知节点类型:直接报错终止(fail-fast)
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine 值提取失败:无法从表达式解析编译期常量", "TypeError")
# 追加89 块设计:fatal_error 调用后补显式 return(运行时不可达,
# 保证函数末尾块被正确 terminated,不做 noreturn 函数名白名单)
return 0
# ============================================================
# _register_cdefine_from_value - 注册 CDefine 常量(浮点/整数自动识别)
#
# 浮点字面量(如 vipermath 的 U_M_PI: t.CDefine = 3.14159...)走
# register_cdefine_double,否则 translate_value 查表得 0 生成
# ret i32 0(llc 报错 "value doesn't match function result type")。
# ============================================================
def _register_cdefine_from_value(self, pool: rowptr[memhub.MemBuddy],
name: str, val_node: ast.AST | t.CPtr) -> None:
"""注册 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 len(cl_args_cd) > 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:
# 字符串 CDefine(如 ZLIB_VERSION: t.CDefine = "1.3.2"):
# 注册为字符串常量,读取端返回 global string(对齐 TPC)。
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, cn_fp.str_val)
return
if cn_fp.const_kind == ast.CONST_FLOAT:
# 源文件重读恢复(最可靠:AST 节点 float_val 可能被 pool 操作覆写,
# 源文件文本不受影响;用输出参数绕开跨模块 double 返回值传递 bug)
use_val: double = cn_fp.float_val
rec_d: t.CDouble = 0.0
if self._recover_cdefine_float(name, t.CPtr(c.Addr(rec_d))) != 0:
use_val = rec_d
else:
# 源文件恢复失败:直接报错终止(fail-fast),避免注册被 pool 覆写的错误浮点值
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine 浮点值源文件恢复失败", "InternalCompilerError")
return
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, use_val)
return
if val_node is None:
# CDefine 声明缺少值节点:直接报错终止(fail-fast)
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 值提取失败:'{name}' 缺少初始值", "TypeError")
return
val_fp: int64 = self.extract_cdefine_int_value(val_node)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, val_fp)
# 源文件重读恢复 CDefine 浮点值:读 "NAME: t.CDefine = value" 行 atof 解析
# (AST 节点 float_val/str_val 可能被 pool 分配覆写,源文件文本最可靠)。
# 是否命中记在实例字段 self.RecoverFound。
def _recover_found(self) -> int:
"""检查最近一次 _recover_cdefine_float 是否成功"""
return self.RecoverFound
def _recover_float_literal(self, name: str, out: t.CDouble | t.CPtr,
require_cdefine: int) -> int:
"""从当前模块源文件按变量名恢复浮点字面量,写入 out[0],成功返回 1
用输出参数传递 double,避免 TPV 跨模块 double 返回值传递 bug。
require_cdefine=1:只认带 t.CDefine 的行(CDefine 注册路径);
=0:模块级普通浮点全局也接受(handle_module_level_var 路径,
如 torch `LCG_M: t.CDouble = 2147483647.0`)。
"""
self.RecoverFound = 0
fp: str = self.Gen.TypeGenResolve.get_current_file()
if fp is None or name is None or out is None:
return 0
f: rowptr[fileio.File] = fileio.File(fp, fileio.MODE.R)
if f.closed:
return 0
BUF_SZ: sizet = 1048576
buf: bytes = stdlib.malloc(BUF_SZ)
if buf is None:
f.close()
return 0
nrd: LONG = f.read_all(buf, BUF_SZ)
f.close()
if nrd <= 0:
stdlib.free(buf)
return 0
if nrd < BUF_SZ:
buf[nrd] = 0
name_len: sizet = string.strlen(name)
total_len: sizet = string.strlen(buf)
pos: sizet = 0
while pos < total_len:
line_start: sizet = pos
while pos < total_len and buf[pos] != '\n':
pos += 1
line_len: sizet = pos - line_start
if pos < total_len:
pos += 1
# 匹配行首的 "name",其后(跳过空格)必须是 ':' 或 '='
# (AnnAssign:`NAME: t.CDouble = 1.0`;无注解赋值:`NAME = 1.0`)
if line_len < name_len + 1:
continue
match_ok: int = 1
ki: sizet
for ki in range(name_len):
if buf[line_start + ki] != name[ki]:
match_ok = 0
break
if match_ok == 0:
continue
sep: sizet = line_start + name_len
while sep < line_start + line_len and buf[sep] == ' ':
sep += 1
if sep >= line_start + line_len:
continue
if buf[sep] != ':' and buf[sep] != '=':
continue
# CDefine 路径要求行内出现 "t.CDefine";普通浮点全局路径跳过该检查
if require_cdefine != 0:
has_cd: int = 0
ci: sizet = line_start + name_len + 1
while ci + 8 <= line_start + line_len:
if (buf[ci] == 't' and buf[ci + 1] == '.' and buf[ci + 2] == 'C' and
buf[ci + 3] == 'D' and buf[ci + 4] == 'e' and buf[ci + 5] == 'f' and
buf[ci + 6] == 'i' and buf[ci + 7] == 'n' and buf[ci + 8] == 'e'):
has_cd = 1
break
ci += 1
if has_cd == 0:
continue
# 找 '='
eq: sizet = line_start
while eq < line_start + line_len and buf[eq] != '=':
eq += 1
if eq >= line_start + line_len:
continue
eq += 1
while eq < line_start + line_len and buf[eq] == ' ':
eq += 1
# Call 包装的浮点常量(t.CDouble(1.0) / float(3.14...)):跳过 "float(" 等
# 前缀,从括号内首个数字/符号/'.' 起取值(否则 atof 只认数字恒得 0.0)。
# 裸字面量(3.14 / -1.5)首字符已是数字/符号/'.',不受影响。
if eq < line_start + line_len:
c0_rf: char = buf[eq]
if not ((c0_rf >= '0' and c0_rf <= '9') or c0_rf == '-' or c0_rf == '+' or c0_rf == '.'):
pfx_rf: sizet = eq
while pfx_rf < line_start + line_len:
cp_rf: char = buf[pfx_rf]
if (cp_rf >= '0' and cp_rf <= '9') or cp_rf == '.' or cp_rf == '-' or cp_rf == '+':
eq = pfx_rf
break
pfx_rf += 1
val_start: sizet = eq
while eq < line_start + line_len and buf[eq] != ' ' and buf[eq] != '\t' \
and buf[eq] != '#' and buf[eq] != '\r' and buf[eq] != '\n':
eq += 1
if eq == val_start:
continue
val_txt: str = stdlib.malloc(eq - val_start + 1)
if val_txt is None:
stdlib.free(buf)
return 0
vi: sizet
for vi in range(eq - val_start):
val_txt[vi] = buf[val_start + vi]
val_txt[eq - val_start] = 0
stdlib.free(buf)
# 用 atof_out(输出参数)绕开跨模块 double 返回值传递 bug
if string.atof_out(val_txt, out) != 0:
self.RecoverFound = 1
return 1
return 0
stdlib.free(buf)
return 0
def _recover_cdefine_float(self, name: str, out: t.CDouble | t.CPtr) -> int:
"""CDefine 浮点值恢复(只认带 t.CDefine 的行),见 _recover_float_literal"""
return self._recover_float_literal(name, out, 1)
# ============================================================
# _annotation_contains_name - 递归检查注解中是否包含指定名称
#
# 支持 BinOp(BitOr) 递归,同时检查 Attribute.attr 和 Name.id。
# ============================================================
def _annotation_contains_name(self, annot: ast.AST | t.CPtr, name: str) -> int:
"""递归检查注解中是否包含指定名称,返回 1=包含 / 0=不包含"""
if annot is None or name is None:
return 0
k: int = annot.kind()
if k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot)
if at.attr is not None and string.strcmp(at.attr, name) == 0:
return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot)
if nm.id is not None and string.strcmp(nm.id, name) == 0:
return 1
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if self._annotation_contains_name(bop.left, name) != 0:
return 1
if self._annotation_contains_name(bop.right, name) != 0:
return 1
return 0
# ============================================================
# _is_ptr_element_annotation - 检查注解是否为 bytes|t.CPtr 或 str|t.CPtr
#
# 返回 1=是(下标按 8 字节步长, i8** 语义), 0=否(下标按 1 字节步长, i8* 语义)
#
# 规则:
# bytes|t.CPtr / str|t.CPtr → i8**(8 字节步长)→ 返回 1
# char|t.CPtr / int8|t.CPtr → i8*(1 字节步长)→ 返回 0
# 纯 bytes / str → i8*(1 字节步长)→ 返回 0
#
# 注: 注解中的 "|" 是"和"的意思(组合类型),非右值中的位或运算
# ============================================================
def _is_ptr_element_annotation(self, annot: ast.AST | t.CPtr) -> int:
"""检查注解是否为 bytes|t.CPtr 或 str|t.CPtr 形式"""
if annot is None:
return 0
k: int = annot.kind()
if k != ast.ASTKind.BinOp:
return 0
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if bop.op != ast.OpKind.BitOr:
return 0
if self._annotation_contains_name(annot, "str") != 0:
return 1
if self._annotation_contains_name(annot, "bytes") != 0:
return 1
# 泛型类型参数/嵌套联合 | t.CPtr(如 _list.vp 的 elem_ptr: T | t.CPtr):
# 先经泛型上下文解析 T 的具体类型——纯标量(int/float 等 sizeof≠8)时
# 元素按标量自身大小(list[int] elem_size=4 匹配 i32),不能按 8 字节
# 指针槽(否则 8 字节写 / 4 字节步长互相覆盖,pop 读到 0);
# 指针/嵌套联合/64 位标量/double(sizeof==8)才按 8 字节。
if self._is_generic_tp_or_nested(bop.left) != 0 or self._is_generic_tp_or_nested(bop.right) != 0:
gp_nm: str = None
if bop.left is not None and bop.left.kind() == ast.ASTKind.Name:
gp_left_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(bop.left)
gp_nm = gp_left_nm.id
elif bop.right is not None and bop.right.kind() == ast.ASTKind.Name:
gp_right_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(bop.right)
gp_nm = gp_right_nm.id
if gp_nm is not None:
# 经全局 gen 委托解析泛型参数(T → 具体类型名)。不能 self.GenericTpNames
# 直读(Mixin @t.NoVTable 静态绑定:字段声明在 TypeGenMixin,AssignGenMixin
# 访问会生成不存在的属性引用 → TPC fast-fail 编译崩溃)
gp_resolved: str = self.Gen.TypeGenBase.resolve_generic_type_name(gp_nm)
if gp_resolved is not None and string.strcmp(gp_resolved, gp_nm) != 0:
# 泛型参数解析为纯标量 → 元素按标量自身大小(非 8 字节指针槽)
if self._is_scalar_ty_name(gp_resolved) != 0:
return 0
return 1
# 64 位标量数值类型 | CPtr(如 CUInt64T | CPtr / CSizeT | CPtr):
# 联合解析后同样退化为指针表示,元素为 8 字节(i64 或指针)。
# 下标必须按 8 字节步长读写——否则 GEP 步长 1 + trunc i8 截断,
# 指针存进数组只剩低 1 字节(BuddyTest OOM/fill 的 ptrs 数组全错位:
# store i8 trunc i64、读回 inttoptr i8 丢高 56 位,free 全部无效)。
if self._is_64bit_scalar_ty(bop.left) != 0 or self._is_64bit_scalar_ty(bop.right) != 0:
return 1
return 0
def _stg_ty_equal(self, pool: rowptr[memhub.MemBuddy],
ta: llvmlite.LLVMType | t.CPtr,
tb: llvmlite.LLVMType | t.CPtr) -> int:
"""类型内容相等比较(storage 绑定判定用)。
llvmlite 类型非 canonical:Ptr/Struct 每次构造都 pool.alloc 新实例
(includes/llvmlite/__types.vp),同类型不同实例地址不同。
此前绑定分支用 uint64(ta) == uint64(tb)(地址比较)判定相等,
对象构造 X = T() 的 rhs 类型实例(CallGenCtor 的 struct_ty)与 storage
类型实例(handle_module_level_var 的 resolved_stg_ty)不同 → 绑定失败
→ 构造结果落栈 alloca 并 store 栈地址到全局(悬垂),storage 保持零
(VKernel devfs devfs_init 双 storage + 栈临时 self 根因)。改为
TypePrint 内容比较(命名结构体比名字、匿名结构体比字段布局)。
"""
if ta is None or tb is None:
return 0
# 快路径:同一实例直接相等
if uint64(ta) == uint64(tb):
return 1
buf_a: str = pool.alloc(256)
buf_b: str = pool.alloc(256)
if buf_a is None or buf_b is None:
return 0
llvmlite.TypePrint(buf_a, 256, ta, pool)
llvmlite.TypePrint(buf_b, 256, tb, pool)
if buf_a[0] == 0 or buf_b[0] == 0:
return 0
return 1 if string.strcmp(buf_a, buf_b) == 0 else 0
def _is_addrlike_call(self, node: ast.AST | t.CPtr) -> int:
"""检查 rhs 是否为 c.Addr/c.Deref 等显式指针操作调用。
此类调用返回指针(如 c.Addr(obj) 返回对象 storage 地址,类型 Ptr(Struct)),
但语义是"取地址/解引用",不是对象值引用/构造——storage 绑定分支不应捕获:
X = c.Addr(Y) 应直接 store &Y 到 @X(X 与 Y 指向同一对象),若误绑定则
@X 自指 X.stg 并值复制 Y 的当前值(devfs DevFSObject 脱钩根因)。
"""
if node is None or node.kind() != ast.ASTKind.Call:
return 0
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute:
return 0
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return 0
vn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if vn.id is None or string.strcmp(vn.id, "c") != 0:
return 0
if at.attr is None:
return 0
if string.strcmp(at.attr, "Addr") == 0: return 1
if string.strcmp(at.attr, "Deref") == 0: return 1
if string.strcmp(at.attr, "Cast") == 0: return 1
if string.strcmp(at.attr, "Ptr") == 0: return 1
return 0
def _is_64bit_scalar_ty(self, node: ast.AST | t.CPtr) -> int:
"""判断类型节点是否为 64 位标量数值类型(CUInt64T/CInt64T/CSizeT),元素 8 字节"""
if node is None:
return 0
ty_nm: str = None
nk: int = node.kind()
if nk == ast.ASTKind.Name:
nm64: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
ty_nm = nm64.id
elif nk == ast.ASTKind.Attribute:
at64: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
ty_nm = at64.attr
if ty_nm is None:
return 0
if string.strcmp(ty_nm, "CUInt64T") == 0:
return 1
if string.strcmp(ty_nm, "CInt64T") == 0:
return 1
if string.strcmp(ty_nm, "CSizeT") == 0:
return 1
return 0
def _is_scalar_ty_name(self, ty_name: str) -> int:
"""判断类型名是否为已知纯标量(整数/浮点/字符),白名单判定
泛型 T | t.CPtr(如 _list.vp 的 elem_ptr)经 Fix A(TypeGenMixin 联合
解析保留泛型标量)后 alloca 变 Ptr(标量):T 为纯标量(int/float/double/
i64 等)时下标按标量自身大小步长,不能再按 8 字节指针槽——否则
Ptr(标量) alloca + IsPtrElement 路径(load 标量当指针)产生非法 IR。
指针/联合/str/bytes 才保持 8 字节指针槽语义。
原实现"排除 Ptr/str/bytes/| 后默认返回 1"把**未知类名**(如泛型上下文
污染产生的 llvmlite.Value)误判为标量 → IsPtrElement=0 → elem_ptr[0]
走通用路径生成 Value* → list[str].get 声明 i8* 与 ret Value* 不匹配
(8fbba llc 报错根因)。改白名单:已知标量才 1,未知类名按 8 字节指针槽。
"""
if ty_name is None:
return 0
# 前缀归一化:剥离 "t."/"c." 内建命名空间前缀,使 "int" 与裸名 "CInt" 判定
# 一致(泛型实参经 _norm_type_arg_name 规则 3 会保留 "t.")。不剥离则
# 误判为非标量 → 8 字节指针槽 → GEP 元素类型退化为指针(整数存指针报 CastError)。
if ty_name[0] == 't' and ty_name[1] == '.':
ty_name += 2
elif ty_name[0] == 'c' and ty_name[1] == '.':
ty_name += 2
if string.strchr(ty_name, '|') is not None:
return 0
if string.strchr(ty_name, '*') is not None:
return 0
if string.strcmp(ty_name, "str") == 0:
return 0
if string.strcmp(ty_name, "bytes") == 0:
return 0
if string.strcmp(ty_name, "CPtr") == 0:
return 0
if string.strstr(ty_name, "Ptr") is not None:
return 0
# === 白名单:已知纯标量(基础名 + stdint 大写别名) ===
if string.strcmp(ty_name, "int") == 0: return 1
if string.strcmp(ty_name, "char") == 0: return 1
if string.strcmp(ty_name, "CInt") == 0: return 1
if string.strcmp(ty_name, "CChar") == 0: return 1
if string.strcmp(ty_name, "CUnsignedChar") == 0: return 1
if string.strcmp(ty_name, "CShort") == 0: return 1
if string.strcmp(ty_name, "CUnsignedShort") == 0: return 1
if string.strcmp(ty_name, "CLong") == 0: return 1
if string.strcmp(ty_name, "CUnsignedLong") == 0: return 1
if string.strcmp(ty_name, "CLongLong") == 0: return 1
if string.strcmp(ty_name, "CUnsignedLongLong") == 0: return 1
if string.strcmp(ty_name, "CSizeT") == 0: return 1
if string.strcmp(ty_name, "BOOL") == 0: return 1
if string.strcmp(ty_name, "BYTE") == 0: return 1
if string.strcmp(ty_name, "UCHAR") == 0: return 1
if string.strcmp(ty_name, "CHAR") == 0: return 1
if string.strcmp(ty_name, "SHORT") == 0: return 1
if string.strcmp(ty_name, "USHORT") == 0: return 1
if string.strcmp(ty_name, "LONG") == 0: return 1
if string.strcmp(ty_name, "ULONG") == 0: return 1
if string.strcmp(ty_name, "LONGLONG") == 0: return 1
if string.strcmp(ty_name, "ULONGLONG") == 0: return 1
if string.strcmp(ty_name, "INT") == 0: return 1
if string.strcmp(ty_name, "UINT") == 0: return 1
if string.strcmp(ty_name, "WORD") == 0: return 1
if string.strcmp(ty_name, "DWORD") == 0: return 1
if string.strcmp(ty_name, "QWORD") == 0: return 1
# 定长整数/浮点(C*T 形式与裸别名,strstr 前缀匹配)
if string.strstr(ty_name, "Int8") is not None: return 1
if string.strstr(ty_name, "Int16") is not None: return 1
if string.strstr(ty_name, "Int32") is not None: return 1
if string.strstr(ty_name, "Int64") is not None: return 1
if string.strstr(ty_name, "UInt8") is not None: return 1
if string.strstr(ty_name, "UInt16") is not None: return 1
if string.strstr(ty_name, "UInt32") is not None: return 1
if string.strstr(ty_name, "UInt64") is not None: return 1
if string.strstr(ty_name, "Float") is not None: return 1
if string.strstr(ty_name, "FLOAT") is not None: return 1
if string.strstr(ty_name, "Double") is not None: return 1
if string.strstr(ty_name, "DOUBLE") is not None: return 1
# 未知类名(结构体/类/污染类型)→ 非标量,按 8 字节指针槽
return 0
def _is_generic_tp_or_nested(self, node: ast.AST | t.CPtr) -> int:
"""判断注解节点是否为泛型类型参数名(单字母大写,如 T)或嵌套联合(BinOp)"""
if node is None:
return 0
nk: int = node.kind()
if nk == ast.ASTKind.BinOp:
return 1
if nk == ast.ASTKind.Name:
nm_tp: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm_tp.id is None:
return 0
tp_len: sizet = string.strlen(nm_tp.id)
if tp_len == 1:
if nm_tp.id[0] >= 65 and nm_tp.id[0] <= 90:
return 1
return 0
# ============================================================
# _init_global_array_from_list — 从列表字面量初始化全局数组
#
# 为每个元素生成 GEP + store 指令,将值存入全局数组的对应位置。
# 适用于模块级 t.CArray[elem_ty, count] = [v0, v1, ...] 的初始化。
#
# Args:
# builder: IRBuilder
# pool: 编译器内存池
# mod: LLVMModule
# array_alloca: 全局数组变量的 Value 引用(类型为 [N x elem_ty]*)
# list_node: ast.List 节点
# trans: Translator 对象
#
# Returns:
# 0 成功,1 失败
# ============================================================
def _init_global_array_from_list(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: rowptr[memhub.MemBuddy],
mod: llvmlite.LLVMModule | t.CPtr,
array_alloca: llvmlite.Value | t.CPtr,
list_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
"""从列表字面量初始化全局数组,为每个元素生成 GEP + store"""
if builder is None or array_alloca is None or list_node is None:
return 1
# 获取数组类型(Pointee of [N x elem_ty]*)
arr_ty: llvmlite.LLVMType | t.CPtr = None
if array_alloca.Ty is not None:
arr_ty = array_alloca.Ty.Pointee
if arr_ty is None:
return 1
# 匹配数组类型获取元素类型与声明长度
elem_ty: llvmlite.LLVMType | t.CPtr = None
arr_count: int = 0
match arr_ty:
case llvmlite.LLVMType.Array(et, cnt):
elem_ty = et
arr_count = cnt
if elem_ty is None:
return 1
# 获取列表元素
lst: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node)
if lst is None or lst.elts is None:
return 1
elts: list[ast.AST | t.CPtr] | t.CPtr = lst.elts
elts_count: sizet = len(elts)
# 特例:单个字符串字面量初始化字符数组([N x i8],N = strlen+1)
# 逐字节写入字符串内容;调用方已先 store zeroinitializer,末字节 NUL 由零保持。
if self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty) == 8 and elts_count == 1 and arr_count >= 1:
e0_s: ast.AST | t.CPtr = elts.get(0)
if e0_s is not None and e0_s.kind() == ast.ASTKind.Constant:
c0_s: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(e0_s)
if c0_s.const_kind == ast.CONST_STR:
sv_s: str = ast.constant_get_str_val(c0_s)
if sv_s is not None:
n_s: sizet = string.strlen(sv_s)
if sizet(arr_count) < n_s:
self.Gen.TypeGenErr.fatal_error(
list_node,
f"字符串字面量长度 {n_s} 超过字符数组声明长度 {arr_count},不允许越界写入",
"OverflowError")
bi_s: sizet = 0
while bi_s < n_s:
bidx_s: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, bi_s)
bp_s: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, arr_ty, elem_ty, array_alloca, bidx_s)
if bp_s is None:
break
bvv: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, sv_s[bi_s])
bsv: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, bvv, elem_ty, e0_s)
llvmlite.build_store(builder, bsv, bp_s)
bi_s += 1
return 0
# 元素数超过声明长度 → fail-fast(否则会生成 idx>=N 的越界 GEP 写)
if arr_count > 0 and sizet(arr_count) < elts_count:
self.Gen.TypeGenErr.fatal_error(
list_node,
f"数组初始化元素数 {elts_count} 超过数组声明长度 {arr_count},不允许越界写入",
"OverflowError")
# 为每个元素生成 GEP + store
for i in range(elts_count):
elem_node: ast.AST | t.CPtr = elts.get(i)
idx_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, i)
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, arr_ty, elem_ty, array_alloca, idx_val)
if elem_ptr is None:
continue
# 内层嵌套数组:元素类型是数组且元素节点是列表字面量 → 递归数组初始化
# (逐元素 GEP + store)。list 字面量赋给 t.CArray 属数组语义,不特化
# list 泛型(否则 _list 未注册时报特化失败);只有显式 list[T] 注解才引入 _list。
inner_arr: int = 0
if self.Gen.ExprGenBase.is_array_type(elem_ty) != 0:
if elem_node is not None and elem_node.kind() == ast.ASTKind.List:
inner_arr = 1
if inner_arr != 0:
self._init_global_array_from_list(builder, pool, mod, elem_ptr, elem_node, trans)
continue
elem_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, elem_node, None, 0, trans)
if elem_val is None:
# 元素翻译失败:直接报错终止(fail-fast),暴露上游根因
self.Gen.TypeGenErr.fatal_error(elem_node, "全局数组初始化元素翻译失败", "TypeError")
# 类型转换(如 i32 → i8 截断)
store_val: llvmlite.Value | t.CPtr = elem_val
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, elem_val, elem_ptr.Ty.Pointee, elem_node)
llvmlite.build_store(builder, store_val, elem_ptr)
return 0
# ============================================================
# _apply_aug_op - 应用增强赋值的二元运算
#
# 支持浮点运算、指针算术: ptr += int / ptr -= int
# 整数运算自动类型提升
# ============================================================
def _apply_aug_op(self, pool: rowptr[memhub.MemBuddy],
builder: llvmlite.IRBuilder | t.CPtr,
op: int,
lhs: llvmlite.Value | t.CPtr,
rhs: llvmlite.Value | t.CPtr,
node: ast.AST | t.CPtr = None,
trans: HT.Translator | t.CPtr = None,
lhs_node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""应用增强赋值的二元运算(浮点 + 指针算术 + 整数运算)"""
lhs_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(rhs.Ty)
lhs_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(rhs.Ty)
# 浮点运算: 任一操作数为浮点时,使用浮点指令(必须在指针算术之前检查,
# 因为 float 的 int bits 为 0,会被误认为指针)
if lhs_fbits != 0 or rhs_fbits != 0:
# 确定目标浮点类型(使用较大的位宽)
target_fbits: int = lhs_fbits
if rhs_fbits > target_fbits:
target_fbits = rhs_fbits
target_float_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
if target_fbits == 32:
target_float_ty = llvmlite.Float(pool)
# 将两个操作数转换为目标浮点类型(int→float via si2fp, float→float via fpext/fptrunc)
lhs = self.Gen.ExprGenBase.coerce_to_type(builder, lhs, target_float_ty, node)
rhs = self.Gen.ExprGenBase.coerce_to_type(builder, rhs, target_float_ty, node)
if op == ast.OpKind.Add: return llvmlite.build_fadd(builder, lhs, rhs)
if op == ast.OpKind.Sub: return llvmlite.build_fsub(builder, lhs, rhs)
if op == ast.OpKind.Mult: return llvmlite.build_fmul(builder, lhs, rhs)
if op == ast.OpKind.Div: return llvmlite.build_fdiv(builder, lhs, rhs)
if op == ast.OpKind.Mod: return llvmlite.build_frem(builder, lhs, rhs)
return None
# 指针算术: ptr += int / ptr -= int → getelementptr(按元素大小缩放)
# 对齐 TPC HandlesAugAssign.py L140-149 / L170-180
if lhs_bits == 0 and rhs_bits != 0:
if op == ast.OpKind.Add or op == ast.OpKind.Sub:
return self.Gen.ExprGenBase.translate_ptr_arith(pool, builder, lhs, rhs, op)
return None
# 整数运算:类型提升
if lhs_bits > rhs_bits and rhs_bits > 0:
rhs = self.Gen.ExprGenBase.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
elif rhs_bits > lhs_bits and lhs_bits > 0:
lhs = self.Gen.ExprGenBase.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
# 无符号操作数(如 u_num_l: uint64 的 //= / %=)→ udiv/urem,有符号走 sdiv/srem。
aug_unsigned: int = 0
if lhs_node is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, lhs_node, trans) != 0:
aug_unsigned = 1
if op == ast.OpKind.Add: return llvmlite.build_add(builder, lhs, rhs)
elif op == ast.OpKind.Sub: return llvmlite.build_sub(builder, lhs, rhs)
elif op == ast.OpKind.Mult: return llvmlite.build_mul(builder, lhs, rhs)
elif op == ast.OpKind.Div:
if aug_unsigned != 0: return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
if aug_unsigned != 0: return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
if aug_unsigned != 0: return llvmlite.build_urem(builder, lhs, rhs)
return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.BitAnd: return llvmlite.build_and(builder, lhs, rhs)
elif op == ast.OpKind.BitOr: return llvmlite.build_or(builder, lhs, rhs)
elif op == ast.OpKind.BitXor: return llvmlite.build_xor(builder, lhs, rhs)
elif op == ast.OpKind.LShift: return llvmlite.build_shl(builder, lhs, rhs)
elif op == ast.OpKind.RShift:
# 无符号 >> → lshr(逻辑右移),有符号 >> → ashr(算术右移)
# 对齐 TPC ExprGen.emit_binary_op:i8/i16 默认无符号;其余查操作数无符号性
if lhs_bits == 8 or lhs_bits == 16:
return llvmlite.build_lshr(builder, lhs, rhs)
if lhs_node is not None and trans is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, lhs_node, trans) != 0:
return llvmlite.build_lshr(builder, lhs, rhs)
return llvmlite.build_ashr(builder, lhs, rhs)
return None
# ============================================================
# translate_augassign - 增强赋值语句翻译(迁移自 HandlesAugAssign.AugAssignHandle.Handle)
#
# 支持 += -= *= /= %= &= |= ^= <<= >>=,local/global/nonlocal 三种作用域
# ============================================================
def translate_augassign(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译增强赋值语句 (x += 1, y -= 2, etc.)"""
if node is None:
return 0
aug: ast.AugAssign | t.CPtr = (ast.AugAssign | t.CPtr)(node)
if aug is None:
return 0
target: ast.AST | t.CPtr = aug.target
if target is None:
return 0
tk: int = target.kind()
# Attribute 目标: self.field += 1
# 流程: get_attribute_ptr → load → binop → store
if tk == ast.ASTKind.Attribute:
pool: rowptr[memhub.MemBuddy] = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
# 1. 获取字段指针
field_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenAttr.get_attribute_ptr(
builder, pool, mod, target, trans)
if field_ptr is None:
# fail-fast: 属性指针解析失败
self.Gen.TypeGenErr.fatal_error(target, "attribute ptr is None", "TypeError")
# 2. 确定字段类型并加载当前值
target_ty: llvmlite.LLVMType | t.CPtr = None
if field_ptr.Ty is not None and field_ptr.Ty.Pointee is not None:
target_ty = field_ptr.Ty.Pointee
if target_ty is None:
# fail-fast: 字段类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "字段类型信息缺失", "TypeError")
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, target_ty, field_ptr)
if cur_val is None:
# fail-fast: 属性当前值加载失败
self.Gen.TypeGenErr.fatal_error(target, "无法加载 attribute 当前值", "TypeError")
# 3. 翻译 RHS 值
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aug.value, None, 0, trans)
if rhs_val is None:
# fail-fast: RHS 翻译失败
self.Gen.TypeGenErr.fatal_error(target, "rhs 翻译失败", "TypeError")
# 4. 应用二元运算
result: llvmlite.Value | t.CPtr = self._apply_aug_op(
pool, builder, aug.op, cur_val, rhs_val, node, trans, target)
if result is None:
# fail-fast: attribute 二元运算失败
self.Gen.TypeGenErr.fatal_error(target, f"attribute 二元运算失败 op={aug.op}", "TypeError")
# 5. 类型对齐并存储
result = self.Gen.ExprGenBase.coerce_to_type(builder, result, target_ty, node)
if result is None:
# fail-fast: 类型对齐失败
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
llvmlite.build_store(builder, result, field_ptr)
return 0
# Subscript 目标: self.state[i] += x / arr[i] += x
# 流程: get_subscript_ptr → load → binop → store
if tk == ast.ASTKind.Subscript:
pool: rowptr[memhub.MemBuddy] = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
# 1. 获取元素指针
elem_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenName.get_subscript_ptr(
builder, pool, mod, target, trans)
if elem_ptr is None:
# fail-fast: 下标指针解析失败
self.Gen.TypeGenErr.fatal_error(target, "subscript ptr is None", "TypeError")
# 2. 确定元素类型并加载当前值
target_ty: llvmlite.LLVMType | t.CPtr = None
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
target_ty = elem_ptr.Ty.Pointee
if target_ty is None:
# fail-fast: 元素类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "元素类型信息缺失", "TypeError")
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, target_ty, elem_ptr)
if cur_val is None:
# fail-fast: 元素当前值加载失败
self.Gen.TypeGenErr.fatal_error(target, "无法加载 subscript 元素当前值", "TypeError")
# 3. 翻译 RHS 值
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aug.value, None, 0, trans)
if rhs_val is None:
# fail-fast: RHS 翻译失败
self.Gen.TypeGenErr.fatal_error(target, "rhs 翻译失败", "TypeError")
# 4. 应用二元运算
result: llvmlite.Value | t.CPtr = self._apply_aug_op(
pool, builder, aug.op, cur_val, rhs_val, node, trans, target)
if result is None:
# fail-fast: subscript 二元运算失败
self.Gen.TypeGenErr.fatal_error(target, f"subscript 二元运算失败 op={aug.op}", "TypeError")
# 5. 类型对齐并存储
result = self.Gen.ExprGenBase.coerce_to_type(builder, result, target_ty, node)
if result is None:
# fail-fast: 类型对齐失败
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
llvmlite.build_store(builder, result, elem_ptr)
return 0
if tk != ast.ASTKind.Name:
# fail-fast: 不支持的 AugAssign 目标类型
self.Gen.TypeGenErr.fatal_error(
target, "AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript", "TypeError")
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
if nm is None or nm.id is None:
return 0
pool: rowptr[memhub.MemBuddy] = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
# 1. 确定变量作用域类型: 0=local, 1=global, 2=nonlocal
scope_type: int = 0
if GenApi.is_global_name(trans, nm.id) != 0:
scope_type = 1
elif GenApi.is_nonlocal_name(trans, nm.id) != 0:
scope_type = 2
# 2. 加载当前值
cur_val: llvmlite.Value | t.CPtr = None
target_alloca: llvmlite.Value | t.CPtr = None
target_ty: llvmlite.LLVMType | t.CPtr = None
if scope_type == 1:
# global 变量
target_alloca = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id)
if target_alloca is not None:
if target_alloca.Ty is None:
# fail-fast: 变量类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "变量类型信息缺失", "TypeError")
target_ty = target_alloca.Ty.Pointee
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
elif scope_type == 2:
# nonlocal 变量(通过闭包 env)
cur_val = self.Gen.StmtGenDecl.load_nonlocal_var(trans, nm.id)
if cur_val is not None:
target_ty = cur_val.Ty
else:
# 普通局部变量
target_alloca = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
if target_alloca is not None:
if target_alloca.Ty is None:
# fail-fast: 变量类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "变量类型信息缺失", "TypeError")
target_ty = target_alloca.Ty.Pointee
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
if cur_val is None:
# fail-fast: 目标变量当前值加载失败
self.Gen.TypeGenErr.fatal_error(target, f"无法加载目标变量 '{nm.id}' 的当前值", "TypeError")
# 3. 翻译 RHS 值
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aug.value, None, 0, trans)
if rhs_val is None:
# fail-fast: RHS 翻译失败
self.Gen.TypeGenErr.fatal_error(target, "rhs 翻译失败", "TypeError")
# 4. 类型对齐 + 应用二元运算
# 注意: AugAssign 不走运算符重载(语义上需要 __iadd__ 而非 __add__)
result: llvmlite.Value | t.CPtr = self._apply_aug_op(
pool, builder, aug.op, cur_val, rhs_val, node, trans, target)
if result is None:
# fail-fast: 二元运算失败
self.Gen.TypeGenErr.fatal_error(
target,
f"对变量 '{nm.id}' 应用二元运算失败 op={aug.op}",
"TypeError")
# 5. 存储结果
result = self.Gen.ExprGenBase.coerce_to_type(builder, result, target_ty, node)
if result is None:
# fail-fast: 类型对齐失败
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
if scope_type == 1:
# global 变量
if target_alloca is not None:
llvmlite.build_store(builder, result, target_alloca)
elif scope_type == 2:
# nonlocal 变量
nl_ptr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
trans, nm.id)
if nl_ptr is not None:
llvmlite.build_store(builder, result, nl_ptr)
else:
# 普通局部变量
if target_alloca is not None:
llvmlite.build_store(builder, result, target_alloca)
return 0
# ============================================================
# is_ctypedef_annotation - 检测注解是否为 t.CTypedef
#
# 迁移自 HandlesAnnAssign.is_ctypedef_annotation(模块级委托壳);
# 真实实现 TypeGenCType._is_ctypedef_annotation 在另一 Mixin,经
# gen 单例(HandlesExpr._g_gen 镜像)跨链调用。
# ============================================================
def is_ctypedef_annotation(self, annot: ast.AST | t.CPtr) -> int:
"""检测注解是否为 t.CTypedef,返回 1 表示是,0 表示否"""
return self.Gen.TypeGenCType._is_ctypedef_annotation(annot)
# ============================================================
# _is_single_param_carray_annot - 检查注解是否为单参 t.CArray[elem_ty]
#
# 单参形式(t.CArray[elem_ty])与 count=None 形式(t.CArray[elem_ty, None])
# 都解析为 Ptr(elem_ty)。当 RHS 是列表字面量时,按 TPC 语义应推断为
# 静态数组 [len x elem_ty](对齐 TPC ParseListAnnotation 的 is_pointer 分支:
# "单参数 list[type] → 指针,但如果有列表字面量初始化值则按数组 list[type, N] 处理")。
# ============================================================
def _is_single_param_carray_annot(self, annot: ast.AST | t.CPtr) -> int:
"""检查注解是否为单参/None 长度 t.CArray[...],返回 1=是 0=否
支持两种书写形式:
1. 模块限定 t.CArray[...](Attribute value)
2. 裸名 CArray[...](from t import * 后使用) # 存疑
"""
if annot is None or annot.kind() != ast.ASTKind.Subscript:
return 0
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
if sub.value is None or sub.slice is None:
return 0
if sub.value.kind() == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub.value)
if at.attr is None or string.strcmp(at.attr, "CArray") != 0:
return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return 0
mv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mv.id is None or string.strcmp(mv.id, "t") != 0:
return 0
elif sub.value.kind() == ast.ASTKind.Name:
# 裸名 CArray(from t import *)/ 内建 rawarray:与 t.CArray 等价
bn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if bn.id is None or (string.strcmp(bn.id, "CArray") != 0 and string.strcmp(bn.id, "rawarray") != 0):
return 0
else:
return 0
# 单参形式:slice 非 Tuple(如 t.CArray[u64] / CArray[str])
if sub.slice.kind() != ast.ASTKind.Tuple:
return 1
# 双参形式:count=None(t.CArray[u64, None])→ 等价单参
tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub.slice)
if tup.elts is None or len(tup.elts) < 2:
return 0
cnt_node: ast.AST | t.CPtr = tup.elts.get(1)
if cnt_node is not None and cnt_node.kind() == ast.ASTKind.Constant:
cn0: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(cnt_node)
if cn0.const_kind == ast.CONST_NONE:
return 1
return 0
# _infer_static_array_ty_from_literal - 单参 CArray + 列表字面量 → [len x elem_ty]
# 单参 t.CArray[elem_ty] 本解析为 Ptr(elem_ty),RHS 为列表字面量时按 TPC
# ParseListAnnotation 语义推断为静态数组(否则误走动态 list 构造 → 链接期 undefined)。
# PreScan 与 Handle 都必须调用:两处 alloca 类型须一致,否则复用旧 alloca 使推断失效。
def _infer_static_array_ty_from_literal(self, pool: rowptr[memhub.MemBuddy],
annot: ast.AST | t.CPtr,
value: ast.AST | t.CPtr,
resolved_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""单参 CArray + 列表字面量 → Array(elem_ty, len);否则返回原类型"""
if value is not None and value.kind() == ast.ASTKind.List and resolved_ty is not None:
if self._is_single_param_carray_annot(annot) != 0:
elem_ty_arr: llvmlite.LLVMType | t.CPtr = resolved_ty.Pointee
lst_arr: ast.List | t.CPtr = (ast.List | t.CPtr)(value)
cnt_arr: int = 0
if lst_arr.elts is not None:
cnt_arr = len(lst_arr.elts)
# 特例:单个字符串字面量(如 hex_digits: t.CArray[char, None] = ["0123.."])
# 语义是"以字符串内容初始化字符数组",长度应取 strlen+1(含结尾 NUL),
# 而不是列表元素个数 1——否则 alloca [1 x i8] 且只写首字节 → 下标 0..15 越界读。
if elem_ty_arr is not None and cnt_arr == 1 and lst_arr.elts is not None:
e0_arr: ast.AST | t.CPtr = lst_arr.elts.get(0)
if e0_arr is not None and e0_arr.kind() == ast.ASTKind.Constant:
c0_arr: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(e0_arr)
if c0_arr.const_kind == ast.CONST_STR:
sv0_arr: str = ast.constant_get_str_val(c0_arr)
if sv0_arr is not None and self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty_arr) == 8:
return llvmlite.Array(pool, elem_ty_arr, int(string.strlen(sv0_arr)) + 1)
if elem_ty_arr is not None and cnt_arr > 0:
return llvmlite.Array(pool, elem_ty_arr, cnt_arr)
return resolved_ty
# ============================================================
# _build_ann_fail_msg - 构建"变量类型注解解析失败"详细报错
#
# 含注解文本(_get_type_name_from_annotation 重建)、节点类型(kind 映射)、
# 以及 builtins 最后匹配的诊断结果(_lookup_builtins_typedef 记录),
# 便于直接定位是"注解本身未注册"还是"builtins 扫描未命中/未展开"。
# ============================================================
def _build_ann_fail_msg(self, pool: rowptr[memhub.MemBuddy],
annot: ast.AST | t.CPtr, nm_id: str) -> str:
"""构建变量类型注解解析失败的详细报错信息
注意:f-string 翻译产物是栈上 alloca 缓冲,在助手函数返回后帧销毁,
缓冲会被后续调用覆盖成垃圾(实测输出 '$')。必须用堆缓冲
(stdlib.malloc + viperlib.snprintf)保证跨帧存活。
"""
ann_txt_x: str = None
if annot is not None:
ann_txt_x = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, annot)
if ann_txt_x is None:
ann_txt_x = "?"
btd_nm_x: str = self.Gen.TypeGenCType.LastBuiltinsTypedefName
btd_rs_x: str = self.Gen.TypeGenCType.LastBuiltinsTypedefResult
msg_len: sizet = 640
msg_buf: str = stdlib.malloc(msg_len)
if msg_buf is None:
return ann_txt_x
if btd_nm_x is not None and btd_rs_x is not None:
viperlib.snprintf(msg_buf, msg_len, "变量类型注解解析失败,变量名: %s,注解: %s。builtins 最后匹配: %s 展开为 %s,仍无法解析", nm_id, ann_txt_x, btd_nm_x, btd_rs_x)
elif btd_nm_x is not None:
viperlib.snprintf(msg_buf, msg_len, "变量类型注解解析失败,变量名: %s,注解: %s。builtins 最后匹配: %s 未命中(builtins.vp 无该 typedef 或扫描未运行)", nm_id, ann_txt_x, btd_nm_x)
else:
viperlib.snprintf(msg_buf, msg_len, "变量类型注解解析失败,变量名: %s,注解: %s。builtins 最后匹配: 未触发", nm_id, ann_txt_x)
return msg_buf
# ============================================================
# translate_annassign_prescan - 预扫描 AnnAssign(迁移自
# HandlesAnnAssign.AnnAssignHandle.PreScan)
#
# 为有类型注解的变量提前创建 alloca 到 entry block 顶部;返回新增的变量数
# ============================================================
def translate_annassign_prescan(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""预扫描 AnnAssign,提前创建 alloca 到 entry block 顶部"""
if node is None:
return 0
k: int = node.kind()
if k != ast.ASTKind.AnnAssign:
return 0
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aa is None or aa.target is None:
return 0
target: ast.AST | t.CPtr = aa.target
if target.kind() != ast.ASTKind.Name:
return 0
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
if nm.id is None:
return 0
# CDefine 注解: 不创建 alloca,但注册常量到全局表
# 供后续 t.CArray[elem_ty, NAME] 编译期解析使用
# 浮点/整数自动识别(与 Handle/handle_module_level_var 路径一致)
if self.is_cdefine_annotation(aa.annotation) != 0:
pool_ps: rowptr[memhub.MemBuddy] = trans.Pool
self._register_cdefine_from_value(pool_ps, nm.id, aa.value)
return 0
# CTypedef 注解: 类型别名(ScanCTypedefDeclarations 已注册到 CTypedef 表),
# 不创建全局/局部存储——否则 stdint.vp 的 float64_t: t.CTypedef = t.CFloat64T
# 等生成 @float64_t 符号,链接 duplicate symbol warning
if self.is_ctypedef_annotation(aa.annotation) != 0:
return 0
# global/nonlocal 变量不需要局部 alloca
if GenApi.is_global_name(trans, nm.id) != 0:
return 0
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
return 0
# 模块级全局变量已由 handle_module_level_var 注册到模块作用域。
# Python 语义:仅模块级代码(Current == Root)时跳过局部 alloca;
# 函数内无 global 声明时应创建局部变量(不遮蔽全局)。
if self.Gen.VarGen.is_module_scope(trans.SymTab) != 0:
if self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id) is not None:
return 0
# 检查是否已存在
existing: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current(
trans.SymTab, nm.id)
if existing is not None:
return 0
pool: rowptr[memhub.MemBuddy] = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
# 从 annotation 推断类型(修复:原硬编码 i32 导致 str 等类型错误)
var_ty: llvmlite.LLVMType | t.CPtr = None
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
# 泛型特化补救(在解析之前无条件执行,_specialize_generic_class 幂等):
# 注解可能是 list[X](Subscript)或 list[X] | t.CPtr(含 CPtr 的 BinOp)。
# 若泛型类尚未特化,resolve_annotation_type 会把 list[X] 退化为不透明
# 基类 list*(丢掉元素类型)——预建的 alloca 因此变成 list*,之后
# body_list.get(i) 会分派到默认 list[int] 特化(返回 i32)→ inttoptr
# i32→AST* 截断指针 → StmtGenMixin._translate_stmt_impl 读垃圾节点崩溃。
# 与 Handle 路径共用 _prespecialize_generic_annotation,保证两处解析一致。
self._prespecialize_generic_annotation(trans, pool, annot)
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
var_ty = resolved
# 单参 t.CArray[elem_ty] + 列表字面量 → 静态数组(与 Handle 推断一致)。
# PreScan 预创建的 alloca 类型必须与 Handle 期望一致,否则 Handle 的
# get_or_create_sym 复用旧 alloca,数组推断失效仍走动态 list → _mbuddy。
var_ty = self._infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty)
# [D31 fail-fast] 泛型注解退化成裸基类 → 立即终止(暴露退化点)
self._fail_on_degenerated_generic_annotation(aa, nm.id, annot, var_ty)
else:
# fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32)
self.Gen.TypeGenErr.fatal_error(
aa,
self._build_ann_fail_msg(pool, annot, nm.id),
"TypeError")
else:
# fail-fast: AnnAssign 必须有 annotation,缺少时立即终止(禁止兜底 i32)
self.Gen.TypeGenErr.fatal_error(
aa,
f"AnnAssign 缺少类型注解,变量名: {nm.id}",
"SyntaxError")
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, trans.Module, var_ty)
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, var_ty)
if alloca is not None:
if self.Gen.VarGen.define_var(
trans.SymTab, nm.id, alloca) == 0:
# 存储原始类型注解的类名(方法调用检测时 Ptr(i8) 回退按类名查结构体)。
# 传 pool:泛型 Subscript 返回 mangled 特化名;pool=None 只得裸模板名,
# 模板未注册时属性访问会报"类未注册"。
if annot is not None:
cls_nm_ps: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
annot, trans._imported_modules, pool, trans._from_imports)
if cls_nm_ps is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, nm.id, cls_nm_ps)
# 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长)
if self._is_ptr_element_annotation(annot) != 0:
self.Gen.VarGen.set_var_ptr_element(
trans.SymTab, nm.id)
return 0
return 0
# ============================================================
# _prespecialize_generic_annotation - 注解为泛型 Subscript(含 | t.CPtr 联合)时
# 先注册特化,使随后的 resolve_annotation_type 得到带类型实参的特化结构体,
# 而非退化为不透明基类(裸 list* 会让 .get() 误分派到 list[int].get 截断指针)。
# PreScan(pre_scan_allocas)与 Handle(translate_annassign)两条路径共用本 helper,
# 保证两处解析结果一致(未复用预扫描 alloca 的路径也必须先特化)。
# _specialize_generic_class 幂等,重复调用安全。
# 返回 1=执行过特化尝试;0=注解形态不适用(非泛型 Subscript)。
def _prespecialize_generic_annotation(self, trans: HT.Translator | t.CPtr,
pool: rowptr[memhub.MemBuddy],
annot: ast.AST | t.CPtr) -> int:
"""泛型注解预特化(见上方注释),返回 1=已尝试 / 0=不适用"""
if annot is None or trans is None:
return 0
# 取注解中的泛型 Subscript 一侧:Subscript 本身,或 A | t.CPtr 的 Subscript 分支
spec_annot: ast.Subscript | t.CPtr = None
if annot.kind() == ast.ASTKind.Subscript:
spec_annot = (ast.Subscript | t.CPtr)(annot)
elif annot.kind() == ast.ASTKind.BinOp:
bop_ps: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if bop_ps.op == ast.OpKind.BitOr:
if bop_ps.left is not None and bop_ps.left.kind() == ast.ASTKind.Subscript:
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.left)
elif bop_ps.right is not None and bop_ps.right.kind() == ast.ASTKind.Subscript:
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.right)
if spec_annot is None:
return 0
sv_ps: ast.AST | t.CPtr = spec_annot.value
if sv_ps is None or sv_ps.kind() != ast.ASTKind.Name:
return 0
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sv_ps)
if sub_nm.id is None:
return 0
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
if template_cd is None:
return 0
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(
pool, spec_annot.slice, trans._from_imports)
if type_args_ps is None or len(type_args_ps) == 0:
return 0
self.Gen.ClassGenGeneric._specialize_generic_class(
trans, sub_nm.id, type_args_ps)
return 1
# ============================================================
# translate_annassign - 翻译 AnnAssign 语句(迁移自
# HandlesAnnAssign.AnnAssignHandle.Handle)
#
# 返回新增的变量数
# ============================================================
def _fail_on_degenerated_generic_annotation(self,
aa: ast.AnnAssign | t.CPtr,
var_name: str,
annot: ast.AST | t.CPtr,
var_ty: llvmlite.LLVMType | t.CPtr) -> int:
"""fail-fast:泛型注解解析成了「裸泛型基类指针」(丢失类型实参)
场景:注解是泛型下标 `list[X]` 或 `list[X] | t.CPtr`,但
`resolve_annotation_type` 退化成裸基类 `list*`(没有 `[...]`)
⇒ 之后该变量的任何泛型方法调用(`.get()`/`.set()`/...)都会按**错误**
元素类型特化(实测 `list[int].get` 返回 i32)⇒ `inttoptr` 把 i32 当
指针 ⇒ 读垃圾 AST 节点(D21/D25/D31 崩溃族,ASan 现场
`MatchGenMixin._handle_simple_match → StmtGenMixin._translate_stmt_impl`)。
与 D25 既有守卫的区别:那条守卫判「复用到的**旧 alloca** 是裸泛型基类」;
本守卫判「**本次注解解析结果本身**就是裸泛型基类」,覆盖「旧 alloca 恰好
也是裸类型 → 两边一致 → 既有守卫不触发」的漏网路径。
返回 0=未退化(继续);退化则 fatal_error 终止(不返回)。
"""
if aa is None or annot is None or var_ty is None:
return 0
# 取注解里的泛型 Subscript 一侧:Subscript 本身,或 A | t.CPtr 的 Subscript 分支
ann_sub: ast.Subscript | t.CPtr = None
if annot.kind() == ast.ASTKind.Subscript:
ann_sub = (ast.Subscript | t.CPtr)(annot)
elif annot.kind() == ast.ASTKind.BinOp:
bop_ff: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if bop_ff.op == ast.OpKind.BitOr:
if bop_ff.left is not None and bop_ff.left.kind() == ast.ASTKind.Subscript:
ann_sub = (ast.Subscript | t.CPtr)(bop_ff.left)
elif bop_ff.right is not None and bop_ff.right.kind() == ast.ASTKind.Subscript:
ann_sub = (ast.Subscript | t.CPtr)(bop_ff.right)
if ann_sub is None or ann_sub.value is None \
or ann_sub.value.kind() != ast.ASTKind.Name:
return 0
base_nm_ff: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ann_sub.value)
if base_nm_ff.id is None:
return 0
# 仅当该基名确实是泛型模板(list/GSList/...)才判定为退化
if self.Gen.ClassGenGeneric._find_generic_template(base_nm_ff.id) is None:
return 0
if var_ty.Pointee is None:
return 0
vt_nm_ff: str = llvmlite.get_struct_name(var_ty.Pointee)
if vt_nm_ff is None:
return 0
vt_pf_ff: int = GenApi.sha1_prefix_len(vt_nm_ff)
vt_short_ff: str = vt_nm_ff + vt_pf_ff if vt_pf_ff != 0 else vt_nm_ff
if string.strchr(vt_short_ff, '[') is not None:
return 0 # 带类型实参:未退化
self.Gen.TypeGenErr.fatal_error(
aa,
f"变量 '{var_name}' 的泛型注解 '{base_nm_ff.id}[...]' 解析成了裸基类 "
f"'{vt_nm_ff}*'(丢失类型实参);后续 .get()/.set() 会按错误元素类型"
f"特化(如 list[int].get 返回 i32)→ inttoptr 截断指针崩溃。"
f"禁止静默继续——请检查该注解在 resolve_annotation_type 中的退化点。",
"TypeError")
return 0
def translate_annassign(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译 AnnAssign(target=Name, annotation=..., value=expr)"""
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aa is None:
return 0
target: ast.AST | t.CPtr = aa.target
if target is None or target.kind() != ast.ASTKind.Name:
return 0
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
if nm.id is None:
return 0
# CDefine 注解: 编译期常量,不生成运行时代码
# 注册到全局 CDefine 表供 t.CArray[elem_ty, NAME] 解析
# 注: PreScan 已注册过,此处保证即使跳过 PreScan 也能正确注册
if self.is_cdefine_annotation(aa.annotation) != 0:
pool_cd: rowptr[memhub.MemBuddy] = trans.Pool
self._register_cdefine_from_value(pool_cd, nm.id, aa.value)
return 0
# CTypedef 注解: 类型别名(mode_t: t.CTypedef = uint32 等)。
# 翻译期 CTypedef 表已被 clear_ctypedefs 清空(每文件隔离;表字符串
# pool 分配,reset_to(mark1) 回收后悬空,不能跨文件累积),必须在此
# 按需注册,否则 posix.vp S_ISREG(mode: mode_t) 等普通函数参数注解
# 解析查表失败 → NameError: mode_t。仅登记类型表达式,不创建全局/
# 局部存储——否则生成 @mode_t 符号,链接 duplicate symbol warning。
if self.is_ctypedef_annotation(aa.annotation) != 0:
pool_ct: rowptr[memhub.MemBuddy] = trans.Pool
ctd_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool_ct, aa.value)
if ctd_str is not None:
self.Gen.TypeGenCType.register_ctypedef(pool_ct, nm.id, ctd_str)
return 0
pool: rowptr[memhub.MemBuddy] = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
# 确定类型(从 annotation 推断)
var_ty: llvmlite.LLVMType | t.CPtr = None
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
# 与 PreScan 同源:先注册泛型特化,避免 list[X] 退化为裸 list* 后 .get()
# 误分派(返回 i32 → inttoptr 截断)。此处为循环体内重声明同名变量、
# 不复用预扫描 alloca 的路径(详见 _prespecialize_generic_annotation)。
self._prespecialize_generic_annotation(trans, pool, annot)
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
var_ty = resolved
else:
# fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32)
self.Gen.TypeGenErr.fatal_error(
aa,
self._build_ann_fail_msg(pool, annot, nm.id),
"TypeError")
else:
# fail-fast: AnnAssign 必须有 annotation,缺少时立即终止(禁止兜底 i32)
self.Gen.TypeGenErr.fatal_error(
aa,
f"AnnAssign 缺少类型注解,变量名: {nm.id}",
"SyntaxError")
# t.Callable 函数指针:注册返回类型签名(供函数指针间接调用精确返回类型)
callable_ret: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenErr.try_extract_callable_ret(
pool, annot, trans._imported_modules, trans._from_imports, trans)
if callable_ret is not None:
self.Gen.CallGenLeafKit.register_func_ptr_sig(nm.id, callable_ret)
# global 变量:写入模块作用域中的全局变量
if GenApi.is_global_name(trans, nm.id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(
trans.SymTab, nm.id)
if mod_alloca is not None and aa.value is not None:
# P1-A(上下文位宽传播):把目标变量的类型作为 target_ty 传入,
# 使整数字面量按目标位宽直接物化(如 uint64 目标 → i64 const),
# 与显式 u64(...) 强转一致,且省去后续 coerce 生成的 zext。
aa_glob_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
aa_glob_ty = mod_alloca.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支(普通局部变量路径)同一口径——必须在翻译 RHS 之前写入
# CurAssignAnnot/CurAssignNode。四处(主分支 / 本 global 分支 /
# 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步,否则
# `x: int = c.Deref(p)` 的读取宽度与 `s: Stack[int] = Stack()`
# 的类型实参推断都会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans, aa_glob_ty)
if rhs_val is not None:
# 对象类全局(有静态 storage):对象构造/赋值 → 绑定 storage + 复制对象
stg_ref_aa: llvmlite.Value | t.CPtr = None
if nm.id is not None:
stg_key_aa: str = pool.alloc(sizet(string.strlen(nm.id)) + 8)
if stg_key_aa is not None:
viperlib.snprintf(stg_key_aa, sizet(string.strlen(nm.id)) + 8, "%s.__stg", nm.id)
stg_ref_aa = self.Gen.VarGen.lookup_module_var(trans.SymTab, stg_key_aa)
if self._is_addrlike_call(aa.value) == 0 \
and stg_ref_aa is not None and rhs_val.Ty is not None \
and stg_ref_aa.Ty is not None and stg_ref_aa.Ty.Pointee is not None:
stg_ty_aa: llvmlite.LLVMType | t.CPtr = stg_ref_aa.Ty.Pointee
rhs_inner_aa: llvmlite.LLVMType | t.CPtr = None
match rhs_val.Ty:
case llvmlite.LLVMType.Ptr(pointee_aa):
rhs_inner_aa = pointee_aa
case _:
pass
if rhs_inner_aa is not None and rhs_inner_aa.kind() == llvmlite.LLVMType.Struct \
and self._stg_ty_equal(pool, stg_ty_aa, rhs_inner_aa) != 0:
llvmlite.build_store(builder, stg_ref_aa, mod_alloca)
stg_obj_val_aa: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, stg_ty_aa, rhs_val)
if stg_obj_val_aa is not None:
llvmlite.build_store(builder, stg_obj_val_aa, stg_ref_aa)
# 清除赋值上下文(防残留):与主分支清空时机同款——
# 提前 return 前必须清,否则泄漏到后续语句
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
target_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
target_ty = mod_alloca.Ty.Pointee
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
llvmlite.build_store(builder, rhs_val, mod_alloca)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续 while 条件/比较等处的 c.Deref 被误判读取宽度
# (i8* 按 i32* 读 4 字节),也会污染裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
# nonlocal 变量:通过闭包 env 写入
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
if aa.value is not None:
# P1-A:先把 nonlocal 槽位指针取出(原先在 RHS 翻译之后),以便把
# 槽位类型作为 target_ty 传入;槽位/类型缺失时直接返回,不再发射
# 无用的 RHS 死值。
nl_ptr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
trans, nm.id)
# 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr is not None and nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支、下方 mod_glob 分支同一口径——必须在
# 翻译 RHS 之前写入 CurAssignAnnot/CurAssignNode。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步,否则
# `nonlocal x: int = c.Deref(p)` 的读取宽度与
# `nonlocal s: Stack[int] = Stack()` 的类型实参推断都会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans, nl_target_ty)
if rhs_val is not None:
if nl_ptr is not None:
if nl_target_ty is None:
# fast-fail: nl_ptr 类型信息丢失,无法安全 store
VLogger.error(
f"nonlocal 变量类型信息丢失,无法写入: {nm.id}",
"HandlesAnnAssign")
# 清除赋值上下文(防残留):与主分支清空时机同款——
# 提前 return 前必须清,否则泄漏到后续语句
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
rhs_coerced: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, nl_target_ty, node)
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步清除,否则残留注解会让
# 后续 while 条件/比较等处的 c.Deref 被误判读取宽度(i8* 按 i32* 读 4
# 字节),也会污染裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
# 模块级全局变量(由 handle_module_level_var 注册到模块作用域)
# Python 语义:仅模块级代码(Current == Root)时修改全局变量;
# 函数内无 global 声明时应创建局部变量(走后面的 get_or_create_sym 路径)。
if self.Gen.VarGen.is_module_scope(trans.SymTab) != 0:
mod_glob: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(
trans.SymTab, nm.id)
else:
mod_glob = None
if mod_glob is not None:
if aa.value is not None:
# 列表字面量 → 逐元素 GEP + store 初始化数组
if aa.value.kind() == ast.ASTKind.List:
self._init_global_array_from_list(
builder, pool, mod, mod_glob, aa.value, trans)
else:
# P1-A:模块级全局目标类型作为 target_ty(同上方 global 分支)
mg_val_ty: llvmlite.LLVMType | t.CPtr = None
if mod_glob.Ty is not None:
mg_val_ty = mod_glob.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支同一口径——翻译 RHS 前写入
# CurAssignAnnot/CurAssignNode。模块级 `s: Stack[int] = Stack()`
# 走的就是本分支,四处(主分支 / global / 本 mod_glob 分支 /
# 上方 nonlocal 分支)必须同步。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val_mg: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans, mg_val_ty)
if rhs_val_mg is not None:
# 对象类全局(有静态 storage):对象构造/赋值 → 绑定 storage + 复制对象
stg_ref_mg: llvmlite.Value | t.CPtr = None
if nm.id is not None:
stg_key_mg: str = pool.alloc(sizet(string.strlen(nm.id)) + 8)
if stg_key_mg is not None:
viperlib.snprintf(stg_key_mg, sizet(string.strlen(nm.id)) + 8, "%s.__stg", nm.id)
stg_ref_mg = self.Gen.VarGen.lookup_module_var(trans.SymTab, stg_key_mg)
if self._is_addrlike_call(aa.value) == 0 \
and stg_ref_mg is not None and rhs_val_mg.Ty is not None \
and stg_ref_mg.Ty is not None and stg_ref_mg.Ty.Pointee is not None:
stg_ty_mg: llvmlite.LLVMType | t.CPtr = stg_ref_mg.Ty.Pointee
rhs_inner_mg: llvmlite.LLVMType | t.CPtr = None
match rhs_val_mg.Ty:
case llvmlite.LLVMType.Ptr(pointee_mg):
rhs_inner_mg = pointee_mg
case _:
pass
if rhs_inner_mg is not None and rhs_inner_mg.kind() == llvmlite.LLVMType.Struct \
and self._stg_ty_equal(pool, stg_ty_mg, rhs_inner_mg) != 0:
llvmlite.build_store(builder, stg_ref_mg, mod_glob)
stg_obj_val_mg: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, stg_ty_mg, rhs_val_mg)
if stg_obj_val_mg is not None:
llvmlite.build_store(builder, stg_obj_val_mg, stg_ref_mg)
# 清除赋值上下文(防残留):与主分支清空时机同款,
# 提前 return 前必须清,否则泄漏到后续语句
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
target_ty_mg: llvmlite.LLVMType | t.CPtr = None
if mod_glob.Ty is not None:
target_ty_mg = mod_glob.Ty.Pointee
if target_ty_mg is not None:
rhs_val_mg = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node)
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 上方 global 分支 / 本 mod_glob 分支 / 上方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续语句的 c.Deref 被误判读取宽度,并污染裸名泛型
# 构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
# 单参 t.CArray[elem_ty] + 列表字面量 → 按静态数组处理(count 从字面量长度推断)
# 与 PreScan 推断一致:get_or_create_sym 复用 PreScan 创建的 alloca,
# 类型一致才能命中局部数组快路径,避免 _find_pool_var 兜底引用 @_mbuddy。
var_ty = self._infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty)
# [D31 fail-fast] 泛型注解退化成裸基类 → 立即终止(暴露退化点)
self._fail_on_degenerated_generic_annotation(aa, nm.id, annot, var_ty)
# 普通局部变量
# 创建 alloca
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.get_or_create_sym(
trans.SymTab, pool, builder, nm.id, var_ty)
if alloca is None:
# fail-fast: 存储创建失败,立即终止(禁止静默跳过)
self.Gen.TypeGenErr.fatal_error(
aa,
f"无法为变量 {nm.id} 创建存储",
"TypeError")
# 复用到的 alloca 类型必须与本次注解解析类型一致:get_or_create_sym 只用
# 「名字 + 作用域」复用(ty 不参与判定,见 VarGenMixin),可能命中类型不同的
# 旧 alloca(同名重声明 / 退化 alloca,如裸 list* 对上 list[ast.ASTptr]*)。
# 只比较「双方都是指针且 pointee 都是结构体」:结构体名不同即 fail-fast(不兜底)。
if alloca is not None and alloca.Ty is not None and var_ty is not None:
av_pt_d: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if av_pt_d is not None and self.Gen.ExprGenBase.is_ptr_type(av_pt_d) != 0 \
and self.Gen.ExprGenBase.is_ptr_type(var_ty) != 0:
av_in: llvmlite.LLVMType | t.CPtr = av_pt_d.Pointee
vt_in: llvmlite.LLVMType | t.CPtr = var_ty.Pointee
if av_in is not None and vt_in is not None:
av_nm_d: str = llvmlite.get_struct_name(av_in)
vt_nm_d: str = llvmlite.get_struct_name(vt_in)
if av_nm_d is not None and vt_nm_d is not None \
and string.strcmp(av_nm_d, vt_nm_d) != 0:
# 仅当旧 alloca 的 pointee 是裸泛型模板基类(丢类型实参,如裸
# list / 裸 GSList)而注解是另一个类型时 fail-fast(后续方法调用
# 会按错误元素类型特化 → 截断);不同具体类(如 Tuple* vs AST*)
# 属合法的同名重声明换类型(都是 8 字节指针槽),不拦截。
av_pf: int = GenApi.sha1_prefix_len(av_nm_d)
av_short: str = av_nm_d + av_pf if av_pf != 0 else av_nm_d
if string.strchr(av_short, '[') is None:
av_tpl: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(av_short)
if av_tpl is not None:
self.Gen.TypeGenErr.fatal_error(
aa,
f"变量 '{nm.id}' 复用到的存储类型为裸泛型基类 '{av_nm_d}*'"
f"(丢了类型实参),与注解解析类型 '{vt_nm_d}*' 不一致;"
f"禁止静默混用——方法调用会按错误元素类型特化并截断指针",
"TypeError")
# FakeDuck: str 变量创建影子结构体 {var}__meta__(per-instance 字段存储)
# 仅裸 str 注解(Name("str"))且 _str 已注册时创建;bytes / char|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 = self.Gen.StructGen.get_struct_type("_str")
if fd_str_ty is not None:
# 确保 _str 完整类型定义在当前模块可用:跨模块结构体默认 opaque,
# 直接 alloca opaque 类型 → llc "Cannot allocate unsized type"
self.Gen.StructGen.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 = self.Gen.VarGen.get_or_create_sym(
trans.SymTab, pool, builder, fd_meta_name, fd_str_ty)
if fd_meta_alloca is None:
# fail-fast: 影子存储创建失败,立即终止
self.Gen.TypeGenErr.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 = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
annot, trans._imported_modules, pool, trans._from_imports)
if cls_nm_hd is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, nm.id, cls_nm_hd)
# 检查是否为 bytes|t.CPtr 或 str|t.CPtr(i8** 语义,8 字节步长)
if self._is_ptr_element_annotation(annot) != 0:
self.Gen.VarGen.set_var_ptr_element(
trans.SymTab, nm.id)
new_vars: int = 0
existing: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current(
trans.SymTab, nm.id)
if existing is None:
new_vars = 1
# 如果有初始值,store
if aa.value is not None:
# 协程(TPV-M1):记录 async 函数调用赋值的变量名(coro.coro_run
# 内联翻译反查帧类型用)。对齐 TPC HandlesAnnAssign.py L319-326:
# value 是 Call 且 func 是 Name 且该名字是 async 函数时,
# 登记 _coro_var_async[target] = callee。
if aa.value.kind() == ast.ASTKind.Call:
val_cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(aa.value)
if val_cl.func is not None and val_cl.func.kind() == ast.ASTKind.Name:
val_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(val_cl.func)
if val_nm.id is not None:
if self.Gen.AsyncGen.is_async_func(trans, val_nm.id) != 0:
self.Gen.AsyncGen.register_coro_var(trans, nm.id, val_nm.id)
# c.Addr(async_call):tt: Coroutine = c.Addr(task()) 就地解读
elif val_cl.func is not None and val_cl.func.kind() == ast.ASTKind.Attribute:
val_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(val_cl.func)
if val_at.attr is not None and string.strcmp(val_at.attr, "Addr") == 0 \
and val_cl.args is not None and len(val_cl.args) >= 1:
icall: ast.AST | t.CPtr = val_cl.args.get(0)
if icall is not None and icall.kind() == ast.ASTKind.Call:
icl2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(icall)
if icl2.func is not None and icl2.func.kind() == ast.ASTKind.Name:
inm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(icl2.func)
if inm2.id is not None:
if self.Gen.AsyncGen.is_async_func(trans, inm2.id) != 0:
self.Gen.AsyncGen.register_coro_var(trans, nm.id, inm2.id)
# 局部数组:列表字面量 → 先整体置零,再逐元素 GEP + store 初始化。
# 不走 translate_list_literal(避免误触发 list[T] 泛型特化)。
local_arr_ty: llvmlite.LLVMType | t.CPtr = None
if aa.value.kind() == ast.ASTKind.List and alloca.Ty is not None:
pt_l: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if self.Gen.ExprGenBase.is_array_type(pt_l) != 0:
local_arr_ty = pt_l
if local_arr_ty is not None:
zero_arr: llvmlite.Value | t.CPtr = llvmlite.ConstZero(
pool, local_arr_ty)
if zero_arr is not None:
llvmlite.build_store(builder, zero_arr, alloca)
self._init_global_array_from_list(
builder, pool, mod, alloca, aa.value, trans)
else:
# 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → 读 i32
# CurAssignNode 记录 RHS 节点:translate_c_deref 据此判定 Deref
# 是否为赋值直接 RHS(对齐 TPC _infer_deref_target_type)。
# 注意:本主分支(普通局部变量)、上方 global 分支、mod_glob 分支、
# nonlocal 分支四处的 set/clear 必须同步(模块级 AnnAssign 走
# mod_glob;闭包内 AnnAssign 走 nonlocal)。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
# P1-A:局部变量 alloca 的 pointee 类型作为 target_ty(同全局分支),
# 使 `x: uint64 = 12345` 直接物化 i64 常量,与 `x: uint64 = u64(12345)` 一致。
lv_target_ty: llvmlite.LLVMType | t.CPtr = None
if alloca.Ty is not None:
lv_target_ty = alloca.Ty.Pointee
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans, lv_target_ty)
if rhs_val is not None:
# 注解退化修复(对齐 TPC 从构造结果推断类型):无实参泛型模板注解
# 会让 alloca 退化为 Ptr(i8),而 rhs 构造返回精确结构体指针 → 用 rhs
# 精确类型重建 alloca 并更新符号表,使方法调用按精确特化类名解析;
# 旧退化 alloca 残留为 entry 块死代码(文本 IR 即时生成无法删除,无害)。
precise_rhs_ty: llvmlite.LLVMType | t.CPtr = None
if rhs_val.Ty is not None:
match rhs_val.Ty:
case llvmlite.LLVMType.Ptr(inner_struct):
match inner_struct:
case llvmlite.LLVMType.Struct(_, _, _):
precise_rhs_ty = rhs_val.Ty
case _:
pass
case _:
pass
if precise_rhs_ty is not None and alloca.Ty is not None:
alloca_pt: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
# 禁止「降级重建」:RHS 的 pointee 若是裸泛型基类(丢类型实参,
# 如 {sha1}.list / {sha1}.GSList),以它重建会把注解已解析出的
# 带类型实参特化覆盖成裸基类 → .get() 按错误元素类型分派 →
# inttoptr 截断崩溃。故仅 RHS 不是裸泛型基类时才执行重建
# (本重建本意是注解退化为裸基类/i8*、RHS 精确的反向升级)。
# 取注解侧泛型基名(Base ∈ list/dict/...,仅 Name 形态的
# Subscript 一侧),用于下方不依赖泛型模板注册表的方向判定。
ann_gen_base: str = None
if annot is not None:
ag_side: ast.AST | t.CPtr = annot
if ag_side.kind() == ast.ASTKind.BinOp:
ag_bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(ag_side)
if ag_bop.op == ast.OpKind.BitOr:
if ag_bop.left is not None and ag_bop.left.kind() == ast.ASTKind.Subscript:
ag_side = ag_bop.left
elif ag_bop.right is not None and ag_bop.right.kind() == ast.ASTKind.Subscript:
ag_side = ag_bop.right
if ag_side.kind() == ast.ASTKind.Subscript:
ag_sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(ag_side)
if ag_sub.value is not None and ag_sub.value.kind() == ast.ASTKind.Name:
ag_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ag_sub.value)
if ag_nm.id is not None:
ann_gen_base = ag_nm.id
rb_is_bare_generic: int = 0
if alloca_pt is not None \
and uint64(alloca_pt) != uint64(precise_rhs_ty):
rhs_pt_rb: llvmlite.LLVMType | t.CPtr = precise_rhs_ty.Pointee
if rhs_pt_rb is not None:
rb_nm: str = llvmlite.get_struct_name(rhs_pt_rb)
if rb_nm is not None:
rb_pf: int = GenApi.sha1_prefix_len(rb_nm)
rb_short: str = rb_nm + rb_pf if rb_pf != 0 else rb_nm
if string.strchr(rb_short, '[') is None:
# ① 裸名命中泛型模板注册表(list/GSList/...)
if self.Gen.ClassGenGeneric._find_generic_template(rb_short) is not None:
rb_is_bare_generic = 1
# ② 与注解同基名:注解是 `Base[...]`(带类型实参,
# 已比 RHS 的裸 Base 精确)⇒ 本次重建方向是**降级**,
# 必须禁止。此判定不依赖泛型模板注册表(list/dict 等
# 内建容器未登记时①会漏判,②仍能命中)。
elif ann_gen_base is not None \
and string.strcmp(rb_short, ann_gen_base) == 0:
rb_is_bare_generic = 1
if alloca_pt is not None and \
uint64(alloca_pt) != uint64(precise_rhs_ty) \
and rb_is_bare_generic == 0:
new_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(
builder, precise_rhs_ty)
if new_alloca is not None:
v_entry_rb: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if v_entry_rb is not None:
v_entry_rb.Alloca = new_alloca
alloca = new_alloca
# 类型转换:将 rhs_val 转换为 alloca 的 pointee 类型
# 修复:整数字面量是 i32,但变量可能是 i8/i16/i64,需 trunc/sext
target_ty: llvmlite.LLVMType | t.CPtr = None
if alloca.Ty is not None:
target_ty = alloca.Ty.Pointee
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
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: GenTypes.FieldEntry | t.CPtr = \
self.Gen.StructGen.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 字节)。
# 四处(本主分支 / global / mod_glob / nonlocal)必须同步清除。
trans.CurAssignAnnot = None
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: GenTypes.FieldEntry | t.CPtr = \
self.Gen.StructGen.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__)。
# 跨 Mixin 经 self.Gen 调用 CallGenFd._inject_requires_fields。
self.Gen.CallGenFd._inject_requires_fields(
trans, pool, builder, mod, fd_meta_alloca, "_str")
return 0
# ============================================================
# translate_assign - 处理 Assign 语句,返回新增变量数(0 或 1)
#
# 迁移自 HandlesAssign.AssignHandle.Handle(对应 TransPyC
# AssignHandle._HandleAssignLlvm)。共享状态从 trans 获取。
# ============================================================
def translate_assign(self, trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""翻译 Assign 语句,返回新增变量数(0 或 1)"""
asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
if asgn is None:
VLogger.error("cast failed", "ASGN")
return 0
targets: list[ast.AST | t.CPtr] | t.CPtr = asgn.targets
if targets is None:
VLogger.error("targets is None", "ASGN")
return 0
# 从 trans 取共享状态(替代 11 个参数)
pool: rowptr[memhub.MemBuddy] = trans.Pool
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
mod: llvmlite.LLVMModule | t.CPtr = trans.Module
# 翻译 RHS 值
# 无注解赋值无目标类型可推断,清空 CurAssignAnnot/CurAssignNode
# (防残留误判 c.Deref 宽度)
trans.CurAssignAnnot = None
trans.CurAssignNode = None
# P1-A(上下文位宽传播):单 Name 目标且其存储类型已知时,把存储类型作为
# target_ty 传入,使 `x = 12345`(x: uint64)与 `x = u64(12345)` 一致
# (直接 i64 常量,省去 coerce 生成的 zext)。多目标/非 Name 目标无法给出
# 唯一目标类型(且 RHS 只翻译一次供全部目标共用)⇒ 维持原行为(None)。
asg_target_ty: llvmlite.LLVMType | t.CPtr = None
if len(targets) == 1:
asg_t0: ast.AST | t.CPtr = targets.get(0)
if asg_t0 is not None and asg_t0.kind() == ast.ASTKind.Name:
asg_nm0: ast.Name | t.CPtr = (ast.Name | t.CPtr)(asg_t0)
if asg_nm0.id is not None:
asg_slot: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, asg_nm0.id)
if asg_slot is None:
asg_slot = self.Gen.VarGen.lookup_module_var(
trans.SymTab, asg_nm0.id)
if asg_slot is not None and asg_slot.Ty is not None:
asg_target_ty = asg_slot.Ty.Pointee
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, asgn.value, None, 0, trans, asg_target_ty)
if rhs_val is None:
# 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位
if asgn.value is not None:
sha1: str = trans.ModuleSha1
val_kind: int = asgn.value.kind()
val_line: int = asgn.value.lineno
# 通过 sha1 查找文件名(人类可读)
rel_path: str = None
if sha1 is not None:
rel_path = StubMergerStore.LookupSha1RelPath(sha1)
# 根据 AST 节点类型提取详情
if val_kind == ast.ASTKind.Attribute:
at_nv: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(asgn.value)
attr_nm: str = at_nv.attr
target_nm: str = "?"
if at_nv.value is not None:
if at_nv.value.kind() == ast.ASTKind.Name:
tn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_nv.value)
target_nm = tn_nm.id
if rel_path is not None:
VLogger.error(f"赋值右侧为 None: {rel_path}:{val_line} '{target_nm}.{attr_nm}' 属性访问失败", "ASGN")
else:
VLogger.error(f"赋值右侧为 None: sha1={sha1}:{val_line} '{target_nm}.{attr_nm}' 属性访问失败", "ASGN")
elif val_kind == ast.ASTKind.Name:
nm_nv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(asgn.value)
if rel_path is not None:
VLogger.error(f"赋值右侧为 None: {rel_path}:{val_line} 变量 '{nm_nv.id}' 未找到", "ASGN")
else:
VLogger.error(f"赋值右侧为 None: sha1={sha1}:{val_line} 变量 '{nm_nv.id}' 未找到", "ASGN")
elif val_kind == ast.ASTKind.Call:
cl_nv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(asgn.value)
func_nm: str = "?"
if cl_nv.func is not None:
if cl_nv.func.kind() == ast.ASTKind.Name:
fn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cl_nv.func)
func_nm = fn_nm.id
if rel_path is not None:
VLogger.error(f"赋值右侧为 None: {rel_path}:{val_line} 调用 '{func_nm}(...)' 返回 None", "ASGN")
else:
VLogger.error(f"赋值右侧为 None: sha1={sha1}:{val_line} 调用 '{func_nm}(...)' 返回 None", "ASGN")
else:
if rel_path is not None:
VLogger.error(f"赋值右侧为 None: {rel_path}:{val_line} [kind={val_kind}]")
else:
VLogger.error(f"赋值右侧为 None: sha1={sha1}:{val_line} [kind={val_kind}]")
else:
VLogger.error("赋值右侧为 None (asgn.value 为空)", "ASGN")
return 0
new_vars: int = 0
tn: sizet = len(targets)
for ti in range(tn):
target: ast.AST | t.CPtr = targets.get(ti)
if target is None:
continue
tk: int = target.kind()
# Tuple 赋值: a, b = f() 多返回值解包
# rhs_val 是匿名 struct 值(函数返回 tuple[T1,T2] 打包)→ alloca +
# store + GEP 逐字段 load → store 到各 Name 目标。
if tk == ast.ASTKind.Tuple:
tgt_tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(target)
tgt_elts: list[ast.AST | t.CPtr] | t.CPtr = tgt_tup.elts
# 守卫:rhs 不是 struct(tuple 打包)时跳过本 target
if tgt_elts is None or rhs_val is None or rhs_val.Ty is None:
continue
match rhs_val.Ty:
case llvmlite.LLVMType.Struct(_, _, _):
tgt_n: sizet = len(tgt_elts)
tmp_s: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, rhs_val.Ty)
if tmp_s is not None:
llvmlite.build_store(builder, rhs_val, tmp_s)
fchain: llvmlite.ParamNode | t.CPtr = llvmlite.get_struct_fields(rhs_val.Ty)
fcur: llvmlite.ParamNode | t.CPtr = fchain
fti: sizet
for fti in range(tgt_n):
tgt_elt: ast.AST | t.CPtr = tgt_elts.get(fti)
if tgt_elt is None or fcur is None:
break
fty: llvmlite.LLVMType | t.CPtr = llvmlite.paramnode_get_ty(fcur)
fcur = llvmlite.paramnode_get_next(fcur)
if fty is None:
# fail-fast: 字段类型缺失,立即终止
self.Gen.TypeGenErr.fatal_error(target, "元组解包字段类型缺失", "TypeError")
# 守卫:非 Name 元素无法解包,跳过
if tgt_elt.kind() != ast.ASTKind.Name:
continue
tgt_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tgt_elt)
if tgt_nm2.id is None:
continue
fp2: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, rhs_val.Ty, fty, tmp_s, fti)
if fp2 is None:
continue
fv2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, fty, fp2)
tgt_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, tgt_nm2.id)
if tgt_alloca is None:
tgt_alloca = self.Gen.VarGen.get_or_create_sym(
trans.SymTab, pool, builder, tgt_nm2.id, fty)
if tgt_alloca is None:
continue
sval: llvmlite.Value | t.CPtr = fv2
if tgt_alloca.Ty is not None and tgt_alloca.Ty.Pointee is not None:
sval = self.Gen.ExprGenBase.coerce_to_type(
builder, fv2, tgt_alloca.Ty.Pointee, node, trans)
llvmlite.build_store(builder, sval, tgt_alloca)
case _:
pass
continue
# Subscript 赋值: arr[i] = val / ptr[i] = val / list[i] = val
if tk == ast.ASTKind.Subscript:
# 检查是否是 list[T] 类型的 Subscript(泛型类不注册 struct)
# list 的 subscript 赋值走 __setitem__ 内联路径
list_obj: llvmlite.Value | t.CPtr = self.Gen.ExprGenName.is_list_subscript(
target, trans)
if list_obj is not None:
# list[T] 类型: 内联生成 __setitem__ 逻辑
sub_node: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(target)
if sub_node is not None and sub_node.slice is not None:
list_idx_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_node.slice, None, 0, trans)
if list_idx_val is None:
# fail-fast: list 下标翻译失败,立即终止
self.Gen.TypeGenErr.fatal_error(target, "list 下标翻译失败", "TypeError")
self.Gen.ExprGenName.list_setitem_inline(
builder, pool, list_obj, list_idx_val, rhs_val, target)
continue
# 普通 Subscript 赋值
elem_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenName.get_subscript_ptr(
builder, pool, mod, target, trans)
if elem_ptr is not None:
store_val: llvmlite.Value | t.CPtr = rhs_val
# 守卫短路:elem 类型完整才做类型转换
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, elem_ptr.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, elem_ptr)
continue
# get_subscript_ptr 返回 None: 尝试 __setitem__ 运算符重载
# 适用于自定义类(如 hashtable[key]=val → hashtable.__setitem__(key, val))
sub_asgn: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(target)
setitem_done: int = 0
if sub_asgn is not None and sub_asgn.value is not None:
if sub_asgn.value.kind() == ast.ASTKind.Name:
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_asgn.value)
if sub_nm.id is not None:
sub_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
trans.SymTab, sub_nm.id)
if sub_alloca is not None and sub_alloca.Ty is not None \
and sub_alloca.Ty.Pointee is not None \
and self.Gen.ExprGenBase.is_ptr_type(sub_alloca.Ty) != 0:
sub_pointee: llvmlite.LLVMType | t.CPtr = sub_alloca.Ty.Pointee
cls_nm_set: str = self.Gen.StructGen.get_class_name_by_type(pool, sub_pointee)
obj_val_set: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, sub_pointee, sub_alloca)
# 指针类型变量 (X|t.CPtr): sub_pointee 是 Ptr(Struct),
# 需 load 获取 Ptr(Struct) 再检查 inner struct
if cls_nm_set is None and obj_val_set is not None \
and self.Gen.ExprGenBase.is_ptr_type(sub_pointee) != 0 \
and sub_pointee.Pointee is not None:
cls_nm_set = self.Gen.StructGen.get_class_name_by_type(
pool, sub_pointee.Pointee)
if cls_nm_set is not None and obj_val_set is not None:
key_val_set: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, trans)
if key_val_set is not None:
arg_vals_set: sizet | t.CPtr = pool.alloc(16)
if arg_vals_set is not None:
arg_vals_set[0] = sizet(key_val_set)
arg_vals_set[1] = sizet(rhs_val)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, cls_nm_set, "__setitem__",
obj_val_set, arg_vals_set, 2, trans)
setitem_done = 1
elif sub_asgn.value.kind() == ast.ASTKind.Attribute:
# self._ht[key] = val → self._ht.__setitem__(key, val)
# 通过 get_attribute_ptr 获取字段指针,再调用 __setitem__
field_ptr_set: llvmlite.Value | t.CPtr = self.Gen.ExprGenAttr.get_attribute_ptr(
builder, pool, mod, sub_asgn.value, trans)
if field_ptr_set is not None and field_ptr_set.Ty is not None \
and field_ptr_set.Ty.Pointee is not None \
and self.Gen.ExprGenBase.is_ptr_type(field_ptr_set.Ty) != 0:
field_pointee_set: llvmlite.LLVMType | t.CPtr = field_ptr_set.Ty.Pointee
cls_nm_attr: str = self.Gen.StructGen.get_class_name_by_type(pool, field_pointee_set)
obj_val_attr: llvmlite.Value | t.CPtr = field_ptr_set
# field_ptr 是 Ptr(Ptr(Struct)) (X|t.CPtr 字段):
# load 解引用获取 Ptr(Struct)
if cls_nm_attr is None \
and self.Gen.ExprGenBase.is_ptr_type(field_pointee_set) != 0 \
and field_pointee_set.Pointee is not None:
cls_nm_attr = self.Gen.StructGen.get_class_name_by_type(
pool, field_pointee_set.Pointee)
if cls_nm_attr is not None:
obj_val_attr = llvmlite.build_load(
builder, field_pointee_set, field_ptr_set)
if cls_nm_attr is not None and obj_val_attr is not None:
key_val_attr: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, trans)
if key_val_attr is not None:
arg_vals_attr: sizet | t.CPtr = pool.alloc(16)
if arg_vals_attr is not None:
arg_vals_attr[0] = sizet(key_val_attr)
arg_vals_attr[1] = sizet(rhs_val)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, cls_nm_attr, "__setitem__",
obj_val_attr, arg_vals_attr, 2, trans)
setitem_done = 1
if setitem_done == 0:
# 通用 fallback: 翻译 sub.value 并尝试 __setitem__ 或直接 GEP+store
# 处理 Name/Attribute 之外的节点以及 SymTab 查找失败的情况
if sub_asgn is not None and sub_asgn.value is not None:
obj_val_gen: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_asgn.value, None, 0, trans)
key_val_gen: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, sub_asgn.slice, None, 0, trans)
if obj_val_gen is not None and key_val_gen is not None \
and obj_val_gen.Ty is not None:
# 内联 _get_custom_struct_cls_nm 逻辑(避免依赖新函数)
cls_nm_gen: str = self.Gen.StructGen.get_class_name_by_type(
pool, obj_val_gen.Ty)
if cls_nm_gen is None and self.Gen.ExprGenBase.is_ptr_type(obj_val_gen.Ty) != 0 \
and obj_val_gen.Ty.Pointee is not None:
cls_nm_gen = self.Gen.StructGen.get_class_name_by_type(
pool, obj_val_gen.Ty.Pointee)
if cls_nm_gen is not None:
# 自定义结构体: 调用 __setitem__
arg_vals_gen: sizet | t.CPtr = pool.alloc(16)
if arg_vals_gen is not None:
arg_vals_gen[0] = sizet(key_val_gen)
arg_vals_gen[1] = sizet(rhs_val)
self.Gen.CallGenCall._call_method_on_ptr(
pool, builder, mod, cls_nm_gen, "__setitem__",
obj_val_gen, arg_vals_gen, 2, trans)
setitem_done = 1
elif self.Gen.ExprGenBase.is_ptr_type(obj_val_gen.Ty) != 0 \
and obj_val_gen.Ty.Pointee is not None:
# 非自定义结构体: 直接 GEP + store
# 处理 get_subscript_ptr 因边界情况返回 None 的指针下标
elem_gen: llvmlite.LLVMType | t.CPtr = obj_val_gen.Ty.Pointee
store_val_gen: llvmlite.Value | t.CPtr = rhs_val
elem_ptr_gen: llvmlite.Value | t.CPtr = None
match elem_gen:
case llvmlite.LLVMType.Array(arr_elem_gen, _):
elem_ptr_gen = llvmlite.build_gep_array(
builder, elem_gen, arr_elem_gen,
obj_val_gen, key_val_gen)
case _:
elem_ptr_gen = llvmlite.build_gep(
builder, elem_gen, obj_val_gen, key_val_gen)
if elem_ptr_gen is not None and elem_ptr_gen.Ty is not None:
if elem_ptr_gen.Ty.Pointee is not None:
store_val_gen = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, elem_ptr_gen.Ty.Pointee, node)
llvmlite.build_store(builder, store_val_gen, elem_ptr_gen)
setitem_done = 1
if setitem_done == 0:
self.Gen.TypeGenErr.fatal_error(target, "subscript ptr is None")
continue
# Attribute 赋值: obj.field = val
if tk == ast.ASTKind.Attribute:
# property setter 拦截: obj.attr = v 且 attr 是 @x.setter 方法时,
# 生成 call Class.attr$set(obj, v),跳过字段存储
# 注意参数顺序:_try_property_setter(pool, builder, mod, ...)。
if self.Gen.CallGenProp._try_property_setter(
pool, builder, mod, target, rhs_val, 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 = self.Gen.VarGen.lookup_current(
trans.SymTab, fd_meta_w)
if fd_meta_vw is not None:
fd_sty_w: llvmlite.LLVMType | t.CPtr = \
self.Gen.StructGen.get_struct_type("_str")
if fd_sty_w is not None:
fd_fe_w: GenTypes.FieldEntry | t.CPtr = \
self.Gen.StructGen.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 = \
self.Gen.ExprGenBase.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 方法时直接生成槽位写入并跳过字段路径。
if self.Gen.CallGenVt._write_vtable_attr(
builder, pool, mod, target, rhs_val, trans) == 1:
continue
field_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenAttr.get_attribute_ptr(
builder, pool, mod, target, trans)
if field_ptr is not None:
# 获取字段类型,对 rhs_val 进行类型转换(如 i32 → i64)
store_val: llvmlite.Value | t.CPtr = rhs_val
if field_ptr.Ty is not None and field_ptr.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, field_ptr.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, field_ptr)
continue
# 守卫:field_ptr 为 None → 构造详细错误信息
attr_node: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(target)
attr_name: str = "(unknown)"
obj_name: str = "(unknown)"
if attr_node is not None:
attr_name = attr_node.attr
if attr_node.value is not None and attr_node.value.kind() == ast.ASTKind.Name:
obj_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(attr_node.value)
obj_name = obj_nm.id
err_buf: str = pool.alloc(256)
if err_buf is not None:
viperlib.snprintf(err_buf, 256,
"attribute ptr is None: %s.%s",
obj_name, attr_name)
self.Gen.TypeGenErr.fatal_error(target, err_buf)
else:
self.Gen.TypeGenErr.fatal_error(target, "attribute ptr is None")
continue
# Name 赋值: var = val
if tk == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(target)
# 守卫:target 名缺失时跳过
if nm.id is None:
continue
# global 变量:写入模块作用域中的全局变量
if GenApi.is_global_name(trans, nm.id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(
trans.SymTab, nm.id)
if mod_alloca is None:
# fail-fast: global 变量未注册,无法赋值
self.Gen.TypeGenErr.fatal_error(
target,
f"global 变量 '{nm.id}' 未注册,无法赋值",
"NameError")
# 对象类全局(裸类名注解 → 静态 storage):对象构造/赋值 → 引用绑定
# 到 storage + 复制对象到 storage;否则 store 栈 alloca 指针悬垂,
# 或模块级构造初始化器被丢弃后全局保持 NULL(见 TranslatorGenMixin)。
stg_ref_as: llvmlite.Value | t.CPtr = None
if nm.id is not None:
stg_key_as: str = pool.alloc(sizet(string.strlen(nm.id)) + 8)
if stg_key_as is not None:
viperlib.snprintf(stg_key_as, sizet(string.strlen(nm.id)) + 8, "%s.__stg", nm.id)
stg_ref_as = self.Gen.VarGen.lookup_module_var(trans.SymTab, stg_key_as)
if self._is_addrlike_call(asgn.value) == 0 \
and stg_ref_as is not None and rhs_val is not None and rhs_val.Ty is not None \
and stg_ref_as.Ty is not None and stg_ref_as.Ty.Pointee is not None:
stg_ty_as: llvmlite.LLVMType | t.CPtr = stg_ref_as.Ty.Pointee
rhs_inner_as: llvmlite.LLVMType | t.CPtr = None
match rhs_val.Ty:
case llvmlite.LLVMType.Ptr(pointee_as):
rhs_inner_as = pointee_as
case _:
pass
if rhs_inner_as is not None and rhs_inner_as.kind() == llvmlite.LLVMType.Struct \
and self._stg_ty_equal(pool, stg_ty_as, rhs_inner_as) != 0:
# 1) @g 引用绑定到 storage(丢弃临时 alloca,避免悬垂)
llvmlite.build_store(builder, stg_ref_as, mod_alloca)
# 2) 复制构造结果(临时对象)到 storage(函数内复制,alloca 仍有效)
stg_obj_val_as: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, stg_ty_as, rhs_val)
if stg_obj_val_as is not None:
llvmlite.build_store(builder, stg_obj_val_as, stg_ref_as)
continue
target_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
target_ty = mod_alloca.Ty.Pointee
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
llvmlite.build_store(builder, rhs_val, mod_alloca)
continue
# nonlocal 变量:通过闭包 env 写入
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
nl_ptr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
trans, nm.id)
if nl_ptr is None:
# fail-fast: nonlocal 变量指针解析失败
self.Gen.TypeGenErr.fatal_error(
target,
f"nonlocal 变量 '{nm.id}' 指针解析失败",
"NameError")
# 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
if nl_target_ty is None:
# fail-fast: nl_ptr 类型信息丢失,无法安全 store
self.Gen.TypeGenErr.fatal_error(
target,
f"nonlocal 变量 '{nm.id}' 类型信息丢失,无法写入",
"TypeError")
rhs_coerced: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, nl_target_ty, node, trans)
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
continue
# 拦截:函数内对模块级全局变量赋值但没 global 声明
# 否则静默创建局部变量遮蔽全局,全局保持 null 导致运行时崩溃
# (_mbuddy = mb 缺 global 声明即此类 bug:Int32 解引用 null 崩溃)
if self.Gen.VarGen.is_module_scope(trans.SymTab) == 0:
mod_gv: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(
trans.SymTab, nm.id)
if mod_gv is not None:
self.Gen.TypeGenErr.fatal_error(target, f"变量 '{nm.id}' 是模块级全局变量,函数内赋值需用 global 声明(否则创建局部变量遮蔽全局,导致全局保持 null 崩溃)", "SyntaxError")
# 普通局部变量
alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.get_or_create_sym(
trans.SymTab, pool, builder, nm.id, rhs_val.Ty)
if alloca is not None:
# 无注解赋值:从 RHS 表达式推断类名设置变量注解
# (联合类型 X|t.CPtr 简化为 Ptr(i8),属性访问按需过滤需要
# AnnotClassName 才能定位结构体字段)
cls_inf: str = self._infer_rhs_annot(pool, mod, asgn.value, trans, rhs_val)
if cls_inf is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, nm.id, cls_inf)
# 按 alloca 类型对值进行转换(如 double → float)
store_val: llvmlite.Value | t.CPtr = rhs_val
if alloca.Ty is not None and alloca.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, alloca.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, alloca)
# lambda 表达式赋给变量:注册为闭包变量名,
# 调用分派(is_closure_name)才走闭包间接调用路径;
# 否则 i8** alloca 被 _is_func_ptr_alloca 误判为
# 函数指针,无参 call 直接跳闭包数据崩溃
if asgn.value is not None \
and asgn.value.kind() == ast.ASTKind.Lambda:
self.Gen.CallGenLeafKit.register_closure_name(nm.id)
existing: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_current(
trans.SymTab, nm.id)
if existing is None:
new_vars += 1
else:
# fail-fast: 变量存储创建失败,立即终止
self.Gen.TypeGenErr.fatal_error(
target,
f"变量 '{nm.id}' 存储创建失败",
"TypeError")
return new_vars
# ============================================================
# _infer_rhs_annot - 从 RHS 表达式推断变量注解类名
#
# 无注解赋值(如 v = Vec2(...) / v3 = v1 + v2)时,变量 AnnotClassName
# 缺失会导致后续属性访问(.x)无法"按需过滤":联合类型 X|t.CPtr 在 LLVM
# 中简化为 Ptr(i8),不携带类名,无法定位结构体字段(字段未找到 → fast-fail)。
# 此函数按 RHS 结构推断类名:
# Call → 类实例化(func 为已注册结构体名才采用,避免普通函数误标);
# func 为 Attribute 时是方法调用(a.cross()),obj 类名 +
# 方法存在 + rhs_val 为指针类型(联合对象)三条件判定
# Name → 源变量的 AnnotClassName
# BinOp/UnaryOp → lhs 类名 + 该类存在对应 dunder 方法(运算符重载返回同类对象)
# Attribute → 字段注解类名(委托 Generator 单例 _resolve_attr_field_class)
# 仅当目标为已注册结构体时采用,与"按需过滤,找不到 fast-fail"原则一致。
# ============================================================
def _infer_rhs_annot(self, pool: rowptr[memhub.MemBuddy],
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr = None) -> str:
"""从 RHS 表达式推断类名,无法推断返回 None"""
if node is None or trans is None:
return None
k: int = node.kind()
# Call: 类实例化 / 方法调用
if k == ast.ASTKind.Call:
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl is None or cl.func is None:
return None
if cl.func.kind() == ast.ASTKind.Name:
# 类实例化:func 为已注册结构体名才采用
cand: str = ((ast.Name | t.CPtr)(cl.func)).id
if cand is None:
return None
se: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(cand)
if se is not None:
return cand
return None
if cl.func.kind() == ast.ASTKind.Attribute:
# 方法调用 a.cross(b):从 vpi 返回注解缓存精确解析返回类名
# (方法可返回非同类对象,如 Transform.world_position → 'Vec3',
# 启发式取 obj 类名会 GEP 错字段)。vpi 未命中时返回 None,
# 由后续属性访问 fast-fail 暴露 vpi 缺失根因(不静默回退)。
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if ca is None or ca.attr is None or ca.value is None:
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 跨模块返回类型解析一致)。
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 = self.Gen.CallGenBase._lookup_module_sha1(
mod_nm_rhs.id)
if mod_sha1_rhs is not None:
temp_dir_rhs: str = None
if self.Gen.CallGenBase.TempDir is not None:
temp_dir_rhs = self.Gen.CallGenBase.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
# 方法定义所在模块 SHA1(优先 struct entry,回退当前模块 sha1)
cls_sha1: str = self.Gen.StructGen.get_struct_sha1(obj_cls)
if cls_sha1 is None and trans.ModuleSha1 is not None:
cls_sha1 = trans.ModuleSha1
ret_cls: str = None
if cls_sha1 is not None:
temp_dir: str = None
if self.Gen.CallGenBase.TempDir is not None:
temp_dir = self.Gen.CallGenBase.TempDir
if temp_dir is not None:
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir)
annot_ret: str = VpiReader._lookup_vpi_ret_annot(
cls_sha1, obj_cls, ca.attr)
if annot_ret is not None:
ret_cls = self._extract_annot_class_name(pool, annot_ret)
return ret_cls
return None
# Name: 源变量注解
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm is None or nm.id is None:
return None
ve: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if ve is None:
return None
return self.Gen.VarGen.var_annot_class_name(ve)
# BinOp/UnaryOp: 运算符重载返回同类对象
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop is None or bop.left is None:
return None
dunder: str = self._op_to_dunder(bop.op)
if dunder is None:
return None
lhs_cls: str = self._infer_rhs_annot(pool, mod, bop.left, trans)
if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0:
return None
return lhs_cls
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop is None or uop.operand is None:
return None
dunder: str = self._op_to_dunder(uop.op)
if dunder is None:
return None
opr_cls: str = self._infer_rhs_annot(pool, mod, uop.operand, trans)
if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0:
return None
return opr_cls
# Attribute: 字段注解类名(属性链递归,如 p = r.origin)
if k == ast.ASTKind.Attribute:
return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node)
return None
# ============================================================
# _extract_annot_class_name - 从返回注解文本提取结构体类名
#
# vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除
# 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/
# CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合
# 多候选时取首个命中段。
# ============================================================
def _extract_annot_class_name(self, pool: rowptr[memhub.MemBuddy],
annot: str) -> str:
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
if pool is None or annot is None:
return None
alen: sizet = string.strlen(annot)
if alen == 0:
return None
seg: str = pool.alloc(256)
if seg is None:
return None
start: sizet = 0
i: sizet
for i in range(alen):
if annot[i] == '|':
seglen: sizet = i - start
# 段 [start, i):去除首尾空格与引号后查注册表
s: sizet = start
e: sizet = i
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
e -= 1
seglen = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
se: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(seg)
if se is not None:
return seg
start = i + 1
# 最后一段(无 '|' 后缀)
s: sizet = start
e: sizet = alen
while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'):
s += 1
while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'):
e -= 1
seglen: sizet = e - s
if seglen > 0 and seglen < 256:
string.strncpy(seg, annot + s, seglen)
seg[seglen] = '\0'
se: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(seg)
if se is not None:
return seg
return None
# ============================================================
# _op_to_dunder - 运算符 → dunder 方法名(本地副本,避免跨 Mixin 调用)
# ============================================================
def _op_to_dunder(self, op: int) -> str:
"""BinOp/UnaryOp 运算符 → dunder 方法名,无映射返回 None"""
if op == ast.OpKind.Add: return "__add__"
if op == ast.OpKind.Sub: return "__sub__"
if op == ast.OpKind.Mult: return "__mul__"
if op == ast.OpKind.Div: return "__truediv__"
if op == ast.OpKind.FloorDiv: return "__floordiv__"
if op == ast.OpKind.Mod: return "__mod__"
if op == ast.OpKind.Pow: return "__pow__"
if op == ast.OpKind.BitAnd: return "__and__"
if op == ast.OpKind.BitOr: return "__or__"
if op == ast.OpKind.BitXor: return "__xor__"
if op == ast.OpKind.LShift: return "__lshift__"
if op == ast.OpKind.RShift: return "__rshift__"
if op == ast.OpKind.USub: return "__neg__"
if op == ast.OpKind.UAdd: return "__pos__"
if op == ast.OpKind.Invert: return "__invert__"
return None
# ============================================================
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder 方法
#
# 与 ExprGenMixin.try_operator_overload 的查找一致:当前模块内定义
# 或跨模块类有 SHA1(stub 中声明)即视为存在。仅在确实存在时
# 才采用运算符重载推断的类名,避免把原生运算误标为类类型。
# ============================================================
def _cls_has_dunder(self, pool: rowptr[memhub.MemBuddy],
mod: llvmlite.LLVMModule | t.CPtr,
class_name: str, dunder: str) -> int:
"""类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1"""
if class_name is None or dunder is None or mod is None:
return 0
lookup_name: str = pool.alloc(128)
if lookup_name is None:
return 0
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
if self.Gen.CallGenBase.find_func_in_module(mod, lookup_name) is not None:
return 1
# 继承链祖先查找
mro_ptr: sizet | t.CPtr = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
if anc is not None:
anc_lookup: str = pool.alloc(128)
if anc_lookup is not None:
viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder)
if self.Gen.CallGenBase.find_func_in_module(mod, anc_lookup) is not None:
return 1
mi += 1
# 跨模块:类有 SHA1(stub 中声明该方法)视为存在
if self.Gen.StructGen.get_struct_sha1(class_name) is not None:
return 1
return 0
def InitAssignGen(self) -> int:
"""初始化 AssignGen 字段(构造时由 Generator 调用)"""
if self is None:
return 1
self.RecoverFound = 0
return 0