diff --git a/lib/core/LLVMCG/StructGen.py b/lib/core/LLVMCG/StructGen.py index 4c5ef7b..acd99da 100644 --- a/lib/core/LLVMCG/StructGen.py +++ b/lib/core/LLVMCG/StructGen.py @@ -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