From 55cbf1d261484722c6e2c2f02a042c3a234ec13b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 11:18:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(HandlesImports):=20=E7=BB=A7=E6=89=BF?= =?UTF-8?q?=E5=89=8D=E7=BC=80=E5=89=8D=E7=BD=AE=E6=94=B9=E4=B8=BA"?= =?UTF-8?q?=E6=8C=89=E8=A1=A8=E5=A4=B4=E5=88=A4=E5=B9=82=E7=AD=89"?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=86=8D=E6=8C=89=E5=90=8D=E5=AD=97=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E6=95=B4=E5=9D=97=EF=BC=88=E9=87=8D=E5=90=8D=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E9=A1=B9=E4=B8=8D=E5=86=8D=E8=A2=AB=E5=88=A0=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a 编译器缺陷(静默删掉布局条目,使继承段下标整体前移、写到类型不同的槽)。 问题(旧判据):`_EmitExternalClassDeclLlvm` 的前置段用 `nm2 not in {class_members[ClassName] 整表所有名字}` 过滤祖先块。该判据把 **与子类自有字段重名的继承项整条删掉**,于是继承段与 body 的继承前缀失去一一对应: 被删项**之后**的继承字段表内下标整体前移 k(k = 被删项数),而查询侧回退路径用 `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型完全不同的槽。 现场(本轮):TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult 的下标 32→27,`= "t.CPtr"` 写进 i32 槽 ⇒ "指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。 新判据(抽成模块级 PrependInheritedBlock,可独立验证): ① 不删块内条目——块内每项都对应 body 的一个继承槽,删任一项都会整体前移; ② 幂等——只问"表头前 len(块) 项的名字序列是否等于祖先块的名字序列",相等即整块 已前置 → 不再前置(保住 2026-08-10 修的重复字段问题:BasicBlock [Next, Next, …]); ③ 同名并存——子类自有字段与继承项同名时两者都留(定义端 body 同样是"继承前缀 + 自有段",两侧槽数一致);源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下(1903ed14)。 冒烟(纯逻辑,跑完删除):首次前置=块+自有;重复调用不再前置;**同名项仍全保留** (对照旧实现会删掉块首项 ⇒ 下标前移);空块 no-op。py_compile 通过。 已知残留(写进函数文档):若此前只前置过部分祖先块(父类表当时较短),本次会整块再前置 一次 ⇒ 该前缀出现重复条目;调用点上方刚对父类 force 重载(块内容稳定),该情形仅在 "父类表被中途改写"时出现,且重复条目会在成员槽失败报文的"名序"里直接可见。 --- lib/core/Handles/HandlesImports.py | 61 ++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 404c13b..61386d5 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -144,6 +144,46 @@ def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequenc _vlog().warning(Message, "Exception") +def PrependInheritedBlock(Gen: object, ClassName: str, Block: list) -> bool: + """把祖先字段块 `Block` 前置到 `Gen.class_members[ClassName]`(幂等,且**不删块内条目**)。 + + 为什么单独成函数:这段判据是"继承段与 body 继承前缀一一对应"的唯一保障,必须可 + 独立验证(见本仓"按'表头是否已等于块'判幂等"的三条性质)。 + + 判据与性质: + ① **不删块内条目**。块里每一项都对应 body 的一个继承槽;只要块内某项被删, + 被删项之后的继承字段的表内下标就会整体前移 k,而查询侧回退路径用 + `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型不同的槽。 + 旧实现按"名字已在整表内"过滤整块,正是这样删掉了**与子类自有字段重名**的 + 继承项(现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 + LastBuiltinsTypedefResult 下标 32→27,写进 i32 槽 → "指针隐式转整数已禁止")。 + ② **幂等**。本函数会被多个加载路径重复调用(_LoadModuleDeclarationsFromFile / + _LoadDeclarationsFromStubLlvm / _TryLoadClassMembersFromPyi 懒加载);判据是 + "表头前 len(Block) 项的名字序列是否等于 Block 的名字序列"——相等即整块已前置, + 直接返回 False(不再前置),避免 2026-08-10 那类重复字段 + (BasicBlock: [Next, Next, Name, ...] → 与 struct elements 数量不匹配 → 偏移错乱)。 + ③ **同名并存**。子类自有字段与继承项同名时,两者都保留(定义端 + HandlesClassDef 的 body 同样是"继承前缀 + 自有段",两侧槽数一致); + 源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下, + 此处只是不让它把布局判据带偏。 + + 已知残留:若此前只前置过**部分**祖先块(父类表当时较短),本次会整块再前置一次, + 该前缀出现重复条目。调用点上方刚对父类做过 force 重载(块内容稳定),故该情形仅在 + "父类表被中途改写"时出现,且重复条目会在成员槽失败报文的"名序"里直接可见。 + + 返回 True 表示本次真的前置了。 + """ + _Members: list = getattr(Gen, 'class_members', {}).get(ClassName) or [] + _BlockNames: list = [nm for nm, _ in Block] + if not _BlockNames: + return False + _HeadNames: list = [nm for nm, _ in _Members[:len(_BlockNames)]] + if _HeadNames == _BlockNames: + return False + getattr(Gen, 'class_members')[ClassName] = list(Block) + list(_Members) + return True + + class ImportHandle(BaseHandle): _struct_Load_cache = {} _pyi_cache = {} @@ -2511,14 +2551,19 @@ class ImportHandle(BaseHandle): # 加载路径重复调用(_LoadModuleDeclarationsFromFile / _LoadDeclarationsFromStubLlvm # / _TryLoadClassMembersFromPyi 懒加载),此前调用已前置的父类字段会被再次 # 前置 → class_members 出现重复字段(如 BasicBlock: [Next, Next, Name, ...]) - # → 与 struct elements 数量不匹配 → 属性偏移错乱。按字段名过滤已存在的 - # 父类字段;跨父类重名字段(不同父类各自声明)仍全部保留。 - _cur_member_names_pre: set = {n for n, _ in Gen.class_members.get(ClassName, [])} - _filtered_inherited: list[tuple] = [ - (nm2, tp2) for nm2, tp2 in _all_inherited if nm2 not in _cur_member_names_pre - ] - if _filtered_inherited: - Gen.class_members[ClassName] = _filtered_inherited + Gen.class_members[ClassName] + # → 与 struct elements 数量不匹配 → 属性偏移错乱。 + # 【判据改为"前置段是否已等于祖先块"(2026-09-30)】旧判据是 + # `nm2 not in {整表所有名字}`——它会把**与子类自有字段重名的继承项**删掉, + # 于是继承段与 body 的继承前缀失去一一对应:被删项**之后**的继承字段表内 + # 下标整体前移 k(k = 被删项数),而查询侧回退路径用 `base + 表内下标` + # (FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。现场: + # TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult + # 下标 32→27,写到 i32 槽 → "指针隐式转整数已禁止"。 + # 新判据(幂等且不删块内条目):只问"表头前 len(块) 项的名字序列是否等于 + # 祖先块的名字序列"——相等 ⇒ 整块已前置(本次是重复调用)⇒ 不再前置; + # 否则整块前置。**任何情况下都不删块内条目**,因此"同名自有字段 + 同名继承项" + # 会并存(body 里本来就是两个槽,定义端 HandlesClassDef 同样两者都在)。 + PrependInheritedBlock(Gen, ClassName, _all_inherited) # 父类字段已处理(可能此前已前置),跳过占位符路径避免叠加 _any_parent_loaded = True if not _any_parent_loaded and _ParentClasses: