fix(tpv-call): 裸名调用同模块函数优先(owner_sha1),修复跨模块同名 stub 抢占致 _advance 误绑

This commit is contained in:
TermiNexus committed 2026-09-08 20:47:54 +08:00
1 parent a260b9316d
commit ab814f55e6
6 files changed
+436 -15

No files matched your search

+31 -1
View File
@@ -501,13 +501,21 @@ class CallGenBase:
def find_func_in_module(self, mod: llvmlite.LLVMModule | t.CPtr,
name: str) -> llvmlite.Function | t.CPtr:
name: str,
owner_sha1: str = None) -> llvmlite.Function | t.CPtr:
"""在模块的函数链表中按名称查找函数
查找策略:
1. 精确匹配(如 "strlen" 匹配 @strlen,有 t.CExport 的函数)
2. 后缀匹配(如 "atoi" 匹配 @"83210a72e829a086.atoi",无 t.CExport 的函数)
owner_sha1: 当前模块 SHA1。非 None 时在本模块 define 判定之前先做
"本模块同名函数"优先扫描(mangled 名以 "{owner_sha1}." 开头且恰为
"{owner_sha1}.{name}")。裸名调用应优先解析为当前模块自身函数——
其前向 declare 在函数体翻译时可能尚未激活(IsDeclared 仍为 1),
pass2 的 IsDeclared==0 判定会跳过它,pass3 又会按链表顺序误绑排在
FuncHead 前面的跨模块 stub declare(lexer.vp _advance 根因)。
使用 llvmlite.function_get_name/next 访问器绕过 stub 类型限制
(stub 类型字段不足且字段类型可能错误,直接字段访问会被静默跳过)。
"""
@@ -522,6 +530,28 @@ class CallGenBase:
if string.strcmp(cur_name, name) == 0:
return cur
cur = llvmlite.function_get_next(cur)
# 第一遍半(owner_sha1 提供时):本模块同名函数优先。
# 仅接受恰为 "{owner_sha1}.{name}" 的形态(mangled 模块级函数名含
# 且只含一个 '.'),不匹配 "{sha1}.Class.method" 等方法名。是否
# IsDeclared==0 均可返回:前向 declare 与 define 同名同符号。
if owner_sha1 is not None:
cur = mod.FuncHead
while cur is not None:
cur_name_o: str = llvmlite.function_get_name(cur)
if cur_name_o is not None:
cur_len_o: t.CSizeT = string.strlen(cur_name_o)
if cur_len_o == 17 + name_len:
if string.strncmp(cur_name_o, owner_sha1, 16) == 0 and cur_name_o[16] == '.':
suffix_start_o: t.CSizeT = cur_len_o - name_len
if cur_name_o[suffix_start_o - 1] == '.':
match_o: int = 1
for i in range(name_len):
if cur_name_o[suffix_start_o + i] != name[i]:
match_o = 0
break
if match_o == 1:
return cur
cur = llvmlite.function_get_next(cur)
# 第二遍:后缀匹配(.name)
# 不跳过 declare:方法 stub 也是 declare,需要通过后缀匹配找到
# 调用处(translate_call 等)负责判断 IsDeclared 并决定使用裸名还是 sha1 前缀名
+274
View File
@@ -393,6 +393,14 @@ class CallGenCall(CallGenBase.CallGenBase):
# 破坏表达式翻译;pending 文件本遍不写盘,下遍重译)
call_ret_ty = llvmlite.Int32(pool)
else:
# [METHDBG] 临时诊断:依赖就绪但返回类型仍无法确定(用后移除)
dbg_ms2: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?"
dbg_cls_short: str = class_name
if dbg_cls_short is not None:
pass
stdio.printf("[METHDBG] fatal(method_ret): mod=%s cls=%s method=%s sha1=%s\n",
dbg_ms2, dbg_cls_short, method_name,
cls_sha1 if cls_sha1 is not None else "none")
err_cm4: str = pool.alloc(256)
if err_cm4 is not None:
viperlib.snprintf(err_cm4, 256,
@@ -681,6 +689,48 @@ class CallGenCall(CallGenBase.CallGenBase):
if obj_ptr is None or cl is None:
return None
# [修复] list 泛型容器方法兜底拦截:接收者被分派为 list 特化类名
# (list[...] / {sha1}.list[...],剥离外层前缀后以 'list' 开头)且方法属
# list 内置集合时,本调用不存在对应 C 函数——若继续走下方 MRO/函数/
# text.ll/vpi stub 返回类型解析必然全部落空 → fatal(mro_ret)
# (AstTest includes/ast/parser.vp _fix_store_ctx 局部变量
# tp_elts: rowptr[list[rowptr[AST]]] 后调 tp_elts.get(i) 根因:函数局部
# 变量经 pre_scan/块作用域登记后,CallGenDispatch 的 list 注解拦截
# 偶发漏判时仍会把 obj_var 当作普通结构体变量送入本函数)。直接转交
# 本类 _translate_list_builtin_call 内联翻译(与 _tc_h6_method_dispatch
# 尾部 list 分支同语义:内联 GEP __data__/__count__/__elem_size__),
# 保证 get/append 等 list 内置方法一律命中、绝不进入 MRO 解析。
if method_name is not None and class_name is not None:
lb_cls: str = class_name
# 剥离跨模块特化名 sha1 前缀 "{16hex}."({sha}.list[...] 形态)
if string.strncmp(lb_cls, "list", 4) != 0 \
and string.strlen(lb_cls) > 17 and lb_cls[16] == '.':
if string.strncmp(lb_cls + 17, "list", 4) == 0:
lb_cls = lb_cls + 17
if self.Gen.CallGenProp._is_list_builtin_method(lb_cls, method_name) != 0:
# 双指针槽形态(rowptr[list[...]] 局部变量 alloca Ptr(Ptr(Struct))
# 直接传入时先 load 一层得到对象指针 Ptr(Struct),与分派方
# _translate_method_call 调用点的 obj_ptr 语义一致)。
lb_obj: llvmlite.Value | t.CPtr = obj_ptr
if lb_obj is not None and lb_obj.Ty is not None:
match lb_obj.Ty:
case llvmlite.LLVMType.Ptr(lb_inner):
match lb_inner:
case llvmlite.LLVMType.Ptr(lb_inner2):
lb_obj = llvmlite.build_load(builder, lb_inner, lb_obj)
case _:
pass
case _:
pass
if lb_obj is not None:
lb_cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
lb_can: t.CSizeT = 0
if lb_cargs is not None:
lb_can = len(lb_cargs)
return self._translate_list_builtin_call(
pool, builder, mod, cl, method_name, lb_obj,
lb_cargs, lb_can, trans)
# 提前获取结构体 entry(用 find_struct_by_type 规避跨模块同名 find_struct 找错)
# 用于继承链遍历、SHA1 查找和虚表分派
mc_entry: GenTypes.StructEntry | t.CPtr = None
@@ -1109,6 +1159,10 @@ class CallGenCall(CallGenBase.CallGenBase):
# None 会破坏表达式翻译,pending 文件本遍不写盘)
call_ret_ty = llvmlite.Int32(pool)
else:
# [METHDBG] 临时诊断:MRO 祖先依赖就绪但返回类型仍解析失败(用后移除)
dbg_ms4: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?"
stdio.printf("[METHDBG] fatal(mro_ret): mod=%s cls=%s method=%s\n",
dbg_ms4, class_name, method_name)
call_ret_ty = self._fail_method_ret_ty(method_name, cl)
# found_func 未命中但返回类型已解析:用方法定义类构造跨模块调用名
@@ -1598,6 +1652,226 @@ class CallGenCall(CallGenBase.CallGenBase):
return self.Gen.CallGenLeafKit._emit_eh_guarded_call(
builder, pool, mod, trans, found_func, call_name, head, total_count,
call_ret_ty, is_variadic_3441)
# ============================================================
# _translate_list_builtin_call — list 内置方法内联翻译(兜底共享实现)
#
# 供 _translate_method_call 顶部 list 兜底拦截调用(语义与
# CallGenDispatch._tc_h6_method_dispatch 尾部 list 内置分支完全一致):
# list 结构布局 (6 字段 × 8 字节):
# __data__(0) __count__(8) __capacity__(16) __pool__(24)
# __elem_size__(32) __iter_index__(40)
# get/__getitem__/set/__setitem__/pop/clear/__iter__/__len__/append 内联
# 翻译,绝不生成对 list 方法函数的跨模块调用(list 方法无对应 C 函数)。
# lm_obj 必须是已 load 的 list 对象指针 Ptr(list-struct)。
# ============================================================
def _translate_list_builtin_call(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
cl: ast.Call | t.CPtr,
lm_name: str,
lm_obj: llvmlite.Value | t.CPtr,
cargs: list[ast.AST | t.CPtr] | t.CPtr,
can: t.CSizeT,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译 list 内置方法调用,返回 call 结果 Value(void 方法返回 None)"""
if pool is None or builder is None or lm_obj is None or lm_name is None:
return None
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 元素类型 T:从 list 特化类型名解析(list[int]→i32、list[str]→i8*、
# 未知/指针元素→i8*),与 CallGenDispatch 尾部 list 分支一致
lm_esty: llvmlite.LLVMType | t.CPtr = \
self.Gen.StructGen.get_struct_type_from_value(lm_obj)
lm_ename: str = None
if lm_esty is not None:
lm_ename = llvmlite.get_struct_name(lm_esty)
lm_elem_ty: llvmlite.LLVMType | t.CPtr = None
if lm_ename is not None:
lm_elem_ty = self.Gen.ExprGenName._list_elem_type_from_name(
pool, lm_ename)
if lm_elem_ty is None:
lm_elem_ty = i8_ptr_ty
# get / __getitem__: 返回 index 处的元素
if string.strcmp(lm_name, "get") == 0 or string.strcmp(lm_name, "__getitem__") == 0:
if can >= 1 and cargs is not None:
# 加载 __data__ (偏移 0)
g_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
g_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
g_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, g_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
g_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
g_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
g_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, g_epp)
# 翻译 index
g_idx_node: ast.AST | t.CPtr = cargs.get(0)
g_idx_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, g_idx_node, None, 0, trans)
if g_data is not None and g_esize is not None and g_idx_val is not None:
g_idx_i64: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, g_idx_val, i64_ty, g_idx_node)
g_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, g_idx_i64, g_esize)
g_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, g_data, g_offset)
if g_addr is not None:
g_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, g_addr, llvmlite.Ptr(pool, lm_elem_ty))
if g_ep is not None:
return llvmlite.build_load(builder, lm_elem_ty, g_ep)
# set / __setitem__: 设置 index 处的元素
elif string.strcmp(lm_name, "set") == 0 or string.strcmp(lm_name, "__setitem__") == 0:
if can >= 2 and cargs is not None:
s_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
s_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
s_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, s_dpp)
s_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
s_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
s_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, s_epp)
s_idx_node: ast.AST | t.CPtr = cargs.get(0)
s_val_node: ast.AST | t.CPtr = cargs.get(1)
s_idx_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, s_idx_node, None, 0, trans)
s_val_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, s_val_node, None, 0, trans)
if s_data is not None and s_esize is not None and s_idx_val is not None and s_val_val is not None:
s_idx_i64: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, s_idx_val, i64_ty, s_idx_node)
s_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, s_idx_i64, s_esize)
s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset)
if s_addr is not None:
s_val_elem: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, s_val_val, lm_elem_ty, s_val_node)
s_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, s_addr, llvmlite.Ptr(pool, lm_elem_ty))
if s_ep is not None:
llvmlite.build_store(builder, s_val_elem, s_ep)
return None
# pop: 返回最后一个元素,count--
elif string.strcmp(lm_name, "pop") == 0:
p_idx1: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 1)
p_cpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, p_idx1)
p_count: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, p_cpp)
p_nc: llvmlite.Value | t.CPtr = llvmlite.build_sub(
builder, p_count, llvmlite.const_int64(pool, 1))
llvmlite.build_store(builder, p_nc, p_cpp)
p_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
p_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, p_idx0)
p_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, p_dpp)
p_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
p_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, p_idx4)
p_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, p_epp)
if p_data is not None and p_esize is not None and p_nc is not None:
p_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, p_nc, p_esize)
p_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, p_data, p_offset)
if p_addr is not None:
p_ep: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, p_addr, llvmlite.Ptr(pool, lm_elem_ty))
if p_ep is not None:
return llvmlite.build_load(builder, lm_elem_ty, p_ep)
# clear: count = 0
elif string.strcmp(lm_name, "clear") == 0:
c_idx1: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 1)
c_cpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, c_idx1)
llvmlite.build_store(builder, llvmlite.const_int64(pool, 0), c_cpp)
return None
# __iter__: iter_index = 0, return self
elif string.strcmp(lm_name, "__iter__") == 0:
it_idx5: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 5)
it_ipp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, it_idx5)
llvmlite.build_store(builder, llvmlite.const_int64(pool, 0), it_ipp)
return lm_obj
# __len__: 返回 __count__ (偏移 8, index 1)
elif string.strcmp(lm_name, "__len__") == 0:
l_idx1: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 1)
l_cpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, l_idx1)
if l_cpp is not None:
return llvmlite.build_load(builder, i64_ty, l_cpp)
# append: 直接调用 list.append(self, item)
# append 需要容量检查和内存重分配,逻辑复杂,直接调用 _list.py 中的实现
elif string.strcmp(lm_name, "append") == 0:
if can >= 1 and cargs is not None:
ap_sha1: str = self._lookup_module_sha1("_list")
if ap_sha1 is None:
# 依赖模块未编译(_list 未预加入编译链):fail-fast,
# 绝不静默跳过 append(元素永远不会被加入列表)
self.Gen.TypeGenErr.fatal_error(
cl, "list.append: 找不到 _list 模块 SHA1(依赖模块未编译)",
"InternalCompilerError")
if ap_sha1 is not None:
# 特化类名提取:list.append 的符号名是
# {sha1}.list[AST|CPtr].append(带类型参数特化名),而非
# {sha1}.list.append。lm_obj 类型即特化结构体 Ptr(list[...]),
# 从类型解析特化类名拼接调用名,否则 ld.lld undefined symbol
ap_spec_nm: str = None
ap_struct_ty: llvmlite.LLVMType | t.CPtr = \
self.Gen.StructGen.get_struct_type_from_value(lm_obj)
if ap_struct_ty is not None:
ap_spec_nm = self.Gen.StructGen.get_class_name_by_type(
pool, ap_struct_ty)
if ap_spec_nm is None:
ap_spec_nm = "list"
# 触发泛型特化翻译(幂等,MethodsEmitted 防重入)
if trans is not None and ap_spec_nm is not None:
lbkt_ap: str = string.strchr(ap_spec_nm, '[')
if lbkt_ap is not None:
rbkt_ap: str = string.strrchr(ap_spec_nm, ']')
if rbkt_ap is not None and rbkt_ap > lbkt_ap:
base_len_ap: t.CSizeT = t.CSizeT(t.CUInt64T(lbkt_ap) - t.CUInt64T(ap_spec_nm))
arg_len_ap: t.CSizeT = t.CSizeT(t.CUInt64T(rbkt_ap) - t.CUInt64T(lbkt_ap) - 1)
if base_len_ap > 0 and arg_len_ap > 0:
base_buf_ap: str = pool.alloc(base_len_ap + 1)
arg_buf_ap: str = pool.alloc(arg_len_ap + 1)
if base_buf_ap is not None and arg_buf_ap is not None:
string.strncpy(base_buf_ap, ap_spec_nm, base_len_ap)
base_buf_ap[base_len_ap] = '\0'
string.strncpy(arg_buf_ap, lbkt_ap + 1, arg_len_ap)
arg_buf_ap[arg_len_ap] = '\0'
args_ap: list[str] | t.CPtr = list[str](pool, 8)
if args_ap is not None:
# 逗号分隔多类型实参(list[X] / dict[K, V])
cur_ap: str = arg_buf_ap
while cur_ap is not None:
comma_ap: str = string.strchr(cur_ap, ',')
if comma_ap is None:
args_ap.append(cur_ap)
break
seg_len_ap: t.CSizeT = t.CSizeT(t.CUInt64T(comma_ap) - t.CUInt64T(cur_ap))
seg_buf_ap: str = pool.alloc(seg_len_ap + 1)
if seg_buf_ap is not None:
string.strncpy(seg_buf_ap, cur_ap, seg_len_ap)
seg_buf_ap[seg_len_ap] = '\0'
args_ap.append(seg_buf_ap)
cur_ap = comma_ap + 1
self.Gen.ClassGenGeneric._specialize_generic_class(
trans, base_buf_ap, args_ap)
# 调用名 "{sha1}.{spec}.append":
# sha1(16) + '.'(1) + spec + '.append'(7) + NUL(1) = spec + 25
ap_mlen: t.CSizeT = string.strlen(ap_sha1) + \
string.strlen(ap_spec_nm) + 26
ap_mangled: str = pool.alloc(ap_mlen)
if ap_mangled is not None:
viperlib.snprintf(ap_mangled, ap_mlen, "%s.%s.append",
ap_sha1, ap_spec_nm)
ap_item_node: ast.AST | t.CPtr = cargs.get(0)
ap_item_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, ap_item_node, None, 0, trans)
if ap_item_val is not None:
llvmlite.value_set_next(lm_obj, None)
llvmlite.value_set_next(ap_item_val, None)
llvmlite.value_set_next(lm_obj, ap_item_val)
ap_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
self._ensure_method_declare(pool, mod, ap_mangled, ap_ret_ty, lm_obj, 2)
return llvmlite.build_call(builder, ap_mangled, lm_obj, 2, ap_ret_ty, 0)
return None
# ============================================================
# _translate_super_call — super().method(args) → 静态调用父类方法
#
+55 -11
View File
@@ -1231,6 +1231,34 @@ class CallGenDispatch(CallGenBase.CallGenBase):
if obj_var is not None and obj_var.Ty is not None:
# 检查是否为 Ptr(Struct(...))(结构体值变量)
# 或 Ptr(Ptr(Struct(...)))(指针变量,需 load)
# list 泛型容器注解先行识别(早期拦截,供下方各 class 分支共用):
# rowptr[list[rowptr[AST]]] 变量(ast/parser.vp _fix_store_ctx
# 的 tp_elts,元素类在另一模块)LLVM 形态为
# Ptr(Ptr(Struct('list[sha.AST]')));list 特化 struct 不注册为
# 普通类,get_class_name_by_type 或返回 None、或对类型名尾部
# (list[sha.AST]) 后缀匹配误命中内层元素类(如 'AST')→ 下方
# 两个 Ptr 分支的 list 内置拦截落空/错判。AnnotClassName 经
# extract_class_name_from_annotation 已剥最外层 rowptr 存为
# "list[元素]",此处再兜底剥一次(rowptr[list[...]] 形态):
# 内层以 'list' 开头且方法属 list 内置集合时,本 obj.method 调用
# 必须交给本方法尾部 list 内置分支(内联 GEP)翻译——置位
# is_list_annot_builtin 后跳过下方所有 class 方法分支/FakeDuck/
# AnnotClassName 方法构造,绝不进入 _translate_method_call/MRO
# 返回类型解析(函数/text.ll/vpi stub 均未命中 → fatal(mro_ret),
# AstTest ast/parser.vp 根因)。普通类/str/bytes 注解不以 'list'
# 开头 → 不置位,行为不变。
var_entry: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, method_obj_nm.id)
is_list_annot_builtin: int = 0
if var_entry is not None and var_entry.AnnotClassName is not None:
la_buf: str = var_entry.AnnotClassName
la_tail: str = la_buf
if string.strncmp(la_buf, "rowptr[", 7) == 0:
la_tail = la_buf + 7
if string.strncmp(la_tail, "list", 4) == 0:
if self.Gen.CallGenProp._is_list_builtin_method(
la_tail, func_name) != 0:
is_list_annot_builtin = 1
match obj_var.Ty:
case llvmlite.LLVMType.Ptr(obj_struct_ty):
class_nm: str = self.Gen.StructGen.get_class_name_by_type(
@@ -1242,7 +1270,11 @@ class CallGenDispatch(CallGenBase.CallGenBase):
# _translate_method_call 会查 append 返回类型(函数/text.ll/
# vpi stub 均未命中)→ fail-fast。检测到 list 方法时
# 跳过 method_call,落到内置分支(NewAstTest body.append 根因)。
if self.Gen.CallGenProp._is_list_builtin_method(class_nm, func_name) == 0:
# 注解兜底(is_list_annot_builtin=1)同跳过:class_nm
# 可能被类型名后缀匹配错解为内层元素类,非 'list' 前缀
# 拦截会漏放 → 走 method_call/MRO fatal。
if self.Gen.CallGenProp._is_list_builtin_method(class_nm, func_name) == 0 \
and is_list_annot_builtin == 0:
if hit_box is not None:
hit_box[0] = 1
return self.Gen.CallGenCall._translate_method_call(
@@ -1255,8 +1287,11 @@ class CallGenDispatch(CallGenBase.CallGenBase):
inner_nm: str = self.Gen.StructGen.get_class_name_by_type(
pool, inner_struct_ty)
if inner_nm is not None:
# list 内置方法同样跳过(同 Ptr(Struct) 分支)
if self.Gen.CallGenProp._is_list_builtin_method(inner_nm, func_name) == 0:
# list 内置方法同样跳过(同 Ptr(Struct) 分支);
# 注解兜底(is_list_annot_builtin=1)同跳过:
# inner_nm 也可能被后缀匹配错解为内层元素类。
if self.Gen.CallGenProp._is_list_builtin_method(inner_nm, func_name) == 0 \
and is_list_annot_builtin == 0:
obj_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, obj_struct_ty, obj_var)
if obj_ptr is not None:
@@ -1269,15 +1304,24 @@ class CallGenDispatch(CallGenBase.CallGenBase):
# 方法调用 → 扩展类 _Type/_str(a.type_id() / a.upper())。
# 前置拦截:常规结构体方法分支全落空时,尝试扩展类方法
# (find_func_in_module 未命中返回 None,不影响正常路径)。
fd_name_res: llvmlite.Value | t.CPtr = self.Gen.CallGenFd._translate_fakeduck_call(
pool, builder, mod, None, func_name, cl, trans)
if fd_name_res is not None:
return fd_name_res
# list 容器注解命中(is_list_annot_builtin=1)时跳过 FakeDuck:
# 接收者非 str/bytes,此调用必须由尾部 list 内置分支翻译。
if is_list_annot_builtin == 0:
fd_name_res: llvmlite.Value | t.CPtr = self.Gen.CallGenFd._translate_fakeduck_call(
pool, builder, mod, None, func_name, cl, trans)
if fd_name_res is not None:
return fd_name_res
# AnnotClassName 回退:联合类型简化为 Ptr(i8) 时,
# 通过原始类型注解的类名查找结构体
var_entry: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, method_obj_nm.id)
if var_entry is not None and var_entry.AnnotClassName is not None:
# 通过原始类型注解的类名查找结构体(var_entry 已在上方
# list 注解识别处查得,同一变量表项)。
# list 容器注解命中(is_list_annot_builtin=1)时跳过本回退:
# 本回退的"剥模块前缀"会按内层元素 '.'(如
# list[rowptr[sha.AST]])把类名错切为 'AST]]' 垃圾 → 下方
# _is_list_builtin_method 前缀匹配不中 → 误走 FakeDuck/sha1
# 方法构造 → _translate_method_call MRO fatal。list 调用由
# 尾部 list 内置分支统一翻译。
if var_entry is not None and var_entry.AnnotClassName is not None \
and is_list_annot_builtin == 0:
annot_val: str = var_entry.AnnotClassName
# 解析模块限定名 "memhub.MemBuddy" → mod_nm="memhub", cls_nm="MemBuddy"
dot_ptr: str = string.strrchr(annot_val, '.')
+24 -2
View File
@@ -946,11 +946,33 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
def find_func_in_table(self, funcs: GenTypes.FuncEntry | t.CPtr,
func_count: int,
name: str) -> llvmlite.Function | t.CPtr:
"""在函数表中按名称查找,返回 Function 指针或 None"""
name: str,
owner_sha1: str = None) -> llvmlite.Function | t.CPtr:
"""在函数表中按名称查找,返回 Function 指针或 None
owner_sha1: 当前模块 SHA1。非 None 时"本模块函数优先"——表里同名条目
可能同时含本模块函数与依赖 stub 注入的外来函数(LoadDepVpiIntoModule
在 forward_declare_functions 之前把依赖模块同名函数以裸名注册进本文件
_funcs,槽位在前;本模块条目注册在后)。本模块函数 mangled 名为
"{owner_sha1}.{name}",按其 16 位前缀优先返回;无本模块条目
(纯 from-import/外部 stub)时回落首个同名条目(旧语义)。
"""
if name is None or funcs is None:
return None
entry_size: t.CSizeT = GenTypes.FuncEntry.__sizeof__()
if owner_sha1 is not None:
# 第一遍:本模块条目优先(mangled 名以 "{owner_sha1}." 开头)。
# 前缀相同但非 mangled 名(CExport 等裸名条目名不含 '.')不命中,
# 由下方通用遍兜底。
for i in range(func_count):
entry_addr: t.CUInt64T = t.CUInt64T(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None and entry.Func is not None:
if string.strcmp(entry.Name, name) == 0:
fn_nm_o: str = llvmlite.function_get_name(entry.Func)
if fn_nm_o is not None:
if string.strncmp(fn_nm_o, owner_sha1, 16) == 0 and fn_nm_o[16] == '.':
return entry.Func
for i in range(func_count):
entry_addr: t.CUInt64T = t.CUInt64T(funcs) + i * entry_size
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
+37 -1
View File
@@ -562,7 +562,43 @@ class CallGenMixin(CallGenBase.CallGenBase):
if decor_wrap_nm is not None:
found_func = self.find_func_in_module(mod, decor_wrap_nm)
else:
found_func = self.find_func_in_module(mod, func_name)
# 本模块已定义函数名的裸名调用:先按裸名在模块函数表
# (FuncEntry 数组,forward_declare_functions 已把所有模块级
# 函数登记进 trans._funcs:Name=裸名、Func=其 declare/define)
# 精确命中,再回落 find_func_in_module 的 LLVM 模块链表扫描。
# 根因:同模块互调(string.vp atof_out 内 atof(src) → 本模块
# @{sha1}.atof)此前只靠 find_func_in_module 扫 FuncHead,
# 链表扫描漏掉本模块 atof 时 found_func=None → 误走 C 库自动
# declare / from_imports 回退,最终报"裸名调用 'atof' 未定义且
# 未导入"。函数表才是同模块函数名的权威注册表(即注释 L540-541
# "优先从函数表按裸名精确匹配" 的原意,此处补落地)。模块表只含
# 模块级函数(无方法),CExport 函数表中 Func 名即裸名,复用
# 不改变既有 CExport/非 CExport 的符号语义。
mod_fn_tb: llvmlite.Function | t.CPtr = None
if actual_funcs is not None:
# 同模块函数优先:_funcs 表在 forward_declare_functions 之前
# 已被 LoadDepVpiIntoModule 注入的依赖模块 stub 污染——外来
# 同名函数(如 lexer.vp 的 _advance)以裸名注册在表前部,
# 普通按名查找从 0 号槽先命中它 → 本模块 _advance 的 163 处
# 裸名调用全被绑成 lexer 版(AstTest 根因)。传 trans.ModuleSha1
# 使表查找优先返回 mangled 名为 "{ModuleSha1}.{name}" 的本模块
# 条目(forward_declare 已把本模块全部模块级函数登记进表)。
tbl_owner_sha1: str = None
if trans is not None:
tbl_owner_sha1 = trans.ModuleSha1
mod_fn_tb = self.Gen.CallGenLeafKit.find_func_in_table(
actual_funcs, actual_func_count, func_name, tbl_owner_sha1)
if mod_fn_tb is not None:
found_func = mod_fn_tb
else:
# 表内无同名条目(如本模块函数未登记/表被清空)时回退扫
# FuncHead。同样传 owner_sha1:本模块同名前向 declare 尚未
# 激活(IsDeclared==1)时 pass2 会跳过、pass3 会按链表顺序
# 误绑排在前面的跨模块 stub declare,前缀优先扫描兜住该时序。
ff_owner_sha1: str = None
if trans is not None:
ff_owner_sha1 = trans.ModuleSha1
found_func = self.find_func_in_module(mod, func_name, ff_owner_sha1)
# 治本修复(对齐 Attribute 路径 L6579-6613):后缀匹配误命中类方法
# (SHA1.Class.method,第 2 段是已注册结构体类)必须拒绝——否则方法体内
# 与 from-import 模块函数同名的裸名调用(如 LLVMModuleCore.SSAValue 内
+15
View File
@@ -0,0 +1,15 @@
fix(tpv-call): 裸名调用同模块优先,修复跨模块同名 stub 抢占致 _advance 误绑
根因:LoadDepVpiIntoModule 在 forward_declare_functions 之前把依赖模块
(lexer)函数以裸名注册进本文件 _funcs(槽位在前),find_func_in_table
与 find_func_in_module 后缀扫描均先命中跨模块 stub declare → parser.vp
内部 163 处 _advance(ps) 全部绑成 lexer._advance,并以 Parser→Lexer**
bitcast 传参(运行期游标永不推进,AstTest 无限 SyntaxError 至 pool 耗尽)。
修复:find_func_in_table / find_func_in_module 增加 owner_sha1(当前模块)
前缀优先遍,仅接受恰为 "{sha1}.{name}" 的本模块函数(declare/define 均可,
前向 declare 未激活时同样命中);translate_call 裸名分支传 trans.ModuleSha1。
本模块无同名定义(纯 from-import/外部 stub)时回落原语义,不改变 CExport/
方法拒绝/装饰器分支行为。
附带同批入库:list 内置方法内联分派与裸名模块表匹配等本会话修复。