fix(tpc): 继承前缀配槽改用 elements 快照,不再对已被重置为 None 的 body 下标

类别: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`。无重入时行为与改前逐字节一致。
This commit is contained in:
TermiNexus committed 2026-09-29 16:26:43 +08:00
1 parent 24573e5af6
commit 467e62a45e
1 file changed
+15 -2
+15 -2
View File
@@ -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