ExprGenFstr._fstr_finalize:结果缓冲改回每站点栈 alloca(线程安全、零运行时依赖;每站点各建一个,站点之间不再共享),docstring 与文件头写明语义:f-string 结果是当前函数内的临时值。 GenApi 新增唯一判据 fail_if_fstring_escape 与唯一拷贝出口 escape_fstring_copy(stdlib.malloc + string.strcpy);逃逸位置接护栏:translate_return(函数返回值)、translate_assign(字段/元素/全局变量赋值)、handle_module_level_var(模块级 AnnAssign 初始化)。 编译器自身 7 处逃逸点(ExprGenBase._cast_fail_where 6 处、AssignGenMixin.const_assign_msg 1 处)改为 escape_fstring_copy 显式拷贝——正是造成 ASan stack-buffer-underflow in vsnprintf 的那条路径。 残留(已在 GenApi 头注释诚实记录):间接逃逸(把 f-string 传进会保存它的函数、或放进容器后带走容器)静态判不出;类字段默认值路径未接护栏。全仓自查:TransPyV/App、includes、ViperApps 均无 容器/字段/全局 存 f-string 的写法,也无残留 return f-string。
3566 lines
214 KiB
Plaintext
3566 lines
214 KiB
Plaintext
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.core.CompilerFactory as CompilerFactory
|
||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||
import lib.core.Gen.GenApi as GenApi
|
||
|
||
# ============================================================
|
||
# AssignGenMixin - 赋值翻译 Mixin
|
||
# ============================================================
|
||
novirtual class AssignGenMixin:
|
||
Gen: Generator.Generator*
|
||
"""赋值翻译 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*) -> int:
|
||
"""检测注解是否为 t.CDefine,返回 1 表示是,0 表示否"""
|
||
if annot is None: return 0
|
||
k: int = annot.kind()
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(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* = ast.BinOp*(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*) -> 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* = ast.Constant*(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* = ast.BinOp*(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* = ast.Name*(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* = ast.Call*(val_node)
|
||
# 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
|
||
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
|
||
cl_args_list: list[ast.AST*]* = cl.args
|
||
if cl_args_list is not None and len(cl_args_list) > 0:
|
||
first_arg: ast.AST* = 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* = ast.UnaryOp*(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
|
||
|
||
# ============================================================
|
||
# define / t.CDefine 整数常量的"可存储 + 宽度"校验(与 TPC 同口径)
|
||
#
|
||
# 唯一规则源 = TransPyC/lib/core/SymbolUtils.py 的 DefineMinWidth /
|
||
# RequireDefineIntStorable / RequireDefineUseWidth(对照表见
|
||
# Debugger/define-constants.md);本处是 TPV 侧的等价实现:
|
||
# · 自动推导(无显式类型)⇒ 64 位位模式 [-2^63, 2^64-1]
|
||
# · 显式类型 T(`T | t.CDefine`,T 在前)⇒ T 的位宽区间 [-(2^(w-1)), 2^w-1]
|
||
# · W_min = 8/16/32/64 中最小可容纳者(报文用)
|
||
# 关键差异:TPV 的整数常量是 int64,**超出 int64 的字面量在词法层就已回绕**
|
||
# (lexer.vp `string.atoll`)⇒ 溢出判定必须回到**源码文本**判定
|
||
# (_cdefine_int_literal_text + _int_text_exceeds_64),绝不用回绕后的值。
|
||
# ============================================================
|
||
def define_min_width(self, value: int64) -> int:
|
||
"""值 → W_min(8/16/32/64 中最小可容纳者),与 TPC DefineMinWidth 同口径。
|
||
|
||
负数按有符号解释(留出符号位),非负数按无符号位模式解释(两解释口径)。
|
||
不做 `1 << 63` 这类会溢出 int64 的移位。
|
||
|
||
**本函数只使用"自编译安全"的字面量**:TPC 编译本文件时,
|
||
`HandlesExpr._HandleConstantLlvm`(HandlesExpr.py:263-265)把**无类型上下文**的
|
||
整数字面量按 `< 0 或 > 0xFFFFFFFF ⇒ i64,否则 i32` 物化 ⇒ 落在
|
||
`[0x80000000, 0xFFFFFFFF]` 的字面量会被压成 i32,随后与 int64 的 `value`
|
||
比较时按有符号 `sext` 提升(HandlesExprOps.py:401-413)⇒ 位模式 `0xFFFFFFFF`
|
||
变成 **-1**。原实现写 `if value <= 4294967295` 正是该形态:阈值退化为
|
||
`value <= -1`,一切正数落入 64 位档(`65536` 被误判为"需要至少 64 位",
|
||
`includes/subprocess.vp:141` 现场)。
|
||
故 32 位上界改用**回绕判定**(与 `_const_int_for_cdefine` 同一惯用法),
|
||
-2^31 改用**算术表达式**(`-2147483647 - 1`),两者都不含危险字面量。
|
||
"""
|
||
if value < 0:
|
||
if value >= -128:
|
||
return 8
|
||
if value >= -32768:
|
||
return 16
|
||
# -2^31:字面量 2147483648 同样落 i32,改用安全字面量的算术等价式
|
||
if value >= -2147483647 - 1:
|
||
return 32
|
||
return 64
|
||
if value <= 255:
|
||
return 8
|
||
if value <= 65535:
|
||
return 16
|
||
if value <= 2147483647:
|
||
return 32
|
||
# 2^31 .. 2^32-1:uint32 回绕后仍等于原值 ⇒ 可容于无符号 32 位位模式
|
||
if uint64(uint32(value)) == value:
|
||
return 32
|
||
return 64
|
||
|
||
def _is_cdefine_marker_node(self, node: ast.AST*) -> int:
|
||
"""节点是否为 t.CDefine 标记(Attribute.attr / Name.id == "CDefine")"""
|
||
if node is None:
|
||
return 0
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(node)
|
||
if at.attr is not None and string.strcmp(at.attr, "CDefine") == 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(node)
|
||
if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0:
|
||
return 1
|
||
return 0
|
||
|
||
def _define_explicit_annot(self, annot: ast.AST*) -> ast.AST*:
|
||
"""`T | t.CDefine` 的显式类型 T 节点;无显式类型 → None。
|
||
|
||
只看联合**首个**操作数(与 TPC 的"首个非 void 基类型胜出"同口径):
|
||
`T | t.CDefine`(define 常量与 TPC 展开产物)⇒ T;
|
||
`t.CDefine | T`(既有手写惯用法,如 CODE_SEG: t.CDefine | t.CUInt16T)⇒
|
||
首个是 CDefine 标记 ⇒ 无显式类型 ⇒ **既有声明行为逐字不变**。
|
||
"""
|
||
if annot is None:
|
||
return None
|
||
if annot.kind() != ast.ASTKind.BinOp:
|
||
return None
|
||
bop: ast.BinOp* = ast.BinOp*(annot)
|
||
if bop.left is None:
|
||
return None
|
||
if self._is_cdefine_marker_node(bop.left) != 0:
|
||
return None
|
||
return bop.left
|
||
|
||
def _annot_int_bits(self, pool: memhub.MemBuddy*,
|
||
annot: ast.AST*) -> int:
|
||
"""任意整数类型注解节点 → 位宽(bit);无注解 / 非整数 / 无法判定 → 0。
|
||
|
||
名称解析与 TPC EnumMemberAnnotationIntWidth 同源:t 模块类型名走
|
||
TypeGenResolve.map_t_type(CLong/CSizeT 等平台相关宽度由它权威给出);
|
||
`int` / `bool` 两个"注解直通名"补在此表(TPC: int → t.CInt 32 位、
|
||
bool → 8 位,与 include/builtins 口径一致)。
|
||
"""
|
||
if annot is None:
|
||
return 0
|
||
k: int = annot.kind()
|
||
nm: str = None
|
||
if k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(annot)
|
||
nm = at.attr
|
||
elif k == ast.ASTKind.Name:
|
||
n2: ast.Name* = ast.Name*(annot)
|
||
nm = n2.id
|
||
if nm is None:
|
||
return 0
|
||
if string.strcmp(nm, "int") == 0:
|
||
return 32
|
||
if string.strcmp(nm, "bool") == 0:
|
||
return 8
|
||
# stdint.vp / builtins 的定长小写别名(非 t 模块名,map_t_type 不认):
|
||
# 与 TPC EnumMemberAnnotationIntWidth 走 builtins typedef 表的同口径。
|
||
if string.strcmp(nm, "int8") == 0 or string.strcmp(nm, "uint8") == 0:
|
||
return 8
|
||
if string.strcmp(nm, "int16") == 0 or string.strcmp(nm, "uint16") == 0:
|
||
return 16
|
||
if string.strcmp(nm, "int32") == 0 or string.strcmp(nm, "uint32") == 0:
|
||
return 32
|
||
if string.strcmp(nm, "int64") == 0 or string.strcmp(nm, "uint64") == 0:
|
||
return 64
|
||
if string.strcmp(nm, "sizet") == 0 or string.strcmp(nm, "size_t") == 0:
|
||
return 64
|
||
ty: llvmlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm)
|
||
if ty is None:
|
||
return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Int(bits):
|
||
if bits == 1:
|
||
return 8
|
||
if bits == 8 or bits == 16 or bits == 32 or bits == 64:
|
||
return bits
|
||
return 0
|
||
case _:
|
||
return 0
|
||
|
||
def _define_annot_bits(self, pool: memhub.MemBuddy*,
|
||
annot: ast.AST*) -> int:
|
||
"""CDefine 声明注解 → 显式类型 T 的整数位宽(bit)。
|
||
|
||
无显式类型(`t.CDefine` 单标记 / T 不在联合首位)⇒ 0(走 64 位口径)。
|
||
"""
|
||
return self._annot_int_bits(pool, self._define_explicit_annot(annot))
|
||
|
||
def _cdefine_int_literal_text(self, name: str) -> str:
|
||
"""按变量名从当前模块源文件取 CDefine 行的**值文本**(原始字面量)。
|
||
|
||
与 _recover_float_literal 同一套"回源文件读取"策略:AST 常量的 int_val
|
||
是 int64,超出 int64 的字面量已在词法层回绕(lexer.vp `string.atoll`)
|
||
⇒ 只有源文本可靠。读取失败 / 未命中 → None。
|
||
"""
|
||
if name is None:
|
||
return None
|
||
fp: str = self.Gen.TypeGenResolve.get_current_file()
|
||
if fp is None:
|
||
return None
|
||
f: rawptr[fileio.File] = fileio.File(fp, fileio.MODE.R)
|
||
if f.closed:
|
||
return None
|
||
BUF_SZ: sizet = 1048576
|
||
buf: bytes = stdlib.malloc(BUF_SZ)
|
||
if buf is None:
|
||
f.close()
|
||
return None
|
||
nrd: LONG = f.read_all(buf, BUF_SZ)
|
||
f.close()
|
||
if nrd <= 0:
|
||
stdlib.free(buf)
|
||
return None
|
||
if nrd < BUF_SZ:
|
||
buf[nrd] = 0
|
||
name_len: sizet = string.strlen(name)
|
||
total_len: sizet = string.strlen(buf)
|
||
pos: sizet = 0
|
||
result: str = None
|
||
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
|
||
if line_len < name_len + 1:
|
||
continue
|
||
ok: int = 1
|
||
ki: sizet
|
||
for ki in range(name_len):
|
||
if buf[line_start + ki] != name[ki]:
|
||
ok = 0
|
||
break
|
||
if 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
|
||
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
|
||
vstart: sizet = eq
|
||
while eq < line_start + line_len and buf[eq] != ' ' and buf[eq] != '\t' \
|
||
and buf[eq] != '#' and buf[eq] != '\r':
|
||
eq += 1
|
||
if eq == vstart:
|
||
continue
|
||
txt: str = stdlib.malloc(eq - vstart + 1)
|
||
if txt is None:
|
||
continue
|
||
vi: sizet
|
||
for vi in range(eq - vstart):
|
||
txt[vi] = buf[vstart + vi]
|
||
txt[eq - vstart] = 0
|
||
result = txt
|
||
break
|
||
stdlib.free(buf)
|
||
return result
|
||
|
||
def _int_text_exceeds_64(self, text: str) -> int:
|
||
"""字面量文本是否超出 64 位位模式 [-2^63, 2^64-1](1=超出,0=未超出/不判定)。
|
||
|
||
**纯文本判定**(不落进任何定宽容器):
|
||
十进制:去 '_' 与前导零后,与 '18446744073709551615'(2^64-1)按长度 +
|
||
字典序比较;负数侧与 '9223372036854775808'(2^63)比较。
|
||
十六进制(0x/0X):去 '_' 与前导零后 > 16 位 ⇒ 超出。
|
||
其余(表达式 / 浮点 / 字符串)⇒ 0(不进入本判据,由注册处的 AST 求值判据负责)。
|
||
"""
|
||
if text is None:
|
||
return 0
|
||
tl: sizet = string.strlen(text)
|
||
if tl == 0:
|
||
return 0
|
||
neg: int = 0
|
||
i: sizet = 0
|
||
if text[0] == '-':
|
||
neg = 1
|
||
i = 1
|
||
elif text[0] == '+':
|
||
i = 1
|
||
if i >= tl:
|
||
return 0
|
||
if i + 1 < tl and text[i] == '0' and (text[i + 1] == 'x' or text[i + 1] == 'X'):
|
||
# 十六进制:去 '_' 与前导零后的有效位数(>16 位 ⇒ 超出 64 位)
|
||
sig: sizet = 0
|
||
j2: sizet = i + 2
|
||
leading: int = 1
|
||
while j2 < tl:
|
||
c2: char = text[j2]
|
||
j2 += 1
|
||
if c2 == '_':
|
||
continue
|
||
if not ((c2 >= '0' and c2 <= '9') or (c2 >= 'a' and c2 <= 'f')
|
||
or (c2 >= 'A' and c2 <= 'F')):
|
||
return 0
|
||
if leading != 0 and c2 == '0':
|
||
continue
|
||
leading = 0
|
||
sig += 1
|
||
if sig > 16:
|
||
return 1
|
||
return 0
|
||
# 十进制:定位首个非零有效位
|
||
s: sizet = i
|
||
while s < tl and (text[s] == '0' or text[s] == '_'):
|
||
s += 1
|
||
digits2: int = 0
|
||
j3: sizet = s
|
||
while j3 < tl:
|
||
c3: char = text[j3]
|
||
j3 += 1
|
||
if c3 == '_':
|
||
continue
|
||
if c3 < '0' or c3 > '9':
|
||
return 0
|
||
digits2 += 1
|
||
if digits2 == 0:
|
||
return 0
|
||
max_text: str = "18446744073709551615"
|
||
mlen: sizet = 20
|
||
if neg != 0:
|
||
max_text = "9223372036854775808"
|
||
mlen = 19
|
||
if digits2 > mlen:
|
||
return 1
|
||
if digits2 < mlen:
|
||
return 0
|
||
# 等长(len(max_text) 位):逐位字典序比较(同位数时字典序 = 数值序)
|
||
d: sizet = 0
|
||
k2: sizet = s
|
||
while k2 < tl and d < mlen:
|
||
c4: char = text[k2]
|
||
k2 += 1
|
||
if c4 == '_':
|
||
continue
|
||
mc: char = max_text[d]
|
||
if c4 > mc:
|
||
return 1
|
||
if c4 < mc:
|
||
return 0
|
||
d += 1
|
||
return 0
|
||
|
||
def require_cdefine_storable(self, pool: memhub.MemBuddy*,
|
||
node: ast.AST*, name: str,
|
||
value: int64, annot: ast.AST*) -> None:
|
||
"""CDefine / define 常量注册前的可存储性校验(fail-fast,与 TPC 同口径)。
|
||
|
||
判据(顺序固定):
|
||
① 源码文本超出 64 位位模式 ⇒ 报错(词法层已回绕,value 不可信);
|
||
② 显式类型 T(`T | t.CDefine`,T 在前)⇒ value 必须落进 T 的位宽区间。
|
||
报文含:常量名 / 声明类型或"自动推导" / W_min / 实际值 / 可存储范围与
|
||
可用类型清单 / 修法(点明"需要扩容")。位置由 fatal_error 自带
|
||
(File + line + 源码行)。
|
||
"""
|
||
text: str = self._cdefine_int_literal_text(name)
|
||
bits: int = self._define_annot_bits(pool, annot)
|
||
wmin: int = self.define_min_width(value)
|
||
if self._int_text_exceeds_64(text) != 0:
|
||
self.Gen.TypeGenErr.fatal_error(node,
|
||
f"[TPV] define 常量数值不可存储: 常量 '{name}' 的初始值 {text} 超出 64 位"
|
||
f"(本语言最宽的整数类型为 64 位,按有符号/无符号两种解释的可存储范围为 "
|
||
f"-9223372036854775808..18446744073709551615,不存在更宽的整数类型 ⇒ 需要扩容)。"
|
||
f"禁止静默截断/降级,已终止编译。可用整数类型: t.CInt8T/t.CUInt8T、"
|
||
f"t.CInt16T/t.CUInt16T、t.CInt32T/t.CUInt32T、t.CInt64T/t.CUInt64T"
|
||
f"(或 int8/uint8/…/uint64、char、sizet)。"
|
||
f"修法: 把值改到 64 位范围内,或为该数字引入更大的整数类型(扩容)。",
|
||
"TypeError")
|
||
return
|
||
if bits == 0:
|
||
return
|
||
# 32 位档的上下界用**安全字面量**构造(同一字面量纪律,见 define_min_width):
|
||
# 2147483648 / 4294967295 落在 [0x80000000, 0xFFFFFFFF],被 TPC 编译时会被压成
|
||
# i32 并在提升/赋值时按有符号解释,存在与现场同族的隐患;改用 2^31-1 的
|
||
# 算术等价式后与 TPC DefineWidthLoHi(32) = (-2147483648, 4294967295) 逐字相同。
|
||
m31: int64 = 2147483647
|
||
lo: int64 = -m31 - 1
|
||
hi: int64 = m31 * 2 + 1
|
||
if bits == 8:
|
||
lo = -128
|
||
hi = 255
|
||
elif bits == 16:
|
||
lo = -32768
|
||
hi = 65535
|
||
elif bits == 64:
|
||
return
|
||
if value < lo or value > hi:
|
||
self.Gen.TypeGenErr.fatal_error(node,
|
||
f"[TPV] define 常量数值不可存储: 常量 '{name}' 的初始值 {value} 不可存储。"
|
||
f"最小可容纳宽度 W_min: {wmin} 位;声明类型位宽 {bits} 位,"
|
||
f"该声明可存储范围: {lo}..{hi}。"
|
||
f"禁止静默截断/降级,已终止编译。"
|
||
f"修法: 把显式类型改为更宽的类型(如 t.CInt32T / t.CInt64T / uint64),"
|
||
f"或修正该字面量。",
|
||
"TypeError")
|
||
|
||
def require_cdefine_use_width(self, pool: memhub.MemBuddy*,
|
||
trans: HT.Translator*,
|
||
target_annot: ast.AST*,
|
||
value_node: ast.AST*) -> None:
|
||
"""**使用处宽度**校验(`a: T = C` 场景,fail-fast;与 TPC 同口径)。
|
||
|
||
仅当满足全部条件时生效(其余情况零副作用、原样返回):
|
||
· RHS 是裸名(`C`);
|
||
· 该名在当前作用域**不是变量**(与 translate_name_value 的同名变量
|
||
优先守卫一致,避免把普通变量误判为常量 ⇒ 不误伤);
|
||
· 该名是已注册的 CDefine 常量(本模块表命中);
|
||
· 目标注解解析出整数位宽 w(非整数注解 → 不判定)。
|
||
判定:`w >= W_min` ⇒ 通过(消费端按 T 物化/升宽,即"上下文更宽 ⇒
|
||
用使用处宽度");`w < W_min` ⇒ 报错终止(替换静默截断)。
|
||
"""
|
||
if value_node is None or target_annot is None:
|
||
return
|
||
if value_node.kind() != ast.ASTKind.Name:
|
||
return
|
||
rn: ast.Name* = ast.Name*(value_node)
|
||
if rn.id is None:
|
||
return
|
||
if trans is not None:
|
||
if self.Gen.VarGen.lookup_var(trans.SymTab, rn.id) is not None:
|
||
return
|
||
cdef_val: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(rn.id)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||
return
|
||
bits: int = self._annot_int_bits(pool, target_annot)
|
||
if bits == 0:
|
||
return
|
||
wmin: int = self.define_min_width(cdef_val)
|
||
if bits >= wmin:
|
||
return
|
||
self.Gen.TypeGenErr.fatal_error(value_node,
|
||
f"[TPV] define 常量使用处宽度不足: 常量 '{rn.id}' 的实际值 {cdef_val} "
|
||
f"需要至少 {wmin} 位(最小可容纳宽度 W_min),"
|
||
f"但使用处(变量声明注解)只提供 {bits} 位。"
|
||
f"禁止静默截断/降级,已终止编译。"
|
||
f"修法: 把变量声明注解加宽到至少 {wmin} 位(如 t.CInt{wmin}T / CUInt{wmin}T),"
|
||
f"或改用更小的常量值。",
|
||
"TypeError")
|
||
|
||
# ============================================================
|
||
# _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")。
|
||
#
|
||
# annot:该 AnnAssign 的注解(`T | t.CDefine` / `t.CDefine`),用于注册前的
|
||
# 可存储性校验(require_cdefine_storable);不传则退化为"无显式类型"口径。
|
||
# ============================================================
|
||
def _register_cdefine_from_value(self, pool: memhub.MemBuddy*,
|
||
name: str, val_node: ast.AST*,
|
||
annot: ast.AST* = None) -> 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* = ast.Call*(val_node)
|
||
if cl_cd is not None and cl_cd.args is not None:
|
||
cl_args_cd: list[ast.AST*]* = cl_cd.args
|
||
if len(cl_args_cd) > 0:
|
||
first_arg_cd: ast.AST* = 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
|
||
# 负浮点字面量(`define NAME: double = -1e308`):RHS 是
|
||
# UnaryOp(USub, Constant(CONST_FLOAT)),不是裸 Constant ⇒ 否则会落到下方的
|
||
# 整数提取,报"无法从表达式解析编译期常量"。此处只剥掉一层一元负号,把内层
|
||
# 浮点 Constant 交给浮点分支:浮点值一律由源文件文本恢复
|
||
# (_recover_cdefine_float 读原文,负号天然保留,不需在此取反)。
|
||
# 负整数不受影响(UnaryOp 继续落到 extract_cdefine_int_value,它本就认
|
||
# UnaryOp(USub, Constant)),故只拦 CONST_FLOAT 这一种内层形态。
|
||
if val_node is not None and val_node.kind() == ast.ASTKind.UnaryOp:
|
||
uu_cd: ast.UnaryOp* = ast.UnaryOp*(val_node)
|
||
if uu_cd is not None and uu_cd.op == OpKind.USub \
|
||
and uu_cd.operand is not None \
|
||
and uu_cd.operand.kind() == ast.ASTKind.Constant:
|
||
ic_cd: ast.Constant* = ast.Constant*(uu_cd.operand)
|
||
if ic_cd is not None and ic_cd.const_kind == ast.CONST_FLOAT:
|
||
val_node = uu_cd.operand
|
||
if val_node is not None and val_node.kind() == ast.ASTKind.Constant:
|
||
cn_fp: ast.Constant* = ast.Constant*(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)
|
||
# 注册前可存储性校验(fail-fast;唯一判据与 TPC 同口径):
|
||
# 源码文本超 64 位 / 显式类型 T 的位宽放不下 ⇒ 报错终止,不注册。
|
||
self.require_cdefine_storable(pool, val_node, name, val_fp, annot)
|
||
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: rawptr[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
|
||
|
||
# `define NAME: T = V`(宏常量关键字形式):名字前有 "define " 前缀,
|
||
# 且行内没有 t.CDefine 文本(CDefine 标记来自关键字本身,见
|
||
# parser.vp 的 _parse_define_const / _is_define_const)。故名字匹配
|
||
# 基准 ns 顺延到关键字之后的第一个非空白字符,并记下 is_define_line。
|
||
ns: sizet = line_start
|
||
is_define_line: int = 0
|
||
if line_len >= 8:
|
||
if buf[ns] == 'd' and buf[ns + 1] == 'e' and buf[ns + 2] == 'f' \
|
||
and buf[ns + 3] == 'i' and buf[ns + 4] == 'n' and buf[ns + 5] == 'e':
|
||
nb: sizet = ns + 6
|
||
while nb < line_start + line_len and (buf[nb] == ' ' or buf[nb] == '\t'):
|
||
nb += 1
|
||
# 后随空白才算关键字(避免匹配到 defineX 之类的标识符)
|
||
if nb > ns + 6:
|
||
is_define_line = 1
|
||
ns = nb
|
||
# 匹配行首(或 define 之后)的 "name",其后(跳过空格)必须是 ':' 或 '='
|
||
# (AnnAssign:`NAME: t.CDouble = 1.0`;无注解赋值:`NAME = 1.0`;
|
||
# 宏常量:`define NAME: double = 3.14`)
|
||
if ns + name_len + 1 > line_start + line_len:
|
||
continue
|
||
match_ok: int = 1
|
||
ki: sizet
|
||
for ki in range(name_len):
|
||
if buf[ns + ki] != name[ki]:
|
||
match_ok = 0
|
||
break
|
||
if match_ok == 0:
|
||
continue
|
||
sep: sizet = ns + 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",或本行是 `define` 关键字形式
|
||
# (宏常量 `define NAME: T = V` 行内无 t.CDefine 文本);普通浮点全局
|
||
# 路径(require_cdefine=0)跳过该检查
|
||
if require_cdefine != 0 and is_define_line == 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*, 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* = ast.Attribute*(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* = ast.Name*(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* = ast.BinOp*(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*) -> 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* = ast.BinOp*(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* = ast.Name*(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* = ast.Name*(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: memhub.MemBuddy*,
|
||
ta: llvmlite.LLVMType*,
|
||
tb: llvmlite.LLVMType*) -> 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*) -> 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* = ast.Call*(node)
|
||
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute:
|
||
return 0
|
||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if at.value is None or at.value.kind() != ast.ASTKind.Name:
|
||
return 0
|
||
vn: ast.Name* = ast.Name*(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*) -> 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* = ast.Name*(node)
|
||
ty_nm = nm64.id
|
||
elif nk == ast.ASTKind.Attribute:
|
||
at64: ast.Attribute* = ast.Attribute*(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
|
||
# Python 内建标量直通名(与上面的 int/char 同族):t.py CTypeRegistry.ResolveName
|
||
# 的 _CNAME_ALIAS 把 'float'→t.CFloat(32)、'double'→t.CDouble(64)、
|
||
# 'bool'→t.CBool(8);泛型实参位可能原样出现这些裸名。
|
||
if string.strcmp(ty_name, "float") == 0: return 1
|
||
if string.strcmp(ty_name, "double") == 0: return 1
|
||
if string.strcmp(ty_name, "bool") == 0: return 1
|
||
if string.strcmp(ty_name, "CInt") == 0: return 1
|
||
if string.strcmp(ty_name, "CUnsignedInt") == 0: return 1
|
||
if string.strcmp(ty_name, "CUnsigned") == 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, "CSignedChar") == 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, "CBool") == 0: return 1
|
||
# 字符宽度族(stdint.vp 的 wchar_t/char8_t/char16_t/char32_t 底层类):
|
||
# 自身宽度 8/16/32 均 ≠ 8 字节指针槽,必须走标量步长。
|
||
if string.strcmp(ty_name, "CWCharT") == 0: return 1
|
||
if string.strcmp(ty_name, "CChar8T") == 0: return 1
|
||
if string.strcmp(ty_name, "CChar16T") == 0: return 1
|
||
if string.strcmp(ty_name, "CChar32T") == 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
|
||
# 小写裸名家族(includes/builtins.vp 的 C 基础类型裸名别名,同一张表;
|
||
# 裸名经 resolve_generic_type_name 解析后可能原样出现在泛型实参位):
|
||
# uchar/short/ushort/uint/long/ulong/longlong/ulonglong
|
||
if string.strcmp(ty_name, "uchar") == 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, "uint") == 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
|
||
# 定长整数/浮点(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*) -> 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* = ast.Name*(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*,
|
||
pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
array_alloca: llvmlite.Value*,
|
||
list_node: ast.AST*,
|
||
trans: HT.Translator*,
|
||
owner_ctx: str = None) -> int:
|
||
"""从列表字面量初始化全局数组,为每个元素生成 GEP + store
|
||
|
||
owner_ctx: 站点描述(如 "类 X 字段 y"),仅用于空元素 fail-fast 报文,
|
||
便于从报错直接定位到"哪个类/哪个字段的第几个元素"。
|
||
"""
|
||
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* = 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* = 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* = ast.List*(list_node)
|
||
if lst is None or lst.elts is None:
|
||
return 1
|
||
elts: list[ast.AST*]* = 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* = elts.get(0)
|
||
if e0_s is not None and e0_s.kind() == ast.ASTKind.Constant:
|
||
c0_s: ast.Constant* = ast.Constant*(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* = llvmlite.const_int32(pool, bi_s)
|
||
bp_s: llvmlite.Value* = llvmlite.build_gep_array(builder, arr_ty, elem_ty, array_alloca, bidx_s)
|
||
if bp_s is None:
|
||
break
|
||
bvv: llvmlite.Value* = llvmlite.const_int32(pool, sv_s[bi_s])
|
||
bsv: llvmlite.Value* = 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* = elts.get(i)
|
||
# === 显式判空(本仓硬约定:指针判空一律 `is None` / `is not None`)===
|
||
# 【反例现场·实测】上一轮曾把本判据写成整型字面量比较 `elem_node == 0`,
|
||
# 并附注"与 `is None` 完全同义",该注**结论错、但 IR 事实对**,逐字证据:
|
||
# TPC `HandlesExprOps._HandleCompareLlvm:429-431` 对"指针 vs 整型字面量 0"
|
||
# 把 0 物化为 `ir.Constant(LeftVal.type, None)`(而非折成常量 false),
|
||
# 再经 `:457-458` 的指针分支生成 `icmp eq`(命名为 `cmpresult`)。
|
||
# 编译产物 `TransPyV\output\818a0677d1e7fe22.ll:17761-17763` 逐字为:
|
||
# %"elem_node.1" = load %"cfc4ca8da95a30e1.AST"*, ... %elem_node
|
||
# %"cmpresult.9" = icmp eq %"cfc4ca8da95a30e1.AST"* %"elem_node.1", null
|
||
# br i1 %"cmpresult.9", label %"then.12", label %"endif.12"
|
||
# (`then.12` = 本守卫的响亮报错体,`endif.12` = 正常路径;合并模块同形,
|
||
# 见 `TransPyV\output\818a0677d1e7fe22_dso.ll:75903-75904`。)
|
||
# ⇒ "`x == 0` 判空无效 / 被折成常量 false" **不成立**(`is None` 走同形比较,
|
||
# 命名 `is_cmp`,见 `TransPyV\output\5626a4dc226462fa.ll:2940`)。
|
||
# 仍统一改用 `is None`:语义直白、不依赖"指针 vs 整型"跨类型特例分支、与
|
||
# ExprGenMixin._translate_value_impl:100 既有写法同族。命中即响亮报错,
|
||
# 直接指名"类名/字段名/元素下标 + 位置"。
|
||
# 该槽为空 ⇒ elts 计数 > 实际填充数 ⇒ 上层某处产生了空 AST 元素
|
||
# (这正是 `list.get` 越界/未填充槽返回裸 0 的后果)。
|
||
#
|
||
# 【硬约定·判据写法取决于静态类型】(wiki/02-type-system.md 同步)
|
||
# · 指针类型(AST* / Value* / str ...):`is None` 或 `== 0` **均可**,
|
||
# 两侧编译器都生成同形 `icmp eq <ptr>, null`(命名 `is_cmp` / `cmpresult`)。
|
||
# · 整型承载的地址(如 `llvmlite.LLVMType.Pointee`,静态类型 i64):
|
||
# 必须用 `== 0` / `!= 0`。写成 `is None` / `is not None` 会被折成**常量**
|
||
# ——TPC `HandlesExprOps.py:388-395`「值类型(IntType) is None ⇒ 恒 0;
|
||
# is not None ⇒ 恒 1」;IR 实证:`TransPyV\output\818a0677d1e7fe22.ll:17983`
|
||
# `%"and.result.4" = phi i1 [0, %"endif.19"], [1, %"and.rhs0.4"]`
|
||
# (`and.rhs0.4` 块内只 `load i64` 后直接 `br`,**没有任何 icmp**)
|
||
# ⇒ `X.Ty.Pointee is not None` 这一合取项恒真、守卫失效。
|
||
# 本函数 1185 行(`elem_ptr.Ty.Pointee`)已按此约定改为 `!= 0`。
|
||
if elem_node is None:
|
||
ctx_dbg: str = ""
|
||
if owner_ctx is not None:
|
||
ctx_dbg = owner_ctx
|
||
src_line: int = 0
|
||
src_col: int = 0
|
||
if list_node is not None:
|
||
src_line = list_node.lineno
|
||
src_col = list_node.col_offset
|
||
self.Gen.TypeGenErr.fatal_error(list_node,
|
||
f"全局数组初始化遇到空 AST 元素:{ctx_dbg}第 {i} 个元素为裸空指针(0),"
|
||
f"但列表元素计数为 {elts_count}(元素与计数不一致)。"
|
||
f"上层某处产生了空 AST 元素(列表字面量节点 {src_line}:{src_col} 的第 {i} 槽未被填充),"
|
||
f"拒绝静默跳过/补零,已终止编译。",
|
||
"ValueError")
|
||
return 1
|
||
# === 对象有效性校验(vptr 非 0)===
|
||
# 上一条只判"指针非 0";@t.CVTable 类的虚调用(kind()/dump()/...) 经对象首字
|
||
# (vptr)间接调用,首字为 0 时紧跟的 `elem_node.kind()` 会生成
|
||
# `load [0 + slot]` ⇒ 立刻 ASan READ at 0x0(本仓崩溃的真实形态:
|
||
# EXPRGenBase._translate_value_impl:102 的 node.kind())。
|
||
# 产生源实证:StructGenMixin._deep_copy_default_ast 旧写法把深拷贝节点的
|
||
# vptr 留成 0(IR 证据见该函数注释 / b5883dc9c114a604.ll:10382-10384)。
|
||
# 读对象首字的**唯一可用**写法是 `c.Deref((uint64*)(p))`(正常发射
|
||
# `bitcast p → i64*` + `load i64`:b5883dc9c114a604.ll:10611-10613);
|
||
# 写成 `rawptr(*(uint64*)(p))` 会被折成裸 0(同文件 :10382),不可用。
|
||
elem_vptr: uint64 = c.Deref((uint64*)(elem_node))
|
||
if elem_vptr == 0:
|
||
ctx_vt: str = ""
|
||
if owner_ctx is not None:
|
||
ctx_vt = owner_ctx
|
||
vt_line: int = 0
|
||
vt_col: int = 0
|
||
if list_node is not None:
|
||
vt_line = list_node.lineno
|
||
vt_col = list_node.col_offset
|
||
self.Gen.TypeGenErr.fatal_error(list_node,
|
||
f"全局数组初始化遇到【对象无效】的 AST 元素(vptr 为 0):{ctx_vt}"
|
||
f"第 {i} 个元素指针非 0(地址 {uint64(elem_node)})但其对象首字(vtable 指针)为 0,"
|
||
f"对它的任何虚调用(kind()/dump()/…) 都会在 `load [0+slot]` 处读地址 0。"
|
||
f"列表字面量节点位置 {vt_line}:{vt_col},元素计数 {elts_count}。"
|
||
f"上层某处产生了「未初始化 vptr 的 AST 节点」(典型源:深拷贝/整块拷贝只搬字段未搬 vptr),"
|
||
f"拒绝继续,已终止编译。",
|
||
"ValueError", vt_col)
|
||
return 1
|
||
idx_val: llvmlite.Value* = llvmlite.const_int32(pool, i)
|
||
elem_ptr: llvmlite.Value* = 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:
|
||
# 嵌套元素判空:同上硬约定,用 `is not None`(不用整型字面量 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, owner_ctx)
|
||
continue
|
||
elem_val: llvmlite.Value* = 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 截断)
|
||
# 判据硬约定:`Pointee` 静态类型是 i64(REnum 载荷槽,非指针)⇒ 必须 `!= 0`;
|
||
# 写成 `is not None` 会被折成常量 true(IR 见上方 1146-1156 注释)。
|
||
store_val: llvmlite.Value* = elem_val
|
||
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee != 0:
|
||
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: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
op: int,
|
||
lhs: llvmlite.Value*,
|
||
rhs: llvmlite.Value*,
|
||
node: ast.AST* = None,
|
||
trans: HT.Translator* = None,
|
||
lhs_node: ast.AST* = None) -> llvmlite.Value*:
|
||
"""应用增强赋值的二元运算(浮点 + 指针算术 + 整数运算)"""
|
||
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* = 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
|
||
|
||
# 双指针算术 fail-fast:判据/报文唯一实现见 ExprGenOps.fail_if_double_ptr_binop
|
||
# (BinOp 与 AugAssign 共用,避免分叉)。指针 ± 整数(含 str += int)已在上面
|
||
# translate_ptr_arith 分流;此处的双指针增量赋值(+=、-=、*=、<<= …)在本语言
|
||
# 非法(str 是 i8*,无 `+` 拼接语义),落到 build_add/build_sub/… 会直出
|
||
# `add i8*, i8*` 这类非法 IR。is_aug=1 ⇒ 报文标注「(增量赋值)」并补全符号。
|
||
self.Gen.ExprGenOps.fail_if_double_ptr_binop(pool, op, lhs, rhs, node, 1)
|
||
|
||
# 整数运算:类型提升
|
||
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 三种作用域
|
||
# ============================================================
|
||
# ============================================================
|
||
# const 对象赋值拦截(与 TPC 同判据、同报文口径)
|
||
# 判据来源:VarEntry.IsConst / FieldEntry.IsConst(注解含 t.CConst 且类型非指针,
|
||
# 形参沿用 TPC 的历史判据——只看注解是否含 CConst)。见
|
||
# Debugger/type-descriptors.md「两侧语义对照表」。
|
||
# ============================================================
|
||
def const_assign_msg(self, kind: str, name: str, src_name: str) -> str:
|
||
"""构造与 TPC `Gen._ConstAssignMessage` 同骨架的 const 赋值报文。
|
||
|
||
位置(文件/行/列 + 源码片段)由 TypeGenErr.fatal_error 的 Traceback 帧补齐,
|
||
与 TPC `_get_node_info()` 的 `in <file>, line N, col C` + `→ 源码行` 同口径。
|
||
『声明处类型(含 const)』本侧取 VarEntry/FieldEntry 的 Spec 注解来源名
|
||
(set_var_annot_class_name/add_field 经 SpecWithSource 写入),拼成
|
||
`t.CConst | <来源名>`;来源名缺失时只报 `注解含 t.CConst`。
|
||
|
||
报文一律用 **f-string 插值**构建:本语言的 str 是 i8*,**没有** `str + str`
|
||
的拼接语义(`+` 会退化成裸指针 add ⇒ 非法 IR: invalid operand type for
|
||
instruction),故禁止对 str 用 `+` 拼接。
|
||
|
||
返回值会**逃逸本函数**(被 const_assign_fail 传给 fatal_error)⇒ 经
|
||
GenApi.escape_fstring_copy 显式拷到 stdlib 堆(f-string 本身是栈临时值)。
|
||
"""
|
||
decl: str = "(注解含 t.CConst,声明处来源名未记录)"
|
||
if src_name is not None:
|
||
decl = f"t.CConst | {src_name}"
|
||
return GenApi.escape_fstring_copy(f"不能对 const 变量 '{name}' {kind}" \
|
||
f"(声明处类型(含 const):{decl}" \
|
||
f";修法:删掉该声明里的 const 描述符 / t.CConst,或改为声明时一次赋值、" \
|
||
f"之后只用非 const 的副本/中间量;const 对象在其作用域内禁止赋值、" \
|
||
f"增量赋值与元素赋值,且不会降级为警告)")
|
||
|
||
def const_assign_fail(self, node: ast.AST*, kind: str,
|
||
name: str, src_name: str) -> int:
|
||
"""const 目标被写入 → 立即 fail-fast(不降级为警告、不静默继续)。"""
|
||
self.Gen.TypeGenErr.fatal_error(node, self.const_assign_msg(kind, name, src_name), "TypeError")
|
||
return 0
|
||
|
||
def translate_augassign(self, trans: HT.Translator*,
|
||
node: ast.AST*) -> int:
|
||
"""翻译增强赋值语句 (x += 1, y -= 2, etc.)"""
|
||
if node is None:
|
||
return 0
|
||
|
||
aug: rawptr[ast.AugAssign] = rawptr[ast.AugAssign](node)
|
||
if aug is None:
|
||
return 0
|
||
|
||
target: ast.AST* = 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* = trans.Pool
|
||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||
mod: llvmlite.LLVMModule* = trans.Module
|
||
|
||
# const 字段:+= 与 TPC 的 `obj.f = ...` 同一条 fail-fast(字段赋值)
|
||
at_ca: ast.Attribute* = ast.Attribute*(target)
|
||
if at_ca is not None and at_ca.attr is not None:
|
||
if self.Gen.ExprGenAttr.attr_field_is_const(pool, trans, target) != 0:
|
||
self.const_assign_fail(target, "字段赋值", at_ca.attr,
|
||
self.Gen.ExprGenAttr.attr_field_decl_src(pool, trans, target))
|
||
|
||
# 1. 获取字段指针
|
||
field_ptr: llvmlite.Value* = 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* = None
|
||
if field_ptr.Ty is not None and field_ptr.Ty.Pointee != 0:
|
||
target_ty = field_ptr.Ty.Pointee
|
||
if target_ty is None:
|
||
# fail-fast: 字段类型信息缺失,禁止兜底 i32
|
||
self.Gen.TypeGenErr.fatal_error(target, "字段类型信息缺失", "TypeError")
|
||
# t.CVolatile 字段:+= 的 load 与 store 均带 volatile 限定符
|
||
vol_fa: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):经其访问的
|
||
# 字段 += 的 load/store 亦带 volatile(两来源取或)。
|
||
at_aug: ast.Attribute* = ast.Attribute*(target)
|
||
if at_aug is not None:
|
||
if self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, at_aug.value) != 0:
|
||
vol_fa = 1
|
||
cur_val: llvmlite.Value* = llvmlite.build_load(builder, target_ty, field_ptr, vol_fa)
|
||
if cur_val is None:
|
||
# fail-fast: 属性当前值加载失败
|
||
self.Gen.TypeGenErr.fatal_error(target, "无法加载 attribute 当前值", "TypeError")
|
||
|
||
# 3. 翻译 RHS 值
|
||
rhs_val: llvmlite.Value* = 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* = 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, vol_fa)
|
||
return 0
|
||
|
||
# Subscript 目标: self.state[i] += x / arr[i] += x
|
||
# 流程: get_subscript_ptr → load → binop → store
|
||
if tk == ast.ASTKind.Subscript:
|
||
pool: memhub.MemBuddy* = trans.Pool
|
||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||
mod: llvmlite.LLVMModule* = trans.Module
|
||
|
||
# const 元素目标:p[i] += 与 TPC 的 `p[i] = ...` 同一条 fail-fast
|
||
# (元素的元素赋值);基址变量自身或基址字段自身 const 即拒绝。
|
||
sub_ca: ast.Subscript* = ast.Subscript*(target)
|
||
if sub_ca is not None and sub_ca.value is not None:
|
||
if sub_ca.value.kind() == ast.ASTKind.Name:
|
||
nm_ca: ast.Name* = ast.Name*(sub_ca.value)
|
||
if nm_ca.id is not None:
|
||
ve_ca: GenTypes.VarEntry* = \
|
||
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_ca.id)
|
||
if self.Gen.VarGen.var_is_const(ve_ca) != 0:
|
||
self.const_assign_fail(target, "的元素赋值", nm_ca.id,
|
||
self.Gen.VarGen.var_annot_class_name(ve_ca))
|
||
elif sub_ca.value.kind() == ast.ASTKind.Attribute:
|
||
at_ca2: ast.Attribute* = ast.Attribute*(sub_ca.value)
|
||
if at_ca2.attr is not None:
|
||
if self.Gen.ExprGenAttr.attr_field_is_const(pool, trans, sub_ca.value) != 0:
|
||
self.const_assign_fail(target, "的元素赋值", at_ca2.attr,
|
||
self.Gen.ExprGenAttr.attr_field_decl_src(pool, trans, sub_ca.value))
|
||
|
||
# 1. 获取元素指针
|
||
elem_ptr: llvmlite.Value* = 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* = None
|
||
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee != 0:
|
||
target_ty = elem_ptr.Ty.Pointee
|
||
if target_ty is None:
|
||
# fail-fast: 元素类型信息缺失,禁止兜底 i32
|
||
self.Gen.TypeGenErr.fatal_error(target, "元素类型信息缺失", "TypeError")
|
||
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p[i] += 的
|
||
# load/store 均带 volatile。仅基址可解析到 VarEntry/FieldEntry 时为 1。
|
||
vol_sa: int = 0
|
||
sub_aug: ast.Subscript* = ast.Subscript*(target)
|
||
if sub_aug is not None:
|
||
vol_sa = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_aug.value)
|
||
cur_val: llvmlite.Value* = llvmlite.build_load(builder, target_ty, elem_ptr, vol_sa)
|
||
if cur_val is None:
|
||
# fail-fast: 元素当前值加载失败
|
||
self.Gen.TypeGenErr.fatal_error(target, "无法加载 subscript 元素当前值", "TypeError")
|
||
|
||
# 3. 翻译 RHS 值
|
||
rhs_val: llvmlite.Value* = 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* = 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, vol_sa)
|
||
return 0
|
||
|
||
# UnaryOp 目标: *p += v —— 解引用增强赋值。
|
||
# 目标节点是 UnaryOp(Deref)(SetStoreContext 不改 UnaryOp 的 ctx),
|
||
# 与 `*p = v` 同一形态;流程同 Subscript 分支:
|
||
# 操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
|
||
if tk == ast.ASTKind.UnaryOp:
|
||
uo_ag: ast.UnaryOp* = ast.UnaryOp*(target)
|
||
if uo_ag is None or uo_ag.operand is None:
|
||
self.Gen.TypeGenErr.fatal_error(target, "增量赋值目标单目运算缺少操作数", "TypeError")
|
||
if uo_ag.op != ast.OpKind.Deref:
|
||
self.Gen.TypeGenErr.fatal_error(target,
|
||
"增量赋值目标只支持单目 '*' 解引用(&x / -x / ~x 不是合法左值)", "SyntaxError")
|
||
pool: memhub.MemBuddy* = trans.Pool
|
||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||
mod: llvmlite.LLVMModule* = trans.Module
|
||
ptr_ag: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_ag.operand, None, 0, trans)
|
||
if ptr_ag is None or ptr_ag.Ty is None \
|
||
or self.Gen.ExprGenBase.is_ptr_type(ptr_ag.Ty) == 0:
|
||
self.Gen.TypeGenErr.fatal_error(target,
|
||
"增量赋值 '*' 的操作数必须是指针类型(类型推断退化)", "TypeError")
|
||
ty_ag: llvmlite.LLVMType* = ptr_ag.Ty.Pointee
|
||
if ty_ag is None:
|
||
self.Gen.TypeGenErr.fatal_error(target,
|
||
"解引用增量赋值的目标类型缺失(pointee 为空)", "TypeError")
|
||
old_ag: llvmlite.Value* = llvmlite.build_load(builder, ty_ag, ptr_ag, 0)
|
||
if old_ag is None:
|
||
self.Gen.TypeGenErr.fatal_error(target, "无法加载解引用当前值", "TypeError")
|
||
rhs_ag: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans)
|
||
if rhs_ag is None:
|
||
self.Gen.TypeGenErr.fatal_error(target, "rhs 翻译失败", "TypeError")
|
||
res_ag: llvmlite.Value* = self._apply_aug_op(pool, builder, aug.op, old_ag, rhs_ag, node, trans, target)
|
||
if res_ag is None:
|
||
self.Gen.TypeGenErr.fatal_error(target, f"解引用二元运算失败 op={aug.op}", "TypeError")
|
||
res_ag = self.Gen.ExprGenBase.coerce_to_type(builder, res_ag, ty_ag, node, trans)
|
||
if res_ag is None:
|
||
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
|
||
llvmlite.build_store(builder, res_ag, ptr_ag, 0)
|
||
return 0
|
||
|
||
if tk != ast.ASTKind.Name:
|
||
# fail-fast: 不支持的 AugAssign 目标类型
|
||
self.Gen.TypeGenErr.fatal_error(target, "AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用", "TypeError")
|
||
|
||
nm: ast.Name* = ast.Name*(target)
|
||
if nm is None or nm.id is None:
|
||
return 0
|
||
|
||
# const 变量:+= 立即 fail-fast(与 TPC HandlesAugAssign 同判据/同报文)
|
||
ve_aug: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||
if self.Gen.VarGen.var_is_const(ve_aug) != 0:
|
||
self.const_assign_fail(target, "增量赋值", nm.id,
|
||
self.Gen.VarGen.var_annot_class_name(ve_aug))
|
||
|
||
# REPL 模式(M2/M3)会话变量增强赋值:读槽 → 二元运算 → 写槽 → 写结果区。
|
||
# 未登记的会话名返回 0(继续原路径 → 明确的"无法加载…当前值"报错)。
|
||
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 本分支整体不经过。
|
||
# 作用域用 repl_in_frag_entry_scope(M3):被提升的会话函数体内 `x += 1`
|
||
# 按普通局部变量路径处理(不写会话槽)。
|
||
if trans.ReplCtx is not None \
|
||
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
|
||
if self.Gen.VarGen.repl_try_augassign(trans, nm.id, aug.op, aug.value, node) != 0:
|
||
return 0
|
||
|
||
pool: memhub.MemBuddy* = trans.Pool
|
||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||
mod: llvmlite.LLVMModule* = 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
|
||
|
||
# t.CVolatile 变量:+= 的 load 与 store 均带 volatile 限定符
|
||
vol_aa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||
|
||
# 2. 加载当前值
|
||
cur_val: llvmlite.Value* = None
|
||
target_alloca: llvmlite.Value* = None
|
||
target_ty: llvmlite.LLVMType* = 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, vol_aa)
|
||
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, vol_aa)
|
||
|
||
if cur_val is None:
|
||
# fail-fast: 目标变量当前值加载失败
|
||
self.Gen.TypeGenErr.fatal_error(target, f"无法加载目标变量 '{nm.id}' 的当前值", "TypeError")
|
||
|
||
# 3. 翻译 RHS 值
|
||
rhs_val: llvmlite.Value* = 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* = 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, vol_aa)
|
||
elif scope_type == 2:
|
||
# nonlocal 变量
|
||
nl_ptr: llvmlite.Value* = 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, vol_aa)
|
||
|
||
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*) -> 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*) -> 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* = ast.Subscript*(annot)
|
||
if sub.value is None or sub.slice is None:
|
||
return 0
|
||
if sub.value.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(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* = ast.Name*(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 / 内建 array(软关键字):
|
||
# 三者均与 t.CArray 等价
|
||
bn: ast.Name* = ast.Name*(sub.value)
|
||
if bn.id is None or (string.strcmp(bn.id, "CArray") != 0 and string.strcmp(bn.id, "rawarray") != 0 and string.strcmp(bn.id, "array") != 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* = ast.Tuple*(sub.slice)
|
||
if tup.elts is None or len(tup.elts) < 2:
|
||
return 0
|
||
cnt_node: ast.AST* = tup.elts.get(1)
|
||
if cnt_node is not None and cnt_node.kind() == ast.ASTKind.Constant:
|
||
cn0: ast.Constant* = ast.Constant*(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: memhub.MemBuddy*,
|
||
annot: ast.AST*,
|
||
value: ast.AST*,
|
||
resolved_ty: llvmlite.LLVMType*) -> llvmlite.LLVMType*:
|
||
"""单参 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* = resolved_ty.Pointee
|
||
lst_arr: ast.List* = ast.List*(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* = lst_arr.elts.get(0)
|
||
if e0_arr is not None and e0_arr.kind() == ast.ASTKind.Constant:
|
||
c0_arr: ast.Constant* = ast.Constant*(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: memhub.MemBuddy*,
|
||
annot: ast.AST*, 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*,
|
||
node: ast.AST*) -> 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* = ast.AnnAssign*(node)
|
||
if aa is None or aa.target is None:
|
||
return 0
|
||
target: ast.AST* = aa.target
|
||
if target.kind() != ast.ASTKind.Name:
|
||
return 0
|
||
|
||
nm: ast.Name* = ast.Name*(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* = 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
|
||
|
||
# REPL 模式(M2/M3):片段**入口**函数本体内的 AnnAssign 一律走会话槽(见
|
||
# translate_annassign 的会话分支),此处不得预建局部 alloca——否则局部名
|
||
# 会遮蔽同名会话变量(此后读 `x` 命中空局部槽,槽值丢失)。
|
||
# 被提升的会话函数体内不走该分支(那里按普通局部声明预建 alloca)。
|
||
# CDefine/CTypedef 两个分支在本次返回之前,其行为不变。
|
||
if trans.ReplCtx is not None \
|
||
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 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* = self.Gen.VarGen.lookup_current(trans.SymTab, nm.id)
|
||
if existing is not None:
|
||
return 0
|
||
|
||
pool: memhub.MemBuddy* = trans.Pool
|
||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||
|
||
# 从 annotation 推断类型(修复:原硬编码 i32 导致 str 等类型错误)
|
||
var_ty: llvmlite.LLVMType* = None
|
||
annot: ast.AST* = 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* = 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* = 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)
|
||
# const 对象(注解含 t.CConst 且声明类型非指针):整个作用域内
|
||
# 禁止对该变量名赋值/增量赋值/元素赋值。判据与 TPC 的
|
||
# BaseGen._IsConstObject 逐条一致(见 Debugger/type-descriptors.md)。
|
||
if self.Gen.TypeGenErr.is_const_object_marker(annot, var_ty) != 0:
|
||
self.Gen.VarGen.set_var_const(trans.SymTab, nm.id)
|
||
# t.CVolatile:声明期即标记该变量的 load/store 带 volatile。
|
||
# 覆盖「无初值声明」`x: t.CVolatile | T`(有初值路径
|
||
# translate_annassign 亦会登记,幂等)。动机:volatile 禁优化器
|
||
# 把忙等循环的重复 load 提升/消除,不能依赖优化器保序。
|
||
if self.Gen.TypeGenErr.has_decorator_marker(annot, "CVolatile", 0) != 0:
|
||
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||
# rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
|
||
# volatile T*):解引用(p[i]/p.field/c.Deref(p))的 load/store
|
||
# 带 volatile。声明期登记,覆盖「无初值声明」。
|
||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot) != 0:
|
||
self.Gen.VarGen.set_var_pointee_volatile(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*,
|
||
pool: memhub.MemBuddy*,
|
||
annot: ast.AST*) -> int:
|
||
"""泛型注解预特化(见上方注释),返回 1=已尝试 / 0=不适用"""
|
||
if annot is None or trans is None:
|
||
return 0
|
||
# 取注解中的泛型 Subscript 一侧:Subscript 本身,或 A | t.CPtr 的 Subscript 分支
|
||
spec_annot: ast.Subscript* = None
|
||
if annot.kind() == ast.ASTKind.Subscript:
|
||
spec_annot = ast.Subscript*(annot)
|
||
elif annot.kind() == ast.ASTKind.BinOp:
|
||
bop_ps: ast.BinOp* = ast.BinOp*(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*(bop_ps.left)
|
||
elif bop_ps.right is not None and bop_ps.right.kind() == ast.ASTKind.Subscript:
|
||
spec_annot = ast.Subscript*(bop_ps.right)
|
||
if spec_annot is None:
|
||
return 0
|
||
sv_ps: ast.AST* = spec_annot.value
|
||
if sv_ps is None or sv_ps.kind() != ast.ASTKind.Name:
|
||
return 0
|
||
sub_nm: ast.Name* = ast.Name*(sv_ps)
|
||
if sub_nm.id is None:
|
||
return 0
|
||
template_cd: ast.ClassDef* = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
|
||
if template_cd is None:
|
||
return 0
|
||
type_args_ps: list[str]* = 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*,
|
||
var_name: str,
|
||
annot: ast.AST*,
|
||
var_ty: llvmlite.LLVMType*) -> 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* = None
|
||
if annot.kind() == ast.ASTKind.Subscript:
|
||
ann_sub = ast.Subscript*(annot)
|
||
elif annot.kind() == ast.ASTKind.BinOp:
|
||
bop_ff: ast.BinOp* = ast.BinOp*(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*(bop_ff.left)
|
||
elif bop_ff.right is not None and bop_ff.right.kind() == ast.ASTKind.Subscript:
|
||
ann_sub = ast.Subscript*(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* = ast.Name*(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 == 0:
|
||
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*,
|
||
node: ast.AST*) -> int:
|
||
"""翻译 AnnAssign(target=Name, annotation=..., value=expr)"""
|
||
aa: ast.AnnAssign* = ast.AnnAssign*(node)
|
||
if aa is None:
|
||
return 0
|
||
|
||
target: ast.AST* = aa.target
|
||
if target is None or target.kind() != ast.ASTKind.Name:
|
||
return 0
|
||
|
||
nm: ast.Name* = ast.Name*(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* = trans.Pool
|
||
self._register_cdefine_from_value(pool_cd, nm.id, aa.value, aa.annotation)
|
||
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: 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
|
||
|
||
# define / CDefine 常量的**使用处宽度**(`a: T = C` 场景):RHS 是已注册常量
|
||
# 裸名且目标注解是整数类型时,T 的位宽不得小于该常量的 W_min(否则会静默
|
||
# 截断);更宽则照常(消费端按 T 物化/升宽)。判据唯一源与 TPC
|
||
# SymbolUtils.RequireDefineUseWidth 同口径。
|
||
self.require_cdefine_use_width(trans.Pool, trans, aa.annotation, aa.value)
|
||
|
||
# REPL 模式(M2/M3)会话变量:片段**入口**函数本体内的带注解声明/赋值
|
||
# (`x: int = 5` 定义并定型;`x: int` 仅定型)。注解先解析并定型条目,
|
||
# 定型后的槽类型再作为 RHS 的 target_ty(与普通 AnnAssign 的 P1-A 同口径)。
|
||
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 本分支整体不经过。
|
||
# 被提升的会话函数体内不走该分支(`x: int = 5` 是局部声明,Python 语义)。
|
||
if trans.ReplCtx is not None \
|
||
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
|
||
rp_e: rawptr[GenTypes.ReplVarEntry] = \
|
||
self.Gen.VarGen.repl_ensure_entry(trans, nm.id, aa)
|
||
if rp_e is not None:
|
||
self.Gen.VarGen.repl_try_annassign(trans, nm.id, aa.annotation, aa.value, aa)
|
||
return 0
|
||
|
||
# 重复声明检测(fail-fast,先于注解解析与 RHS 翻译):
|
||
# 模块级已有同名变量时,函数内**再次用注解声明**同名变量一律判为重复声明:
|
||
# ① 无 global/nonlocal 声明 → 会把全局遮蔽成局部,属重复声明
|
||
# ② 有 global/nonlocal 声明 → 注解与既有全局声明重复,属重复声明
|
||
# 判据**不含**"类型是否一致":注解类型即便与全局完全相同也是重复声明
|
||
# (无条件失败)。同口径的合法写法只有两种:
|
||
# - 修改全局:函数内 `global x` + **无注解**赋值 `x = v`
|
||
# - 独立局部:换一个不与全局重名的变量名
|
||
# 目的:不让错误延后到 RHS 的 coerce_to_type,暴露成与根因无关的
|
||
# 「指针隐式转整数」(如 PointerTest/App/main.vp:46 `_g_ptr: int = None`:
|
||
# 全局已是 t.CUInt32T | t.CPtr)。范围仅函数内——模块级自身的重复注解
|
||
# 不归本检查(模块级由 handle_module_level_var 登记,本就不构成"遮蔽")。
|
||
if nm.id is not None and trans is not None \
|
||
and self.Gen.VarGen.is_module_scope(trans.SymTab) == 0 \
|
||
and self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id) is not None:
|
||
dup_decl: int = 0
|
||
if GenApi.is_global_name(trans, nm.id) != 0 \
|
||
or GenApi.is_nonlocal_name(trans, nm.id) != 0:
|
||
dup_decl = 1
|
||
if dup_decl != 0:
|
||
self.Gen.TypeGenErr.fatal_error(aa,
|
||
f"重复声明:'{nm.id}' 已是模块级变量,此处声明 global/nonlocal 之后"
|
||
f"又用类型注解重新声明。请删除本行的类型注解,直接赋值即可修改全局",
|
||
"TypeError")
|
||
else:
|
||
self.Gen.TypeGenErr.fatal_error(aa,
|
||
f"重复声明:'{nm.id}' 已是模块级变量,函数内未声明 global 时不得再用"
|
||
f"类型注解声明同名变量(会把全局遮蔽为局部)。如需独立局部变量请换名;"
|
||
f"如需修改全局请在函数内加 global 声明并删除本行注解",
|
||
"TypeError")
|
||
|
||
pool: memhub.MemBuddy* = trans.Pool
|
||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||
mod: llvmlite.LLVMModule* = trans.Module
|
||
|
||
# 确定类型(从 annotation 推断)
|
||
var_ty: llvmlite.LLVMType* = None
|
||
annot: ast.AST* = 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* = 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* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, annot, trans._imported_modules, trans._from_imports, trans)
|
||
if callable_ret is not None:
|
||
# 返回值:0=成功/同名同类型幂等命中;非 0 已在 register_func_ptr_sig
|
||
# 内部 fatal_error(表满/同名不同类型/表分配失败)终止编译。此处分支
|
||
# 仅为防御——报错通道异常时不带着未登记的签名继续翻译。
|
||
if self.Gen.CallGenLeafKit.register_func_ptr_sig(pool, nm.id, callable_ret, aa) != 0:
|
||
return 0
|
||
|
||
# 值位置 volatile 调用前缀:`a: int = volatile f()` / `a: S = volatile S(...)`
|
||
# ⇒ 结果槽按 volatile 存取(下述三条"条目已先存在"的分支——global /
|
||
# nonlocal / mod_glob——在此统一登记,使各分支读取 var_is_volatile 时已置位)。
|
||
# 函数内**新声明**的局部变量此刻条目尚未建立,由主分支(alloca 就绪后)登记。
|
||
# LLVM 的 call 无 volatile 限定符,故语义落在内存存取上:本次 store 带
|
||
# volatile,且此后该变量的 load/store 亦带,避免这次调用/构造的往返访问
|
||
# 被优化器消除或提升。
|
||
if aa.value is not None and aa.value.kind() == ast.ASTKind.Call \
|
||
and (ast.Call*(aa.value)).IsVolatile != 0 and nm.id is not None:
|
||
if self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id) is not None:
|
||
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||
|
||
# global 变量:写入模块作用域中的全局变量
|
||
if GenApi.is_global_name(trans, nm.id) != 0:
|
||
mod_alloca: llvmlite.Value* = 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* = None
|
||
if mod_alloca.Ty is not None:
|
||
aa_glob_ty = mod_alloca.Ty.Pointee
|
||
# 供裸名泛型构造从注解推断类型实参(消费者见 CallGenCtor.vp 的
|
||
# CurAssignNode 判定):与主分支(普通局部变量路径)同一口径——
|
||
# 必须在翻译 RHS 之前写入 CurAssignAnnot/CurAssignNode。四处
|
||
# (主分支 / 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal
|
||
# 分支)必须同步,否则 `s: Stack[int] = Stack()` 的类型实参推断
|
||
# 会拿不到注解。
|
||
trans.CurAssignAnnot = aa.annotation
|
||
trans.CurAssignNode = aa.value
|
||
rhs_val: llvmlite.Value* = 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* = 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 != 0:
|
||
stg_ty_aa: llvmlite.LLVMType* = stg_ref_aa.Ty.Pointee
|
||
rhs_inner_aa: llvmlite.LLVMType* = 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* = 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* = 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)
|
||
# t.CVolatile(global 声明)全局:初始化 store 带 volatile 限定符
|
||
vol_gaa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||
llvmlite.build_store(builder, rhs_val, mod_alloca, vol_gaa)
|
||
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
|
||
# 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步清除,
|
||
# 否则残留注解会污染后续语句里裸名泛型构造的类型实参推断
|
||
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* = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(trans, nm.id)
|
||
# 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
|
||
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
|
||
nl_target_ty: llvmlite.LLVMType* = None
|
||
if nl_ptr is not None and nl_ptr.Ty is not None:
|
||
nl_target_ty = nl_ptr.Ty.Pointee
|
||
# 供裸名泛型构造从注解推断类型实参:与主分支、上方 global 分支、
|
||
# 下方 mod_glob 分支同一口径——必须在翻译 RHS 之前写入
|
||
# CurAssignAnnot/CurAssignNode。四处(主分支 / global / mod_glob /
|
||
# 本 nonlocal 分支)必须同步,否则
|
||
# `nonlocal s: Stack[int] = Stack()` 的类型实参推断会拿不到注解。
|
||
trans.CurAssignAnnot = aa.annotation
|
||
trans.CurAssignNode = aa.value
|
||
rhs_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aa.value,
|
||
None, 0, trans, nl_target_ty, "闭包变量(nonlocal)")
|
||
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* = 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 分支)必须同步清除,否则残留注解会
|
||
# 污染后续语句里裸名泛型构造的类型实参推断
|
||
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* = 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* = None
|
||
if mod_glob.Ty is not None:
|
||
mg_val_ty = mod_glob.Ty.Pointee
|
||
# 供裸名泛型构造从注解推断类型实参:与主分支、上方 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* = 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* = 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 != 0:
|
||
stg_ty_mg: llvmlite.LLVMType* = stg_ref_mg.Ty.Pointee
|
||
rhs_inner_mg: llvmlite.LLVMType* = 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* = 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* = 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)
|
||
# t.CVolatile 模块级全局:初始化 store 带 volatile 限定符
|
||
vol_mga: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||
llvmlite.build_store(builder, rhs_val_mg, mod_glob, vol_mga)
|
||
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
|
||
# 上方 global 分支 / 本 mod_glob 分支 / 上方 nonlocal 分支)必须同步清除,
|
||
# 否则残留注解会污染后续语句里裸名泛型构造的类型实参推断
|
||
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* = 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* = 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* = av_pt_d.Pointee
|
||
vt_in: llvmlite.LLVMType* = 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* = 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* = None
|
||
fd_meta_alloca: llvmlite.Value* = None
|
||
fd_meta_name: str = None
|
||
if annot is not None and annot.kind() == ast.ASTKind.Name:
|
||
fd_annot: ast.Name* = ast.Name*(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* = 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* = ast.Call*(aa.value)
|
||
if val_cl.func is not None and val_cl.func.kind() == ast.ASTKind.Name:
|
||
val_nm: ast.Name* = ast.Name*(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* = ast.Attribute*(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* = val_cl.args.get(0)
|
||
if icall is not None and icall.kind() == ast.ASTKind.Call:
|
||
icl2: ast.Call* = ast.Call*(icall)
|
||
if icl2.func is not None and icl2.func.kind() == ast.ASTKind.Name:
|
||
inm2: ast.Name* = ast.Name*(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* = None
|
||
if aa.value.kind() == ast.ASTKind.List and alloca.Ty is not None:
|
||
pt_l: llvmlite.LLVMType* = 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* = 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:
|
||
# 供裸名泛型构造从注解推断类型实参(消费者见 CallGenCtor.vp 的
|
||
# CurAssignNode 判定:`s: Stack[int] = Stack()` 用注解里的 int 特化)。
|
||
# 注意:本主分支(普通局部变量)、上方 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* = None
|
||
if alloca.Ty is not None:
|
||
lv_target_ty = alloca.Ty.Pointee
|
||
rhs_val: llvmlite.Value* = 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* = 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* = 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* = annot
|
||
if ag_side.kind() == ast.ASTKind.BinOp:
|
||
ag_bop: ast.BinOp* = ast.BinOp*(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* = ast.Subscript*(ag_side)
|
||
if ag_sub.value is not None and ag_sub.value.kind() == ast.ASTKind.Name:
|
||
ag_nm: ast.Name* = ast.Name*(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* = 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* = self.Gen.VarGen._alloca_at_entry(builder, precise_rhs_ty)
|
||
if new_alloca is not None:
|
||
v_entry_rb: GenTypes.VarEntry* = 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* = 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)
|
||
# t.CVolatile 局部变量:标记 VarEntry 并让本次 store 带 volatile
|
||
vol_laa: int = 0
|
||
if aa.annotation is not None and nm.id is not None:
|
||
# const 对象局部变量:登记 VarEntry.IsConst(与 prescan 幂等)。
|
||
# 用 alloca 的值类型(target_ty)判指针性,判据同 TPC。
|
||
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, target_ty) != 0:
|
||
self.Gen.VarGen.set_var_const(trans.SymTab, nm.id)
|
||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "CVolatile", 0) != 0:
|
||
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||
vol_laa = 1
|
||
# rawptr[t.CVolatile | T](volatile 指针指向物):登记
|
||
# PointeeVolatile;本 store 写的是指针变量自身,不影响。
|
||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0:
|
||
self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
|
||
# 值位置 volatile 调用前缀:`a: int = volatile f()` /
|
||
# `a: S = volatile S(...)`——LLVM 的 call 指令没有 volatile
|
||
# 限定符,故语义落在内存存取上:结果槽按 volatile 存取
|
||
# (本次 store 立即带 volatile,且此后该变量的 load/store 也带),
|
||
# 使"这次调用/构造的往返内存访问"不会被优化器消除或提升。
|
||
if aa.value is not None and aa.value.kind() == ast.ASTKind.Call \
|
||
and (ast.Call*(aa.value)).IsVolatile != 0:
|
||
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||
vol_laa = 1
|
||
llvmlite.build_store(builder, rhs_val, alloca, vol_laa)
|
||
# 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* = \
|
||
self.Gen.StructGen.lookup_field(fd_str_ty, "__data__")
|
||
if fd_data_fe is not None:
|
||
fd_data_ptr: llvmlite.Value* = \
|
||
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 翻译期间有效,否则后续语句里裸名泛型构造的类型
|
||
# 实参推断会拿到残留注解。
|
||
# 四处(本主分支 / 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* = \
|
||
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* = llvmlite.ConstNull(pool, fd_buddy_fe.Ty, "null")
|
||
fd_buddy_ptr: llvmlite.Value* = \
|
||
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*,
|
||
node: ast.AST*) -> int:
|
||
"""翻译 Assign 语句,返回新增变量数(0 或 1)"""
|
||
|
||
asgn: ast.Assign* = ast.Assign*(node)
|
||
if asgn is None:
|
||
VLogger.error("cast failed", "ASGN")
|
||
return 0
|
||
|
||
targets: list[ast.AST*]* = asgn.targets
|
||
if targets is None:
|
||
VLogger.error("targets is None", "ASGN")
|
||
return 0
|
||
|
||
# 从 trans 取共享状态(替代 11 个参数)
|
||
pool: memhub.MemBuddy* = trans.Pool
|
||
builder: llvmlite.IRBuilder* = trans._cur_builder
|
||
mod: llvmlite.LLVMModule* = trans.Module
|
||
|
||
# const 目标:赋值前即 fail-fast(与 TPC HandlesAssign 同判据/同报文)。
|
||
# 覆盖 Name(变量自身)/ Attribute(字段自身)/ Subscript(元素赋值,
|
||
# 基址是 const 变量或 const 字段时拒绝)。放在 RHS 翻译之前,避免先发射
|
||
# 无用 IR 再报错。
|
||
for ti_c in range(len(targets)):
|
||
tgt_c: ast.AST* = targets.get(ti_c)
|
||
if tgt_c is None:
|
||
continue
|
||
if tgt_c.kind() == ast.ASTKind.Name:
|
||
nm_c: ast.Name* = ast.Name*(tgt_c)
|
||
if nm_c.id is None:
|
||
continue
|
||
ve_c: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_c.id)
|
||
if self.Gen.VarGen.var_is_const(ve_c) != 0:
|
||
self.const_assign_fail(tgt_c, "赋值", nm_c.id,
|
||
self.Gen.VarGen.var_annot_class_name(ve_c))
|
||
elif tgt_c.kind() == ast.ASTKind.Attribute:
|
||
at_c: ast.Attribute* = ast.Attribute*(tgt_c)
|
||
if at_c.attr is None:
|
||
continue
|
||
if self.Gen.ExprGenAttr.attr_field_is_const(pool, trans, tgt_c) != 0:
|
||
self.const_assign_fail(tgt_c, "字段赋值", at_c.attr,
|
||
self.Gen.ExprGenAttr.attr_field_decl_src(pool, trans, tgt_c))
|
||
elif tgt_c.kind() == ast.ASTKind.Subscript:
|
||
sub_c: ast.Subscript* = ast.Subscript*(tgt_c)
|
||
if sub_c.value is None:
|
||
continue
|
||
if sub_c.value.kind() == ast.ASTKind.Name:
|
||
nm_c2: ast.Name* = ast.Name*(sub_c.value)
|
||
if nm_c2.id is None:
|
||
continue
|
||
ve_c2: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_c2.id)
|
||
if self.Gen.VarGen.var_is_const(ve_c2) != 0:
|
||
self.const_assign_fail(tgt_c, "的元素赋值", nm_c2.id,
|
||
self.Gen.VarGen.var_annot_class_name(ve_c2))
|
||
elif sub_c.value.kind() == ast.ASTKind.Attribute:
|
||
at_c2: ast.Attribute* = ast.Attribute*(sub_c.value)
|
||
if at_c2.attr is None:
|
||
continue
|
||
if self.Gen.ExprGenAttr.attr_field_is_const(pool, trans, sub_c.value) != 0:
|
||
self.const_assign_fail(tgt_c, "的元素赋值", at_c2.attr,
|
||
self.Gen.ExprGenAttr.attr_field_decl_src(pool, trans, sub_c.value))
|
||
|
||
# f-string 逃逸:f-string 结果是**当前函数内的临时值**(栈缓冲,见
|
||
# ExprGenFstr._fstr_finalize),写进字段 / 元素 / 全局等函数外可见的位置即
|
||
# 悬空(调用方之后读到已弹栈的帧)。局部 Name 目标允许——生命周期与 f-string
|
||
# 缓冲同在当前函数。判据与拷贝出口见 GenApi.fail_if_fstring_escape 头注释。
|
||
for ti_f in range(len(targets)):
|
||
tgt_f: ast.AST* = targets.get(ti_f)
|
||
if tgt_f is None:
|
||
continue
|
||
tk_f: int = tgt_f.kind()
|
||
if tk_f == ast.ASTKind.Attribute:
|
||
GenApi.fail_if_fstring_escape(asgn.value, "字段赋值")
|
||
elif tk_f == ast.ASTKind.Subscript:
|
||
GenApi.fail_if_fstring_escape(asgn.value, "元素赋值")
|
||
elif tk_f == ast.ASTKind.Name:
|
||
nm_f: ast.Name* = ast.Name*(tgt_f)
|
||
if nm_f.id is not None and GenApi.is_global_name(trans, nm_f.id) != 0:
|
||
GenApi.fail_if_fstring_escape(asgn.value, "全局变量赋值")
|
||
|
||
# 翻译 RHS 值
|
||
# 无注解赋值无目标类型可推断,清空 CurAssignAnnot/CurAssignNode
|
||
# (防残留污染裸名泛型构造的类型实参推断)
|
||
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* = None
|
||
if len(targets) == 1:
|
||
asg_t0: ast.AST* = targets.get(0)
|
||
if asg_t0 is not None and asg_t0.kind() == ast.ASTKind.Name:
|
||
asg_nm0: ast.Name* = ast.Name*(asg_t0)
|
||
if asg_nm0.id is not None:
|
||
asg_slot: llvmlite.Value* = 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
|
||
elif trans.ReplCtx is not None \
|
||
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
|
||
# REPL 模式(M2/M3):已定型的会话变量也用槽类型做 target_ty
|
||
# (与 P1-A 同口径;未定型则维持 None,由 RHS 类型定型)。
|
||
# 只在片段入口函数生效(被提升的会话函数体内同名赋值是局部变量)
|
||
asg_re: rawptr[GenTypes.ReplVarEntry] = \
|
||
self.Gen.VarGen.repl_find_entry(trans.ReplCtx, asg_nm0.id)
|
||
if asg_re is not None \
|
||
and asg_re.Kind != GenTypes.REPL_KIND_UNTYPED:
|
||
asg_target_ty = self.Gen.VarGen.repl_slot_ty(pool, asg_re)
|
||
rhs_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, asgn.value, None, 0, trans, asg_target_ty)
|
||
if rhs_val is None:
|
||
# 归属:RHS 为 None 多数是分配失败(池耗尽)。“[MEM] alloc 失败”只给池基址,
|
||
# 分不清是主池(mark/reset 回收)还是常驻池(从不 reset)满,也无法知道
|
||
# 当前阶段/文件。故此处把两个池的用量与池标识一并打出来。
|
||
mb_diag: memhub.MemBuddy* = CompilerFactory.factory.Pool
|
||
pp_diag: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||
if mb_diag is not None:
|
||
mb_diag.dump_stats("主池(Pool)")
|
||
if pp_diag is not None:
|
||
pp_diag.dump_stats("常驻池(PermPool)")
|
||
stdio.fflush(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* = ast.Attribute*(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* = ast.Name*(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* = ast.Name*(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* = ast.Call*(asgn.value)
|
||
func_nm: str = "?"
|
||
if cl_nv.func is not None:
|
||
if cl_nv.func.kind() == ast.ASTKind.Name:
|
||
fn_nm: ast.Name* = ast.Name*(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* = targets.get(ti)
|
||
if target is None:
|
||
continue
|
||
|
||
tk: int = target.kind()
|
||
|
||
# Tuple/List 赋值: a, b = f() / [a, b] = f() 多返回值解包
|
||
# rhs_val 是匿名 struct 值(函数返回 tuple[T1,T2] 打包)→ alloca +
|
||
# store + GEP 逐字段 load → store 到各 Name 目标。
|
||
# ast.List 与 ast.Tuple 字段表逐字相同(Nodes.py:119-120 均为
|
||
# ('elts','ctx')),故按 Tuple 视图统一取 elts;此前只认 Tuple ⇒
|
||
# `[a, b] = f()` 落入无分支区被静默丢弃(不落盘、不报错)。
|
||
if tk == ast.ASTKind.Tuple or tk == ast.ASTKind.List:
|
||
tgt_tup: ast.Tuple* = ast.Tuple*(target)
|
||
tgt_elts: list[ast.AST*]* = 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* = llvmlite.build_alloca(builder, rhs_val.Ty)
|
||
if tmp_s is not None:
|
||
llvmlite.build_store(builder, rhs_val, tmp_s)
|
||
fchain: llvmlite.Function* = llvmlite.get_struct_fields(rhs_val.Ty)
|
||
fcur: llvmlite.Function* = fchain
|
||
fti: sizet
|
||
for fti in range(tgt_n):
|
||
tgt_elt: ast.AST* = tgt_elts.get(fti)
|
||
if tgt_elt is None or fcur is None:
|
||
break
|
||
fty: llvmlite.LLVMType* = 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* = ast.Name*(tgt_elt)
|
||
if tgt_nm2.id is None:
|
||
continue
|
||
fp2: llvmlite.Value* = llvmlite.build_gep_struct(builder, rhs_val.Ty, fty, tmp_s, fti)
|
||
if fp2 is None:
|
||
continue
|
||
fv2: llvmlite.Value* = llvmlite.build_load(builder, fty, fp2)
|
||
tgt_alloca: llvmlite.Value* = 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* = fv2
|
||
if tgt_alloca.Ty is not None and tgt_alloca.Ty.Pointee != 0:
|
||
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* = self.Gen.ExprGenName.is_list_subscript(target, trans)
|
||
if list_obj is not None:
|
||
# list[T] 类型: 内联生成 __setitem__ 逻辑
|
||
sub_node: ast.Subscript* = ast.Subscript*(target)
|
||
if sub_node is not None and sub_node.slice is not None:
|
||
list_idx_val: llvmlite.Value* = 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* = self.Gen.ExprGenName.get_subscript_ptr(builder, pool, mod, target, trans)
|
||
if elem_ptr is not None:
|
||
store_val: llvmlite.Value* = rhs_val
|
||
# fail-fast:rawptr[T] 元素为对象 T、而 RHS 是指向 T 的指针时,
|
||
# 下游 coerce_to_type 会静默 load 整对象再按 sizeof(T) 存储
|
||
# (写 sizeof(T) 而非 8 字节指针)。数组槽(t.CArray[T,N])除外。
|
||
self._reject_whole_object_copy_into_ptr_slot(pool, trans, target, elem_ptr, rhs_val)
|
||
# 守卫短路:elem 类型完整才做类型转换
|
||
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee != 0:
|
||
store_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, elem_ptr.Ty.Pointee, node, trans)
|
||
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p[i] = v
|
||
# 的 store 带 volatile。仅基址可解析到条目时为 1(否则文本不变)。
|
||
vol_sw: int = 0
|
||
sub_wr: ast.Subscript* = ast.Subscript*(target)
|
||
if sub_wr is not None:
|
||
vol_sw = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_wr.value)
|
||
llvmlite.build_store(builder, store_val, elem_ptr, vol_sw)
|
||
continue
|
||
# get_subscript_ptr 返回 None: 尝试 __setitem__ 运算符重载
|
||
# 适用于自定义类(如 hashtable[key]=val → hashtable.__setitem__(key, val))
|
||
sub_asgn: ast.Subscript* = ast.Subscript*(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* = ast.Name*(sub_asgn.value)
|
||
if sub_nm.id is not None:
|
||
sub_alloca: llvmlite.Value* = 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 != 0 \
|
||
and self.Gen.ExprGenBase.is_ptr_type(sub_alloca.Ty) != 0:
|
||
sub_pointee: llvmlite.LLVMType* = sub_alloca.Ty.Pointee
|
||
cls_nm_set: str = self.Gen.StructGen.get_class_name_by_type(pool, sub_pointee)
|
||
obj_val_set: llvmlite.Value* = 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 != 0:
|
||
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* = 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* = 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* = 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 != 0 \
|
||
and self.Gen.ExprGenBase.is_ptr_type(field_ptr_set.Ty) != 0:
|
||
field_pointee_set: llvmlite.LLVMType* = 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* = 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 != 0:
|
||
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* = 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* = 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* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub_asgn.value, None, 0, trans)
|
||
key_val_gen: llvmlite.Value* = 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 != 0:
|
||
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* = 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 != 0:
|
||
# 非自定义结构体: 直接 GEP + store
|
||
# 处理 get_subscript_ptr 因边界情况返回 None 的指针下标
|
||
elem_gen: llvmlite.LLVMType* = obj_val_gen.Ty.Pointee
|
||
store_val_gen: llvmlite.Value* = rhs_val
|
||
elem_ptr_gen: llvmlite.Value* = 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 != 0:
|
||
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
|
||
|
||
# UnaryOp 目标: *p = val —— 解引用写入(与读侧同一节点形态)。
|
||
# 解析链:语句级 Assign 走 vast_c.c ParseSimpleStmt 的
|
||
# ParseExprList + SetStoreContext;`*p` 由 ParseFactor 产出
|
||
# UnaryOp(Deref)(与 `*p` 读侧完全同形),而 SetStoreContext 只改
|
||
# Attribute/Subscript/Name/Tuple/List/Starred 的 ctx,**不改 UnaryOp**
|
||
# ⇒ 赋值目标就是 ctx=Load 的 UnaryOp(Deref)。
|
||
# 此前目标分派(Tuple/Subscript/Attribute/Name)无 UnaryOp 分支且无兜底
|
||
# ⇒ 该语句被整条静默丢弃(既不落盘也不报错):memhub 的块头
|
||
# `(order<<1)|1`、空闲链 next、canary 三处写入全不发射 ⇒ 块头恒为 0
|
||
# (free 被拒)、free_lists 链读到未初始化 0xBE、池单调漏满 ⇒ 1 GiB OOM。
|
||
# `&x`/`-x` 作目标不是合法左值,故除 Deref 外一律 fail-fast。
|
||
if tk == ast.ASTKind.UnaryOp:
|
||
uo_w: ast.UnaryOp* = ast.UnaryOp*(target)
|
||
if uo_w is None or uo_w.operand is None:
|
||
self.Gen.TypeGenErr.fatal_error(target, "赋值目标单目运算缺少操作数", "TypeError")
|
||
if uo_w.op != ast.OpKind.Deref:
|
||
self.Gen.TypeGenErr.fatal_error(target,
|
||
"赋值目标只支持单目 '*' 解引用(&x / -x / ~x 不是合法左值)", "SyntaxError")
|
||
ptr_w: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_w.operand, None, 0, trans)
|
||
if ptr_w is None or ptr_w.Ty is None \
|
||
or self.Gen.ExprGenBase.is_ptr_type(ptr_w.Ty) == 0:
|
||
self.Gen.TypeGenErr.fatal_error(target,
|
||
"单目 '*' 赋值的操作数必须是指针类型(类型推断退化)", "TypeError")
|
||
pointee_w: llvmlite.LLVMType* = ptr_w.Ty.Pointee
|
||
if pointee_w is None:
|
||
self.Gen.TypeGenErr.fatal_error(target,
|
||
"解引用写入的目标类型缺失(pointee 为空)", "TypeError")
|
||
sv_w: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, pointee_w, node, trans)
|
||
llvmlite.build_store(builder, sv_w, ptr_w)
|
||
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* = ast.Attribute*(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* = ast.Name*(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* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_meta_w)
|
||
if fd_meta_vw is not None:
|
||
fd_sty_w: llvmlite.LLVMType* = \
|
||
self.Gen.StructGen.get_struct_type("_str")
|
||
if fd_sty_w is not None:
|
||
fd_fe_w: GenTypes.FieldEntry* = \
|
||
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* = \
|
||
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* = \
|
||
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* = 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* = rhs_val
|
||
if field_ptr.Ty is not None and field_ptr.Ty.Pointee != 0:
|
||
store_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, field_ptr.Ty.Pointee, node, trans)
|
||
# t.CVolatile 字段:store 带 volatile 限定符
|
||
vol_fw: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p.field = v
|
||
# 的 store 亦带 volatile(两来源取或)。
|
||
at_wr: ast.Attribute* = ast.Attribute*(target)
|
||
if at_wr is not None:
|
||
if self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, at_wr.value) != 0:
|
||
vol_fw = 1
|
||
llvmlite.build_store(builder, store_val, field_ptr, vol_fw)
|
||
continue
|
||
# 守卫:field_ptr 为 None → 构造详细错误信息
|
||
attr_node: ast.Attribute* = ast.Attribute*(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* = ast.Name*(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* = ast.Name*(target)
|
||
# 守卫:target 名缺失时跳过
|
||
if nm.id is None:
|
||
continue
|
||
# global 变量:写入模块作用域中的全局变量
|
||
if GenApi.is_global_name(trans, nm.id) != 0:
|
||
mod_alloca: llvmlite.Value* = 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* = 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 != 0:
|
||
stg_ty_as: llvmlite.LLVMType* = stg_ref_as.Ty.Pointee
|
||
rhs_inner_as: llvmlite.LLVMType* = 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* = 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* = 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)
|
||
# t.CVolatile 全局:store 带 volatile 限定符
|
||
vol_gas: int = 0
|
||
if nm.id is not None:
|
||
vol_gas = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||
llvmlite.build_store(builder, rhs_val, mod_alloca, vol_gas)
|
||
continue
|
||
|
||
# nonlocal 变量:通过闭包 env 写入
|
||
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
|
||
nl_ptr: llvmlite.Value* = 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* = 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* = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, nl_target_ty, node, trans)
|
||
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
|
||
continue
|
||
|
||
# REPL 模式(M2/M3)会话变量:片段**入口**函数本体内的 Name 目标即
|
||
# 会话变量(首次出现=定义:申请槽位 + 按 RHS 定型 + 写槽 + 写结果区)。
|
||
# 守卫 trans.ReplCtx(非 REPL 恒 None ⇒ 本分支整体不经过);
|
||
# repl_in_frag_entry_scope 排除被提升的会话函数体与片段内嵌套函数体
|
||
# (那些仍用普通局部 alloca:Python 局部语义 + 防形参同名串味)。
|
||
if trans.ReplCtx is not None \
|
||
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
|
||
rp_entry: rawptr[GenTypes.ReplVarEntry] = \
|
||
self.Gen.VarGen.repl_ensure_entry(trans, nm.id, target)
|
||
if rp_entry is not None:
|
||
self.Gen.VarGen.repl_try_assign(trans, nm.id, rhs_val, asgn.value, node)
|
||
continue
|
||
|
||
# 拦截:函数内对模块级全局变量赋值但没 global 声明
|
||
# 否则静默创建局部变量遮蔽全局,全局保持 null 导致运行时崩溃
|
||
# (_mbuddy = mb 缺 global 声明即此类 bug:Int32 解引用 null 崩溃)
|
||
if self.Gen.VarGen.is_module_scope(trans.SymTab) == 0:
|
||
mod_gv: llvmlite.Value* = 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* = 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* = rhs_val
|
||
if alloca.Ty is not None and alloca.Ty.Pointee != 0:
|
||
store_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, alloca.Ty.Pointee, node, trans)
|
||
# t.CVolatile 局部变量:store 带 volatile 限定符
|
||
vol_las: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||
llvmlite.build_store(builder, store_val, alloca, vol_las)
|
||
# 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* = 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
|
||
|
||
# ============================================================
|
||
# _reject_whole_object_copy_into_ptr_slot - 下标赋值的"整对象拷贝"fail-fast
|
||
#
|
||
# 目标形态(唯一的判据):赋值左值是 **rawptr[T] 下标**(元素类型 = 对象/结构体
|
||
# T,基座是指针存储),而 RHS 的**值类型是指向 T 的指针**。此时下游
|
||
# coerce_to_type 的"指针 → 非指针值: build_load 解引用"分支会生成
|
||
# %v = load %T, %T* %rhs
|
||
# store %T %v, %T* %elem (写 sizeof(T) 字节)
|
||
# 而语义上该槽只该写 8 字节指针(槽是按 8 字节排布的缓冲/空闲链表)⇒
|
||
# 每写一个元素越界 sizeof(T)-8 字节,写穿相邻块(MemBuddy 空闲链表被写穿的
|
||
# 静默降级根因)。
|
||
#
|
||
# 不拦(判据刻意收窄,避免误伤):
|
||
# · 元素是标量(rawptr[int] 等):元素类型不是 Struct,直接返回;
|
||
# · 元素本身是指针(rawptr[rawptr[T]]、rawptr[T]|t.CPtr、str|t.CPtr):
|
||
# elem_ptr.Ty.Pointee 是 Ptr,不是 Struct,直接返回;
|
||
# · 数组槽(t.CArray[T,N] / 内建 Array):subscript_base_store_kind==1,
|
||
# `v[i] = T(...)`(构造函数返回临时对象地址)是合法的整对象复制;
|
||
# · RHS 的所指类型与元素类型不同、或基座无法静态定型(kind==-1):
|
||
# 不表态,保持既有 coerce 行为(不新增静默降级,也不误拦)。
|
||
# · list[rawptr[T]].set / list.__setitem__:走 is_list_subscript 的
|
||
# 内联路径或 list_setitem_inline,不经过本函数。
|
||
# ============================================================
|
||
def _reject_whole_object_copy_into_ptr_slot(self, pool: memhub.MemBuddy*,
|
||
trans: HT.Translator*,
|
||
target: ast.AST*,
|
||
elem_ptr: llvmlite.Value*,
|
||
rhs_val: llvmlite.Value*) -> None:
|
||
"""rawptr[T] 下标赋值 + RHS 为指向 T 的指针 ⇒ fatal_error(整对象拷贝)"""
|
||
if elem_ptr is None or elem_ptr.Ty is None:
|
||
return
|
||
if rhs_val is None or rhs_val.Ty is None:
|
||
return
|
||
elem_ty: llvmlite.LLVMType* = None
|
||
match elem_ptr.Ty:
|
||
case llvmlite.LLVMType.Ptr(pointee_wo):
|
||
elem_ty = pointee_wo
|
||
case _:
|
||
return
|
||
if elem_ty is None:
|
||
return
|
||
# 元素必须是对象(结构体)。标量元素(rawptr[int])与指针元素
|
||
# (rawptr[rawptr[T]] / X|t.CPtr)在此返回。
|
||
if elem_ty.kind() != llvmlite.LLVMType.Struct:
|
||
return
|
||
rhs_inner_wo: llvmlite.LLVMType* = None
|
||
match rhs_val.Ty:
|
||
case llvmlite.LLVMType.Ptr(pointee_rhs_wo):
|
||
rhs_inner_wo = pointee_rhs_wo
|
||
case _:
|
||
return
|
||
if rhs_inner_wo is None:
|
||
return
|
||
if rhs_inner_wo.kind() != llvmlite.LLVMType.Struct:
|
||
return
|
||
# 所指类型必须与元素类型一致(否则不是"隐式解引用得 T 再整对象存储")
|
||
if self._stg_ty_equal(pool, elem_ty, rhs_inner_wo) == 0:
|
||
return
|
||
# 数组槽(t.CArray[T,N])整对象赋值合法 → 不拦;
|
||
# 仅基座确认为指针存储(kind==0)时才拦,-1(无法判定)不表态。
|
||
if self.Gen.ExprGenName.subscript_base_store_kind(pool, trans, target) != 0:
|
||
return
|
||
elem_nm_wo: str = llvmlite.get_struct_name(elem_ty)
|
||
if elem_nm_wo is None:
|
||
elem_nm_wo = "<匿名结构体>"
|
||
rhs_nm_wo: str = llvmlite.get_struct_name(rhs_inner_wo)
|
||
if rhs_nm_wo is None:
|
||
rhs_nm_wo = elem_nm_wo
|
||
elem_sz_wo: int = self.Gen.ClassGenEnum._get_type_size(elem_ty)
|
||
ptr_sz_wo: int = 8
|
||
over_wo: int = elem_sz_wo - ptr_sz_wo
|
||
if over_wo < 0:
|
||
over_wo = 0
|
||
col_s_wo: int = 0
|
||
col_e_wo: int = 0
|
||
if target is not None:
|
||
col_s_wo = target.col_offset + 1
|
||
col_e_wo = target.end_col_offset + 1
|
||
self.Gen.TypeGenErr.fatal_error(target,
|
||
f"下标赋值的左值元素是对象 '{elem_nm_wo}'({elem_sz_wo} 字节),"
|
||
f"右值却是指向 '{rhs_nm_wo}' 的指针:编译器会静默生成整对象拷贝"
|
||
f"(load {elem_nm_wo} + store {elem_nm_wo},写 {elem_sz_wo} 字节),"
|
||
f"而不是按指针写 {ptr_sz_wo} 字节。若该地址实际按 8 字节槽存放指针"
|
||
f"(如 rawptr[T] 缓冲/空闲链表),每写一个元素就越界 {over_wo} 字节,"
|
||
f"直接写穿相邻块。修法:把指针槽的基座类型写成指针元素形态"
|
||
f"(`rawptr[T] | t.CPtr` 或 `rawptr[rawptr[T]]`)后再赋值;"
|
||
f"或把基座显式转为 t.CPtr(无类型指针)后按 8 字节指针语义写入。",
|
||
"TypeError", col_s_wo, col_e_wo)
|
||
|
||
# ============================================================
|
||
# _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*,
|
||
mod: llvmlite.LLVMModule*,
|
||
node: ast.AST*,
|
||
trans: HT.Translator*,
|
||
rhs_val: llvmlite.Value* = 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* = ast.Call*(node)
|
||
if cl is None or cl.func is None:
|
||
return None
|
||
if cl.func.kind() == ast.ASTKind.Name:
|
||
# 类实例化:func 为已注册结构体名才采用
|
||
cand: str = (ast.Name*(cl.func)).id
|
||
if cand is None:
|
||
return None
|
||
se: GenTypes.StructEntry* = 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* = ast.Attribute*(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* = ast.Name*(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* = ast.Name*(node)
|
||
if nm is None or nm.id is None:
|
||
return None
|
||
ve: GenTypes.VarEntry* = 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* = ast.BinOp*(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* = ast.UnaryOp*(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*,
|
||
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* = 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* = 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*,
|
||
mod: llvmlite.LLVMModule*,
|
||
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* = 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)(mro_ptr[mi])
|
||
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
|