fix(tpc): struct body 解冻改用 llvmlite 公开属性 elements(原 _elements 是空操作)
类别:a 编译器缺陷(机制漏豁免:错误的"解冻"字段名使所有 body 重建路径失效)。
现场(连续多轮复现):TransPyV Generator.vp:200
`self.ExprGenFstr = SubExprGenFstr` 报「指针隐式转换已禁止(层数不同):
%"a7b68c79f39cb4d3.ExprGenFstr"* → i8**」,[成员槽来源] 显示
class_members[Generator] 下标 11 声明槽类型**正确**,而布局
'47382dc8277c9d28.Generator' 的 elements[11]=i8**。
取证(已核对全部产物与共享符号表,排除"来源侧退化"):
- TransPyV/output/*.stub.ll 与 temp/47382dc8277c9d28.stub.ll 里
%"47382dc8277c9d28.Generator" 的 slot11 全部是 %"a7b68c79f39cb4d3.ExprGenFstr"*,
**没有任何 stub 含 i8**;
- 共享符号表(temp/_shared_sym.json → symbol_table_bytes)里
Generator.Members['ExprGenFstr'] = CTypeInfo(BaseType=CStruct('ExprGenFstr'),
PtrCount=1, IsStruct=True),也**正确**。
真根因:llvmlite 本版本 IdentifiedStructType 的字段是**公开属性 `elements`**
(types.py: `self.elements = None`;`is_opaque` = `elements is None`;
`set_body` 仅在 `elements is not None` 时抛 "already defined")。而本仓所有
"清除 body 以便重建"的路径都写 `st._elements = None`(不存在的属性)⇒ **空操作** ⇒
⇒ `if struct_type.elements is None` 恒假 ⇒ set_body 永不执行 ⇒ 早期冻结的错误布局
(如 i8**)无法被任何后续权威重建覆盖:
· StructGen Step 4 逐元素校验已判定不一致(_slot_types_ok=False),清空失败 ⇒
既不能 skip 也不能重建 ⇒ 错布局被静默保留;
· HandlesClassDef 的本地定义权威重建点(class_members 已重建为正确类型后)
同样无法刷新 body。
两处叠加正是"class_members 正确、布局却是 i8**"的稳定现象,也解释了此前
TypeGenCDef(槽3) / ExprGenFstr(槽11) 在不同构建里交替出现(取决于谁先被冻结)。
修复:四处解冻改为写公开属性 `elements`:
· StructGen.py Step 4 前置校验失败后的清空;
· StructGen.py sha1 全名 key 的 struct 清空;
· StructGen.py T* 占位清空;
· HandlesClassDef.py @t.NoVTable 本地定义点的清空。
清空后 is_opaque 恢复为真 ⇒ _TryLoadStructFromStub(declare 侧权威 body)或
class_members 重建可真正 set_body。
This commit is contained in:
1 parent
9d8aedec4a
commit
a6c9003a19
2 files changed
+22
-10
No files matched your search
@@ -700,10 +700,13 @@ class ClassHandle(BaseHandle):
|
||||
Gen._cross_module_vtable_classes.discard(ClassName)
|
||||
Gen._cross_module_novtable.add(ClassName)
|
||||
# 清除可能从导入 stub 加载的旧 struct body(含错误 vtable 指针),
|
||||
# 让 _generate_structs 用本地 class_members 重新生成
|
||||
# 让 _generate_structs 用本地 class_members 重新生成。
|
||||
# 字段名必须是 llvmlite 的**公开属性 `elements`**(is_opaque = elements is None;
|
||||
# set_body 仅在 elements is not None 时拒绝)——写 `_elements` 在本版本是空操作,
|
||||
# 会让"权威重建点"永远无法刷新已冻结的错误布局。
|
||||
_old_st = Gen.structs.get(ClassName)
|
||||
if isinstance(_old_st, ir.IdentifiedStructType) and _old_st.elements is not None:
|
||||
_old_st._elements = None
|
||||
_old_st.elements = None
|
||||
_ih = getattr(Gen, '_import_handler_ref', None)
|
||||
if _ih is not None:
|
||||
_ck = (id(Gen), ClassName)
|
||||
|
||||
@@ -499,7 +499,8 @@ class StructGenMixin:
|
||||
# 治本修复:检查 struct body 是否不完整(元素数少于 class_members + vtable 预期)。
|
||||
# 常见于跨模块继承:子类 struct 在父类 class_members 加载前从 stub 生成,
|
||||
# stub 中的定义可能不完整或类型不正确(如 GSList* 代替 AST*、i8* 代替 i32)。
|
||||
# 此处检测到不完整后清除 _elements 和 stub 缓存,让后续代码重新从 stub 加载。
|
||||
# 此处检测到不完整后清除 elements(公开属性,见下方 Step 4 说明)和
|
||||
# stub 缓存,让后续代码重新从 stub 加载。
|
||||
_has_vtable_precheck: bool = ClassName in self.class_vtable or ClassName in self._cross_module_vtable_classes
|
||||
if not _has_vtable_precheck:
|
||||
_p_pre: str | None = self.class_parent.get(ClassName)
|
||||
@@ -554,19 +555,26 @@ class StructGenMixin:
|
||||
if ClassName in self.class_packed and not existing_st.packed:
|
||||
existing_st.packed = True
|
||||
continue
|
||||
# struct body 不完整,清除 _elements 和 stub 缓存以便重新加载
|
||||
existing_st._elements = None
|
||||
# 同时清除 sha1 全名 key 的 _elements:
|
||||
# struct body 不完整,清除 elements 和 stub 缓存以便重新加载。
|
||||
# 注意:本仓 llvmlite 版本的字段名是**公开属性 `elements`**
|
||||
# (types.py: `self.elements = None`;`is_opaque` = `elements is None`;
|
||||
# `set_body` 只在 `elements is not None` 时拒绝)。此前写
|
||||
# `_elements = None` 在本版本**不存在该属性** ⇒ 清空是空操作 ⇒
|
||||
# 后续 `if struct_type.elements is None` 恒假 ⇒ set_body 永不执行 ⇒
|
||||
# 错误布局(现场 Generator elements[11]=i8**)被永久冻结,而
|
||||
# class_members 已被权威重建为正确类型 ⇒ 赋值时报"层数不同"。
|
||||
existing_st.elements = None
|
||||
# 同时清除 sha1 全名 key 的 elements:
|
||||
# self.structs 中可能同时存在 'Value'(短名) 和 'f9e36e2cd6fa659f.Value'(sha1全名) 两个 key,
|
||||
# _TryLoadStructFromStub 调用 _get_or_create_struct 时会返回 sha1全名 key 的 struct。
|
||||
# 若只清除短名 key 的 _elements,sha1全名 key 的 struct 仍保持错误 body(is_opaque=False),
|
||||
# 若只清除短名 key 的 elements,sha1全名 key 的 struct 仍保持错误 body(is_opaque=False),
|
||||
# 导致 _TryLoadStructFromStub 跳过 set_body,struct 保持不完整字段数。
|
||||
_imported_sha1_pre: str = self.class_sha1_map.get(ClassName, '')
|
||||
if _imported_sha1_pre:
|
||||
_full_key_pre: str = f"{_imported_sha1_pre}.{ClassName}"
|
||||
_full_st_pre: ir.IdentifiedStructType | None = self.structs.get(_full_key_pre)
|
||||
if _full_st_pre is not None and isinstance(_full_st_pre, ir.IdentifiedStructType):
|
||||
_full_st_pre._elements = None
|
||||
_full_st_pre.elements = None
|
||||
_import_handler_pre: Any = self._import_handler_ref
|
||||
if _import_handler_pre is not None:
|
||||
_cache_key_pre: tuple = (id(self), ClassName)
|
||||
@@ -672,7 +680,8 @@ class StructGenMixin:
|
||||
struct_type = self.structs.get(ClassName, struct_type)
|
||||
# 检测 stub body 中的 T* 占位类型(未特化的泛型类型参数)。
|
||||
# 若 stub 使用了未特化的基类(如 GSListNode 而非 GSListNode[BasicBlock]),
|
||||
# 清除 _elements 让 set_body 用正确的 class_members 重新设置。
|
||||
# 清除 elements 让 set_body 用正确的 class_members 重新设置
|
||||
# (字段名必须是公开属性 `elements`,见上方 Step 4 的说明)。
|
||||
if struct_type.elements is not None and member_types:
|
||||
_has_t_placeholder_post: bool = False
|
||||
for _elem_post in struct_type.elements:
|
||||
@@ -683,7 +692,7 @@ class StructGenMixin:
|
||||
_has_t_placeholder_post = True
|
||||
break
|
||||
if _has_t_placeholder_post:
|
||||
struct_type._elements = None
|
||||
struct_type.elements = None
|
||||
if struct_type.elements is None:
|
||||
# 防御性检查:member_types 为空时不调用 set_body。
|
||||
# set_body(*[]) 会设置 elements=() 使 is_opaque=False,
|
||||
|
||||
Reference in new issue
Block a user