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 == ast.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 , 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 , 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") # void 不能声明为变量(用户 2026-10-01:void 只能用于 `void*` 与 # `typedef 不透明结构体 = void`)——否则会按 LLVM void 建槽,生成非法 IR。 if var_ty is not None and self.Gen.StmtGenDecl._is_void_type(var_ty) != 0: self.Gen.TypeGenErr.fatal_error(aa, f"不能声明 void 类型的变量 '{nm.id}'(void 只能用于 void* 与 typedef 别名)", "TypeError") # 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 # 禁止「上转被窄化」重建(方向判定其三):注解写明的被指类是 # RHS 被指类的**基类**时(如 `node: rawptr[AST] = Name(...)`, # AST 是 Name 的基类),注解就是合法的「基类指针」声明,RHS 只是 # 继承上转(子→父)。以 RHS 重建 alloca 会把**声明类型窄化**成派生 # 类,使同一变量随后再赋基类值(`node = _parse_fstring(...)` 返回 # rawptr[AST])变成被禁止的**父→子下转**: # 现场 includes/ast/parser.vp:1919 报 # "指针隐式转指针已禁止:源类型 %AST* → 目标类型 %Name*" # (与 TPC 侧 HandlesAnnAssign 的 _rb_is_upcast 同判据同口径)。 # 判据只拦「上转被窄化」这一个确定的方向错误:注解退化为 # i8*/裸占位(两侧类名不构成继承关系)时不受影响。 rb_is_upcast: int = 0 if alloca_pt is not None and uint64(alloca_pt) != uint64(precise_rhs_ty) \ and self.Gen.ExprGenBase.is_ptr_type(alloca_pt) != 0: ann_pt_rb: llvmlite.LLVMType* = alloca_pt.Pointee rhs_pt_up: llvmlite.LLVMType* = precise_rhs_ty.Pointee if ann_pt_rb is not None and rhs_pt_up is not None \ and uint64(ann_pt_rb) != uint64(rhs_pt_up): if self.Gen.ExprGenBase._is_inheritance_upcast(pool, rhs_pt_up, ann_pt_rb) != 0: rb_is_upcast = 1 if alloca_pt is not None and \ uint64(alloca_pt) != uint64(precise_rhs_ty) \ and rb_is_bare_generic == 0 and rb_is_upcast == 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.ParamNode* = llvmlite.get_struct_fields(rhs_val.Ty) fcur: llvmlite.ParamNode* = 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: # 目标类型已由上面的 ASTKind 判据确定为 Attribute ⇒ 在此显式窄化。 # 必须窄化:_try_property_setter 的形参是 ast.Attribute*,而 target # 是基类 ast.AST*;基类指针传派生类形参属向下转型,严格指针规则下禁止。 at_tg: ast.Attribute* = ast.Attribute*(target) # 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, at_tg, 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* = at_tg 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 # 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态 # `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与 # ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径; # 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。 while e > s and (annot[e - 1] == ' ' or 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 # 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态 # `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与 # ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径; # 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。 while e > s and (annot[e - 1] == ' ' or 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 = rawptr(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