diff --git a/lib/core/Handles/HandlesAssign.py b/lib/core/Handles/HandlesAssign.py index 3b9c30b..a88957e 100644 --- a/lib/core/Handles/HandlesAssign.py +++ b/lib/core/Handles/HandlesAssign.py @@ -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 '' 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: diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index 3a709c9..43740ed 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -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)]), diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index dc95aee..38eecc9 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -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() diff --git a/lib/core/Handles/HandlesWith.py b/lib/core/Handles/HandlesWith.py index 1dc1ddf..6591250 100644 --- a/lib/core/Handles/HandlesWith.py +++ b/lib/core/Handles/HandlesWith.py @@ -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")