fix(LLVMCG): 成员表对齐改为「前缀锚 vs 尾部锚」按类型可写入性打分,修 Generator.vp:164 误报
类别: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 通过。
This commit is contained in:
1 parent
1776dfa60a
commit
4d14334bd2
1 file changed
+55
-59
+55
-59
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user