fix(HandlesExprCall): 构造的 self 按 callee 形参类型解析(同名类消歧),并合并"sha1 全名键"分支
类别: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,一并修掉)。
This commit is contained in:
1 parent
ba7e117ecb
commit
ab906f2cde
1 file changed
+60
-50
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user