fix(LLVMCG): 尾部对齐加「前缀数 == 父类 struct 槽数」前置校验,修正 Generator.vp:136 误报

类别:a 编译器缺陷(TPC 侧;修正 b06dac1d 引入的过宽判据)

现象:翻译 TransPyV/App/lib/core/Gen/Generator.vp 时 `SubTypeGen.Gen = self`(:136)报
「指针隐式转换已禁止(非基本类型):不能把 %"47382dc8277c9d28.Generator"* 隐式转为
%"6fbe68e8fca81223.TypeSpecArena"*」。

判据(事实链):
· SubTypeGen 静态类型 = rawptr[TypeGenMixin.TypeGenMixin],Gen 字段声明在基类 TypeGenBase;
  该 struct 41 槽:槽 0 = Generator*(Gen),槽 40 = TypeSpecArena*(Specs)。
· 抛错点 `_StoreWithCoerce` 用的是 **GEP 出来的槽类型**(TargetType = Ptr.type.pointee)
  ⇒ 报错里的 TypeSpecArena* 说明 "Gen 的偏移被算成了 40"(Specs 那一槽),而不是 0。
· 成因:b06dac1d 的「尾部对齐」在"成员表短于 body 字段区"时按 `len(elements) - len(表)`
  求前缀数;TypeGenMixin 的表只有 ['Gen'] 一项(且其声明类型已退化成通用指针 ⇒
  类型自校验被放行),于是被锚到 body 末尾 = 槽 40。

修法:尾部对齐加**必需的前缀校验**——前缀数必须等于**父类 struct 的槽数**(展平前缀就是
父类整份布局,含其继承链);父类 struct 不可得(跨模块未加载 / opaque)时**不介入**。
TypeGenMixin:40 ≠ len(TypeGenBase)=41 ⇒ 不介入,落回下标法得 0(Generator*,赋值合法)。
(正面场景仍生效:AnnAssign 表仅 4 项自有字段、body 12 槽、父类 Stmt 8 槽 ⇒ 8 == 8。)

验证:冒烟两例——反例 TypeGenMixin.Gen → 0(修前 40);正例 AnnAssign 仅自有字段表仍得
target/annotation/value/simple = 8/9/10/11;FuncGen py_compile 通过。
This commit is contained in:
TermiNexus committed 2026-09-29 23:58:09 +08:00
1 parent bd92dfdadf
commit 1776dfa60a
1 file changed
+16 -1
+16 -1
View File
@@ -604,8 +604,23 @@ class FuncGenMixin:
# _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 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