From b8ed33a200209550be10321b7f944b14bbff116b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 29 Sep 2026 00:06:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20struct=20body=20=E5=86=BB=E7=BB=93?= =?UTF-8?q?=E5=89=8D=E9=80=90=E5=85=83=E7=B4=A0=E6=A0=A1=E9=AA=8C=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E4=B8=80=E8=87=B4=E6=80=A7=EF=BC=88=E9=94=99=E4=BD=8D?= =?UTF-8?q?=20body=20=E4=B8=8D=E5=86=8D=E8=A2=AB=E5=BD=93"=E5=AE=8C?= =?UTF-8?q?=E6=95=B4"=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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*、继承上转、数组退让照旧放行,不误清)。 --- lib/core/LLVMCG/StructGen.py | 50 +++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) 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