feat(HandlesAnnAssign/HandlesExprAttr): 左值注解靶型提示(target-typing)——属性链读错类名时按注解类兜回
现场: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 例全部符合预期——
无提示原样 / 原类不声明且靶型声明⇒改判(本次现场)/ 原类声明该字段⇒不改判 /
原键即靶型⇒原样 / 原键解析不出且靶型声明⇒改判 / 靶型也不声明⇒原样 / 靶型类不存在⇒原样。
This commit is contained in:
1 parent
a1c43a7b3b
commit
64ddabbe17
2 files changed
+77
-2
No files matched your search
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user