From ab906f2cde31995e79e144a6febd0d502c6a93e7 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 17:00:50 +0800 Subject: [PATCH] =?UTF-8?q?fix(HandlesExprCall):=20=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E7=9A=84=20self=20=E6=8C=89=20callee=20=E5=BD=A2=E5=8F=82?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=A7=A3=E6=9E=90=EF=BC=88=E5=90=8C=E5=90=8D?= =?UTF-8?q?=E7=B1=BB=E6=B6=88=E6=AD=A7=EF=BC=89=EF=BC=8C=E5=B9=B6=E5=90=88?= =?UTF-8?q?=E5=B9=B6"sha1=20=E5=85=A8=E5=90=8D=E9=94=AE"=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类别:a(编译器缺陷) 现象(用户重跑后仍复现):includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)` 报 FailFastError: 指针隐式转换已禁止(非基本类型(结构体/类/联合)):调用 8312ce86a2fd82c0.Keyword.__new__ 第 0 个实参 %"fef783da4d24330d.Keyword"* 不能隐式转为形参 %"8312ce86a2fd82c0.Keyword"* 根因:`Gen.structs` 的键恒为**短名**(StructGen._generate_structs 以 ClassName 作键, IR 名才带 sha1 前缀),别名注册只是**浅拷贝**(HandlesImports `Gen.structs[asname] = Gen.structs[name]`)。Keyword 同时存在于 ast.tokens (fef783da4d24330d) 与 ast.astaux(8312ce86a2fd82c0):裸名 'Keyword' 先被 .tokens 占用, 别名 'KwNode' 键又只是浅拷贝 ⇒ 构造的 self 按名字取到 tokens 的实例,而 callee (_find_function('KwNode.__new__'))命中的是 astaux 的。llvmlite 的类型相等按 **IR 名字** 判定(实测同名不同对象 == 为真,见提交说明)⇒ 两份不同名实例必被 _ClassifyPtrImplicitConvert 拒绝。 上一版修复(ba7e117e)不成立:它按"callee 限定类名 + 与 ClassName 同短名"改判,而 ClassName 是别名 'KwNode'、限定类名是 '...Keyword',同短名判据恒假 ⇒ 从未生效。本版删除。 修复: 1) 新增 _CalleeSelfType():self 的 struct 直接取 callee(__new__ → __before_init__ → __init__)第 0 个形参的 pointee —— 正是 _adjust_args 的比较对象,无歧义;形参上的 实例未具化时按 IR 名换同名已具化实例(_FindStructByIrName;IR 里类型身份就是名字)。 2) _get_struct_type 统一解析序:callee 形参 → sha1 全名键 → 名字键。 3) 合并"sha1 全名键命中"分支:原实现只解析 StructType、**不产出 alloca / 构造调用**, 控制流随后落到函数末尾 `return i32 0` ⇒ 该路径上的跨模块构造会**静默不构造** (同为类别 a,一并修掉)。 --- lib/core/Handles/HandlesExprCall.py | 110 +++++++++++++++------------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index db00575..f4131ea 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -5063,47 +5063,65 @@ class ExprCallHandle(BaseHandle): print(f"[DEBUG_CLASSNEW] _HandleClassNewLlvm called for Asm, module_sha1={module_sha1}", file=_dbg_sys.stderr) # 跨模块类型引用:优先用 {module_sha1}.{ClassName} 查找 struct _struct_full_key: str | None = f"{module_sha1}.{ClassName}" if module_sha1 else None - # 【同名类消歧(2026-09-30)】ClassName 可能是**导入别名**( - # `from .astaux import Keyword as KwNode` 后写作 KwNode)或短名,而 - # `Gen.structs` / `Gen.functions` 的**短名键**在"两个模块各定义同名类"时是有歧义的 - # ——现场:Keyword 同时存在于 ast.astaux(8312ce86a2fd82c0) 与 - # ast.tokens(fef783da4d24330d)。ctor 的 self 实参按短名取到了 tokens.Keyword*, - # 而 callee 是 astaux 的 `8312ce86a2fd82c0.Keyword.__new__` ⇒ `_adjust_args` 报 - # "指针隐式转换已禁止(非基本类型(结构体/类/联合)):第 0 个实参 - # %"fef783da4d24330d.Keyword"* 不能隐式转为形参 %"8312ce86a2fd82c0.Keyword"*" - # (includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)`)。 - # 真名从**已解析到的构造函数对象**上取(其 IR 名是 `{sha1}.{Class}.{method}`), - # 与 callee 同源;取不到时保持原行为(短名回退),对无同名冲突的类零变化。 - _QCls: str | None = None - for _probe_key in (f'{ClassName}.__new__', f'{ClassName}.__before_init__', f'{ClassName}.__init__'): - _probe_fn: Any = Gen._find_function(_probe_key) - _probe_nm: str = (getattr(_probe_fn, 'name', '') or '') if _probe_fn is not None else '' - if _probe_nm.count('.') >= 2: - _QCls = _probe_nm.rsplit('.', 1)[0] - break - - def _get_struct_type(name: str) -> ir.Type | None: - """优先用 SHA1 前缀完整名查找,再按"与 callee 同源的限定名"消歧,最后回退短名""" - if _struct_full_key and _struct_full_key in Gen.structs: - return Gen.structs[_struct_full_key] - if _QCls and _QCls != name and _QCls in Gen.structs: - _qc: Any = Gen.structs[_QCls] - # 只在"**同短名**(即确实是同名类的另一份实例)且**已具化**"时改判: - # 同短名保证不会把无关类引进来;已具化保证 `_allocaEntry` 的尺寸正确 - # (opaque 实例算不出大小,会让 ctor 的 self 指向尺寸错误的块)。 - if (isinstance(_qc, ir.IdentifiedStructType) - and (getattr(_qc, 'name', '') or '').rsplit('.', 1)[-1] - == (name or '').rsplit('.', 1)[-1] - and _qc.elements is not None): - return _qc - return Gen.structs.get(name) - if hasattr(self.Trans.ClassHandler, '_generic_class_templates') and ClassName in self.Trans.ClassHandler._generic_class_templates: return self._HandleGenericClassNewLlvm(Node, ClassName) NewFuncName = f'{ClassName}.__before_init__' InitFuncName = f'{ClassName}.__init__' NewMethodFuncName = f'{ClassName}.__new__' has_new_method = Gen._find_function(NewMethodFuncName) is not None + + # 【self 的类型必须与 callee 同源(2026-09-30)】 + # `Gen.structs` 的键有"裸短名 / sha1 全名 / import 别名"三种:定义端恒以**短名** + # 作键(StructGen._generate_structs),别名注册只是**浅拷贝**(HandlesImports 对 + # `Gen.structs[asname] = Gen.structs[name]`)。因此"两个模块各定义同名类"时, + # `ClassName` 无论是别名(`from .astaux import Keyword as KwNode` ⇒ 'KwNode')、 + # 还是被别的模块先占用的短名('Keyword' 被 .tokens 占用),按名字查都可能拿到 + # **另一份实例**。唯一无歧义的判据是 **callee 自己**:构造函数 + # (__new__/__before_init__/__init__)第 0 个形参(self)的 pointee 就是编译器 + # 写下的期望类型,也正是 `_adjust_args` 用来比较的对象(llvmlite 的类型相等按 + # **IR 名字**判定,实测同名不同对象 `==` 为真,故取 callee 形参的 pointee 即与之 + # 逐字同名;按短名另取一份只会继续 fail-fast)。 + # 现场:includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)` + # —— callee 是 astaux 的 `8312ce86a2fd82c0.Keyword.__new__`,self 实参却按 + # 短名/别名叫到了 tokens 的 `fef783da4d24330d.Keyword` ⇒ + # "指针隐式转换已禁止(非基本类型(结构体/类/联合))"。 + def _CalleeSelfType() -> ir.Type | None: + """callee 第 0 个形参 self 的 pointee(未具化时按 IR 名补已具化的同类型)。 + + 按 __new__ → __before_init__ → __init__ 顺序取第一个可用的:三者都是 self + 的接收者,且同名类的多份实例只可能因"查名"分歧而不同源,取 callee 侧即可 + 消除分歧。取不到(无形参 / 非具名结构体)时返回 None,调用方退回按名字查。 + 未具化的实例不能用于 `_allocaEntry`(算不出尺寸),故按 **IR 名**换已具化的 + 同类型对象 —— IR 里类型的身份就是名字,同名即同类型(判据同 + `BaseGen._FindStructByIrName` 的文档)。 + """ + for _fn_name in (NewMethodFuncName, NewFuncName, InitFuncName): + _Fn: Any = Gen._find_function(_fn_name) + _Args: Any = getattr(getattr(_Fn, 'function_type', None), 'args', None) + if not _Args: + continue + _P0: Any = _Args[0] + if not isinstance(_P0, ir.PointerType): + continue + _PT: Any = _P0.pointee + if not isinstance(_PT, ir.IdentifiedStructType): + continue + if _PT.elements is not None: + return _PT + _Twin: Any = Gen._FindStructByIrName(getattr(_PT, 'name', '') or '') + if isinstance(_Twin, ir.IdentifiedStructType) and _Twin.elements is not None: + return _Twin + return None + + def _get_struct_type(name: str) -> ir.Type | None: + """self 用的 struct:callee 形参(无歧义)→ sha1 全名键 → 名字键""" + _SelfTy: ir.Type | None = _CalleeSelfType() + if _SelfTy is not None: + return _SelfTy + if _struct_full_key and _struct_full_key in Gen.structs: + return Gen.structs[_struct_full_key] + return Gen.structs.get(name) + if ClassName == 'Asm': import sys as _dbg_sys print(f"[DEBUG_CLASSNEW] Asm: has_before_init={Gen._has_function(NewFuncName)}, has_new={has_new_method}, struct_in_gen={ClassName in Gen.structs}", file=_dbg_sys.stderr) @@ -5160,21 +5178,13 @@ class ExprCallHandle(BaseHandle): adjusted_init = Gen._adjust_args(InitArgs, InitFunc) Gen.builder.call(InitFunc, adjusted_init, name=f"init_{ClassName}") return result - if _struct_full_key and _struct_full_key in Gen.structs: - StructType = Gen.structs[_struct_full_key] - if isinstance(StructType, ir.PointerType): - StructType = StructType.pointee - is_opaque = isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0) - if is_opaque: - self._ensure_struct_declared(ClassName, module_sha1=module_sha1) - StructType = Gen.structs.get(_struct_full_key, Gen.structs.get(ClassName, StructType)) - if isinstance(StructType, ir.PointerType): - StructType = StructType.pointee - is_opaque = isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0) - if is_opaque: - return None - elif ClassName in Gen.structs: - StructType = Gen.structs[ClassName] + # 【两支合并(2026-09-30)】原实现的"sha1 全名键命中"那一支只解析 StructType、 + # **不产出 alloca / 构造调用**,控制流随后落到函数末尾 `return i32 0` —— 即跨模块 + # 同名类的构造会**静默不构造**("名字键命中"那一支才是完整实现)。`_get_struct_type` + # 现已统一「callee 形参 → sha1 全名键 → 名字键」的解析顺序,故两支可合并为一条 + # 条件;行为差异只剩"不再静默返回 i32 0"。 + if (_struct_full_key and _struct_full_key in Gen.structs) or ClassName in Gen.structs: + StructType = _get_struct_type(ClassName) if isinstance(StructType, ir.PointerType): StructType = StructType.pointee is_opaque = isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0)