From 1903ed14f62401b3aa06cf71802b2b786ded87ad Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 10:50:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(HandlesClassDef):=20=E7=B1=BB=E4=BD=93?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=B8=8E=E7=A5=96=E5=85=88=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E9=87=8D=E5=90=8D=20=E2=87=92=20fail-fast=EF=BC=88=E6=B6=88"?= =?UTF-8?q?=E7=BB=A7=E6=89=BF=E6=AE=B5=E9=94=99=E4=BD=8D=E5=86=99=E9=94=99?= =?UTF-8?q?=E6=A7=BD"=E4=B8=8E=20split-brain=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a 编译器缺陷(静默写错槽)+ c 源码违规(子类重复声明父类字段)的入口拦截。 判据(为什么重名必须终止,而不是容忍): 1. 子类同名声明会成为**自有字段**,与**同名继承项**在 class_members 里并存;跨模块侧 的前置过滤器(HandlesImports._FlattenExt 前置段 :2516-2521)按"名字已在表内"过滤 ——它本意只是防同一函数重入时重复前置,却把**同名继承项整条丢掉** ⇒ 继承段与 struct body 的继承前缀失去一一对应:被丢项**之后**的继承字段表内下标整体前移 k, 而查询侧回退路径用 `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型完全 不同的槽。现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult(下标 32)前移为 27 = CTypedefNameIdxBuilt(i32) ⇒ `self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 报 "指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。 2. 父类方法按父类布局访问该名字的槽、子类方法按自有槽访问 ⇒ 同一变量 split-brain (一侧写、另一侧读),运行期静默错值。 实现:在类体自有字段收集段之后(:1047-1116)比对"自有字段名"与"沿 class_parents 传递收集到的全部祖先声明字段名",命中即 compile_error(含 文件:行:列、源码行、 祖先类名与逐字段明细,行号按 _line_offset 还原)+ sys.exit(1)。自有段用快照切分 (:903),祖先未就绪时视为无祖先 ⇒ 只少报不误报。 预检(避免新检查打断既有构建):用 TPC 自家解析器扫 App+includes 全部 .vp (226 文件 / 382 类,口径=类体 AnnAssign + __init__ 带注解 self.x),既有代码 重名遮蔽数 = 0。 冒烟(合成样例,跑完删除): 正例 class Child(Base) 重复声明 A ⇒ SystemExit code=1,报文给出 Child/A/Base/6:4; 反例 Child 只声明新字段 C ⇒ 正常翻译。 配套:TransPyV 侧已撤销 TypeGenCType 的重复声明(0e5ebd0)。 --- lib/core/Handles/HandlesClassDef.py | 77 +++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index f23b851..8286029 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -5,6 +5,7 @@ if TYPE_CHECKING: from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin import vast as ast import copy +import sys import llvmlite.ir as ir from lib.core.Exportable import EnumMember as ExportEnumMember from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta @@ -897,6 +898,9 @@ class ClassHandle(BaseHandle): Gen.class_requires[ClassName] = values elif field_name == '__require_must__': Gen.class_require_must[ClassName] = values + # 自有字段追加段的起点(供下方"字段名遮蔽"检查切分自有/继承两段; + # 此时 class_members[ClassName] 只含继承前缀,见 :825)。 + _OwnFieldStart: int = len(Gen.class_members[ClassName]) for item in Node.body: # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): @@ -1040,6 +1044,79 @@ class ClassHandle(BaseHandle): else: Gen.class_member_signeds[ClassName][VarName] = None Gen.class_member_bitfields[ClassName][VarName] = 0 + # ============================================================ + # 【字段名遮蔽 fail-fast】类体声明的字段名与**任一祖先声明的字段名**重名 ⇒ 终止编译 + # + # 这不是风格问题:本仓两条既有机制决定了"同名"必然产出**写错槽**,且是静默的。 + # ① 子类同名声明会成为**自有字段**,与**同名继承项**在 class_members 里并存; + # 跨模块侧的前置过滤器(HandlesImports._FlattenExt 前置段 :2516-2521)按 + # "名字已在表内"过滤——它本意只是防同一函数重入时重复前置,却会把**同名继承项 + # 整条丢掉** ⇒ 继承段与 struct body 的继承前缀失去一一对应:被丢项**之后**的 + # 继承字段表内下标整体前移 k(k = 被丢项数),而查询侧回退路径用的是 + # `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。 + # 现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 + # LastBuiltinsTypedefResult(下标 32)前移为 27 = CTypedefNameIdxBuilt(i32) + # ⇒ `self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 报 + # "指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。 + # ② 父类方法按**父类布局**访问该名字的槽、子类方法按**自有槽**访问 ⇒ 同名两槽 + # split-brain(一侧写、另一侧读),运行期静默错值。 + # 唯一合法写法:字段只在其**归属类**声明一次;子类要新字段就换名字。 + # 合法例外(不在本检查范围):两个**互不为祖先**的类各自声明同名字段 + # (如 CallGenBase / TypeGenBase 的 TempDir)——那不属于遮蔽。 + # 判据来源:自有 = 本类类体追加段(:903 快照起);祖先 = 沿 class_parents 传递收集 + # 各祖先 class_members 的声明名(含仅从 .vpi 加载的祖先;祖先未就绪则该项不成判据, + # 不产生误报)。 + # ============================================================ + _AncestorFieldNames: dict[str, str] = {} + _AncSeen: set[str] = set() + # class_parents 可能尚未建立(HandlesImports 同族代码也用 hasattr 兜底), + # 缺失时视为"无祖先"——只可能少报、不可能误报。 + _ParentsMap: dict = getattr(Gen, 'class_parents', None) or {} + _AncStack: list[str] = list(_ParentsMap.get(ClassName) or []) + while _AncStack: + _Anc: str = _AncStack.pop() + if not _Anc or _Anc in _AncSeen: + continue + _AncSeen.add(_Anc) + for _An, _ in Gen.class_members.get(_Anc) or []: + if _An != '__vtable__': + _AncestorFieldNames.setdefault(_An, _Anc) + _AncStack.extend(_ParentsMap.get(_Anc) or []) + _OwnFieldNames: list[str] = [n for n, _ in Gen.class_members[ClassName][_OwnFieldStart:]] + _Shadowed: list[str] = sorted({_n for _n in _OwnFieldNames if _n in _AncestorFieldNames}) + if _Shadowed: + _Decl: ast.AST | None = None + for _it in Node.body: + if (isinstance(_it, ast.AnnAssign) and isinstance(_it.target, ast.Name) + and _it.target.id in _Shadowed): + _Decl = _it + break + _src: str = getattr(Gen, '_current_source_file', '') or '' + _ln: int = (getattr(_Decl, 'lineno', 0) or getattr(Node, 'lineno', 0) or 0) + _col: int = getattr(_Decl, 'col_offset', -1) if _Decl is not None else -1 + # 行号还原:AST 行号含 _inject_auto_imports 注入的 N 行(同 + # HandlesBase._FailFastFixedArrayKeyword / _ErrorStack 各站点)。 + _off: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _off > 0 and _ln > _off: + _ln -= _off + _code: str = '' + try: + _code = (Gen._get_source_line(_ln) or '').strip() + except Exception: + _code = '' + _Pairs: str = '、'.join(f"'{_n}'(祖先 '{_AncestorFieldNames[_n]}' 已声明)" + for _n in _Shadowed) + _msg: str = ( + f"类 '{ClassName}' 的类体字段与祖先类声明的字段**重名**:{_Pairs}。" + f"字段只能在其归属类声明一次(子类要新字段请改名;若确实要复用父类字段," + f"删掉子类的这条声明即可)——重名会让该名字在 class_members 里变成" + f"「自有字段 + 同名继承项」两份,跨模块展平的按名过滤会丢掉继承项," + f"使其后所有继承字段的表内下标整体前移、写入落到类型不同的槽" + f"(典型:指针值写进 i32 槽 → 「指针隐式转整数已禁止」);" + f"父类方法与子类方法还会各自访问不同槽(同一变量 split-brain)。" + f"位置 {_src}:{_ln}:{_col}") + _vlog().compile_error(_msg, _src, _ln, _code) + sys.exit(1) # 字段收集完成,清除标志,允许后续 _generate_structs 正常设置 body Gen._collecting_members_set.discard(ClassName) existing_members: set[str] = {m[0] for m in Gen.class_members[ClassName]}