diff --git a/App/lib/core/Gen/Call/CallGenBase.vp b/App/lib/core/Gen/Call/CallGenBase.vp index 61a3db1..97a3b4e 100644 --- a/App/lib/core/Gen/Call/CallGenBase.vp +++ b/App/lib/core/Gen/Call/CallGenBase.vp @@ -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 前缀名 diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index cdacc56..4a3d17e 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -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) → 静态调用父类方法 # diff --git a/App/lib/core/Gen/Call/CallGenDispatch.vp b/App/lib/core/Gen/Call/CallGenDispatch.vp index ebd3e52..852c30f 100644 --- a/App/lib/core/Gen/Call/CallGenDispatch.vp +++ b/App/lib/core/Gen/Call/CallGenDispatch.vp @@ -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, '.') diff --git a/App/lib/core/Gen/Call/CallGenLeafKit.vp b/App/lib/core/Gen/Call/CallGenLeafKit.vp index 8047518..727b8b8 100644 --- a/App/lib/core/Gen/Call/CallGenLeafKit.vp +++ b/App/lib/core/Gen/Call/CallGenLeafKit.vp @@ -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)) diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index 44e3784..08fda1e 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -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 内 diff --git a/_commit_msg.tmp b/_commit_msg.tmp new file mode 100644 index 0000000..f16400b --- /dev/null +++ b/_commit_msg.tmp @@ -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 内置方法内联分派与裸名模块表匹配等本会话修复。