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,