fix(gen): TPC 治本——struct opaque 具化、多返回值 fail-fast、with MRO 修复

1. HandlesExprCall: vtable 分派前 _EnsureReceiverStructConcrete 主动具化 struct
   body(stub + class_members 双路径),消除元数据已注册、本体仍 opaque 的
   撕裂状态,失败即 fail-fast 暴露上游 stub 加载时序问题;
2. HandlesImports: stub 加载后仍 opaque 时不保留成功缓存(可重试),
   避免 opaque 状态被固化导致 vtable 分派 GEP 崩溃;
3. HandlesAssign: 跨模块多返回值 CReturnTypes 恢复(LiteralStructType 回退)
   + 解析失败 fail-fast,不再静默跳过致目标变量未初始化触发堆损坏;
4. HandlesWith: MRO 继承方法解析跳过无函数体的 stub declare(Phase1 继承
   包装声明),bitcast 目标改用函数签名参数类型避免 opaque 占位对象。
This commit is contained in:
TermiNexus committed 2026-08-13 07:51:27 +08:00
1 parent a99f126cfa
commit 2f0b741d55
4 files changed
+132 -10

No files matched your search

+71
View File
@@ -2309,6 +2309,10 @@ class ExprCallHandle(BaseHandle):
Gen.class_vtable.add(ClassName)
# 设置方法列表(与 prescan 逻辑一致:基类方法在前)
Gen.class_methods[ClassName] = methods
# 根本修复:注册 vtable 元数据后同步具化 struct body,从源头消除
# "元数据已注册、本体仍 opaque" 的撕裂状态(配合 _HandleMethodCallLlvm
# 中 _EnsureReceiverStructConcrete 的使用前校验,双保险)。
import_handler._TryLoadStructFromStub(ClassName, Gen, source_sha1=class_sha1)
# 设置 class_parent / class_parents(用于虚分派时的函数查找回退与 MRO)
if parent_name and ClassName not in Gen.class_parent:
Gen.class_parent[ClassName] = parent_name
@@ -2943,6 +2947,16 @@ class ExprCallHandle(BaseHandle):
self._EnsureClassParentsLoaded(ClassName, Gen)
if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods:
# 根本修复:vtable 分派要求接收者 struct 已具化。元数据(vtable 集合 /
# class_methods)与 struct body 可能因并行 include 翻译时序撕裂——
# _try_register_vtable_ondemand 只注册元数据不具化 body,若此时 struct
# 仍 opaque,下方 GEP [0,0] 取 vtable 指针会抛 llvmlite 裸 TypeError
# (典型:list[AST|t.CPtr].append 中 self.__pool__.alloc(...),
# __pool__ 类型 memhub.MemManager|t.CPtr,MemManager 已进 vtable 集合
# 但 struct body 未从 stub 具化)。
# 此处先主动具化(stub + class_members 双路径),仍失败则 fail-fast
# 暴露上游 stub 加载时序问题,而非静默崩溃。
ObjVal = self._EnsureReceiverStructConcrete(ClassName, class_sha1, ObjVal, Gen)
methods = Gen.class_methods[ClassName]
method_idx = -1
for mi, mn in enumerate(methods):
@@ -3100,6 +3114,63 @@ class ExprCallHandle(BaseHandle):
return result
raise Exception(f"Undefined method '{MethodName}' in class '{ClassName}' (no function declaration found for '{FullMethodName}')")
def _EnsureReceiverStructConcrete(self, ClassName: str, class_sha1: str | None, ObjVal: Any, Gen: "LlvmGeneratorMixin") -> Any:
"""根本修复:vtable 分派前保证接收者 struct 已具化(非 opaque)。
背景:跨模块类的 vtable 元数据(class_vtable / _cross_module_vtable_classes /
class_methods)由 _precollect_vtable_state 预扫描或 _try_register_vtable_ondemand
按需注册,而 struct body 依赖 _TryLoadStructFromStub 从 stub.ll 加载。
二者时序可能撕裂:body 仍 opaque 就对 struct 做 GEP [0,0] 取 vtable 指针,
llvmlite 在 types.py 的 elements[i] 处抛裸 TypeError('NoneType' object is
not subscriptable)。
本方法:
1. 先走 stub.ll 补 body(source_sha1 精确匹配优先)
2. 再补 class_members(供 _generate_structs 用成员构造 body 的备选路径)
3. 若 Gen.structs 中已具化版本与 ObjVal 指向的 opaque 占位对象不同,
bitcast 替换 ObjVal 到具体版本
4. 仍 opaque → fail-fast 报带源码上下文的编译错误(遵循"不兜底"原则,
暴露上游 stub 加载/时序问题而非让 llvmlite 裸崩)
Returns:
ObjVal 本身或 bitcast 到具体 struct 版本后的新值。
"""
if not isinstance(ObjVal.type, ir.PointerType):
return ObjVal
pointee: ir.Type = ObjVal.type.pointee
if not isinstance(pointee, ir.IdentifiedStructType) or not pointee.is_opaque:
return ObjVal
import_handler: Any = self.Trans.ImportHandler
# 1) stub.ll 补 body(source_sha1 精确匹配优先)
if class_sha1:
import_handler._TryLoadStructFromStub(ClassName, Gen, source_sha1=class_sha1)
else:
import_handler._TryLoadStructFromStub(ClassName, Gen)
# 2) 补 class_members(备选具化路径)
stub_basename: str = f'{class_sha1}.stub.ll' if class_sha1 else f'{ClassName}.stub.ll'
import_handler._TryLoadClassMembersFromPyi(ClassName, stub_basename, Gen)
# 3) 从 Gen.structs 找已具化版本(短名优先,再按 sha1 全名)
concrete_st: ir.IdentifiedStructType | None = None
_st_short: Any = Gen.structs.get(ClassName)
if isinstance(_st_short, ir.IdentifiedStructType) and not _st_short.is_opaque:
concrete_st = _st_short
if concrete_st is None and class_sha1:
_st_full: Any = Gen.structs.get(f'{class_sha1}.{ClassName}')
if isinstance(_st_full, ir.IdentifiedStructType) and not _st_full.is_opaque:
concrete_st = _st_full
if concrete_st is None:
# 4) 仍 opaque → fail-fast(暴露上游 stub 加载/时序问题)
raise Exception(
f"vtable 分派失败: 类 '{ClassName}' 已注册虚表(class_vtable/class_methods)"
f"但 struct 类型无法从 stub 具化(仍为 opaque){Gen._get_node_info()}"
)
if concrete_st is pointee:
return ObjVal
# ObjVal 指向旧 opaque 占位对象:bitcast 到具体版本
return Gen.builder.bitcast(ObjVal, ir.PointerType(concrete_st), name=f"concrete_self_{ClassName}")
_C_LIB_FUNCS = {
'malloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.IntType(64)]),
'calloc': lambda: (ir.PointerType(ir.IntType(8)), [ir.IntType(64), ir.IntType(64)]),