fix(tpc): struct body 冻结前逐元素校验类型一致性(错位 body 不再被当"完整")
a 编译器缺陷:_generate_structs Step 4 判定既有 struct body 是否"完整"时只比
「元素个数 / 首元素是 vtable 指针 / 是否含 T 占位」—— **类型错位**的 body
(个数命中、元素名也全对,但某槽被冻结成通用多层指针)会被直接 continue;
而 llvmlite 的 set_body 只能设一次 ⇒ 错布局被永久冻结,后续 .vpi / .stub 重载
只修 class_members、改不了 body ⇒ 赋值时报「层数不同」。
现场(成员槽来源明细实锤:声明侧正确、布局侧坏):
class_members[Generator] 项数=47 base=0 下标=3
声明槽类型=%"d827f3045bd28467.TypeGenCDef"*
布局 struct name='fd4cd0ec583f42b9.Generator' 元素数=47 elements[3]=i8**
→ self.TypeGenCDef = SubTypeGenCDef
指针隐式转换已禁止(层数不同):不能把 %…TypeGenCDef"* 隐式转为 i8**
修法:新增 _MemberSlotAccepts(判据与存储侧同一套 _ClassifyPtrImplicitConvert),
把 class_members 声明类型与 elements[base+i] 逐项比对;只有**存储时必被拒绝**的
组合(层数不同 / 指针与非指针混位)判为不一致 ⇒ 走既有「清 _elements + 清 stub
缓存」路径重建布局(通用指针槽 i8*、继承上转、数组退让照旧放行,不误清)。
This commit is contained in:
1 parent
c49dc1d915
commit
b8ed33a200
1 file changed
+49
-1
@@ -293,6 +293,32 @@ class StructGenMixin:
|
||||
|
||||
return ir_text
|
||||
|
||||
def _MemberSlotAccepts(self, Declared: Any, Slot: Any) -> bool:
|
||||
"""声明类型能否**隐式写进**该 struct 槽(判据与存储侧同一套)。
|
||||
|
||||
用于判定已冻结的 struct body 是否与 `class_members` 自洽。判定放宽到与
|
||||
存储侧一致,避免误清:通用指针槽(i8*/void*)照旧放行(历史 stub 普遍
|
||||
如此),继承上转/数组退让亦放行;只有**存储时必被拒绝**的组合——层数
|
||||
不同、指针与非指针混位——才判为不一致,触发重建。
|
||||
"""
|
||||
if Declared is None or Slot is None:
|
||||
return True
|
||||
if Declared == Slot or str(Declared) == str(Slot):
|
||||
return True
|
||||
_d_ptr: bool = isinstance(Declared, ir.PointerType)
|
||||
_s_ptr: bool = isinstance(Slot, ir.PointerType)
|
||||
if _d_ptr != _s_ptr:
|
||||
return False
|
||||
if not _d_ptr:
|
||||
# 数值/浮点宽度差异不在本次收紧范围:保持既有行为(不清 body)。
|
||||
return True
|
||||
try:
|
||||
Allowed, _Reason = self._ClassifyPtrImplicitConvert(Declared, Slot)
|
||||
return bool(Allowed)
|
||||
except Exception:
|
||||
# 判据不可用(缺上下文)时保守放行,避免误清已正确的布局。
|
||||
return True
|
||||
|
||||
def _generate_structs(self) -> None:
|
||||
all_classes: set[str] = set(self.class_methods.keys()) | set(self.class_members.keys())
|
||||
|
||||
@@ -470,7 +496,29 @@ class StructGenMixin:
|
||||
if _short_pre == 'T':
|
||||
_has_t_placeholder_pre = True
|
||||
break
|
||||
if len(existing_st.elements) >= _expected_count_pre and _vtable_first_ok and not _has_t_placeholder_pre:
|
||||
# 逐元素类型一致性(治本):仅比"个数 / 首元素是 vtable 指针 / T 占位"
|
||||
# 会漏掉**类型错位**的已冻结 body —— 现场 Generator:47 个元素个数正确、
|
||||
# 元素名字也全对,但第 4 槽是 i8** 而 class_members 声明
|
||||
# %"d827f3045bd28467.TypeGenCDef"*,个数命中即被当作"完整"直接 continue;
|
||||
# 而 llvmlite 的 set_body 只能设一次 ⇒ 错布局被永久冻结(后续 .vpi 重载
|
||||
# 只修 class_members,改不了 body)⇒ 赋值时报"层数不同"。
|
||||
# 判据与存储侧同一套(_MemberSlotAccepts → _ClassifyPtrImplicitConvert):
|
||||
# 声明类型**能隐式写进该槽**才算一致 —— 通用指针槽(i8*)照旧放行,不误清。
|
||||
_slot_types_ok: bool = True
|
||||
_members_pre: list = self.class_members.get(ClassName, [])
|
||||
if _members_pre:
|
||||
_base_pre: int = 1 if _has_vtable_precheck else 0
|
||||
_elems_pre: list = list(existing_st.elements)
|
||||
for _i_pre, _item_pre in enumerate(_members_pre):
|
||||
_j_pre: int = _base_pre + _i_pre
|
||||
if _j_pre >= len(_elems_pre):
|
||||
break
|
||||
_decl_pre: Any = _item_pre[1] if isinstance(_item_pre, tuple) and len(_item_pre) >= 2 else None
|
||||
if not self._MemberSlotAccepts(_decl_pre, _elems_pre[_j_pre]):
|
||||
_slot_types_ok = False
|
||||
break
|
||||
if (len(existing_st.elements) >= _expected_count_pre and _vtable_first_ok
|
||||
and not _has_t_placeholder_pre and _slot_types_ok):
|
||||
if ClassName in self.class_packed and not existing_st.packed:
|
||||
existing_st.packed = True
|
||||
continue
|
||||
|
||||
Reference in new issue
Block a user