fix(classdef,base,attr): 成员注解解析静默兜底改 fail-fast;新增按需输出的字段布局探针

HandlesBase.CollectAnnAssignMember: 注解解析返回 None / _ctype_to_llvm 得 void / 抛异常三处静默 i32、i8* 兜底改 fail-fast,报错带成员名+注解原文+BaseType/PtrCount 现场

HandlesClassDef: 方法内 self.x: T = ... 的同类三处兜底同样改 fail-fast(带类名/成员名/注解原文)

HandlesExprAttr: 新增 _diag_member_layout(默认静默,仅当 offset 为 None / members[offset] 名不符 / 类型与 elements[offset] 不一致时打一行,上限 15 行)
This commit is contained in:
TermiNexus committed 2026-09-26 21:25:52 +08:00
1 parent c83fc752c6
commit 50cea6c7b3
3 files changed
+104 -16

No files matched your search

+27 -10
View File
@@ -1001,21 +1001,21 @@ class BaseHandle:
) -> tuple[str, _ir.Type, CTypeInfo | None] | None:
"""从 ast.AnnAssign 节点解析成员信息。
返回 (VarName, MemberType, TypeInfo);TypeInfo 为 None 表示解析回退到 i32。
返回 (VarName, MemberType, TypeInfo)。
**不再静默兜底**(原实现:注解解析返回 None → i32;_ctype_to_llvm 得 void → i8*;
任何异常 → i32 + TypeInfo=None)。这些兜底会把"注解没解析出来"伪装成一个
具体类型,下游于是报出与真因无关的错——实测:`free_lists: uint64*`(脱糖为
`uint64 | t.CPtr`)在解析异常时被当成 **i32**,`self.free_lists = base`(i8*)
便误报"指针隐式转整数已禁止"。类型信息缺失一律响亮失败。
非 AnnAssign/Name 目标时返回 None。
"""
if not isinstance(item, ast.AnnAssign) or not isinstance(item.target, ast.Name):
return None
VarName: str = item.target.id
AnnotationText: str = ast.unparse(item.annotation)
try:
TypeInfo: CTypeInfo | None = self.ResolveAnnotationType(item.annotation)
if TypeInfo is None:
TypeInfo = CTypeInfo()
TypeInfo.BaseType = t.CInt()
MemberType: _ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(MemberType, _ir.VoidType):
MemberType = _ir.PointerType(_ir.IntType(8))
return VarName, MemberType, TypeInfo
except RecursionError:
# RecursionError 不是"可忽略异常":它是类型解析/声明发射链**成环**的
# 真缺陷信号。此前被下方 `except Exception` 吞成一行
@@ -1026,8 +1026,25 @@ class BaseHandle:
# 让环路根因暴露在错误栈顶,不再被 WARN 掩埋。
raise
except Exception as e:
_vlog().warning(f"HandlesBase: 忽略异常 {e}", exc_info=e)
return VarName, _ir.IntType(32), None
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析失败:"
f"{type(e).__name__}: {e} —— 拒绝静默退化为 i32"
) from e
if TypeInfo is None:
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析不出类型"
f"(ResolveAnnotationType 返回 None)—— 拒绝静默退化为 i32"
)
MemberType: _ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(MemberType, _ir.VoidType):
raise ValueError(
f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析为 void"
f"(BaseType={getattr(TypeInfo, 'BaseType', None)} "
f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} "
f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} "
f"Name={getattr(TypeInfo, 'Name', None)})—— 拒绝静默退化为 i8*"
)
return VarName, MemberType, TypeInfo
def LookupFunctionSymbol(self, name: str, *, module_path: str | None = None) -> CTypeInfo | None:
"""查找函数符号,先尝试 module_path.name,再回退到 name。
+26 -6
View File
@@ -1273,17 +1273,37 @@ class ClassHandle(BaseHandle):
and node.target.value.id == 'self'):
VarName: str = node.target.attr
if VarName not in existing_members:
# fail-fast(项目约定:类型信息缺失立即终止,不兜底)。
# 此前三处静默 i32 兜底(FromNode 返回 None / _ctype_to_llvm 得 void /
# 解析抛异常)会把"声明没解析出来"伪装成"该成员是 i32",下游赋值/取址
# 检查于是报出与真因无关的错——实测:`free_lists: uint64*` 被当成 i32,
# `self.free_lists = base`(i8*) 因此误报"指针隐式转整数已禁止"。
AnnotationText: str = ast.unparse(node.annotation)
try:
TypeInfo: CTypeInfo | None = CTypeInfo.FromNode(node.annotation, self.Trans.SymbolTable)
if TypeInfo is None:
TypeInfo = CTypeInfo()
TypeInfo.BaseType = t.CInt()
raise ValueError(
f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 "
f"'{AnnotationText}' 解析不出类型(CTypeInfo.FromNode 返回 None)"
f"—— 拒绝静默当作 i32"
)
MemberType: ir.Type = Gen._ctype_to_llvm(TypeInfo)
if isinstance(MemberType, ir.VoidType):
MemberType = ir.IntType(32)
except Exception as e: # 回退:成员类型解析失败时使用默认 i32
_vlog().warning(f"HandlesClassDef: 忽略异常 {e}", exc_info=e)
MemberType = ir.IntType(32)
raise ValueError(
f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 "
f"'{AnnotationText}' 解析为 void(BaseType="
f"{getattr(TypeInfo, 'BaseType', None)} PtrCount="
f"{getattr(TypeInfo, 'PtrCount', None)} IsTypedef="
f"{getattr(TypeInfo, 'IsTypedef', None)} Name="
f"{getattr(TypeInfo, 'Name', None)})—— 拒绝静默当作 i32"
)
except Exception as e: # 成员类型解析失败:带注解原文与类/成员名终止
if isinstance(e, ValueError) and str(e).startswith('[FATAL]'):
raise
raise ValueError(
f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 "
f"'{AnnotationText}' 解析失败:{type(e).__name__}: {e}"
) from e
Gen.class_members[ClassName].append((VarName, MemberType))
Gen.class_member_signeds[ClassName][VarName] = None
Gen.class_member_bitfields[ClassName][VarName] = 0
+51
View File
@@ -866,6 +866,7 @@ class ExprAttrHandle(BaseHandle):
if Node.attr in Gen.class_member_bitoffsets.get(ResolvedKey, {}):
return self._load_bitfield_member(CastedObj, ResolvedKey, Node.attr)
offset: int | None = Gen._get_member_offset(Node.attr, ResolvedKey)
self._diag_member_layout(Gen, ResolvedKey, Node.attr, offset)
if offset is None:
return None
if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType):
@@ -896,6 +897,7 @@ class ExprAttrHandle(BaseHandle):
if Node.attr in bitfield_offsets:
return self._load_bitfield_member(ObjVal, CN, Node.attr)
offset = Gen._get_member_offset(Node.attr, CN)
self._diag_member_layout(Gen, CN, Node.attr, offset)
if offset is None:
break
# 泛型基础类型字段特化(根本修复):
@@ -1625,6 +1627,55 @@ class ExprAttrHandle(BaseHandle):
return Gen.builder.gep(obj_val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr")
return None
def _diag_member_layout(self, Gen: LlvmCodeGenerator, ClassName: str, FieldName: str,
Offset: int | None) -> None:
"""字段布局对照探针(**只在"索引/类型对不上"时打一行**,默认完全静默)。
判据(打印条件,任一成立才打):
· offset 为 None(该字段没能解析出索引);或
· members[offset] 的名字不是本次查询的字段名(= 名字→索引表与结构体布局错位,
即"往 elements[offset] 上取类型/取值"会取到别的字段);或
· members[offset] 的声明类型与 elements[offset] 不是同一类型(含指针/整数错配,
如 `Constant.lineno` 取到 `MemManager*`)。
只打关键三元组 + 两表长度;长度不等时再补一行字段名清单(短)。
上限 15 行,按 (类, 字段) 去重,避免刷屏。
"""
try:
Seen: set = getattr(self, '_MemberLayoutDumped', None)
if Seen is None:
Seen = set()
self._MemberLayoutDumped = Seen
self._MemberLayoutDumpCount = 0
Key: tuple = (ClassName, FieldName)
if Key in Seen:
return
Members: list = list(Gen.class_members.get(ClassName, []))
ST: Any = Gen.structs.get(ClassName)
if isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None:
Elems: list = list(ST.elements)
else:
Elems = []
NameAt: str = ''
TypeAt: Any = None
if Offset is not None and 0 <= Offset < len(Members):
NameAt, TypeAt = Members[Offset][0], Members[Offset][1]
ElemAt: Any = Elems[Offset] if (Offset is not None and 0 <= Offset < len(Elems)) else None
TypeSame: bool = (TypeAt is ElemAt) or (TypeAt is not None and str(TypeAt) == str(ElemAt))
Suspicious: bool = (Offset is None) or (NameAt != FieldName) or (not TypeSame)
if not Suspicious:
return
if getattr(self, '_MemberLayoutDumpCount', 0) >= 15:
return
Seen.add(Key)
self._MemberLayoutDumpCount = getattr(self, '_MemberLayoutDumpCount', 0) + 1
print(f'[DIAG] 字段布局错位 {ClassName}.{FieldName}: offset={Offset} '
f'members[offset]名={NameAt!r} 声明类型={TypeAt} element类型={ElemAt} '
f'| members={len(Members)} elements={len(Elems)}', flush=True)
if len(Members) != len(Elems):
print(f' members 名序=[{", ".join(n for n, _t in Members)}]', flush=True)
except Exception:
pass
def _resolve_struct_class_for_attr(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None:
if isinstance(Node, ast.Name):
VarName: str = Node.id