From 51de3b6dcff9bfc381d5f6f7ac1f7f48651e11d8 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 28 Sep 2026 22:20:09 +0800 Subject: [PATCH] =?UTF-8?q?chore(tpc):=20[=E4=B8=B4=E6=97=B6=E8=AF=8A?= =?UTF-8?q?=E6=96=AD]=20=E5=A3=B0=E6=98=8E=E6=9D=A5=E6=BA=90=20+=20i32*=20?= =?UTF-8?q?=E5=BD=A2=E5=8F=82=E5=88=A4=E5=AE=9A=E4=BE=9D=E6=8D=AE=E5=B9=B6?= =?UTF-8?q?=E5=85=A5=20fail-fast=20=E6=8A=A5=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 只为定位"跨模块函数形参退化成 i32*"(现场 function_get_name 第 0 形参 i32*, 实参 %"46c7dc5230ea5a52.Function"*)。静态排查已穷尽:_ctype_to_llvm / _type_str_to_llvm 产生 i32* 的出口只有「BaseType 退化成 i32 + PtrCount=1」 (含 CTypeInfo.ToString() 的 'CInt *' → i32*)与「IsEnum + Name + PtrCount=1」, 离线复现不出,需要运行时证据。 做法:把判定依据直接拼进 FailFastError 报文,使其随 Traceback 一起被看到: [DIAG-decl] 实参类 / structs / _struct_sha1_map / class_sha1_map / symtab 标志 / 声明函数类型 / 声明来源(创建者调用栈) 并在四个声明创建点记录首个创建者: FuncGen._get_or_declare_func / get_or_declare_c_func、 BaseGen._get_or_declare_function、HandlesBase.GetOrCreateFuncDecl(会替换声明)。 定位完成后整体回滚(连同记录点)。 --- lib/core/Handles/HandlesBase.py | 6 +++ lib/core/LLVMCG/BaseGen.py | 5 +++ lib/core/LLVMCG/FuncGen.py | 66 ++++++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index 503d675..6e7da8e 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -1143,6 +1143,12 @@ class BaseHandle: 若 name 已存在且类型不匹配,通过 _replace_function_decl 替换。 replace_name 传递给 _replace_function_decl(通常为未 mangle 的 func_name)。 """ + # [DIAG-decl] 临时诊断:记录声明创建来源(本方法会**替换**已有声明, + # 是"形参被改成 i32*"的头号嫌疑点)。定位后删除。 + try: + Gen._RecordDeclFrom(name, _ir.FunctionType(ret_type, param_types, var_arg=is_variadic)) + except Exception: + pass if name not in Gen.functions: # Gen.functions 未登记、但 module.globals 已有同名函数(本模块其它调用点 # 或依赖 stub 先建过声明)→ 复用并登记,绝不重建:llvmlite 的 NameScope diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index 5652b37..5304eae 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -869,6 +869,11 @@ class BaseGenMixin: return [] def _get_or_declare_function(self, mangled_name: str, func_type: ir.FunctionType) -> ir.Function: + # [DIAG-decl] 临时诊断:记录声明创建来源(定位"形参 i32* vs 实参 %sha1.CLS*") + try: + self._RecordDeclFrom(mangled_name, func_type) + except Exception: + pass # DDEBUG: 检测 mangled_name 含双点(不应到达这里) if '..' in mangled_name: import sys diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 51fae57..ffc5125 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -116,6 +116,64 @@ class FuncGenMixin: new_args[i] = slot return new_args + def _RecordDeclFrom(self, name: str, func_type: Any) -> None: + """[DIAG-decl] 临时诊断:记录函数声明的**首个创建来源**(调用栈 + 函数类型)。 + + 用于定位"形参 i32* vs 实参 %sha1.CLS*"这类跨层不一致是谁创建的声明 + (import 声明发射器 / stub 解析 / 符号表签名 / 继承包装 等)。 + 只记录一次(首个创建者即最终生效者)。定位完成后连同 + _DescribeI32ParamDiag 与两处调用点一起删除。 + """ + try: + _reg = getattr(self, '_decl_from', None) + if _reg is None: + _reg = {} + self._decl_from = _reg + if name in _reg: + return + import traceback as _tb + _reg[name] = ' <- '.join( + '%s:%d:%s' % (f.filename.replace('\\', '/').rsplit('/', 1)[-1], f.lineno, f.name) + for f in _tb.extract_stack()[-7:-1]) + except Exception: + pass + + def _DescribeI32ParamDiag(self, func: ir.Function, arg: ir.Value, param: ir.Value) -> str: + """[DIAG-decl] 临时诊断:形参退化成"指向 i32 的指针"时,拼出判定依据。 + + 只在"形参是 i32* 且实参是指向具体类的指针"时输出,随 fail-fast 报文一起 + 显示(避免探针输出被忽略)。定位完成后删除。 + """ + try: + _p_poi: Any = getattr(param.type, 'pointee', None) + if not (isinstance(_p_poi, ir.IntType) and _p_poi.width == 32): + return '' + _a_poi: Any = getattr(arg.type, 'pointee', None) + _full: str | None = getattr(_a_poi, 'name', None) + if not _full: + return '' + _short: str = _full.split('.')[-1] + _sym: Any = getattr(self, 'SymbolTable', None) + _e: Any = None + try: + _e = _sym.lookup(_short) if _sym else None + except Exception: + _e = None + _reg: dict = getattr(self, '_decl_from', {}) or {} + _from: str = _reg.get(func.name) or _reg.get(_short) or '未知' + return ( + f"\n [DIAG-decl] 实参类={_full}" + f" | structs={'有' if _short in self.structs else '无'}" + f" | _struct_sha1_map={getattr(self, '_struct_sha1_map', {}).get(_short)!r}" + f" | class_sha1_map={getattr(self, 'class_sha1_map', {}).get(_short)!r}" + f" | symtab[IsStruct={getattr(_e, 'IsStruct', None)},IsEnum={getattr(_e, 'IsEnum', None)}," + f"IsEnumMember={getattr(_e, 'IsEnumMember', None)},IsTypedef={getattr(_e, 'IsTypedef', None)}," + f"Base={type(getattr(_e, 'BaseType', None)).__name__}]" + f" | 声明函数类型={func.function_type}" + f" | 声明来源={_from}") + except Exception: + return '' + def _adjust_args(self, args: list[ir.Value], func: ir.Function) -> list[ir.Value]: adjusted: list[ir.Value] = [] for i, (arg, param) in enumerate(zip(args, func.args)): @@ -189,10 +247,14 @@ class FuncGenMixin: Reason: str Allowed, Reason = self._ClassifyPtrImplicitConvert(arg.type, param.type) if not Allowed: + # [DIAG-decl] 临时诊断(定位"形参 i32* vs 实参 %sha1.CLS*"): + # 把判定依据直接拼进报文,使其随 Traceback 一起被看到; + # 定位完成后连同 _RecordDeclFrom 调用点一起删除。 + _diag_extra: str = self._DescribeI32ParamDiag(func, arg, param) raise FailFastError( f"指针隐式转换已禁止({Reason}):调用 {func.name} 第 {i} 个实参" f" {arg.type} 不能隐式转为形参 {param.type};如需转换请显式 cast" - f"(如 t.CPtr(...)){self._DescribeValueNode()}") + f"(如 t.CPtr(...)){self._DescribeValueNode()}{_diag_extra}") adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) else: if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): @@ -807,6 +869,7 @@ class FuncGenMixin: return ir.IntType(32) def _get_or_declare_func(self, name: str, func_type: ir.FunctionType) -> ir.Function: + self._RecordDeclFrom(name, func_type) # [DIAG-decl] 临时诊断 for fname, fobj in self.functions.items(): if fname == name: return fobj @@ -817,6 +880,7 @@ class FuncGenMixin: return func_decl def get_or_declare_c_func(self, name: str, fallback_type: ir.FunctionType | None = None) -> ir.Function | None: + self._RecordDeclFrom(name, fallback_type) # [DIAG-decl] 临时诊断 if name in self.functions: return self.functions[name] for fname, fobj in self.functions.items():