From 64ddabbe171f2ae58b36952424c69be15bafef93 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 12:28:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(HandlesAnnAssign/HandlesExprAttr):=20?= =?UTF-8?q?=E5=B7=A6=E5=80=BC=E6=B3=A8=E8=A7=A3=E9=9D=B6=E5=9E=8B=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=EF=BC=88target-typing=EF=BC=89=E2=80=94=E2=80=94?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E9=93=BE=E8=AF=BB=E9=94=99=E7=B1=BB=E5=90=8D?= =?UTF-8?q?=E6=97=B6=E6=8C=89=E6=B3=A8=E8=A7=A3=E7=B1=BB=E5=85=9C=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现场:CallGenDispatch.vp:787 `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee` 报 "[fast-fail] 属性 'Pointee' 在类 'ParamNode' 中找不到(class_members=2 个: ['Next','Ty'])"。 'Pointee' 的真实归属是 LLVMType 的 REnum 变体镜像字段(LLVMType.Ptr.Pointee, includes/llvmlite/__types.vp:33-34);ParamNode 只有 Next/Ty。更隐蔽的是**两边都有 Ty** (Value.Ty = rawptr[LLVMType]、ParamNode.Ty = 裸 rawptr = i8*)⇒ 读错类不会当场报错, 只静默把类型变成 i8*,再往下 `.Pointee` 才炸。 机制(与 TPV translate_value 的 target_ty「使用处真实站点种类」同思路): ① HandlesAnnAssign._EvalRhsWithTargetHint:注解赋值求值 RHS 前,把**注解指向的结构体类** 作为 `Gen._TargetStructHint` 传给属性链解析;求值结束(含异常)复位,不跨语句泄漏。 两个 RHS 求值点(_reg_values 提升路径 + 常规路径)都改走该入口。 ② HandlesExprAttr._apply_target_struct_hint:**仅当两条件同时成立**才改判—— Ⅰ 当前解析出的类(或解析不出)**不声明**该字段(此刻原本会 fail-fast); Ⅱ 靶型类**确实声明**该字段。 任一不满足即原样返回(不猜、不篡改本来正确的链);改判时 `_vlog().warning` 留痕。 接入点:char*(i8*)分支与单级 Name 分支(含"原键解析不出"的情形)。 验证:py_compile 通过;`_apply_target_struct_hint` 冒烟 7 例全部符合预期—— 无提示原样 / 原类不声明且靶型声明⇒改判(本次现场)/ 原类声明该字段⇒不改判 / 原键即靶型⇒原样 / 原键解析不出且靶型声明⇒改判 / 靶型也不声明⇒原样 / 靶型类不存在⇒原样。 --- lib/core/Handles/HandlesAnnAssign.py | 32 +++++++++++++++++-- lib/core/Handles/HandlesExprAttr.py | 47 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/lib/core/Handles/HandlesAnnAssign.py b/lib/core/Handles/HandlesAnnAssign.py index 35b63a0..36691b7 100644 --- a/lib/core/Handles/HandlesAnnAssign.py +++ b/lib/core/Handles/HandlesAnnAssign.py @@ -257,6 +257,34 @@ class AnnAssignHandle(BaseHandle): ) Gen._store(Value, Ptr) + def _EvalRhsWithTargetHint(self, Node: ast.AnnAssign, VarType: Any, Gen: Any) -> Any: + """求值注解赋值的 RHS,并把**左值注解的靶型类**作为提示传给属性链解析。 + + 与 TPV `translate_value` 的 `target_ty`(使用处真实站点种类)同思路:注解是 + 源码级权威意图(RHS 必须能赋给它)。两个类的成员表都有同名字段时,属性链读错 + 类名不会当场报错,只会**静默**给出错类型(现场 CallGenDispatch.vp:787 + `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee`:`.Pointee` 属 LLVMType 的 REnum + 变体镜像,却因接收者被判成 ParamNode(只有 Next/Ty) 而 fail-fast;ParamNode.Ty 是 + 裸 rawptr ⇒ 内层读回 i8*,外层再找不到 Pointee)。传提示可把这类情形兜回来。 + + 提示只在"解析出的类**不声明**该字段"时生效(见 + HandlesExprAttr._apply_target_struct_hint 的两条件判定),故不会篡改本来正确的链; + 求值结束(含异常)即复位,不跨语句泄漏。 + """ + _Saved: Any = getattr(Gen, '_TargetStructHint', None) + _Hint: Any = _Saved + if (isinstance(VarType, ir.PointerType) + and isinstance(getattr(VarType, 'pointee', None), ir.IdentifiedStructType)): + try: + _Hint = Gen._extract_short_name(getattr(VarType.pointee, 'name', '') or '') + except Exception: + _Hint = _Saved + try: + Gen._TargetStructHint = _Hint + return self.HandleExprLlvm(Node.value, VarType=VarType) + finally: + Gen._TargetStructHint = _Saved + def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None: """处理 list[T] = [...] → 构造 list[T] 对象 + append 序列。 @@ -719,7 +747,7 @@ class AnnAssignHandle(BaseHandle): Gen.variables[VarName] = var del Gen._reg_values[VarName] if Node.value: - InitValue: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=VarType) + InitValue: ir.Value | None = self._EvalRhsWithTargetHint(Node, VarType, Gen) if InitValue: self._StoreOrStrCopy(VarName, var, InitValue) elif isinstance(Node.value, ast.Attribute): @@ -733,7 +761,7 @@ class AnnAssignHandle(BaseHandle): RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None RhsInsnStart: int = len(RhsBlock.instructions) if RhsBlock is not None else -1 if Node.value: - InitValue = self.HandleExprLlvm(Node.value, VarType=VarType) + InitValue = self._EvalRhsWithTargetHint(Node, VarType, Gen) # 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型 if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): CalledName: str = Node.value.func.id diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index 4caf966..c053df7 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -4,6 +4,7 @@ if TYPE_CHECKING: from lib.core.translator import Translator from lib.core.LlvmCodeGenerator import LlvmCodeGenerator from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta +from lib.core.VLogger import get_logger as _vlog import vast as ast import llvmlite.ir as ir @@ -21,6 +22,48 @@ class ExprAttrHandle(BaseHandle): val: ir.Value = Gen._load(ElemPtr, name="arr_elem_load") return Gen._apply_bswap_if_big(val, arr_byte_order, "bswap_arr_load") + def _apply_target_struct_hint(self, Gen: LlvmCodeGenerator, FieldName: str, + ResolvedKey: str | None) -> str | None: + """左值注解**靶型提示**:解析出的类**不声明**该字段时,用赋值左侧注解指向的类兜一次。 + + 与 TPV `translate_value` 的 `target_ty`(使用处真实站点种类)同思路:赋值左侧的 + 注解是**源码级权威意图**(RHS 必须能赋给它)。仅在**两个条件同时成立**时改判: + ① 当前解析出的类(或解析不出)**不声明**该字段 —— 此刻原本会 fail-fast; + ② 靶型类**确实声明**了该字段。 + 任一不满足即原样返回(**不猜**);改判时告警留痕。 + + 现场:CallGenDispatch.vp:787 `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee` + —— `.Pointee` 属 LLVMType(REnum 变体镜像 LLVMType.Ptr.Pointee),却因接收者 + 类名被判成 ParamNode(只有 Next/Ty)而 fail-fast 终止编译;靶型 `llvmlite.LLVMType*` + 正是该字段的归属类。 + + 提示由 HandlesAnnAssign 在求值 RHS 前置入 `Gen._TargetStructHint`(注解为结构体 + 指针时取其短类名),求值后复位;提示只在"类不声明该字段"时才生效,故嵌套求值 + (如调用的实参链)复用同一提示也不会篡改本来解析正确的链。 + """ + _TH: str | None = getattr(Gen, '_TargetStructHint', None) + if not _TH: + return ResolvedKey + if ResolvedKey and ResolvedKey == _TH: + return ResolvedKey + if ResolvedKey and any(_n == FieldName + for _n, _ in (Gen.class_members.get(ResolvedKey) or [])): + return ResolvedKey + _THKey: str | None = None + try: + _THKey = self._find_struct_key_for_class_hint(Gen, _TH) + except Exception: + _THKey = None + if (not _THKey or _THKey == ResolvedKey or _THKey not in Gen.structs + or not any(_n2 == FieldName + for _n2, _ in (Gen.class_members.get(_THKey) or []))): + return ResolvedKey + _vlog().warning( + f"[属性类名] 字段 '{FieldName}' 不在解析出的类 {ResolvedKey!r} 中," + f"按左值注解靶型类 '{_THKey}' 解析(源码级权威意图:RHS 必须可赋给该注解)", + "AttrClass") + return _THKey + @staticmethod def _PointeeMatchesStruct(Pointee: Any, ST: Any) -> bool: """pointee 与该 struct 实例是否算**同一个类**(判据分级,宽到窄)。 @@ -731,6 +774,8 @@ class ExprAttrHandle(BaseHandle): f"pointee 既非该类 struct,静态类名也未能 bitcast 兜底)") return None + # 左值注解靶型提示(见 _apply_target_struct_hint):类不声明该字段时改用注解类 + ClassName = self._apply_target_struct_hint(Gen, AttrName, ClassName) st: Any ok: bool st, ok = self._try_Load_struct_from_stub(Gen, ClassName) @@ -946,6 +991,8 @@ class ExprAttrHandle(BaseHandle): AttrClassName = (getattr(Gen, 'var_struct_class', {}).get(_mva.attr) or getattr(Gen, 'global_struct_class', {}).get(_mva.attr)) ResolvedKey: str | None = self._find_struct_key_for_class_hint(Gen, AttrClassName) + # 左值注解靶型提示:解析出的类不声明该字段时,用注解类兜一次(两条件同时成立才改判) + ResolvedKey = self._apply_target_struct_hint(Gen, Node.attr, ResolvedKey) if ResolvedKey is None or ResolvedKey not in Gen.structs: self._NoteAttrNone( Gen, AttrClassName, Node.attr,