From 716cd61a75200a55b6925a7d0c943771766c6252 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 16:50:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E5=B1=9E=E6=80=A7=E5=8F=96?= =?UTF-8?q?=E5=80=BC=E5=A4=B1=E8=B4=A5=E7=9A=84=E6=8A=A5=E6=96=87=E8=A1=A5?= =?UTF-8?q?"=E5=9F=BA=E5=9D=80=E8=AF=8A=E6=96=AD"=20+=20=E8=A1=A5=E6=8E=89?= =?UTF-8?q?=E5=A3=B0=E6=98=8E=E5=8A=A0=E8=BD=BD=E5=8E=BB=E9=87=8D=E7=AD=BE?= =?UTF-8?q?=E5=90=8D=E7=9A=84=E7=9C=9F=E5=AE=9E=E7=BC=BA=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a(编译器缺陷)—— 两者都是"缺事实/漏输入",不是放宽判定。 【一】`_RaiseUnresolvedExpr` 增打基址事实(HandlesBase) 现场:TransPyV/App/lib/core/Gen/MatchGenMixin.vp:231 `val: ast.AST* = expr_node.value`(base 是 `expr_node: rawptr[ast.Expr]`) → Attribute 取值返回 None → 本报文 + sys.exit(1)。 属性读回 None 的根因只有两类:① 类名登记退化(var_struct_class 存 'rawptr' / 'rawptr[X]' / 折叠态,或压根没登记);② 登记名与 Gen.structs / class_members 的键 不同源(短名 vs sha1 全名)。原报文只有"目标变量 + 注解 + 源码",不含任何一项事实 ⇒ 只能从"求值为空"反推。现追加一行(仅 Attribute + Name 基址时,其余形态逐字不变): 基址诊断: 'expr_node' 的 var_struct_class=<...>;alloca 类型=<...>; 该名在 Gen.structs=<是/否>;class_members= 与 `_DescribeMemberSlot` 同口径:列数据、不猜。 【二】`_ClassMembersReloadSignature` 的"类数"改为"逐类 (键, 表长) 元组"(HandlesImports) 上一轮 3399b44c 的签名里全局进度只记 `len(class_members)`(键数)。缺口:**某已有类的 成员表继续增长时键数不变**(典型=父类成员后到),而父类成员正是继承前缀的唯一来源 ⇒ 极端时序下会漏掉一次必要重跑。改为逐类 (键, 长度) 元组,并补记 `len(class_parents)` / `len(class_parent)`(父链登记本身也是输入)。 代价 O(类数)(约 300 项/次调用),仍远低于一次重跑的成本。 验证:GetDiagnostics 干净;py 层 import + 既有 16 例类型判定 smoke test 全过。 未跑编译器(无 includes 改动,无需重编)。 --- lib/core/Handles/HandlesBase.py | 28 ++++++++++++++++++++++++++++ lib/core/Handles/HandlesImports.py | 10 ++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index f43c814..32a12b8 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -658,6 +658,34 @@ class BaseHandle: f"{(',目标变量 ' + repr(VarName)) if VarName else ''}" f"{(',注解 ' + ann_text) if ann_text else ''}。位置 {loc}" f"{(',源码: ' + code_line) if code_line else ''}") + # 【基址诊断】属性读返回 None 的根因只有两类(先说事实,不再从"求值为空"反推): + # ① 类名登记退化 —— var_struct_class 存的是占位/折叠态('rawptr'、'rawptr[X]'、 + # 'CVoid'…)或压根没登记; + # ② 登记名与 Gen.structs / class_members 的键不同源(短名 vs sha1 全名)。 + # 故把"基址变量登记成什么 + alloca 实际 LLVM 类型 + 该登记名在两表的命中情况" + # 一并打进报文(与 _DescribeMemberSlot 同口径:列数据而非猜)。仅 Attribute + Name + # 基址时追加,其余形态报文逐字不变。 + if isinstance(Node, ast.Attribute) and isinstance(Node.value, ast.Name): + _bn: str = Node.value.id + _vc: str | None = None + _alloc: Any = None + try: + _vc = (getattr(Gen, 'var_struct_class', None) or {}).get(_bn) + if not _vc: + _vc = (getattr(Gen, 'global_struct_class', None) or {}).get(_bn) + _alloc = (getattr(Gen, 'variables', None) or {}).get(_bn) + except Exception: # noqa: BLE001 + pass + _slot_ty: object = getattr(_alloc, 'type', None) + if _vc: + _hit_st: str = '是' if _vc in Gen.structs else '否' + _hit_cm: str = ('%d 个字段' % len(Gen.class_members.get(_vc) or [])) \ + if _vc in Gen.class_members else '无该键' + else: + _hit_st, _hit_cm = '—', '—' + msg += (f"\n│ 基址诊断: '{_bn}' 的 var_struct_class={_vc!r};" + f"alloca 类型={_slot_ty};该名在 Gen.structs={_hit_st};" + f"class_members={_hit_cm}") _vlog().compile_error(msg, src_file, lineno, code_line) sys.exit(1) diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 33fe841..601143f 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -3206,7 +3206,10 @@ class ImportHandle(BaseHandle): """`_TryLoadClassMembersFromPyi` 重跑的依赖签名(纯读,不产生副作用)。 收录"重跑能否产生新结果"的全部输入: - - 全局进度:`len(class_members)` / `len(_members_flattened)` + - 全局进度:**逐类**(`class_members` 键, 该表长度) 的完整元组(不是"类数"—— + 只看键数会在"某已有类的成员表继续增长"(父类成员后到)时看不出变化, + 而父类成员正是继承前缀的来源);`_members_flattened` 长度; + `class_parents` / `class_parent` 的条目数(父链登记本身也是输入) - 本类自身:成员表长度、是否已登记已展平、struct body 元素数(None ⇒ -1) - 各父类:成员表长度 + 是否已展平(父类从 `class_parents` / `class_parent` 取; 两者都缺时用空元组 —— 此时本类依赖尚未建立,签名仍稳定,不影响收敛) @@ -3225,9 +3228,12 @@ class ImportHandle(BaseHandle): (_p, len(Gen.class_members.get(_p, []) or []), _p in _flat) for _p in _parents ) + _cm_sig: tuple = tuple((_k, len(_v)) for _k, _v in Gen.class_members.items()) return ( - len(Gen.class_members), + _cm_sig, len(_flat), + len(getattr(Gen, 'class_parents', {}) or {}), + len(getattr(Gen, 'class_parent', {}) or {}), len(Gen.class_members.get(class_name, []) or []), class_name in _flat, _st_len,