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)