fix(TPV): 方法先声明后定义(消除方法查找的源顺序依赖),删除兄弟 Mixin 兜底
现场:Test/FileIoTest 编译 includes/w32/fileio.vp 报
CastError: 源 %"b0721da228be3d55.FileW"* → 目标 %"b0721da228be3d55.File"*
(指针隐式转指针已禁止:被指类型是非基本类型)
根因(有旧 IR 佐证,非本轮改动引起):FileW.__init__(源 304 行)里的
self.seek(0, SEEK_END) 被解析成 File.seek,而不是 FileW.seek:
1) _translate_method 只在方法体翻译时才 create_function,于是方法查找
(find_func_in_module)依赖方法在类体内的书写顺序:FileW.seek 在源 403 行,
晚于 __init__ → 查不到 → None;
2) MRO 无父类 → None;
3) 兄弟 Mixin 兜底随即遍历全部已注册 struct,取"本模块存在同名方法"的
第一个候选类 → 命中 File → 绑成 File.seek,self 再被 this-adjust
bitcast 成 File*。
旧 IR(TransPyV\.tpv_build\temp\b0\b0721da228be3d55.text.ll,mtime 9/27,
早于本轮全部改动)里就是:
%97 = bitcast %"...FileW"* %96 to %"...File"*
%98 = call i32 @"...File.seek"(%"...File"* %97, i32 0, i32 2)
即靠一层隐式跨类 bitcast 静默通过;指针规则收紧后以 CastError 报出。
(File.__init__ 的 self.seek 同样查不到,只是兜底恰好扫到 File.seek 而"碰巧正确"。)
改动:
1. ClassGenVTable._translate_oop_methods:翻译方法体之前,先对本类**全部方法**
跑一遍 sig_only=1 建前向声明(declare + 参数 + 函数表登记)——与
FuncGenDef.forward_declare_functions 对模块级函数做的事完全同构,
使方法查找不再依赖类体内的书写顺序。
2. ClassGenVTable._translate_method 新增 sig_only 形参:
· 签名部分改用 create_declare(IsDeclared=1),已存在同名前向声明则复用;
· 发射函数体前把 IsDeclared 清零转 define(对齐 FuncGenDef.vp:544-545),
否则打印端按 IsDeclared 输出 declare、函数体整体丢失;
· 复用声明时跳过重复的 add_param 与 add_func_to_table;
· 命中同名函数且 IsDeclared==0(函数体已发射)时直接返回,杜绝重复发射
(llvmlite.create_function 不做同名去重,旧写法会造 duplicate symbol)。
3. CallGenCall._translate_method_call:按"不兜底、fail-fast"决定,整块删除
兄弟 Mixin 兜底(遍历 struct 表猜候选类)及其死分支
(mixin_hit / mixin_ret_ty / mixin_anc_name / mixin_anc_sha1 的声明与消费点)。
方法未命中即为真未定义,交下方 text.ll / vpi / 本模块豁免路径
(CallGenCall.vp:962 起的 is_local_mod 分支,从已声明的本模块函数直接取
返回类型)处理,不再猜候选类。
4. 同步修正 ClassGenVTable 顶部 _translate_method 的注释块与函数文档串。
TPC 侧无需同步:TPC 的声明阶段把类成员全部登记进 SymbolTable,本就不存在
"方法查找依赖源顺序",也没有这段兄弟 Mixin 扫描(HandlesExprCall.py:3877-3914
的兜底带"调用类自身未声明该方法"守卫,与本次删除的 TPV 版不同)。
待验证(不自行执行编译命令):重编译 TransPyV(自举需编两遍),再跑
FileIoTest / NoVTableCrossMixinTest / DictTest / 泛型相关测试。
This commit is contained in:
1 parent
875a9beb08
commit
455c7608b1
2 files changed
+157
-167
No files matched your search
@@ -527,6 +527,23 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
oop_has_vt_pre = oop_entry.HasVTable
|
||||
if oop_has_vt_pre == 1:
|
||||
self._collect_vtable_methods(trans, cd, class_name)
|
||||
|
||||
# 先声明后定义:翻译方法体之前,先为本类**全部方法**建立前向声明
|
||||
# (declare + 参数 + 函数表登记),使方法查找(find_func_in_module)不再
|
||||
# 依赖方法在类体中的书写顺序。与 FuncGenDef.forward_declare_functions
|
||||
# 对模块级函数做的事完全同构。缺此步的现场:includes/w32/fileio.vp 的
|
||||
# FileW.__init__(源 304 行)调 self.seek(...),而 FileW.seek 定义在源
|
||||
# 403 行 → 查不到 → 被同名方法扫描误绑到 File.seek → 指针规则以
|
||||
# FileW* → File* 报 CastError(旧 IR 靠 bitcast 静默掩盖)。
|
||||
for ci_pd in range(cn):
|
||||
stmt_pd: ast.AST* = children.get(ci_pd)
|
||||
if stmt_pd is None:
|
||||
continue
|
||||
if stmt_pd.kind() == ast.ASTKind.FunctionDef:
|
||||
fd_pd: ast.FunctionDef* = ast.FunctionDef*(stmt_pd)
|
||||
if fd_pd is not None and fd_pd.name is not None:
|
||||
self._translate_method(trans, fd_pd, struct_ty, class_name, 1)
|
||||
|
||||
for ci in range(cn):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None:
|
||||
@@ -561,8 +578,19 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
def _translate_method(self, trans: HT.Translator*,
|
||||
fd: ast.FunctionDef*,
|
||||
struct_ty: llvmlite.LLVMType*,
|
||||
class_name: str) -> int:
|
||||
"""翻译单个方法,返回 0"""
|
||||
class_name: str,
|
||||
sig_only: int = 0) -> int:
|
||||
"""翻译单个方法,返回 0
|
||||
|
||||
sig_only: 1=只建前向声明(declare + 参数 + 函数表登记),不发射函数体。
|
||||
_translate_oop_methods 在翻译方法体之前对本类**全部方法**先跑一遍
|
||||
sig_only=1(与 FuncGenDef.forward_declare_functions 对模块级函数做的事
|
||||
完全同构),使方法查找(find_func_in_module)不再依赖方法在类体中的
|
||||
书写顺序。反例:includes/w32/fileio.vp 的 FileW.__init__(源 304 行)
|
||||
调用 self.seek(...),而 FileW.seek 定义在源 403 行——没有预声明时
|
||||
查不到 FileW.seek,会落到"同名方法扫描"并误绑 File.seek,再被指针
|
||||
规则以 FileW* → File* 报 CastError。
|
||||
"""
|
||||
if trans is None or fd is None or struct_ty is None or class_name is None:
|
||||
return 0
|
||||
|
||||
@@ -649,104 +677,131 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
param_types_str: str = self.Gen.TypeGenErr.build_param_types_str(pool, fd.args)
|
||||
ret_ty = self.Gen.TypeGenErr.infer_return_type(pool, fd.children, param_types_str)
|
||||
|
||||
# 创建 LLVM 函数
|
||||
func: llvmlite.Function* = llvmlite.create_function(pool, mod, mangled_name, ret_ty)
|
||||
if func is None:
|
||||
return 0
|
||||
# 创建/复用 LLVM 函数。
|
||||
# 复用场景:本类的"先声明后定义"预扫描(sig_only=1)已用 create_declare 建好
|
||||
# 同名前向声明(IsDeclared=1),此处跳过参数创建与函数表登记(声明期已完成),
|
||||
# 只把 IsDeclared 清零转 define——与 FuncGenDef.forward_declare_functions
|
||||
# (FuncGenDef.vp:25-30)→ translate_function_def(FuncGenDef.vp:544-545)的
|
||||
# 模块级函数前向声明机制同构。
|
||||
func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, mangled_name)
|
||||
decl_reuse: int = 0
|
||||
if func is not None:
|
||||
if func.IsDeclared == 0:
|
||||
# 同名函数体已发射:禁止重复发射(duplicate symbol)
|
||||
return 0
|
||||
decl_reuse = 1
|
||||
else:
|
||||
func = llvmlite.create_declare(pool, mod, mangled_name, ret_ty)
|
||||
if func is None:
|
||||
return 0
|
||||
|
||||
# 提取默认参数信息(普通方法 args[0] 是 self,不含在 param_count 中;
|
||||
# @staticmethod 无 self,param_count 为完整参数数)
|
||||
# 注意: 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
|
||||
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
|
||||
md_defaults: t.CPtr = None
|
||||
md_default_count: int = 0
|
||||
md_param_count: int = 0
|
||||
md_args_node: ast.Arguments* = fd.args
|
||||
if md_args_node is not None:
|
||||
md_ags: ast.Arguments* = ast.Arguments*(md_args_node)
|
||||
if md_ags.args is not None:
|
||||
md_alist: list[ast.AST*]* = md_ags.args
|
||||
if is_static_m != 0:
|
||||
md_param_count = len(md_alist)
|
||||
else:
|
||||
md_param_count = len(md_alist) - 1
|
||||
if md_ags.defaults is not None:
|
||||
md_dlist: list[ast.AST*]* = md_ags.defaults
|
||||
md_defaults = md_dlist
|
||||
md_default_count = len(md_dlist)
|
||||
if sig_only != 0:
|
||||
# 前向声明模式:已声明过直接返回;否则下方登记完参数/函数表后返回。
|
||||
if decl_reuse != 0:
|
||||
return 0
|
||||
else:
|
||||
# 发射函数体前必须把 IsDeclared 清零,否则打印端按 IsDeclared 输出
|
||||
# declare、函数体整体丢失(对齐 FuncGenDef.vp:544-545)。
|
||||
func.IsDeclared = 0
|
||||
|
||||
# 注册到函数表(用 ClassName.method_name 作为查找名,支持后缀匹配)
|
||||
max_funcs: int = 1024
|
||||
if self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs,
|
||||
md_defaults, md_default_count, md_param_count,
|
||||
pool) == 0:
|
||||
trans._func_count = func_count + 1
|
||||
if decl_reuse == 0:
|
||||
# 提取默认参数信息(普通方法 args[0] 是 self,不含在 param_count 中;
|
||||
# @staticmethod 无 self,param_count 为完整参数数)
|
||||
# 注意: 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
|
||||
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
|
||||
md_defaults: t.CPtr = None
|
||||
md_default_count: int = 0
|
||||
md_param_count: int = 0
|
||||
md_args_node: ast.Arguments* = fd.args
|
||||
if md_args_node is not None:
|
||||
md_ags: ast.Arguments* = ast.Arguments*(md_args_node)
|
||||
if md_ags.args is not None:
|
||||
md_alist: list[ast.AST*]* = md_ags.args
|
||||
if is_static_m != 0:
|
||||
md_param_count = len(md_alist)
|
||||
else:
|
||||
md_param_count = len(md_alist) - 1
|
||||
if md_ags.defaults is not None:
|
||||
md_dlist: list[ast.AST*]* = md_ags.defaults
|
||||
md_defaults = md_dlist
|
||||
md_default_count = len(md_dlist)
|
||||
|
||||
# 添加 self/cls 参数(Ptr(struct_ty));@staticmethod 无 self,跳过
|
||||
# @classmethod 的 cls 也用 Ptr(struct_ty),首参名取 args[0](如 cls)
|
||||
if is_static_m == 0:
|
||||
first_pname: str = "self"
|
||||
if is_class_m != 0 and fd.args is not None:
|
||||
fd_ags: ast.Arguments* = ast.Arguments*(fd.args)
|
||||
if fd_ags.args is not None and len(fd_ags.args) > 0:
|
||||
fd_first_arg: ast.Arg* = ast.Arg*(fd_ags.args.get(0))
|
||||
if fd_first_arg is not None and fd_first_arg.arg is not None:
|
||||
first_pname = fd_first_arg.arg
|
||||
first_pname_buf: str = pool.alloc(32)
|
||||
if first_pname_buf is not None:
|
||||
viperlib.snprintf(first_pname_buf, 32, "%%%s", first_pname)
|
||||
llvmlite.add_param(pool, func, self_ptr_ty, first_pname_buf)
|
||||
# 注册到函数表(用 ClassName.method_name 作为查找名,支持后缀匹配)
|
||||
max_funcs: int = 1024
|
||||
if self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs,
|
||||
md_defaults, md_default_count, md_param_count,
|
||||
pool) == 0:
|
||||
trans._func_count = func_count + 1
|
||||
|
||||
# 添加其他参数(支持类型注解;@staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls)
|
||||
args_node: ast.Arguments* = fd.args
|
||||
if args_node is not None:
|
||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
start_idx: int = 0
|
||||
if is_static_m == 0:
|
||||
start_idx = 1
|
||||
for ai in range(start_idx, an):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
param_ty: llvmlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
|
||||
if resolved is None:
|
||||
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32
|
||||
# (方法签名与调用点/实现体三方不一致)
|
||||
self.Gen.TypeGenErr.fatal_error(arg, f"方法 '{class_name}.{method_name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
|
||||
return 0
|
||||
# 裸类形参 → Ptr(struct):按"对象即指针"引用传参。
|
||||
# 若按 struct 值传参,self.Head = node 会取局部拷贝地址
|
||||
# (append 返回后悬垂,G5 Test4 head.Value 读到垃圾)。
|
||||
# CArray 形参 → 元素指针 ty*(数组衰减,C 语义,与函数形参一致)。
|
||||
match resolved:
|
||||
case llvmlite.LLVMType.Struct(_, _, _):
|
||||
param_ty = llvmlite.Ptr(pool, resolved)
|
||||
case llvmlite.LLVMType.Array(aelem_ty, _):
|
||||
param_ty = llvmlite.Ptr(pool, aelem_ty)
|
||||
case _:
|
||||
param_ty = resolved
|
||||
pname: str = pool.alloc(32)
|
||||
if pname is not None:
|
||||
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
||||
llvmlite.add_param(pool, func, param_ty, pname)
|
||||
# 添加 self/cls 参数(Ptr(struct_ty));@staticmethod 无 self,跳过
|
||||
# @classmethod 的 cls 也用 Ptr(struct_ty),首参名取 args[0](如 cls)
|
||||
if is_static_m == 0:
|
||||
first_pname: str = "self"
|
||||
if is_class_m != 0 and fd.args is not None:
|
||||
fd_ags: ast.Arguments* = ast.Arguments*(fd.args)
|
||||
if fd_ags.args is not None and len(fd_ags.args) > 0:
|
||||
fd_first_arg: ast.Arg* = ast.Arg*(fd_ags.args.get(0))
|
||||
if fd_first_arg is not None and fd_first_arg.arg is not None:
|
||||
first_pname = fd_first_arg.arg
|
||||
first_pname_buf: str = pool.alloc(32)
|
||||
if first_pname_buf is not None:
|
||||
viperlib.snprintf(first_pname_buf, 32, "%%%s", first_pname)
|
||||
llvmlite.add_param(pool, func, self_ptr_ty, first_pname_buf)
|
||||
|
||||
# OOP iter 协议:__next__ 方法注入隐藏参数 __stop_iter_flag__(i1*)。
|
||||
# raise StopIteration 时置 1(对齐 TPC HandlesFunctions.py L703-705)。
|
||||
# 该参数不计入 md_param_count(逻辑参数),调用侧(for 循环)显式传。
|
||||
if string.strcmp(method_name, "__next__") == 0:
|
||||
i1_ty_si: llvmlite.LLVMType* = llvmlite.Int1(pool)
|
||||
if i1_ty_si is not None:
|
||||
i1_ptr_ty_si: llvmlite.LLVMType* = llvmlite.Ptr(pool, i1_ty_si)
|
||||
if i1_ptr_ty_si is not None:
|
||||
llvmlite.add_param(pool, func, i1_ptr_ty_si, "%__stop_iter_flag__")
|
||||
# 添加其他参数(支持类型注解;@staticmethod 从索引 0 开始,其余跳过索引 0 的 self/cls)
|
||||
args_node: ast.Arguments* = fd.args
|
||||
if args_node is not None:
|
||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
start_idx: int = 0
|
||||
if is_static_m == 0:
|
||||
start_idx = 1
|
||||
for ai in range(start_idx, an):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
param_ty: llvmlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
|
||||
if resolved is None:
|
||||
# [S4 fail-fast] 有注解但解析失败:不再静默用 i32
|
||||
# (方法签名与调用点/实现体三方不一致)
|
||||
self.Gen.TypeGenErr.fatal_error(arg, f"方法 '{class_name}.{method_name}' 形参 '{arg.arg}' 类型注解解析失败", "NameError")
|
||||
return 0
|
||||
# 裸类形参 → Ptr(struct):按"对象即指针"引用传参。
|
||||
# 若按 struct 值传参,self.Head = node 会取局部拷贝地址
|
||||
# (append 返回后悬垂,G5 Test4 head.Value 读到垃圾)。
|
||||
# CArray 形参 → 元素指针 ty*(数组衰减,C 语义,与函数形参一致)。
|
||||
match resolved:
|
||||
case llvmlite.LLVMType.Struct(_, _, _):
|
||||
param_ty = llvmlite.Ptr(pool, resolved)
|
||||
case llvmlite.LLVMType.Array(aelem_ty, _):
|
||||
param_ty = llvmlite.Ptr(pool, aelem_ty)
|
||||
case _:
|
||||
param_ty = resolved
|
||||
pname: str = pool.alloc(32)
|
||||
if pname is not None:
|
||||
viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
||||
llvmlite.add_param(pool, func, param_ty, pname)
|
||||
|
||||
# OOP iter 协议:__next__ 方法注入隐藏参数 __stop_iter_flag__(i1*)。
|
||||
# raise StopIteration 时置 1(对齐 TPC HandlesFunctions.py L703-705)。
|
||||
# 该参数不计入 md_param_count(逻辑参数),调用侧(for 循环)显式传。
|
||||
if string.strcmp(method_name, "__next__") == 0:
|
||||
i1_ty_si: llvmlite.LLVMType* = llvmlite.Int1(pool)
|
||||
if i1_ty_si is not None:
|
||||
i1_ptr_ty_si: llvmlite.LLVMType* = llvmlite.Ptr(pool, i1_ty_si)
|
||||
if i1_ptr_ty_si is not None:
|
||||
llvmlite.add_param(pool, func, i1_ptr_ty_si, "%__stop_iter_flag__")
|
||||
|
||||
# 参数添加完成,补全全局函数表的 Spec(供 vtable 在 func 被 reset 后派生类型文本)
|
||||
self.Gen.CallGenLeafKit.update_global_func_spec(full_name, func)
|
||||
|
||||
# 前向声明模式:签名与函数表登记已完成,不发射函数体
|
||||
if sig_only != 0:
|
||||
return 0
|
||||
|
||||
# 创建 entry 块
|
||||
entry_blk: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, "entry")
|
||||
if entry_blk is None:
|
||||
|
||||
Reference in new issue
Block a user