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:
1 parent
a99f126cfa
commit
2f0b741d55
4 files changed
+132
-10
No files matched your search
@@ -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)]),
|
||||
|
||||
Reference in new issue
Block a user