fix(compiler,peephole): TPC 属性链解析与窥孔输出瘦身

- TPC(lib/core): 任意级属性链按链前向类解析恢复 stub 退化字段; cast 局部 rowptr[X] 登记回填; 通用指针槽按静态类名 bitcast 兜底; 赋值 RHS 求值失败 fail-fast 防未初始化变量; 报错行按源码文本锚定并附 ATTR-DIAG 现场
- 窥孔(includes/llvmlite/__function.vp): 成员访问单级链模式; 窥孔移至 Renumber 前使 SSA 编号连续; T3 no-op bitcast 消除; 替换缓冲按最坏上界分配
- __builder/__module/string.vp 配套; 清理 Test 构建产物与 PROF 杂项
This commit is contained in:
TermiNexus committed 2026-09-08 11:02:37 +08:00
1 parent 45e78ad107
commit 76d1ee6076
173 files changed
+1104 -3090

No files matched your search

+62
View File
@@ -558,6 +558,56 @@ class AnnAssignHandle(BaseHandle):
Gen.var_struct_class[VarName] = _union_struct_name2
if getattr(Gen, 'global_struct_class', None) is not None:
Gen.global_struct_class[VarName] = _union_struct_name2
# Subscript rowptr[X] / t.CPtr[X] 注解的本地结构指针变量(如
# includes/ast/parser.vp _fix_store_ctx 里 `tp: rowptr[Tuple] =
# (rowptr[Tuple])(node)`,随后 `tp_elts = tp.elts`):TypeMerge 的
# builtins 分支把 rowptr[X] 折叠为 i8*(PtrCount=1/CVoid),既不登记
# var_struct_class 也不保留内层 X —— 单级 Name 字段取值(tp.elts)
# 在属性访问层 ClassName 解析为 None → 求值 None → 赋值被 fail-fast
# 拦截(parser.vp L427 bootstrap 根因)。与上方 BinOp 联合的短名回退
# 对称:把内层 X 解析为真实 struct 键(兼容短名 / 带 class_sha1 前缀
# 的键)并登记;仅在缺失或指针占位(rowptr…)时覆盖,不碰已精确登记值。
_rst_sub: ast.AST | None = None
if isinstance(Node.annotation, ast.Subscript):
_rst_v: ast.expr = Node.annotation.value
_rst_nm: str = ''
if isinstance(_rst_v, ast.Name):
_rst_nm = _rst_v.id
elif isinstance(_rst_v, ast.Attribute) and isinstance(_rst_v.value, ast.Name):
_rst_nm = f'{_rst_v.value.id}.{_rst_v.attr}'
if _rst_nm in ('rowptr', 't.CPtr'):
_rst_sub = Node.annotation
if _rst_sub is not None:
_rst_sl: ast.AST = _rst_sub.slice
if hasattr(ast, 'Index') and isinstance(_rst_sl, ast.Index):
_rst_sl = _rst_sl.value
_rst_tn: str = ''
if isinstance(_rst_sl, ast.Name):
_rst_tn = _rst_sl.id
elif isinstance(_rst_sl, ast.Attribute) and isinstance(_rst_sl.value, ast.Name):
_rst_tn = _rst_sl.attr
_rst_tk: str | None = None
if _rst_tn:
if _rst_tn in Gen.structs:
_rst_tk = _rst_tn
else:
_rst_sha: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_rst_tn)
if _rst_sha and f'{_rst_sha}.{_rst_tn}' in Gen.structs:
_rst_tk = f'{_rst_sha}.{_rst_tn}'
else:
for _rst_k3 in Gen.structs:
_rst_short3: str = _rst_k3.split('.')[-1] if '.' in _rst_k3 else _rst_k3
if _rst_short3 == _rst_tn:
_rst_tk = _rst_k3
break
if _rst_tk:
_rst_cur: str | None = Gen.var_struct_class.get(VarName)
_rst_need: bool = (not _rst_cur or _rst_cur == 'rowptr' or '.rowptr' in _rst_cur
or _rst_cur.startswith('rowptr['))
if _rst_need and _rst_cur != _rst_tk:
Gen.var_struct_class[VarName] = _rst_tk
if getattr(Gen, 'global_struct_class', None) is not None:
Gen.global_struct_class[VarName] = _rst_tk
# 检测指针到指针缓冲区变量:注解为 StructName | t.CPtr,且赋值来自 t.CPtr(...) 调用。
# 这种变量(如 list[T].__getitem__ 中的 elem_ptr: T | t.CPtr = t.CPtr(...))
# 实际指向存储结构体指针的缓冲区(T=AST|t.CPtr 时特化为 AST*,缓冲区存 AST* 数组)。
@@ -598,6 +648,10 @@ class AnnAssignHandle(BaseHandle):
InitValue: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=VarType)
if InitValue:
self._StoreOrStrCopy(VarName, var, InitValue)
elif isinstance(Node.value, ast.Attribute):
# 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp
# 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen)
Gen._record_var_signedness(VarName, IsUnsigned)
return
del Gen._reg_values[VarName]
@@ -609,6 +663,10 @@ class AnnAssignHandle(BaseHandle):
CalledName: str = Node.value.func.id
if CalledName in Gen._closure_return_types:
Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName]
if InitValue is None and Node.value is not None and isinstance(Node.value, ast.Attribute):
# 防静默:带值带注解的属性链求值失败(返回 None)必须报编译错误,
# 否则变量 alloca 已创建却无 store → 后续 load 未初始化栈变量。
self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen)
if VarName in Gen._reg_values:
del Gen._reg_values[VarName]
if VarName in Gen.variables and Gen.variables[VarName] is not None:
@@ -853,6 +911,10 @@ class AnnAssignHandle(BaseHandle):
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType)
if Value:
self._HandleAttributeStoreLlvm(Node.target, Value)
elif isinstance(Node.value, ast.Attribute):
# 防静默:self 字段带注解赋值右侧属性链求值失败必须报错
self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen)
if Value:
AttrName: str = Node.target.attr
len_name: str = f'{AttrName}__len'
ClassName: str | None = self._CurrentCpythonObjectClass
+14 -4
View File
@@ -173,6 +173,10 @@ class AssignHandle(BaseHandle):
if CalledName in Gen._closure_return_types:
Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName]
if not Value:
# 防静默:右侧是属性链(如 blk = func.Blocks.Head)却求值失败时
# 必须报错,禁止跳过 store 留下未初始化变量(随机崩溃)。
if isinstance(Node.value, ast.Attribute):
self._RaiseUnresolvedExpr("赋值", Node.value, Gen)
return
if isinstance(Value.type, ir.VoidType):
return
@@ -447,8 +451,11 @@ class AssignHandle(BaseHandle):
TargetType = m_type
break
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType)
if Value:
self._HandleAttributeStoreLlvm(Target, Value)
if not Value:
if isinstance(Node.value, ast.Attribute):
self._RaiseUnresolvedExpr("属性写入", Node.value, Gen)
return
self._HandleAttributeStoreLlvm(Target, Value)
elif isinstance(Target, ast.Subscript):
TargetType: ir.Type | None = None
SubVarName: str | None = None
@@ -480,8 +487,11 @@ class AssignHandle(BaseHandle):
elif isinstance(var_type, ir.ArrayType):
TargetType = var_type.element
Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=TargetType)
if Value:
self._HandleSubscriptStoreLlvm(Target, Value)
if not Value:
if isinstance(Node.value, ast.Attribute):
self._RaiseUnresolvedExpr("下标写入", Node.value, Gen)
return
self._HandleSubscriptStoreLlvm(Target, Value)
elif isinstance(Target, ast.Tuple):
if isinstance(Node.value, ast.Call):
FuncName: str | None = None
+118
View File
@@ -525,6 +525,124 @@ class BaseHandle:
def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None) -> _ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
@staticmethod
def _ExprText(Node: ast.AST) -> str:
"""把属性链/名字节点渲染成源码文本(如 func.Blocks.Head),供错误信息使用"""
if isinstance(Node, ast.Attribute):
parts: list[str] = []
cur: ast.AST = Node
while isinstance(cur, ast.Attribute):
parts.append(cur.attr)
cur = cur.value
if isinstance(cur, ast.Name):
parts.append(cur.id)
if parts:
return '.'.join(reversed(parts))
if isinstance(Node, ast.Name):
return Node.id
return getattr(Node, 'attr', '') or '<expr>'
def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any) -> None:
"""fail-fast:属性/成员访问表达式在编译期求值失败(返回 None)时终止。
属性链(含两级自动解引用链,如 blk = func.Blocks.Head)若解析不出
目标类型,_handle_attr_nested 会返回 None;赋值调用方若静默跳过
store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机崩溃)。
此处用 VLogger 输出 文件名:行:列 + 表达式源码后退出(严格模式语义)。
"""
import sys
raw_lineno: int = getattr(Node, 'lineno', 0) or 0
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
col: int = getattr(Node, 'col_offset', -1)
src_file: str = getattr(Gen, '_current_source_file', '') or ''
expr_text: str = self._ExprText(Node)
code_line: str = ''
if lineno > 0 and src_file:
# 行号基准修正(vasts AST 行号 vs 磁盘原文件行号偏差):
# Phase2Translator._inject_auto_imports 会在解析前向源码头部注入
# 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件
# 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、
# FuncGenMixin.vp 真实 514 报 515)。而 _get_source_line 基于
# _current_source_lines(原文件),导致报错行号偏大且"代码行"
# 取错行。修正:以表达式文本为锚,从原始行号向上(至多 8 行)
# 回溯取"最近且包含该表达式文本"的源码行作为真实行号。
if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'):
_src_lines: Any = getattr(Gen, '_current_source_lines', None)
if isinstance(_src_lines, list) and _src_lines:
for cand in range(lineno, max(0, lineno - 8), -1):
if 0 < cand <= len(_src_lines) and expr_text in _src_lines[cand - 1]:
lineno = cand
break
try:
code_line = (Gen._get_source_line(lineno) or '').strip()
except Exception:
code_line = ''
loc: str = src_file
if lineno > 0:
loc += f":{lineno}"
if col >= 0:
loc += f":{col}"
msg: str = (f"{What}表达式 '{expr_text}' 编译期求值为空(属性访问未能解析出"
f"目标类型/成员),赋值将被跳过、变量保持未初始化。位置 {loc}"
f"{(',源码: ' + code_line) if code_line else ''}")
# 现场诊断:dump 最内层基变量在各类注册表/存储中的状态,定位属性解析
# 在哪一级断裂(var_struct_class 缺失 / struct 键形态不匹配 / 存储退化)。
diag: str = self._AttrFailDiag(Node, Gen)
if diag:
msg += f"\n[ATTR-DIAG]{diag}"
_vlog().compile_error(msg, src_file, lineno, code_line)
sys.exit(1)
@staticmethod
def _AttrFailDiag(Node: ast.AST, Gen: Any) -> str:
"""提取属性链失败现场:最内层 Name 变量的类登记与存储状态"""
try:
inner: ast.AST = Node
while isinstance(inner, ast.Attribute):
inner = inner.value
if not isinstance(inner, ast.Name):
return ''
var: str = inner.id
seg: list[str] = []
seg.append(f"var={var}")
vsc: Any = getattr(Gen, 'var_struct_class', {}) or {}
gsc: Any = getattr(Gen, 'global_struct_class', {}) or {}
seg.append(f"var_struct_class[{var}]={vsc.get(var)!r}")
seg.append(f"global_struct_class[{var}]={gsc.get(var)!r}")
def _ty_str(ptr: Any) -> str:
try:
if ptr is None:
return 'None'
return str(ptr.type)
except Exception:
return '?'
for _tab_name, _tab in (('variables', getattr(Gen, 'variables', {}) or {}),
('_reg_values', getattr(Gen, '_reg_values', {}) or {}),
('_direct_values', getattr(Gen, '_direct_values', {}) or {}),
('global_vars', getattr(Gen, 'global_vars', {}) or {})):
if var in _tab:
seg.append(f"{_tab_name}[{var}]={_ty_str(_tab[var])}")
# 最内层类名 → 是否在 class_member_element_class 登记字段
cn: Any = vsc.get(var) or gsc.get(var)
cmap: Any = getattr(Gen, 'class_member_element_class', {}) or {}
import re as _re
def _short(k: str) -> str:
return _re.sub(r'^[0-9a-fA-F]{16}\.', '', k)
if cn:
hit: list[str] = []
for key, sub in cmap.items():
if _short(key) == _short(cn):
hit.append(f"{key}:{sub}")
seg.append(f"elem_class_map_hits={hit or 'NONE'}")
# Gen.structs 中含 cn 短名的键(供键形态比对)
if cn:
struct_keys: list[str] = [k for k in (getattr(Gen, 'structs', {}) or {})
if _short(k) == _short(cn)]
seg.append(f"structs_keys={struct_keys or 'NONE'}")
return '\n'.join(seg)
except Exception:
return ''
def HandleBodyLlvm(self, Body: ast.AST | list[ast.stmt]) -> Any:
return self.Trans.BodyHandler.HandleBodyLlvm(Body)
+362 -35
View File
@@ -444,6 +444,14 @@ class ExprAttrHandle(BaseHandle):
# 解析结构体类名:若 VarName 是结构体变量,优先走结构体字段访问路径
# 避免结构体字段名(如 stdout)与全局变量名冲突时被误解析为全局变量
ClassName: str | None = self._resolve_var_classname(Gen, VarName)
# 查找端键归一:var_struct_class 可能记录裸短名('Tuple'),而 Gen.structs
# 的键是带 class_sha1 前缀的全名(如 '43ec967d0517abc1.Tuple'),或反之。
# 单级 Name 字段取值(tp.elts)要求 ClassName 能直接定位 Gen.structs 并
# 加载 stub body 后才能 GEP —— 与 _handle_attr_nested 各分支一致先归一。
if ClassName and ClassName not in Gen.structs:
_cn_key: str | None = self._find_struct_key_for_class_hint(Gen, ClassName)
if _cn_key:
ClassName = _cn_key
# 尝试 attr 直接作为全局变量(仅当 VarName 不是结构体变量时)
if not ClassName and AttrName in Gen.module.globals:
@@ -455,6 +463,20 @@ class ExprAttrHandle(BaseHandle):
return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr")
return Gen.builder.load(gv, name=AttrName)
if not ClassName:
# 单级 Name 字段取值的类名恢复(跨模块 stub 下 var_struct_class 缺失的
# 兜底):本地变量(如 `tp: rowptr[Tuple] = (rowptr[Tuple])(node)`)可能
# 因 rowptr[X] 被折叠为 i8* 而漏登记。若变量 alloca / 值本身可辨识为
# 结构体指针,按实际 pointee 恢复真实 Gen.structs 键并回填,避免属性
# 访问求值 None 被 fail-fast 拦截(parser.vp `tp_elts = tp.elts` 根因)。
if (VarName in Gen.variables or VarName in Gen._reg_values
or VarName in Gen._direct_values or VarName in Gen.global_vars):
_recovered: str | None = self._recover_struct_class(Gen, VarName)
if _recovered:
ClassName = _recovered
Gen.var_struct_class[VarName] = _recovered
if getattr(Gen, 'global_struct_class', None) is not None:
Gen.global_struct_class[VarName] = _recovered
if not ClassName:
return None
@@ -591,6 +613,19 @@ class ExprAttrHandle(BaseHandle):
ClassName = cur_spec2
else:
Gen.var_struct_class[VarName] = ClassName
# 通用指针槽兜底:静态类名(var_struct_class/键归一后)有效且 ObjVal 是指针,
# 但 pointee 非 struct(如 cast 局部变量 tp: rowptr[Tuple] 的槽被建为 i32**,
# Name 求值 load 一次得到 i32*,上述 struct 匹配/回退均不命中 → return None)。
# 此时按静态类名信任 bitcast 到 struct* 再走成员 GEP(tp.elts 根因,ATTR-DIAG
# 实证 var_struct_class='Tuple'、structs 有 'Tuple',仅槽位为通用指针形态)。
if (not struct_match and ClassName and ClassName in Gen.structs
and isinstance(ObjVal.type, ir.PointerType)
and not isinstance(ObjVal.type.pointee, ir.IdentifiedStructType)
and not isinstance(ObjVal.type.pointee, ir.LiteralStructType)):
ObjVal = Gen.builder.bitcast(
ObjVal, ir.PointerType(Gen.structs[ClassName]),
name=f"cast_slot_{ClassName}")
struct_match = True
if not struct_match:
return None
@@ -637,6 +672,45 @@ class ExprAttrHandle(BaseHandle):
result = self._gep_and_Load_member(Gen, ObjVal, offset, AttrName, ClassName, st)
return result
def _recover_struct_class(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None:
"""由变量的 LLVM 存储类型恢复结构体类键(var_struct_class 缺失时兜底)。
单级 Name 字段取值(如 tp.elts)的本地变量,若其注解 rowptr[X] 被 TypeMerge
builtins 分支折叠成 i8*、且 var_struct_class 未登记 X,属性访问层无法解析
类名。此处直接检查 Gen.variables / _reg_values / _direct_values /
global_vars 中该变量的 alloca(或值)指针 pointee:能辨识出
IdentifiedStruct 就按 pointee 恢复真实 Gen.structs 键(精确匹配失败时按
短名找元素最多的完整版本),供调用方回填并继续 GEP 成员访问。不发射任何
额外 IR(纯查表)。
"""
if not VarName:
return None
probe: ir.Type | None = None
vp: Any = Gen.variables.get(VarName)
if vp is not None and isinstance(vp.type, ir.PointerType):
probe = vp.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
if probe is None:
for reg in (Gen._reg_values.get(VarName), Gen._direct_values.get(VarName)):
if reg is not None and isinstance(reg.type, ir.PointerType):
probe = reg.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
break
if probe is None and VarName in Gen.global_vars and hasattr(Gen, 'module') and VarName in Gen.module.globals:
gv: Any = Gen.module.globals[VarName]
if isinstance(gv, ir.GlobalVariable) and isinstance(gv.type, ir.PointerType):
probe = gv.type.pointee
if isinstance(probe, ir.PointerType):
probe = probe.pointee
if probe is None or not isinstance(probe, ir.IdentifiedStructType):
return None
found: Any = Gen.find_struct_by_pointee(probe)
if found:
return found[0]
return self._pointee_llvm_to_struct_key(Gen, probe)
def _handle_attr_nested(self, Node: ast.Attribute) -> ir.Value | None:
"""处理 a.b.c 嵌套属性访问"""
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
@@ -694,35 +768,60 @@ class ExprAttrHandle(BaseHandle):
# char* → struct* cast + 成员访问
if isinstance(pointee, ir.IntType) and pointee.width == 8:
AttrClassName: str | None = self._get_attr_class(Node.value, Gen)
if AttrClassName and AttrClassName in Gen.structs:
st: Any
ok: bool
st, ok = self._try_Load_struct_from_stub(Gen, AttrClassName)
if not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
return None
CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_{AttrClassName}")
offset: int | None = Gen._get_member_offset(Node.attr, AttrClassName)
if offset is None:
return None
if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType):
return None
return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, AttrClassName, st)
# 两级自动解引用链(变量 = 指针.字段.成员,如 blk = func.Blocks.Head)
# 根因修复:跨模块 stub 会把容器/类内指针字段(rowptr[GSList[BasicBlock]]
# 等)退化为 i8*,此处 ObjVal 是刚从结构体字段 load 出的 i8*。必须恢复
# 该字段声明的元素/特化类(如 'GSList[BasicBlock]')才能
# bitcast + GEP + load 出成员。否则本函数返回 None → AnnAssign/Assign
# 静默跳过 store → 目标 alloca 永不初始化(_peephole_opt 读未初始化
# 栈变量随机崩溃,见 __function.vp L783 注释)。
# 解析顺序:_resolve_chain_result_class 沿链前向解析父表达式(可为任意
# 级数,如 ve.Alloca.Ty)的结果类;失败回退 _get_attr_class 直查
# class_member_element_class;再回退 _resolve_attr_parent_hint(与下方
# struct 分支同一 parent_hint 机制:func 的 var_struct_class +
# class_member_element_class 给出 GSList[BasicBlock] 提示);三者都兼容
# 裸名/带 sha1 前缀的键。
AttrClassName: str | None = self._resolve_chain_result_class(Node.value, Gen)
if not AttrClassName:
AttrClassName = self._get_attr_class(Node.value, Gen)
if not AttrClassName:
AttrClassName = self._resolve_attr_parent_hint(Node.value, Gen)
ResolvedKey: str | None = self._find_struct_key_for_class_hint(Gen, AttrClassName)
if ResolvedKey is None or ResolvedKey not in Gen.structs:
return None
st: Any
ok: bool
st, ok = self._try_Load_struct_from_stub(Gen, ResolvedKey)
if not isinstance(st, ir.IdentifiedStructType):
return None
if st.elements is None:
# stub 仍 opaque(如跨模块泛型仅声明):走泛型特化候选访问
spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st, Node, ResolvedKey, AttrClassName)
if spec_val is not None:
return spec_val
return None
CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_{ResolvedKey}")
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)
if offset is None:
return None
if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType):
return None
return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, ResolvedKey, st)
# 已知结构体类型 → 直接成员访问
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
# 提取父上下文特化提示:当 Node.value 是 Attribute(Name, attr) 时,
# 从 var_struct_class 和 class_member_element_class 获取字段声明的特化名。
# 例如 func.Blocks.Head 中,func 是 Function,Blocks 字段声明为 GSList[BasicBlock],
# 提示为 'GSList[BasicBlock]',避免在所有 GSList[T] 都有 Head 字段时选错特化版本。
parent_hint: str | None = None
if isinstance(Node.value, ast.Attribute) and isinstance(Node.value.value, ast.Name):
p_var: str = Node.value.value.id
p_attr: str = Node.value.attr
p_CN: str | None = Gen.var_struct_class.get(p_var)
if p_CN:
p_elem_map: Any = Gen.class_member_element_class.get(p_CN, {})
parent_hint = p_elem_map.get(p_attr)
# 父上下文特化提示:Node.value 是 Attribute(Name, attr)(如
# func.Blocks)时,从 var_struct_class/global_struct_class +
# class_member_element_class 取字段声明的特化名(如 'GSList[BasicBlock]'),
# 避免在所有 GSList[T] 都有 Head/Tail/Count 字段时选错特化版本。
# 与 char* 分支共用 _resolve_attr_parent_hint(兼容裸名/sha1 前缀键);
# 父表达式为多级链(Node.value 是 Attribute)时 _resolve_attr_parent_hint
# 无法取提示,用 _resolve_chain_result_class 沿链前向解析补充。
parent_hint: str | None = self._resolve_attr_parent_hint(Node, Gen)
if not parent_hint and isinstance(Node.value, ast.Attribute):
parent_hint = self._resolve_chain_result_class(Node.value, Gen) or parent_hint
for CN, ST in Gen.structs.items():
if pointee == ST or (isinstance(pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and pointee.name == ST.name):
st, ok = self._try_Load_struct_from_stub(Gen, CN)
@@ -1584,23 +1683,251 @@ class ExprAttrHandle(BaseHandle):
return ir.Constant(ir.IntType(32), info.value)
return None
def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None:
"""在 class_member_element_class 中按"属主类 + 字段名"查找元素/特化类。
class_member_element_class 的 key 与调用方持有的类名(var_struct_class /
_CurrentCpythonObjectClass 等)可能存在"裸名 vs 带 sha1 前缀"的不一致
(如 key='Function',而持有者是 '732a8c24abb9b516.Function')。先精确
匹配,再按短名(剥离 16 位 hex 前缀)遍历所有 key 匹配,确保能恢复
字段声明的特化类(如 rowptr[GSList[BasicBlock]] → 'GSList[BasicBlock]')。
"""
if not OwnerClass or not FieldName:
return None
elem_map: Any = getattr(Gen, 'class_member_element_class', {}) or {}
if OwnerClass in elem_map:
sub_map: Any = elem_map[OwnerClass]
if FieldName in sub_map:
return sub_map[FieldName]
owner_short: str = Gen._extract_short_name(OwnerClass)
for key, sub_map in elem_map.items():
if Gen._extract_short_name(key) != owner_short:
continue
if FieldName in sub_map:
return sub_map[FieldName]
return None
def _resolve_attr_parent_hint(self, Node: ast.Attribute, Gen: LlvmCodeGenerator) -> str | None:
"""从父对象上下文解析字段的元素/容器特化提示(parent_hint)。
例:func.Blocks.Head —— Node.value 是 func.Blocks,父变量 func 的
var_struct_class/global_struct_class 是 'Function',Blocks 字段声明为
rowptr[GSList[BasicBlock]],class_member_element_class 记录元素提示
'GSList[BasicBlock]'。该提示用于把退化为 i8* 的字段指针恢复成目标
特化类(char* 分支),或在所有 GSList[T] 都有 Head/Tail/Count 时
选择正确特化版本(struct 分支)。兼容裸名/带 sha1 前缀键。
"""
if not (isinstance(Node, ast.Attribute) and isinstance(Node.value, ast.Name)):
return None
p_var: str = Node.value.id
p_attr: str = Node.attr
p_CN: str | None = Gen.var_struct_class.get(p_var)
if not p_CN and getattr(Gen, 'global_struct_class', None):
p_CN = Gen.global_struct_class.get(p_var)
if not p_CN:
return None
return self._lookup_member_elem_class(Gen, p_CN, p_attr)
def _find_struct_key_for_class_hint(self, Gen: LlvmCodeGenerator, ClassHint: str | None) -> str | None:
"""把 class_member_element_class 记录的类/特化名解析为 Gen.structs 的真实键。
记录值可能是裸名('GSList[BasicBlock]')或带 sha1 前缀
('8e92345fd262a343.GSList[BasicBlock]'),而 Gen.structs 的键可能是
另一种形态。依次尝试:精确键 → 短名键 → class_sha1_map 重建前缀键 →
遍历 Gen.structs 按短名匹配。
"""
if not ClassHint:
return None
if ClassHint in Gen.structs:
return ClassHint
short: str = Gen._extract_short_name(ClassHint)
if short in Gen.structs:
return short
expected_sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(short)
if expected_sha1:
prefixed: str = f"{expected_sha1}.{short}"
if prefixed in Gen.structs:
return prefixed
for key in Gen.structs:
if Gen._extract_short_name(key) == short:
return key
return None
def _get_attr_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None:
if isinstance(Node, ast.Attribute):
# 注: 不再硬编码 ZLIB_VERSION / C_OK,它们应从符号表查找
if isinstance(Node.value, ast.Name) and Node.value.id == 'self':
ClassName: str | None = self.Trans._CurrentCpythonObjectClass
if ClassName and ClassName in Gen.class_member_element_class:
return Gen.class_member_element_class[ClassName].get(Node.attr)
return self._lookup_member_elem_class(Gen, ClassName, Node.attr)
if isinstance(Node.value, ast.Name):
VarName: str = Node.value.id
if VarName in Gen.var_struct_class:
ParentClass: str = Gen.var_struct_class[VarName]
if ParentClass in Gen.class_member_element_class:
return Gen.class_member_element_class[ParentClass].get(Node.attr)
ClassName: str | None = Gen.var_struct_class.get(VarName)
if not ClassName and getattr(Gen, 'global_struct_class', None):
ClassName = Gen.global_struct_class.get(VarName)
if ClassName:
return self._lookup_member_elem_class(Gen, ClassName, Node.attr)
if isinstance(Node.value, ast.Attribute):
ParentClass = self._get_attr_class(Node.value, Gen)
if ParentClass and ParentClass in Gen.class_member_element_class:
return Gen.class_member_element_class[ParentClass].get(Node.attr)
ParentClass: str | None = self._get_attr_class(Node.value, Gen)
if ParentClass:
return self._lookup_member_elem_class(Gen, ParentClass, Node.attr)
return None
# ========== 任意级属性链:逐段前向类解析 ==========
def _pointee_llvm_to_struct_key(self, Gen: LlvmCodeGenerator, llvm_type: ir.Type) -> str | None:
"""把 LLVM 成员类型(Ptr->IdentifiedStruct / IdentifiedStruct)解析为
Gen.structs 的真实键。
先精确匹配(find_struct_by_pointee),失败再按短名匹配(优先选择
有完整 elements 的定义),兼容 sha1 前缀冲突/泛型特化场景。
"""
cand: ir.Type = llvm_type
if isinstance(cand, ir.PointerType):
cand = cand.pointee
if not isinstance(cand, ir.IdentifiedStructType):
return None
found: Any = Gen.find_struct_by_pointee(cand)
if found:
return found[0]
cand_short: str = Gen._extract_short_name(cand.name)
best_key: str | None = None
best_elems: int = -1
for k in Gen.structs:
if Gen._extract_short_name(k) != cand_short:
continue
st_v: Any = Gen.structs[k]
if not isinstance(st_v, ir.IdentifiedStructType):
continue
n_elems: int = len(st_v.elements) if st_v.elements is not None else 0
if n_elems > best_elems:
best_elems = n_elems
best_key = k
return best_key
def _owner_member_llvm_types(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[tuple[str, ir.Type]]:
"""收集 OwnerClass(含继承父类)的成员 (名, LLVM 类型) 列表。
class_members 的键可能是带 sha1 前缀全名,此处按短名/剥 '[' 泛型
参数后的基名匹配,并沿 class_parents 继承链向上补充父类字段。
"""
owner_short: str = Gen._extract_short_name(OwnerClass)
base_short: str = owner_short.split('[')[0] if '[' in owner_short else owner_short
members_map: dict[str, list] = {}
for k, v in getattr(Gen, 'class_members', {}).items():
k_short: str = Gen._extract_short_name(k)
k_base: str = k_short.split('[')[0] if '[' in k_short else k_short
if k_short == owner_short or k_base == base_short:
members_map[k] = list(v)
ordered: list[tuple[str, ir.Type]] = []
seen: set[str] = set()
for m in self._class_and_parents(Gen, OwnerClass):
m_short: str = Gen._extract_short_name(m)
for k, v in members_map.items():
if Gen._extract_short_name(k) != m_short:
continue
for name, ty in v:
if name not in seen:
seen.add(name)
ordered.append((name, ty))
return ordered
def _class_and_parents(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[str]:
"""OwnerClass 自身 + 全部父类短名链(子类在前)"""
chain: list[str] = []
seen: set[str] = set()
cur: str = Gen._extract_short_name(OwnerClass)
parents_map: Any = getattr(Gen, 'class_parents', {}) or {}
single_parent: Any = getattr(Gen, 'class_parent', {}) or {}
while cur and cur not in seen:
seen.add(cur)
chain.append(cur)
nxt: list[str] = parents_map.get(cur, [])
if not nxt and cur in single_parent and single_parent[cur]:
nxt = [single_parent[cur]]
if not nxt:
break
# 多继承:只取第一条链向上(深度优先声明顺序)保持与
# _get_member_offset 的继承链回退一致
cur = Gen._extract_short_name(nxt[0])
return chain
def _field_element_key(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, AttrName: str) -> str | None:
"""解析 OwnerClass 上 AttrName 字段的"元素 struct 键"(可在 Gen.structs 定位)。
供任意级链(ve.Alloca.Ty.Pointee)逐段恢复目标类:跨模块 stub 会把
rowptr[X] 指针字段退化为 i8*,load 出的值丢失 struct 身份;此处综合
class_member_element_class(登记的元素/特化类)→ class_members LLVM
成员类型 → struct elements 字段类型 推断该字段指向的 struct 键,
兼容裸名/sha1 前缀/泛型特化名。
"""
if not OwnerClass or not AttrName:
return None
# 1) class_member_element_class 直查(含短名模糊匹配)
hint: str | None = self._lookup_member_elem_class(Gen, OwnerClass, AttrName)
if hint:
k: str | None = self._find_struct_key_for_class_hint(Gen, hint)
if k:
return k
# 2) class_members 成员 LLVM 类型
for m_name, m_type in self._owner_member_llvm_types(Gen, OwnerClass):
if m_name != AttrName:
continue
k2: str | None = self._pointee_llvm_to_struct_key(Gen, m_type)
if k2:
return k2
break
# 3) struct elements 字段类型(加载 stub 后按偏移取值)
owner_key: str | None = self._find_struct_key_for_class_hint(Gen, OwnerClass)
if owner_key and owner_key in Gen.structs:
st_v: Any
ok_v: bool
st_v, ok_v = self._try_Load_struct_from_stub(Gen, owner_key)
if isinstance(st_v, ir.IdentifiedStructType) and st_v.elements is not None:
off: int | None = Gen._get_member_offset(AttrName, owner_key)
if off is not None and off < len(st_v.elements):
k3: str | None = self._pointee_llvm_to_struct_key(Gen, st_v.elements[off])
if k3:
return k3
# 4) 继承父类 class_members 兜底(_owner_member_llvm_types 已含继承链,
# 此处仅处理父类 struct elements 的字段类型)
for parent_short in self._class_and_parents(Gen, OwnerClass)[1:]:
p_key: str | None = self._find_struct_key_for_class_hint(Gen, parent_short)
if not p_key or p_key not in Gen.structs:
continue
p_st: Any
p_ok: bool
p_st, p_ok = self._try_Load_struct_from_stub(Gen, p_key)
if isinstance(p_st, ir.IdentifiedStructType) and p_st.elements is not None:
p_off: int | None = Gen._get_member_offset(AttrName, p_key)
if p_off is not None and p_off < len(p_st.elements):
k4: str | None = self._pointee_llvm_to_struct_key(Gen, p_st.elements[p_off])
if k4:
return k4
return None
def _resolve_chain_result_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None:
"""沿链前向解析表达式(Name 或任意级 Attribute 链)的最终 struct 键。
例如 ve.Alloca.Ty.Pointee:最内层 Name ve 查 var_struct_class /
global_struct_class / 模块全局 / 变量 alloca pointee 得首个类(Value),
之后每段用 _field_element_key 在当前类上取字段的元素类推进,直到最外层。
对自指/REnum(如 LLVMType.Pointee → LLVMType)或 class_member_element_class
缺项的字段,由 _field_element_key 的 class_members / struct elements 兜底。
"""
if isinstance(Node, ast.Name):
if Node.id == 'self':
return self.Trans._CurrentCpythonObjectClass
return self._resolve_var_classname(Gen, Node.id)
if isinstance(Node, ast.Attribute):
ParentKey: str | None = self._resolve_chain_result_class(Node.value, Gen)
if not ParentKey:
return None
if ParentKey not in Gen.structs:
resolved_parent: str | None = self._find_struct_key_for_class_hint(Gen, ParentKey)
if not resolved_parent:
return None
ParentKey = resolved_parent
return self._field_element_key(Gen, ParentKey, Node.attr)
return None
def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: