fix(Handles/PythonParser/DeclarationGenerator): 补齐 3 处"调用了不存在成员"的死路径 + 1 处被 hasattr 掩盖的静默降级

类别: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。
This commit is contained in:
TermiNexus committed 2026-09-30 18:23:25 +08:00
1 parent 52ea21f082
commit c2f53852ee
4 files changed
+134 -2

No files matched your search

+55
View File
@@ -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:
+60
View File
@@ -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: