diff --git a/lib/core/Handles/HandlesFunctions.py b/lib/core/Handles/HandlesFunctions.py index 7732f5b..79352de 100644 --- a/lib/core/Handles/HandlesFunctions.py +++ b/lib/core/Handles/HandlesFunctions.py @@ -1305,6 +1305,11 @@ class FunctionHandle(BaseHandle): if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: Gen.functions[FuncName] = Gen.functions[MangledName] except Exception as _e: + # fail-fast 一律上抛(本仓既定口径):吞掉它 = 声明静默缺失 + + # 半注册名字留给下一轮,真因与现场脱节。 + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise if _config_mode == "strict": self.Trans.LogWarning(f"异常被忽略: {_e}") self.Trans.FunctionDefCache[FuncName] = Node @@ -1315,7 +1320,13 @@ class FunctionHandle(BaseHandle): if old_func is None: try: return ir.Function(Gen.module, NewFuncType, name=MangledName) - except Exception: # 回退:函数已存在时返回旧函数 + except Exception as _e: # 回退:函数已存在时返回旧函数 + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # 创建中途失败(如形参/返回类型不是 llvmlite Type)由 BaseGen 的 + # Function 守卫转成 fail-fast:必须上抛,原实现会 `return old_func` + # (此处 old_func 必为 None)⇒ 调用点把 None 当声明用。 + raise return old_func try: old_name: str = old_func.name diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 601143f..5642d6a 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -664,6 +664,15 @@ class ImportHandle(BaseHandle): else: self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # fail-fast 一律上抛(本仓既定口径:FailFastError 不被吞,见 + # HandlesBase.py 的说明)。原实现把它与其它异常一起吞成一行 + # "忽略节点 … 异常" WARN 并 continue ⇒ 该节点(如某个 ClassDef 的 + # 全部方法声明)被整条静默丢弃,创建中途失败的**半注册名字**留到 + # 下一轮才以"函数声明名字悬空占用"暴露,真因与现场彻底脱节 + # (现场:50741ad4b4b0934d.LLVMTypeCore.__init__)。 + raise _vlog().warning( f"HandlesImports[_LoadModuleDeclarationsFromFile]: 忽略节点 " f"{type(node).__name__} {getattr(node, 'name', '?')} 异常: {_e}", @@ -3624,10 +3633,18 @@ class ImportHandle(BaseHandle): short_name = func_name.split('.', 1)[1] Gen.functions[short_name] = func except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # 声明创建/签名的 fail-fast 必须上抛(见 _LoadModuleDeclarationsFromFile + # 同款说明):吞掉它会让该声明静默缺失、并把半注册名字留给下一轮。 + raise if _config_mode == "strict": raise _vlog().warning(f"处理导入失败: {_e}", "Exception") except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise if _config_mode == "strict": raise _vlog().warning(f"处理导入失败: {_e}", "Exception") @@ -3707,9 +3724,19 @@ class ImportHandle(BaseHandle): short_fn = func_name.split('.', 1)[1] Gen.functions[short_fn] = func loaded_any = True - except Exception: - pass + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # 见 _LoadModuleDeclarationsFromFile 同款说明:fail-fast 必须上抛。 + # 原实现是裸 `except Exception: pass` —— 创建中途失败(半注册名字) + # 会在这里被彻底吞掉,后续同名创建只能报"名字悬空占用"。 + raise + _vlog().warning( + f"按需加载 output stub 声明失败: '{stripped}' → {_e}", "Exception") except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise if _config_mode == "strict": raise _vlog().warning(f"按需加载 output stub 失败: {_e}", "Exception") diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index 7ca84a1..63ea982 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -79,6 +79,78 @@ def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str raise FailFastError(_Msg) +_TPC_FuncInitGuardInstalled: bool = False + + +def InstallFuncCreateGuard() -> None: + """给 llvmlite 的 `ir.Function.__init__` 装一次守卫:创建中途失败**不留悬空名字**。 + + 背景(llvmlite 的构造不是事务性的):`Function.__init__` 的顺序是 + super().__init__ → `_set_name` → `module.scope.register(name)` 【名字占用】 + → 逐形参 `Argument(...)` / `ReturnValue(...)`(内部 `assert isinstance(typ, types.Type)`) + → `parent.add_global(self)` 【进入 globals】 + 中间任一步抛错都不会回滚 scope ⇒ globals 查无、scope 却已占用的"悬空名字"。此后同名 + 创建只会得到 llvmlite 的裸 DuplicatedNameError(报文只有名字),或本仓 + `_CreateOrReuseFuncDecl` 的"名字悬空占用"fail-fast —— 两者都只能指向"上一处失败被 + 吞了",而**那一处**的调用点与异常早已丢失(现场:50741ad4b4b0934d.LLVMTypeCore.__init__, + 上游首建失败被 HandlesImports 的节点级 `except Exception` 吞成一行 WARN)。 + + 本守卫做两件事(均在**失败路径**上,合法创建零额外开销): + 1. 回滚本次注册的名字(`scope._useset.discard`)——补上 llvmlite 缺的那半截事务, + 模块状态可重试,不再积累悬空名字; + 2. 抛 FailFastError,报文给出函数名、签名、原始异常,以及**发起创建的 TPC 调用点** + (栈上第一个不在 llvmlite 包内、也不是本包装函数的帧)。 + `DuplicatedNameError`(名字已被占用)原样上抛:那是各调用点**有意**处理的正常分支 + (如 `_replace_function_decl` 的"已存在则复用"),不属于"创建中途失败"语义。 + """ + global _TPC_FuncInitGuardInstalled + if _TPC_FuncInitGuardInstalled: + return + _TPC_FuncInitGuardInstalled = True + _OrigInit: Any = ir.Function.__init__ + + def _GuardedFuncInit(self: Any, module: Any, ftype: Any, name: Any) -> None: + try: + _OrigInit(self, module, ftype, name) + return + except Exception as _e: + from llvmlite.ir._utils import DuplicatedNameError as _Dup + if isinstance(_e, _Dup): + raise + # 名字已在 `_set_name` 阶段注册(进入本分支即证明 register 成功)⇒ 回滚 + _scope: Any = getattr(module, 'scope', None) + _useset: Any = getattr(_scope, '_useset', None) + if _useset is not None: + _useset.discard(name) + _useset.discard(str(name)) + import traceback as _tb + _site: str = '(栈中未找到非 llvmlite 帧)' + for _fr in reversed(_tb.extract_stack()): + _fn: str = _fr.filename.replace('\\', '/') + if 'site-packages/llvmlite' in _fn or _fr.name == '_GuardedFuncInit': + continue + _site = f"{_fr.filename}:{_fr.lineno} (in {_fr.name})" + break + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"函数创建失败(已回滚悬空名字):'{name}'" + f"\n · 本次签名: {ftype}" + f"\n · 原始异常: {type(_e).__name__}: {_e}" + f"\n · 创建调用点: {_site}" + f"\n 成因:llvmlite 的 Function 构造不是事务性的——名字在 `_set_name` 就进了" + f" module.scope,之后逐个建 Argument/ReturnValue 时才做类型断言,最后才" + f" add_global。任何形参 / 返回类型不是 llvmlite Type(典型 None:注解或 stub" + f" declare 的类型没解析出来)都会在「已注册、未进 globals」之间失败 ⇒ 留下" + f"悬空名字,后续同名创建只报裸 DuplicatedNameError 或「名字悬空占用」。" + f"\n 本守卫已回滚名字(模块状态可重试);请修上面那一行的签名来源。" + ) from _e + + ir.Function.__init__ = _GuardedFuncInit + + +InstallFuncCreateGuard() + + class _GuardIrBuilder(ir.IRBuilder): """默认 IR 构建器:在 GEP/load/store 处对**指针操作数**做类型守卫。