From 467e62a45e7c2b15bf7e5e715f66780d34432275 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 16:26:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E7=BB=A7=E6=89=BF=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E9=85=8D=E6=A7=BD=E6=94=B9=E7=94=A8=20elements=20?= =?UTF-8?q?=E5=BF=AB=E7=85=A7=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=AF=B9=E5=B7=B2?= =?UTF-8?q?=E8=A2=AB=E9=87=8D=E7=BD=AE=E4=B8=BA=20None=20=E7=9A=84=20body?= =?UTF-8?q?=20=E4=B8=8B=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a(编译器缺陷)—— 裸 Python TypeError 崩溃 + 外层静默吞掉整条类声明。 现场:编译 includes/ast/astaux.vp 时 HandlesImports.py:2526 `_inherited_ph.append((_nm_ph, _st.elements[_slot0_ph + _i_ph]))` → TypeError: 'NoneType' object is not subscriptable (栈由 VLogger.warning 打印 exc_info 后首次可见;外层 HandlesImports.py:647 `except Exception` 只 WARN "忽略节点 ClassDef" 就 continue ⇒ 该类声明整条 被静默丢弃,类型解析退化。) 根因(TOCTOU):guard 在 :2500 通过(elements is not None),但两侧之间 `_ResolveInheritedFieldNames`(:2535)会加载父类成员: _ResolveInheritedFieldNames → _TryLoadClassMembersFromPyi → _EmitExternalClassDeclLlvm → _generate_structs 而 `_generate_structs` 判定"struct body 与 class_members 不一致"时会**主动** `existing_st.elements = None`(StructGen.py:579/:590,既有机制,为强制重新派生) ⇒ 配对时读到的 elements 已变 None。 修复:guard 通过时立即对 elements 取快照 `_elements_ph`,槽位数与槽位类型取自 **同一时刻**的布局(`_num_elements` 本就取自它,二者必须同源);配对用快照, 不再触碰可能被重入重置的 `_st.elements`。无重入时行为与改前逐字节一致。 --- lib/core/Handles/HandlesImports.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 8ecb790..1da2993 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -2497,8 +2497,21 @@ class ImportHandle(BaseHandle): # 父类未加载到 class_members(常见于泛型特化基类如 GSListNode[Value], # 其特化不在 .pyi 中)。用 struct body 元素数推断继承字段数。 _st = Gen.structs.get(ClassName) + # 【必须先取快照】`elements` 不是稳定值:`StructGen._generate_structs` 判定 + # "struct body 与 class_members 不一致"时会**主动置 `elements = None`** + # (StructGen.py:579/:590,为强制重新派生的既有机制)。本分支在下方 + # `_ResolveInheritedFieldNames`(加载父类成员 → _TryLoadClassMembersFromPyi + # → _EmitExternalClassDeclLlvm → _generate_structs)里会重入,重入期间本类 + # 的 `elements` 可能被置回 None ⇒ 若在配对时(:2526)才读 `_st.elements`, + # 就是对 None 下标:裸 `TypeError: 'NoneType' object is not subscriptable` + # (2026-09-29 现场:astaux.vp 编译期 `_EmitExternalClassDeclLlvm` 崩, + # 外层 :647 `except Exception` 只 WARN "忽略节点 ClassDef" 就 continue ⇒ + # 该类声明被整条静默丢弃)。 + # 快照与 `_num_elements` 同源(都是 guard 通过的这一份 body),二者必须 + # 一致:槽位数与槽类型取自同一时刻的布局,不能一个用旧、一个用新。 if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: - _num_elements = len(_st.elements) + _elements_ph: list = list(_st.elements) + _num_elements = len(_elements_ph) # 如果类有 vtable ptr(struct 首元素),需减去 1 # 否则 vtable ptr 会被误当成 __inherited_0 占位条目, # 导致子类继承时多出一个字段(如 Name(AST) 从 9 字段变成 10) @@ -2523,7 +2536,7 @@ class ImportHandle(BaseHandle): _inherited_ph: list[tuple] = [] for _i_ph in range(_num_inherited): _nm_ph: str = _inh_names[_i_ph] if _i_ph < len(_inh_names) else f'__inherited_{_i_ph}' - _inherited_ph.append((_nm_ph, _st.elements[_slot0_ph + _i_ph])) + _inherited_ph.append((_nm_ph, _elements_ph[_slot0_ph + _i_ph])) # 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致 Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName] _placeholder_applied = True