From 4d14334bd287b9e1a7222e64e2e10337d6fcb2d5 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 00:39:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(LLVMCG):=20=E6=88=90=E5=91=98=E8=A1=A8?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E6=94=B9=E4=B8=BA=E3=80=8C=E5=89=8D=E7=BC=80?= =?UTF-8?q?=E9=94=9A=20vs=20=E5=B0=BE=E9=83=A8=E9=94=9A=E3=80=8D=E6=8C=89?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=8F=AF=E5=86=99=E5=85=A5=E6=80=A7=E6=89=93?= =?UTF-8?q?=E5=88=86=EF=BC=8C=E4=BF=AE=20Generator.vp:164=20=E8=AF=AF?= =?UTF-8?q?=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a 编译器缺陷(TPC 侧;修正 1776dfa6 / b06dac1d 的过宽判据) 现象:翻译 TransPyV/App/lib/core/Gen/Generator.vp 时 :164 `SubTypeGenErr.Gen = self` 报 「指针隐式转换已禁止:不能把 %"47382dc8277c9d28.Generator"* 隐式转为 %"6fbe68e8fca81223.TypeSpecArena"*」—— 目标类型是槽 41 的 Specs,而 Gen 的真身在槽 0。 判据:TypeGenErr 继承 TypeGenBase(41 槽)且只有 1 个自有字段 ⇒ body 42 槽;其成员表只含 ['Gen'] 一项(**残缺前缀**,真身在 body 头部)。上一轮"尾部对齐"的前置校验(前缀数 == 父类 struct 槽数)在本例**巧合成立**:42 - 1 == 41 ⇒ 该表被锚到槽 41(Specs)⇒ 取到错误槽类型。 同族上一例为 Generator.vp:136(TypeGenMixin,已由 1776dfa6 挡住,但同类巧合仍会复现)。 修法:不再用"长度关系 / 父类槽数"去断言表的对齐方式(那是猜),改为**比较两个候选锚**的 逐项得分(判据与建 body / 存储侧同一套 `_MemberSlotAccepts`): · 前缀锚 `_BaseNow()` —— 展平表 / 残缺前缀; · 尾部锚 `len(body) - len(表)` —— 仅自有字段(父类前缀未补齐,真身在 body 尾部)。 只有尾部锚**严格更高分**才采用它;同分/更低一律保持原行为(配对 → 下标法)⇒ 对既有正确 布局零行为变化,同时消除"巧合成立"的假阳性。命中尾部锚时每类留一次告警(报出两锚得分)。 验证:5 例冒烟全部通过 —— A TypeGenErr(声明类型分别为通用指针 / Generator*,body 42 槽).Gen → 0(修前 41); C AnnAssign 仅自有字段表 → target/annotation/value/simple = 8/9/10/11(尾部锚仍生效); D ExceptHandler 重名表 + 联合类型 children → name = 9(布局配对仍生效); E 自洽展平表 .value → 10(行为不变);FuncGen.py py_compile 通过。 --- lib/core/LLVMCG/FuncGen.py | 114 ++++++++++++++++++------------------- 1 file changed, 55 insertions(+), 59 deletions(-) diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 3919cf5..95cf436 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -594,66 +594,62 @@ class FuncGenMixin: _OffMembers: list = self.class_members.get(_ResolvedClass) or [] _BitFields: Any = getattr(self, 'class_member_bitfields', None) if _OffEls and _OffMembers and not (_BitFields and _BitFields.get(ClassName)): - # ---- 尾部对齐(布局真源之二):表**短于** body 字段区时 ---- - # 展平规则是"父类前缀 + 自有字段"(EnsureClassMembersFlattened 前置前缀、 - # StructGen 按展平表建 body)⇒ 自有字段恒为 body 尾部 len(表) 项。表比 - # 字段区(len(elements) - vtable 前缀)短,即"父类前缀尚未补齐"(父类 - # 未就绪/展平失败):此时 base(0/1) + 表内下标 会把整类字段整体前移 - # (现场:AnnAssign 表仅 4 项自有字段 ⇒ 'value' 读槽 3,见上)。改为按 - # 尾部求前缀数,并**逐项类型比对**自校验(判据与建 body 侧同一套 - # _MemberSlotAccepts):表自洽(长度 == 字段区)时本路径不介入, - # 对既有正确行为零变化;REnum 是 max-variant 镜像布局,不适用。 - _FieldArea: int = len(_OffEls) - _BaseNow() - # 【前置校验·必需】尾部对齐只对"**缺父类前缀**"的表成立,判据是 - # 前缀数 == 父类 struct 的槽数(展平前缀就是父类整份布局,含其继承链)。 - # 缺了这条,一张**只含前若干字段**的残缺表会被锚到 body 末尾: - # 现场 `SubTypeGen.Gen = self`(Generator.vp:136)——TypeGenMixin 表 - # 仅 ['Gen'](且其声明类型是通用指针/未解析),body 41 槽 ⇒ 被锚到 - # 槽 40(= Specs,%TypeSpecArena*)⇒ 报"Generator* 不能隐式转 - # TypeSpecArena*"。父类 struct 不可得(跨模块未加载/opaque)时 - # **不介入**(保守落回配对 / 旧路径)。 - _ParentCls: str | None = self.class_parent.get(_ResolvedClass) - _ParentSt: Any = self.structs.get(_ParentCls) if _ParentCls else None - _ParentLen: int = -1 - if isinstance(_ParentSt, ir.IdentifiedStructType) and _ParentSt.elements is not None: - _ParentLen = len(_ParentSt.elements) - if (len(_OffMembers) < _FieldArea - and not self._IsRenumLayoutClass(_ResolvedClass) - and _ParentLen > 0 - and len(_OffEls) - len(_OffMembers) == _ParentLen): - _TailBase: int = len(_OffEls) - len(_OffMembers) - _TailOk: bool = _TailBase >= 0 - _iT: int = 0 - while _TailOk and _iT < len(_OffMembers): - _nmT, _mtT = _OffMembers[_iT] - if not self._MemberSlotAccepts(_mtT, _OffEls[_TailBase + _iT]): - _TailOk = False - _iT += 1 - if _TailOk: - _TIdx: int = -1 - for _i5, (_n5, _) in enumerate(_OffMembers): - if _n5 == field_name: - _TIdx = _i5 - break - if _TIdx >= 0: - # 每类只留一次痕(大类的每个字段都会命中本路径) - _TailKey: str = _ResolvedClass - _TailRep: set = getattr(self, '_MemberTailAlignedReported', None) - if _TailRep is None: - _TailRep = set() - self._MemberTailAlignedReported = _TailRep - if _TailKey not in _TailRep: - _TailRep.add(_TailKey) - _vlog().warning( - f"[成员偏移] 类 '{_ResolvedClass}' 的成员表短于 struct 字段区" - f"(表 {len(_OffMembers)} 项 vs 字段区 {_FieldArea} 槽," - f"body {len(_OffEls)} 槽)⇒ 按**尾部对齐**取槽:" - f"字段 '{field_name}' = {_TailBase + _TIdx}" - f"(前缀 {_TailBase} 项 = body 尾部之外的部分)", - "MemberBase") - return _TailBase + _TIdx - _Cursor: int = _BaseNow() + # ---- 布局真源之二:两种对齐候选按"逐项类型可写入"**打分**取高者 ---- + # 成员表可能是①展平表(含父类前缀)②仅自有字段(父类前缀未补齐 ⇒ 真身是 + # body **尾部**)③残缺前缀(如仅 ['Gen'] 一项 ⇒ 真身在 body **头部**)。 + # 只用"长度关系/父类槽数"来断言①③是猜,已被两轮现场打脸 + # (Generator.vp:136 TypeGenMixin 被锚到槽 40;:164 TypeGenErr 因恰好 + # 只有 1 个自有字段、`body-len(表) == 父类槽数` 巧合成立而被锚到槽 41)。 + # 改为**比较**两个候选锚(前缀锚 `_BaseNow()` vs 尾部锚 `len(body)-len(表)`) + # 的逐项得分(判据与建 body/存储侧同一套 `_MemberSlotAccepts`): + # 只有尾部锚**严格更高分**才采用它,同分/更低一律保持原行为 + # (配对 → 下标法),因此对既有正确布局零风险。 _ElemN: int = len(_OffEls) + _AnchorA: int = _BaseNow() + _AnchorB: int = _ElemN - len(_OffMembers) + _ScoreA: int = 0 + _aA: int = 0 + while _aA < len(_OffMembers): + if _AnchorA + _aA >= _ElemN: + break + _nA, _tA = _OffMembers[_aA] + if self._MemberSlotAccepts(_tA, _OffEls[_AnchorA + _aA]): + _ScoreA += 1 + _aA += 1 + _ScoreB: int = 0 + if _AnchorB >= 0 and _AnchorB != _AnchorA and not self._IsRenumLayoutClass(_ResolvedClass): + _aB: int = 0 + while _aB < len(_OffMembers): + if _AnchorB + _aB >= _ElemN: + break + _nB, _tB = _OffMembers[_aB] + if self._MemberSlotAccepts(_tB, _OffEls[_AnchorB + _aB]): + _ScoreB += 1 + _aB += 1 + if _ScoreB > _ScoreA and _ScoreB > 0: + _TIdx: int = -1 + for _i5, (_n5, _) in enumerate(_OffMembers): + if _n5 == field_name: + _TIdx = _i5 + break + if _TIdx >= 0: + # 每类只留一次痕(大类的每个字段都会命中本路径) + _TailKey: str = _ResolvedClass + _TailRep: set = getattr(self, '_MemberTailAlignedReported', None) + if _TailRep is None: + _TailRep = set() + self._MemberTailAlignedReported = _TailRep + if _TailKey not in _TailRep: + _TailRep.add(_TailKey) + _vlog().warning( + f"[成员偏移] 类 '{_ResolvedClass}' 的成员表({len(_OffMembers)} 项)" + f"在 body({_ElemN} 槽)上按**尾部对齐**得分更高" + f"(尾部锚 {_AnchorB} 得 {_ScoreB} vs 前缀锚 {_AnchorA} 得 {_ScoreA})" + f"⇒ 判定该表缺的是**父类前缀**:字段 '{field_name}' = " + f"{_AnchorB + _TIdx}", + "MemberBase") + return _AnchorB + _TIdx + _Cursor: int = _BaseNow() _PairedNames: set = set() for _nmP, _mtP in _OffMembers: if _Cursor >= _ElemN: