Files
ViperC/App/lib/core/Gen/AssignGenMixin.vp
T

2240 lines
125 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.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.TypeGenCType as TypeGenCType
import lib.core.Gen.ExprGenAttr as ExprGenAttr
import lib.core.Gen.CallGenBase as CallGenBase
import lib.core.Gen.CallGenFd as CallGenFd
import lib.core.Gen.CallGenVt as CallGenVt
import lib.Stub.StubGen.VpiReader as VpiReader
# ============================================================
# AssignGenMixin - 赋值翻译 Mixin(P6 方法化)
#
# 从 HandlesAssign/HandlesAnnAssign/HandlesAugAssign 迁移:
# 模块级无状态函数(trans/pool 首参)→ 类方法;全局状态
# (HandlesAnnAssign._g_recover_found)→ 本类字段 RecoverFound。
#
# 迁移原则(与 P1-P5 一致,行为零变化):
# 1. 方法保留原函数签名(含 trans/pool 首参),仅函数体内的
# 本模块函数调用改为 self.xxx(...)
# 2. 外部模块调用(HandlesType.xxx/HandlesExpr.xxx 等)保留模块前缀
# 3. 原 Handles 模块函数保留为"委托":本地模块级单例 _g_gen
# (BuildPipeline/main 注入)cast 成 Generator 类型
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
@t.NoVTable
class AssignGenMixin:
Gen: Generator.Generator | t.CPtr
"""赋值翻译 Mixin(P6 方法化)"""
# --- 原 HandlesAnnAssign._g_recover_found 模块级全局(迁移为实例字段) ---
RecoverFound: int
# ============================================================
# is_cdefine_annotation - 检测注解是否为 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) -> t.CInt64T:
"""从值节点提取整数常量(支持 Constant/BinOp/Name/Call/UnaryOp)
64 位返回:大整数 CDefine(如 sys.maxsize=9223372036854775807)
必须全精度提取,否则 int(32 位) 截断成 -1(SysLibTest maxsize FAIL 根因)。
"""
if val_node is None:
return 0
k: int = val_node.kind()
# 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: t.CInt64T = self.extract_cdefine_int_value(bop.left)
right_val: t.CInt64T = self.extract_cdefine_int_value(bop.right)
if bop.op == ast.OpKind.BitOr:
return left_val | right_val
if bop.op == ast.OpKind.BitAnd:
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:
# 整数常量除法:与 FloorDiv 一致(对齐 TPC ConstEvaluator L354-355)
return left_val // right_val
if bop.op == ast.OpKind.FloorDiv:
return left_val // right_val
if bop.op == ast.OpKind.Mod:
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(对齐 TPC:bool 在 CDefine 语境按整数处理)
if string.strcmp(nm.id, "True") == 0:
return 1
if string.strcmp(nm.id, "False") == 0:
return 0
looked_up: t.CInt64T = 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: t.CInt64T = 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: t.CInt64T = 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: memhub.MemBuddy | t.CPtr,
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 cl_args_cd.__len__() > 0:
first_arg_cd: ast.AST | t.CPtr = cl_args_cd.get(0)
if first_arg_cd is not None and first_arg_cd.kind() == ast.ASTKind.Constant:
val_node = first_arg_cd
if val_node is not None and val_node.kind() == ast.ASTKind.Constant:
cn_fp: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(val_node)
if cn_fp.const_kind == ast.CONST_STR:
# 字符串 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: t.CInt64T = self.extract_cdefine_int_value(val_node)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, val_fp)
# ============================================================
# 源文件重读恢复 CDefine 浮点值
#
# 背景:AST 节点的 float_val/str_val 字段可能被 pool 分配操作覆写
# (MemBuddy 内存冲突),导致注册的浮点 CDefine 值错误(llc ret i32 0)。
# 本函数直接读源文件找 "NAME: t.CDefine = value" 行,atof 解析 value——
# 源文件文本不受 pool 影响,是最可靠的恢复路径。
#
# 原模块级 _g_recover_found 全局已迁移为实例字段 self.RecoverFound。
# ============================================================
def _recover_found(self) -> int:
"""检查最近一次 _recover_cdefine_float 是否成功"""
return self.RecoverFound
def _recover_cdefine_float(self, name: str, out: t.CDouble | t.CPtr) -> int:
"""从当前模块源文件恢复浮点 CDefine 值,写入 out[0],成功返回 1
用输出参数传递 double,避免 TPV 跨模块 double 返回值传递 bug。
"""
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: fileio.File | t.CPtr = fileio.File(fp, fileio.MODE.R)
if f.closed:
return 0
BUF_SZ: t.CSizeT = 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
else:
buf[BUF_SZ - 1] = 0
name_len: t.CSizeT = string.strlen(name)
total_len: t.CSizeT = string.strlen(buf)
pos: t.CSizeT = 0
while pos < total_len:
line_start: t.CSizeT = pos
while pos < total_len and buf[pos] != '\n':
pos += 1
line_len: t.CSizeT = pos - line_start
if pos < total_len:
pos += 1
# 匹配 "name:"
if line_len < name_len + 1:
continue
match_ok: int = 1
ki: t.CSizeT
for ki in range(name_len):
if buf[line_start + ki] != name[ki]:
match_ok = 0
break
if match_ok == 0:
continue
if buf[line_start + name_len] != ':':
continue
# 行内包含 "t.CDefine"
has_cd: int = 0
ci: t.CSizeT = 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: t.CSizeT = 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
val_start: t.CSizeT = 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: t.CSizeT
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
# ============================================================
# _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
# t.CChar|t.CPtr / t.CInt8T|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 _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
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, "t.CPtr") == 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, "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: t.CSizeT = 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: memhub.MemBuddy | t.CPtr,
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
match arr_ty:
case llvmlite.LLVMType.Array(et, _):
elem_ty = et
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: t.CSizeT = len(elts)
# 为每个元素生成 GEP + store
i: t.CSizeT = 0
while i < elts_count:
elem_node: ast.AST | t.CPtr = elts.get(i)
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")
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 not None:
# 类型转换(如 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)
i += 1
return 0
# ============================================================
# _apply_aug_op - 应用增强赋值的二元运算
#
# 支持浮点运算、指针算术: ptr += int / ptr -= int
# 整数运算自动类型提升
# ============================================================
def _apply_aug_op(self, pool: memhub.MemBuddy | t.CPtr,
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)
elif rhs_bits > lhs_bits and lhs_bits > 0:
lhs = self.Gen.ExprGenBase.coerce_to_type(builder, lhs, rhs.Ty, node)
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:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
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: memhub.MemBuddy | t.CPtr = 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: memhub.MemBuddy | t.CPtr = 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: memhub.MemBuddy | t.CPtr = 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 HT.is_global_name(trans, nm.id) != 0:
scope_type = 1
elif HT.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 * 后使用,如 Termination4K3 core.vp
的 `__python_suffix__: CArray[str] = ["py", ...]`)
"""
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 *):与 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:
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 + 列表字面量 → 静态数组
#
# 单参 t.CArray[elem_ty] 解析为 Ptr(elem_ty),但当 RHS 是列表字面量时,
# 按 TPC 语义应推断为 [len x elem_ty](对齐 TPC ParseListAnnotation):
# 避免误走动态 list 构造(_find_pool_var 兜底引用外部 @_mbuddy → 链接期
# undefined)。PreScan 与 Handle 都调用(PreScan 预创建 alloca 的类型必须
# 与 Handle 一致,否则 get_or_create_sym 复用旧 alloca 使推断失效)。
# ============================================================
def _infer_static_array_ty_from_literal(self, pool: memhub.MemBuddy | t.CPtr,
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)
if elem_ty_arr is not None and cnt_arr > 0:
return llvmlite.Array(pool, elem_ty_arr, cnt_arr)
return resolved_ty
# ============================================================
# 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: memhub.MemBuddy | t.CPtr = 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 HT.is_global_name(trans, nm.id) != 0:
return 0
if HT.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: memhub.MemBuddy | t.CPtr = 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:
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans)
if resolved is None:
# 尝试特化泛型类注解(如 list[str])
# resolve_annotation_type 返回 None 可能是因为泛型类未特化
if annot.kind() == ast.ASTKind.Subscript:
sub_annot: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
if sub_annot.value is not None and sub_annot.value.kind() == ast.ASTKind.Name:
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_annot.value)
if sub_nm.id is not None:
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
spec_name_ps: str = None
if template_cd is not None:
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_annot.slice)
if type_args_ps is not None and len(type_args_ps) > 0:
spec_name_ps = self.Gen.ClassGenGeneric._specialize_generic_class(
trans, sub_nm.id, type_args_ps)
if spec_name_ps is not None:
resolved = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans)
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)
else:
# fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32)
self.Gen.TypeGenErr.fatal_error(
aa,
f"变量类型注解解析失败,变量名: {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 name(GSList[BasicBlock] → 特化
# struct 名),否则 pool=None 只返回裸类名(GSList → 泛型模板未注册
# → 属性访问 lookup_field_by_class("GSList") 报"类未注册",LLVMLiteTest
# __function.vp blocks.Tail 根因;对齐 ClassGenMixin L3159 BUG 1 方案3)
if annot is not None:
cls_nm_ps: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
annot, trans._imported_modules, pool)
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
# ============================================================
# translate_annassign - 翻译 AnnAssign 语句(迁移自
# HandlesAnnAssign.AnnAssignHandle.Handle)
#
# 返回新增的变量数
# ============================================================
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: memhub.MemBuddy | t.CPtr = trans.Pool
self._register_cdefine_from_value(pool_cd, nm.id, aa.value)
return 0
# CTypedef 注解: 类型别名(mode_t: t.CTypedef = t.CUInt32T 等)。
# 翻译期 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: memhub.MemBuddy | t.CPtr = 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: memhub.MemBuddy | t.CPtr = 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:
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans)
if resolved is not None:
var_ty = resolved
else:
# fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32)
self.Gen.TypeGenErr.fatal_error(
aa,
f"变量类型注解解析失败,变量名: {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 HT.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:
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans)
if rhs_val is not None:
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)
llvmlite.build_store(builder, rhs_val, mod_alloca)
return 0
# nonlocal 变量:通过闭包 env 写入
if HT.is_nonlocal_name(trans, nm.id) != 0:
if aa.value is not None:
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans)
if rhs_val is not None:
nl_ptr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
trans, nm.id)
if nl_ptr is not None:
# 从 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:
# fast-fail: nl_ptr 类型信息丢失,无法安全 store
VLogger.error(
f"nonlocal 变量类型信息丢失,无法写入: {nm.id}",
"HandlesAnnAssign")
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)
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:
rhs_val_mg: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans)
if rhs_val_mg is not None:
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)
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)
# 普通局部变量
# 创建 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")
# FakeDuck: str 变量创建影子结构体 {var}__meta__(per-instance 字段存储)
# 仅裸 str 注解(Name("str"))且 _str 已注册时创建;bytes / t.CChar|t.CPtr 不创建。
# a 保持为 i8*(可 printf/len),a__meta__ 存 __data__/__mbuddy__ 等字段,
# 属性读写(fd3)与方法调用(fd5)均通过该影子解析。
fd_str_ty: llvmlite.LLVMType | t.CPtr = None
fd_meta_alloca: llvmlite.Value | t.CPtr = None
fd_meta_name: str = None
if annot is not None and annot.kind() == ast.ASTKind.Name:
fd_annot: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot)
if fd_annot is not None and fd_annot.id is not None:
if string.strcmp(fd_annot.id, "str") == 0:
fd_str_ty = 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)
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 初始化
# 修复:此前局部 CArray[...] = [x, y] 会走 translate_list_literal,
# 错误触发 list[T] 泛型特化(堆对象)导致崩溃(如 this.vp 的
# t.CArray[t.CChar, 256] = [0])。局部 alloca 未零初始化,
# 先整体置零再写列表元素(与全局数组默认零初始化语义一致)。
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)。
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)
if rhs_val is not None:
# 注解退化修复(对齐 TPC 从构造结果推断类型):
# 无实参泛型模板类注解(如 vi: vector.Vector | CPtr,模板
# 未注册 struct → resolve 退化)会让 alloca 类型退化为
# Ptr(i8),但 rhs 构造返回精确结构体指针(如 Ptr(Vector[int]
# 结构))。此时按退化 alloca store 会丢失特化类型,后续
# vi.push 方法调用按 Ptr(i8) 查不到类名 → AnnotClassName
# 回退到模板名(无实参)→ vpi 查不到返回类型 fail-fast。
# 用 rhs 精确类型重建 alloca 并更新符号表注册,使方法调用
# 按精确特化类名解析。旧退化 alloca 行残留为 entry block
# 死代码(文本 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
if alloca_pt is not None and \
t.CUInt64T(alloca_pt) != t.CUInt64T(precise_rhs_ty):
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)
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 字节)。
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: memhub.MemBuddy | t.CPtr = 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
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, asgn.value, None, 0, trans)
if rhs_val is None:
# Phase A 依赖未就绪(_deps_pending=1):跨模块函数/方法因 text.ll
# 未生成返回类型退化为 void/None(CallGenMixin L4493
# g_vta._resolve_attr_field_class 根因)。此时整文件不写盘、下遍重译
# (Phase2.vp L590-599),跳过本语句不报错——否则 fatal 使多遍
# 重试机制永远无法收敛(本语句只是"本遍保持默认类型"的退化结果,
# 下遍依赖已生成即可精确解析)。依赖就绪时 _deps_pending=0,
# 此处仍 fail-fast,不掩盖真实错误。
if trans is not None and trans._deps_pending != 0:
return 0
# 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位
fb: str = VLogger.fmt_buf()
if fb is not None and asgn.value is not None:
sha1: str = trans.ModuleSha1
val_kind: int = asgn.value.kind()
val_line: t.CInt = 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:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d '%s.%s' 属性访问失败",
rel_path, val_line, target_nm, attr_nm)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d '%s.%s' 属性访问失败",
sha1, val_line, target_nm, attr_nm)
elif val_kind == ast.ASTKind.Name:
nm_nv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(asgn.value)
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d 变量 '%s' 未找到",
rel_path, val_line, nm_nv.id)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d 变量 '%s' 未找到",
sha1, val_line, nm_nv.id)
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:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d 调用 '%s(...)' 返回 None",
rel_path, val_line, func_nm)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d 调用 '%s(...)' 返回 None",
sha1, val_line, func_nm)
else:
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d [kind=%d]",
rel_path, val_line, val_kind)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d [kind=%d]",
sha1, val_line, val_kind)
VLogger.error(fb, "ASGN")
else:
VLogger.error("赋值右侧为 None (asgn.value 为空)", "ASGN")
return 0
new_vars: int = 0
tn: t.CSizeT = 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: t.CSizeT = 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: t.CSizeT
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)
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)
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: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_set is not None:
arg_vals_set[0] = t.CSizeT(key_val_set)
arg_vals_set[1] = t.CSizeT(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: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_attr is not None:
arg_vals_attr[0] = t.CSizeT(key_val_attr)
arg_vals_attr[1] = t.CSizeT(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: t.CSizeT | t.CPtr = pool.alloc(16)
if arg_vals_gen is not None:
arg_vals_gen[0] = t.CSizeT(key_val_gen)
arg_vals_gen[1] = t.CSizeT(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, ...)——
# 旧代码传 (builder, pool, ...) 导致 pool/builder 错位,pool 参数
# 实际是 IRBuilder 对象,pool.alloc 虚调用读 IRBuilder[0] 当 vtable
# 跳到垃圾地址崩溃(BuiltinDecoratorTest Phase A 崩溃根因)。
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)
llvmlite.build_store(builder, store_val, field_ptr)
continue
# 守卫:field_ptr 为 None → 构造详细错误信息
# Phase A 依赖未就绪(_deps_pending):get_attribute_ptr 已因
# 父类/依赖类未注册跳过字段,此处同样跳过不报错,整文件下遍重译
# (对齐 HandlesAssign L69-78 与 get_attribute_ptr 的 pending 容忍)
if trans is not None and trans._deps_pending != 0:
continue
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 HT.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")
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)
llvmlite.build_store(builder, rhs_val, mod_alloca)
continue
# nonlocal 变量:通过闭包 env 写入
if HT.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)
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:
fb_g: str = VLogger.fmt_buf()
msg_g: str = "global 声明缺失"
if fb_g is not None:
viperlib.snprintf(fb_g, 1024, "变量 '%s' 是模块级全局变量,函数内赋值需用 global 声明(否则创建局部变量遮蔽全局,导致全局保持 null 崩溃)", nm.id)
msg_g = fb_g
self.Gen.TypeGenErr.fatal_error(target, msg_g, "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)
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: memhub.MemBuddy | t.CPtr,
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 跨模块返回类型解析一致)。
# WebTest resp = requests.get(...) 根因:无注解赋值变量
# AnnotClassName 推断失败(annot=None)→ 属性访问按需过滤
# (依赖 AnnotClassName)失效 → "属性访问失败: .status_code"。
if ca.value.kind() == ast.ASTKind.Name:
mod_nm_rhs: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ca.value)
if mod_nm_rhs.id is not None:
mod_sha1_rhs: str = 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 ve.AnnotClassName
# 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: memhub.MemBuddy | t.CPtr,
annot: str) -> str:
"""从返回注解文本提取已注册结构体类名,无命中返回 None"""
if pool is None or annot is None:
return None
alen: t.CSizeT = string.strlen(annot)
if alen == 0:
return None
seg: str = pool.alloc(256)
if seg is None:
return None
start: t.CSizeT = 0
i: t.CSizeT
for i in range(alen):
if annot[i] == '|':
seglen: t.CSizeT = i - start
# 段 [start, i):去除首尾空格与引号后查注册表
s: t.CSizeT = start
e: t.CSizeT = 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: t.CSizeT = start
e: t.CSizeT = 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: t.CSizeT = 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: memhub.MemBuddy | t.CPtr,
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: t.CSizeT | 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