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
@@ -533,7 +533,28 @@ class AssignHandle(BaseHandle):
|
||||
ret_type: ir.Type = func_ft.return_type
|
||||
if isinstance(ret_type, ir.LiteralStructType):
|
||||
CReturnTypes = [None] * len(ret_type.elements)
|
||||
if not CReturnTypes:
|
||||
# 跨模块多返回值兜底:短名未注册时(函数以 ModulePath.名 /
|
||||
# sha1.名 全名注册),从其 LiteralStructType 返回类型恢复 CReturnTypes。
|
||||
# 否则 CReturnTypes 为空会走下方静默 return,目标变量未初始化,
|
||||
# 运行期 free(垃圾指针) 触发堆损坏(HashLibTest 0xC0000374)。
|
||||
func = None
|
||||
if ModulePath:
|
||||
func = Gen.functions.get(f"{ModulePath}.{FuncName}")
|
||||
if func is None:
|
||||
for key in Gen.functions:
|
||||
if key.endswith(f".{FuncName}"):
|
||||
func = Gen.functions[key]
|
||||
break
|
||||
if func:
|
||||
func_ft: ir.FunctionType | None = getattr(func, 'ftype', None)
|
||||
if func_ft:
|
||||
ret_type: ir.Type = func_ft.return_type
|
||||
if isinstance(ret_type, ir.LiteralStructType):
|
||||
CReturnTypes = [None] * len(ret_type.elements)
|
||||
if CReturnTypes and len(Target.elts) == len(CReturnTypes):
|
||||
if func is None:
|
||||
func = Gen.functions.get(FuncName)
|
||||
CallArgs: list[ir.Value | None] = []
|
||||
for arg in Node.value.args:
|
||||
ArgVal: ir.Value | None = self.HandleExprLlvm(arg)
|
||||
@@ -593,7 +614,7 @@ class AssignHandle(BaseHandle):
|
||||
var: ir.AllocaInstr = Gen._alloca(VarType, name=VarName)
|
||||
Gen.variables[VarName] = var
|
||||
if CReturnTypes[j] is not None:
|
||||
Gen._record_var_signedness(VarName, ReturnTypeStr)
|
||||
Gen._record_var_signedness(VarName, str(ReturnTypeInfo) if ReturnTypeInfo else '')
|
||||
elif ret_type and isinstance(ret_type, ir.LiteralStructType) and j < len(ret_type.elements):
|
||||
elem_type: ir.Type = ret_type.elements[j]
|
||||
if isinstance(elem_type, ir.IntType):
|
||||
@@ -604,6 +625,18 @@ class AssignHandle(BaseHandle):
|
||||
elif elem_type.width == 64:
|
||||
Gen._record_var_signedness(VarName, 'unsigned long long')
|
||||
Gen._store(field_val, var)
|
||||
elif isinstance(Node.value, ast.Call):
|
||||
# fail-fast:多返回值赋值但 CReturnTypes 解析失败(跨模块签名丢失),
|
||||
# 绝不能静默跳过——目标变量不会被初始化,运行期 free(垃圾指针)
|
||||
# 触发堆损坏(HashLibTest 0xC0000374)。
|
||||
target_names: list[str] = [
|
||||
e.id if isinstance(e, ast.Name) else '<expr>' for e in Target.elts
|
||||
]
|
||||
raise Exception(
|
||||
f"多返回值赋值解析失败: '{FuncName}' 的返回类型无法解析为"
|
||||
f"元组/CReturn(ModulePath={ModulePath}),目标 {target_names} 未被初始化"
|
||||
f"{Gen._get_node_info()}"
|
||||
)
|
||||
return
|
||||
|
||||
def _InitStructAtPtr(self, call_node: ast.Call, ClassName: str, VarPtr: ir.Value) -> None:
|
||||
|
||||
@@ -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)]),
|
||||
|
||||
@@ -2145,6 +2145,16 @@ class ImportHandle(BaseHandle):
|
||||
_vlog().warning(f"处理导入失败: {_e}", "Exception")
|
||||
stub_filename = actual_sha1 + '.stub.ll'
|
||||
self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen)
|
||||
# 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、
|
||||
# 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久
|
||||
# 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。
|
||||
# 失败状态可重试,待 stub 索引刷新 / 定义文件就绪后再加载。
|
||||
_final_st = Gen.structs.get(class_name)
|
||||
if isinstance(_final_st, ir.IdentifiedStructType) and _final_st.is_opaque:
|
||||
try:
|
||||
del self._struct_Load_cache[cache_key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin) -> None:
|
||||
ProjectRoot = self._find_project_root()
|
||||
|
||||
@@ -15,12 +15,17 @@ class WithHandle(BaseHandle):
|
||||
_generate_inherited_method_wrappers 只为 class_methods[ClassName] 中的方法生成包装,
|
||||
不包括父类独有方法,因此 with 语句需要单独处理继承查找。
|
||||
"""
|
||||
# 先检查子类自身
|
||||
# 先检查子类自身:必须有函数体(define)才算真方法。
|
||||
# 子类继承方法的 stub declare(如 MemPool.__enter__,Phase1 生成的继承
|
||||
# 包装声明)无函数体且 TPC 不生成对应定义,直接调用会链接 undefined,
|
||||
# 必须跳过并沿 MRO 找父类实现。
|
||||
FullName: str = f'{ClassName}.{MethodName}'
|
||||
func = Gen._find_function(FullName) if hasattr(Gen, '_find_function') else Gen.functions.get(FullName)
|
||||
if func:
|
||||
if func and len(list(func.blocks)) > 0:
|
||||
return (ClassName, func)
|
||||
# 沿 MRO 继承链查找(多继承:先声明父类优先)
|
||||
# 沿 MRO 继承链查找(多继承:先声明父类优先)。
|
||||
# 祖先的 stub declare(无函数体)是真实定义的声明(定义在其模块 obj 中),
|
||||
# 可直接调用——与 _HandleMethodCallLlvm 的 MRO 回退语义一致。
|
||||
for anc in Gen._get_mro_chain(ClassName)[1:]:
|
||||
parent_full: str = f'{anc}.{MethodName}'
|
||||
parent_func = Gen._find_function(parent_full) if hasattr(Gen, '_find_function') else Gen.functions.get(parent_full)
|
||||
@@ -84,9 +89,12 @@ class WithHandle(BaseHandle):
|
||||
enter_class, enter_func = self._ResolveInheritedMethod(ClassName, '__enter__', Gen)
|
||||
if enter_func is not None:
|
||||
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
|
||||
# 父类方法需要 self 为父类指针类型,bitcast 子类指针
|
||||
if enter_class != ClassName and enter_class in Gen.structs:
|
||||
parent_ptr_type = ir.PointerType(Gen.structs[enter_class])
|
||||
# 父类方法需要 self 为父类指针类型,bitcast 子类指针。
|
||||
# bitcast 目标用 enter_func 签名参数类型(args[0] 本身即
|
||||
# MemManager* 指针),避免 Gen.structs 查找返回 opaque/异
|
||||
# sha1 占位对象导致 llvmlite 类型不匹配。
|
||||
if enter_class != ClassName:
|
||||
parent_ptr_type: ir.Type = enter_func.function_type.args[0]
|
||||
if ctx_Loaded.type != parent_ptr_type:
|
||||
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_enter_self")
|
||||
enter_call: ir.CallInstr = Gen.builder.call(enter_func, [ctx_Loaded], name="call_enter")
|
||||
@@ -169,9 +177,9 @@ class WithHandle(BaseHandle):
|
||||
exit_class, exit_func = self._ResolveInheritedMethod(ClassName, '__exit__', Gen)
|
||||
if exit_func is not None:
|
||||
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
|
||||
# 父类方法需要 self 为父类指针类型,bitcast 子类指针
|
||||
if exit_class != ClassName and exit_class in Gen.structs:
|
||||
parent_ptr_type = ir.PointerType(Gen.structs[exit_class])
|
||||
# 父类方法需要 self 为父类指针类型,bitcast 子类指针(同上)
|
||||
if exit_class != ClassName:
|
||||
parent_ptr_type: ir.Type = exit_func.function_type.args[0]
|
||||
if ctx_Loaded.type != parent_ptr_type:
|
||||
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_exit_self")
|
||||
Gen.builder.call(exit_func, [ctx_Loaded], name="call_exit")
|
||||
|
||||
Reference in new issue
Block a user