From c2f53852ee404697bef77e6469607290770fa6b2 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 18:23:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(Handles/PythonParser/DeclarationGenerator):?= =?UTF-8?q?=20=E8=A1=A5=E9=BD=90=203=20=E5=A4=84"=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E4=BA=86=E4=B8=8D=E5=AD=98=E5=9C=A8=E6=88=90=E5=91=98"?= =?UTF-8?q?=E7=9A=84=E6=AD=BB=E8=B7=AF=E5=BE=84=20+=201=20=E5=A4=84?= =?UTF-8?q?=E8=A2=AB=20hasattr=20=E6=8E=A9=E7=9B=96=E7=9A=84=E9=9D=99?= =?UTF-8?q?=E9=BB=98=E9=99=8D=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a(编译器缺陷) 现场:翻译 TransPyV/App/lib/core/Gen/Call/CallGenLeafKit.vp 时终止编译 RuntimeError: 翻译 ... 失败: 'ClassHandle' object has no attribute '_PreRegisterClassMembers' (FuncGen.py:965 有调用,方法全仓无定义) 按要求做了全仓静态扫描(ast:属性访问 vs 全仓属性定义;symtable:未定义全局名),共修 4 处: 1) HandlesClassDef.ClassHandle._PreRegisterClassMembers(缺失 → 实现) 调用点:FuncGen._get_member_offset 的"struct body 已存在、class_members[类名] 为空"分支 —— 该类的成员表在本次翻译里从未被收集,而按名定位字段槽位必须有表。 实现与既有两处成员收集段同源(CollectAnnAssignMember → 追加 (名, 类型)),但**只做收集**: · 不展平继承前缀(调用方随后经 _member_base → EnsureClassMembersFlattened 补齐); · 不调用 _generate_structs(body 已存在,重入 set_body 会把残缺布局永久固化); · 只追加尚无同名的条目(幂等,重复命中不会把表撑长)。 跳过 __provides__/__requires__/__require_must__(非布局槽位),并登记字段限定符。 2) HandlesExprAttr.ExprAttrHandle._load_bitfield_member(缺失 → 实现) 读路径 7 处调用(位域分支)全部指向不存在的方法 ⇒ 任何位域字段读取都会 AttributeError。 实现与 HandlesAssign._store_bitfield_member 严格对称:位域类 body 只有一个 storage 槽 (elements[0]),字段位置由 class_member_bitoffsets/bitfields/signeds 表达, 读取 = `(storage >> 位偏移) & ((1<<位宽)-1)`,有符号字段再做符号扩展; 缺位偏移 / 位宽 ≤ 0 / struct 未具化 / storage 非整数一律 fail-fast(不返回 i32 兜底)。 3) PythonParserMixin 的 __set_default__ 分支(幽灵调用 → 明确报未支持) 原调用 self._handle_set_default_in_parse(...) 全仓无定义。核实:该注解在 TransPyC/lib 内 无任何消费端(只有 t.py 的定义与 StubGen/Converter 的"剥离该调用保留类型"), includes 全仓亦无源使用 ⇒ 不静默丢弃源码语义,改为 NotImplementedError 明确报"未支持"。 4) DeclarationGenerator._llvm_type_size_str 的数组分支(静默降级 → 修好) 原写作 `self._ARR_TYPE_RE`(少一个 A)并以 hasattr 守卫 —— 该属性全仓从未定义 ⇒ 守卫恒假 ⇒ 数组字段一律落到"未知类型 → 8",与 docstring 声明的"_EmitREnumLlvm 尺寸口径对齐"不符。 改用本类已定义的 _ARRAY_TYPE_RE,并去掉守卫(属性再丢失应当响亮失败)。 核验:compileall 全 lib 通过;属性扫描剩余 26 条全为仓外对象(logging / vast AST / llvmlite Value·Instruction·IRBuilder / 内置 Exception.args / __class__ / __dict__); symtable 未定义全局名仅 __file__(运行时内置)。未新增/放宽任何 fail-fast。 --- lib/Projectrans/DeclarationGenerator.py | 7 ++- lib/core/Handles/HandlesClassDef.py | 55 +++++++++++++++++++++++ lib/core/Handles/HandlesExprAttr.py | 60 +++++++++++++++++++++++++ lib/core/Translator/PythonParser.py | 14 +++++- 4 files changed, 134 insertions(+), 2 deletions(-) diff --git a/lib/Projectrans/DeclarationGenerator.py b/lib/Projectrans/DeclarationGenerator.py index e1aef71..48c7894 100644 --- a/lib/Projectrans/DeclarationGenerator.py +++ b/lib/Projectrans/DeclarationGenerator.py @@ -1081,7 +1081,12 @@ class DeclarationGenerator: if s.endswith('*'): return 8 # 数组类型 [N x T] - m = self._ARR_TYPE_RE.match(s) if hasattr(self, '_ARR_TYPE_RE') else None + # 注:此处原写作 `self._ARR_TYPE_RE`(少一个 A)并用 `hasattr` 守卫 —— 该属性 + # **全仓从未定义** ⇒ 守卫恒假 ⇒ 本分支恒不执行:数组字段一律落到下方"未知类型 → 8" + # 的保守值,与 docstring 声明的"_EmitREnumLlvm 尺寸口径对齐"不符(静默降级)。 + # 现改用本类已定义的 `_ARRAY_TYPE_RE`(见文件顶部),并去掉守卫: + # 属性若再丢失应当响亮失败,而不是静默退回 8。 + m = self._ARRAY_TYPE_RE.match(s) if m: try: n: int = int(m.group(1)) diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index 342b73e..434e647 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -1969,6 +1969,61 @@ class ClassHandle(BaseHandle): Gen.builder = saved_builder Gen.func = saved_func + def _PreRegisterClassMembers(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: + """兜底:把一个类的**自有字段**从 ClassDef 体收集进 `Gen.class_members[类名]`。 + + 调用点:[FuncGen._get_member_offset] 的"struct body 已存在、而 + `class_members[类名]` 为空"分支——该类的成员表在本次翻译里从未被收集 + (时序/跨模块窗口),而按名定位字段槽位必须有表。 + 此前本方法**全仓无定义**(调用点却存在)⇒ 一旦进入即 + `AttributeError: 'ClassHandle' object has no attribute '_PreRegisterClassMembers'` + (现场:翻译 TransPyV/App/lib/core/Gen/Call/CallGenLeafKit.vp 终止编译)。 + + 判据与实现刻意与既有两处成员收集段同源: + - `_EmitClassLlvm` 的自有字段段(`CollectAnnAssignMember` → 追加 `(名, 类型)`); + - `HandlesImports._EmitExternalClassDeclLlvm` 的同名段。 + 但**只做收集**: + - 不展平继承前缀——调用方随后经 `_member_base` → + `EnsureClassMembersFlattened` 按父类补齐(表空且有父类时会被前置); + - 不调用 `_generate_structs()`:body 已存在,重入 `set_body` 会把残缺布局 + 永久固化(llvmlite 的 `set_body` 只能设一次,见 + HandlesImports._EmitExternalClassDeclLlvm 的 `_collecting_members_set` 说明); + - 只追加**尚无同名**的条目(幂等:重复命中不会把表撑长,也就不会让 + `base + 表内下标` 整体后移)。 + """ + if Node is None or Gen is None: + return + ClassName: str = getattr(Node, 'name', None) + if not ClassName: + return + if ClassName not in Gen.class_members: + Gen.class_members[ClassName] = [] + _Existing: set[str] = {n for n, _ in Gen.class_members[ClassName]} + Gen._collecting_members_set.add(ClassName) + try: + for item in Node.body: + # 编译期元数据字段(__provides__/__requires__/__require_must__)不是布局槽位 + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + if item.target.id in ('__provides__', '__requires__', '__require_must__'): + continue + parsed = self.CollectAnnAssignMember(item, Gen) + if not parsed: + continue + VarName, MemberType, TypeInfo = parsed + if VarName in _Existing: + continue + Gen.class_members[ClassName].append((VarName, MemberType)) + _Existing.add(VarName) + Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation) + _Signeds: dict = getattr(Gen, 'class_member_signeds', {}).get(ClassName) + if isinstance(_Signeds, dict): + _Signeds[VarName] = None + _BitFields: dict = getattr(Gen, 'class_member_bitfields', {}).get(ClassName) + if isinstance(_BitFields, dict): + _BitFields[VarName] = 0 + finally: + Gen._collecting_members_set.discard(ClassName) + def _BuildScalarConstant(self, node: ast.AST, llvm_type: ir.Type, Gen: LlvmGeneratorMixin | None = None) -> ir.Constant | None: """构建标量常量,委托给统一的 _BuildScalarConstantUnified 方法。""" if Gen is None: diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index b1ecbf3..0b77647 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -154,6 +154,66 @@ class ExprAttrHandle(BaseHandle): _sn: str = getattr(ST, 'name', '') or '' return bool(_pn and _pn == _sn) + def _load_bitfield_member(self, ObjPtr: ir.Value, ClassName: str, AttrName: str) -> ir.Value: + """位域成员**读取**——与 `HandlesAssign._store_bitfield_member` 严格对称。 + + 布局约定(唯一真源:StructGen 的位域折叠分支):位域类的整个 struct body 只有 + **一个 storage 槽** `elements[0]`(该类位域总位宽 ≤ 该整数宽度);每个字段的 + **位**偏移 / 位宽 / 符号性分别记在 `class_member_bitoffsets` / + `class_member_bitfields` / `class_member_signeds`(都不占独立槽位)。 + + 读取 = `(storage >> 位偏移) & ((1 << 位宽) - 1)`;有符号字段再在 storage 宽度内 + 做符号扩展(与存储侧的 sext/zext 对称),结果类型 = storage 整数类型(= 该类位域 + 注解的基类型)。 + + 此前本方法**全仓无定义**而读路径有 7 处调用(如 `_get_attr_ptr` 的位域分支)—— + 任何对位域字段的读取都会 `AttributeError`(与 `_PreRegisterClassMembers` 同族)。 + 判据缺失(无位偏移 / 位宽 ≤ 0 / struct 未具化 / storage 非整数)一律 fail-fast, + **不返回 i32 之类的兜底值**:走到这些分支只能是"布局或元数据不一致", + 静默降级会把错值写进程序语义。 + """ + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + _Offsets: dict = Gen.class_member_bitoffsets.get(ClassName, {}) + _Widths: dict = Gen.class_member_bitfields.get(ClassName, {}) + if AttrName not in _Offsets: + raise TypeError( + f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 缺位偏移" + f"(class_member_bitoffsets['{ClassName}'] = {sorted(_Offsets)!r})") + BitOffset: int = _Offsets[AttrName] + BitWidth: int = _Widths.get(AttrName, 0) + if BitWidth <= 0: + raise TypeError( + f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 位宽非法({BitWidth})" + f"(class_member_bitfields['{ClassName}'] = {_Widths!r})") + StructType: Any = Gen.structs.get(ClassName) + if (not isinstance(StructType, ir.IdentifiedStructType) + or StructType.elements is None or len(StructType.elements) == 0): + raise TypeError( + f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 的 struct 未具化" + f"(structs['{ClassName}'] = {getattr(StructType, 'name', StructType)!r})" + f"⇒ storage 槽未知") + StorageType: ir.Type = StructType.elements[0] + if not isinstance(StorageType, ir.IntType): + raise TypeError( + f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 的 storage 槽不是整数" + f"(elements[0] = {StorageType})") + StoragePtr: ir.Value = Gen.builder.gep( + ObjPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], + name=f"{AttrName}_bf_storage_ptr") + RawVal: ir.Value = Gen._load(StoragePtr, name=f"{AttrName}_bf_storage") + Shifted: ir.Value = Gen.builder.lshr( + RawVal, ir.Constant(StorageType, BitOffset), name=f"{AttrName}_bf_shr") + Masked: ir.Value = Gen.builder.and_( + Shifted, ir.Constant(StorageType, (1 << BitWidth) - 1), name=f"{AttrName}_bf_and") + _Signeds: Any = (getattr(Gen, 'class_member_signeds', {}) or {}).get(ClassName) or {} + if _Signeds.get(AttrName) and BitWidth < StorageType.width: + _Rest: int = StorageType.width - BitWidth + _LShift: ir.Value = Gen.builder.shl( + Masked, ir.Constant(StorageType, _Rest), name=f"{AttrName}_bf_sign_shl") + Masked = Gen.builder.ashr( + _LShift, ir.Constant(StorageType, _Rest), name=f"{AttrName}_bf_sign_ashr") + return Masked + def _gep_and_Load_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, offset: int | None, AttrName: str, ClassName: str, ST: ir.Type | None = None) -> ir.Value | None: """从结构体指针通过 GEP 取成员,处理 array/struct pointer/class_members 特殊情况""" if offset is None: diff --git a/lib/core/Translator/PythonParser.py b/lib/core/Translator/PythonParser.py index 97616d2..177be53 100644 --- a/lib/core/Translator/PythonParser.py +++ b/lib/core/Translator/PythonParser.py @@ -225,7 +225,19 @@ class PythonParserMixin: # 检查是否有 __set_default__ 初始化 if AnnotationContainsName(NextNode.annotation, '__set_default__'): - self._handle_set_default_in_parse(ClassKey, TargetName, ClassName, NextNode) + # 【fail-fast,替代**不存在的** _handle_set_default_in_parse + # (2026-09-30)】原调用指向的方法全仓无定义 ⇒ 一旦命中即 + # `AttributeError`(与 ClassHandle._PreRegisterClassMembers + # 同族:死调用,非"未走到")。 + # 现状核实:`__set_default__` 在 TransPyC/lib 内只有 + # t.py 的定义与 StubGen/Converter 的"剥离该调用、保留类型", + # **没有任何消费端**读取它的 kwargs;includes 全仓亦无源使用。 + # 因此这里既不调用幽灵方法,也不静默丢弃源码语义:明确报未支持, + # 语义(字段默认值如何落到 class_member_defaults)确定后再实现。 + raise NotImplementedError( + f"[未支持] 注解里的 __set_default__(...):类 '{ClassKey}' " + f"字段 '{TargetName}'(TPC 翻译路径无消费端;" + f"拒绝静默丢弃该默认值语义)") j += 1 continue # 再处理旧格式:xxx: t.CTypedef = xxx 或 xxx: t.CPtr = xxx