From faea6801162de3a97078c0ca75b6a1068eb99fd3 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 24 Oct 2026 17:02:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(overload):=20=E7=B1=BB=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E4=B8=8E=E8=99=9A=E5=87=BD=E6=95=B0=E9=87=8D=E8=BD=BD=EF=BC=88?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7/=E5=87=BD=E6=95=B0=E8=A1=A8=E9=94=AE/vtable?= =?UTF-8?q?=20=E6=A7=BD=E5=90=8D=E4=B8=80=E8=87=B4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 类内同名方法 >=2 时,方法短名追加 _v{参数签名hash}(跳过 self,static 不跳):LLVM 符号 {sha1}.Class.m_v{hash}、函数表键 Class.m_v{hash}、vtable 槽名 m_v{hash} 三者一致。vtable 收集改为先用 _method_slot_name 建槽名表:继承槽的覆盖判定按槽名比较(只有签名相同的同名方法才算覆盖),本类槽按槽名去重(重载各占一槽);__init__/__before_init__/__new__ 仍按原始名排除。实例方法调用与 Class.m(obj,...) 两条路径在候选 >=2 时用同一套决议(prefix 模式 + param_base=1),并把 method_name 换成带后缀的短名,使虚调用取到正确槽位。注:本提交含此前未提交的既有改动。 --- App/lib/core/Gen/Call/CallGenCall.vp | 181 +++++++++++++++-------- App/lib/core/Gen/Call/CallGenLeafKit.vp | 28 +++- App/lib/core/Gen/Class/ClassGenVTable.vp | 97 +++++++++--- App/lib/core/Gen/Func/FuncGenDef.vp | 6 +- 4 files changed, 222 insertions(+), 90 deletions(-) diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index ed1f3a8..a777edc 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -69,6 +69,12 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): # 4. bitcast to i8** to access the __vtable__ field (index 0). The vtable # pointer is always first, so a direct bitcast works even for opaque types. + # 前提:vtable_base 必须是**对象指针**。结构体值没有地址语义(bitcast struct -> ptr 是 + # 非法 IR,mlir-opt 报 "operand #0 must be ... non-aggregate type" 且远离调用点), + # 这里 fail-fast 暴露上游把值当接收者的 bug。 + if vtable_base.Ty is None or self.Gen.ExprGenBase.is_ptr_type(vtable_base.Ty) == 0: + self.Gen.TypeGenErr.fatal_error(None, f"virtual dispatch receiver of '{class_name}.{method_name}' is not a pointer: the receiver must be an object pointer (a bare class value has no address); write the variable as '{class_name}*' or pass '&obj'", "TypeError") + return None vtable_slot: mlirlite.Value* = mlirlite.build_bitcast(builder, vtable_base, i8_ptr_ptr_ty) if vtable_slot is None: return None @@ -199,8 +205,28 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): if lookup_name is None: return None viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name) - # Find the function (supports SHA1 suffix matching). - found_func: mlirlite.Function* = self.find_func_in_module(mod, lookup_name) + # 重载:同名方法 >=2 时按实参决议(param_base=1 跳过 self);短名同步换成带签名后缀的形式。 + found_func: mlirlite.Function* = None + m_ovl_done2: int = 0 + if trans is not None and trans._funcs is not None: + if self.Gen.CallGenLeafKit.count_owned_overloads(trans._funcs, trans._func_count, lookup_name, trans.ModuleSha1, 1) >= 2: + m_argc2: sizet = 0 + if arg_nodes is not None: m_argc2 = len(arg_nodes) + ovl_m2: mlirlite.Function* = self.Gen.CallGenLeafKit.resolve_overload_call(pool, trans, trans._funcs, trans._func_count, lookup_name, trans.ModuleSha1, arg_nodes, m_argc2, node, 1, 1) + if ovl_m2 is None: return None + fn_sym2: str = mlirlite.function_get_name(ovl_m2) + if fn_sym2 is not None: + pre_len2: sizet = 17 + string.strlen(lookup_name) + if string.strlen(fn_sym2) > pre_len2: + sfx2: str = fn_sym2 + pre_len2 + short_cap2: sizet = string.strlen(method_name) + string.strlen(sfx2) + 1 + short_buf2: str = pool.alloc(short_cap2) + if short_buf2 is not None: + viperlib.snprintf(short_buf2, short_cap2, "%s%s", method_name, sfx2) + method_name = short_buf2 + found_func = ovl_m2 + m_ovl_done2 = 1 + if m_ovl_done2 == 0: found_func = self.find_func_in_module(mod, lookup_name) # Determine the call name. call_name: str = lookup_name @@ -353,7 +379,10 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): mdf_param_count = mdf_entry.ParamCount # Always query the global default table: the snapshot-rebuild branch below # depends on mdf_gd (the only holder of stdlib constant snapshots). - mdf_gd: GenTypes.FuncDefaultsEntry* = self.Gen.CallGenLeafKit.find_func_defaults(lookup_name) + # 必须带方法所属模块 SHA1:裸名跨模块会命中同名方法(`run`/`init`)的签名。 + mdf_gd: GenTypes.FuncDefaultsEntry* = None + cmop_sha: str = self.Gen.StructGen.get_struct_sha1(class_name) + if cmop_sha is not None: mdf_gd = self.Gen.CallGenLeafKit.find_func_defaults(lookup_name, cmop_sha) if mdf_defaults is None and mdf_gd is not None: mdf_defaults = mdf_gd.Defaults mdf_default_count = mdf_gd.DefaultCount @@ -395,8 +424,8 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): mdf_offset: int = mdf_param_count - mdf_default_count mdf_pi: int = extra_count while mdf_pi < mdf_param_count: - # Prefer the defined default value. - if mdf_defaults is not None and mdf_default_count > 0 and mdf_pi >= mdf_offset: + # Prefer the defined default value(快照在 mdf_gd 里,AST 列表可能已被回收)。 + if (mdf_defaults is not None or mdf_gd is not None) and mdf_default_count > 0 and mdf_pi >= mdf_offset: mdf_di: int = mdf_pi - mdf_offset if mdf_di < mdf_default_count: # Rebuild from the stdlib constant snapshot (the pool AST may be @@ -449,7 +478,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): if seg_buf_v is not None: string.strncpy(seg_buf_v, vpi_pa + seg_start_v, seg_len_v) seg_buf_v[seg_len_v] = '\0' - rty_v: mlirlite.LLVMType* = self.Gen.CallGenBase._vpi_resolve_param_annot_ty(pool, seg_buf_v) + rty_v: mlirlite.LLVMType* = self.Gen.CallGenBase._vpi_resolve_param_annot_ty(pool, seg_buf_v, vpi_sha1_p) if rty_v is not None: pty_v = rty_v stdlib.free(seg_buf_v) zero_val_v: mlirlite.Value* = mlirlite.ConstZero(pool, pty_v) @@ -699,8 +728,33 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): if lookup_name is None: return None viperlib.snprintf(lookup_name, lk_cap, "%s.%s", class_name, method_name) + # 重载:类内同名方法 >=2 时按实参类型决议(候选签名第 0 个形参是 self,param_base=1), + # prefix 模式让候选集覆盖 "Class.m" 与 "Class.m_v{hash}"。决议成功后把 method_name + # 换成带签名后缀的短名,后续虚调用按它取槽(与 VTableMethods 里存的一致)。 + found_func: mlirlite.Function* = None + m_overload_done: int = 0 + if trans is not None and trans._funcs is not None: + if self.Gen.CallGenLeafKit.count_owned_overloads(trans._funcs, trans._func_count, lookup_name, trans.ModuleSha1, 1) >= 2: + m_args_o: list[ast.AST*]* = cl.args + m_argc_o: sizet = 0 + if m_args_o is not None: m_argc_o = len(m_args_o) + ovl_m: mlirlite.Function* = self.Gen.CallGenLeafKit.resolve_overload_call(pool, trans, trans._funcs, trans._func_count, lookup_name, trans.ModuleSha1, m_args_o, m_argc_o, cl.func, 1, 1) + if ovl_m is None: return None + fn_sym_o: str = mlirlite.function_get_name(ovl_m) + if fn_sym_o is not None: + pre_len_o: sizet = 17 + string.strlen(lookup_name) + if string.strlen(fn_sym_o) > pre_len_o: + sfx_o: str = fn_sym_o + pre_len_o + short_cap: sizet = string.strlen(method_name) + string.strlen(sfx_o) + 1 + short_buf_o: str = pool.alloc(short_cap) + if short_buf_o is not None: + viperlib.snprintf(short_buf_o, short_cap, "%s%s", method_name, sfx_o) + method_name = short_buf_o + found_func = ovl_m + m_overload_done = 1 + # Find the function (supports SHA1 suffix matching). - found_func: mlirlite.Function* = self.find_func_in_module(mod, lookup_name) + if m_overload_done == 0: found_func = self.find_func_in_module(mod, lookup_name) # Generic template methods: the specialized symbol carries type args # ({sha1}.list[int].get), so match by suffix using mc_entry.Name. @@ -823,9 +877,6 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): # Generic specialization: the specialized function may not be # registered yet; trigger it (idempotent), then re-lookup. gen_done: int = 0 - # Hoist and initialize the text.ll result explicitly: an uninitialized - # value would overwrite a correct call_ret_ty with junk. - looked_up_ty: mlirlite.LLVMType* = None if class_name is not None: lbkt_g: str = string.strchr(class_name, '[') if lbkt_g is not None: @@ -891,8 +942,13 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): if lrt_lm is not None: call_ret_ty = lrt_lm gen_done = 1 + # Return-type resolution for the not-found case. Everything lives in ONE block + # (declaration + all reads): splitting the declaration into a nested block and + # reading it outside silently loses the hit (现场: map_builtin_type 的 vpi 命中 + # 被丢弃,随后退到 text.ll 并误报缺失). + if gen_done == 0: # Try vpi for the real return type (authoritative annotation; no hardcoded whitelist). - vpi_ret_ty: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, ret_anc_sha1, ret_anc_name, method_name) + vpi_ret_ty: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, ret_anc_sha1, ret_anc_name, method_name, trans) # Retry along the MRO ancestor chain: the method may be defined in a # base class in another module; query vpi per ancestor class + sha1. if vpi_ret_ty is None: @@ -902,56 +958,36 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): anc_v2: str = rawptr(mro_v2[mi_v2]) if anc_v2 is not None and string.strcmp(anc_v2, class_name) != 0: anc_sha1_v2: str = self.Gen.StructGen.get_struct_sha1(anc_v2) - vpi_ret_ty = self._lookup_method_ret_ty_via_vpi(pool, anc_sha1_v2, anc_v2, method_name) + vpi_ret_ty = self._lookup_method_ret_ty_via_vpi(pool, anc_sha1_v2, anc_v2, method_name, trans) if vpi_ret_ty is not None: ret_anc_name = anc_v2 ret_anc_sha1 = anc_sha1_v2 mi_v2 += 1 - if gen_done == 0 and vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty - elif gen_done == 0: - # Fallback: find the real return type from the text.ll define line so - # pointer returns are not truncated by the default i32. - looked_up_ty = self._LookupMethodRetTyFromTextLL(pool, ret_anc_sha1, ret_anc_name, method_name) - # Retry along the MRO, using each ancestor class name + its module sha1. - if looked_up_ty is None: - mro_t2: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name) - mi_t2: int = 1 - while mro_t2 is not None and mro_t2[mi_t2] != 0 and looked_up_ty is None: - anc_t2: str = rawptr(mro_t2[mi_t2]) - if anc_t2 is not None and string.strcmp(anc_t2, class_name) != 0: - anc_sha1_t2: str = self.Gen.StructGen.get_struct_sha1(anc_t2) - looked_up_ty = self._LookupMethodRetTyFromTextLL(pool, anc_sha1_t2, anc_t2, method_name) - if looked_up_ty is not None: - ret_anc_name = anc_t2 - ret_anc_sha1 = anc_sha1_t2 - mi_t2 += 1 - if looked_up_ty is not None: call_ret_ty = looked_up_ty + if vpi_ret_ty is not None: + call_ret_ty = vpi_ret_ty else: - # Phase A: the dependency's text.ll is not generated yet, so keep - # the default type; ret_anc_sha1 None also means not ready. - if ret_anc_sha1 is None or (ret_anc_sha1 is not None and self._dep_text_ll_exists(pool, ret_anc_sha1) == 0): - # Dependency not ready: keep default i32, not Void, since - # build_call(Void) returns None and breaks expression translation. - call_ret_ty = mlirlite.Int32(pool) + # Fallback: find the real return type from the text.ll define line so + # pointer returns are not truncated by the default i32. + looked_up_ty: mlirlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, ret_anc_sha1, ret_anc_name, method_name) + # Retry along the MRO, using each ancestor class name + its module sha1. + if looked_up_ty is None: + mro_t2: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name) + mi_t2: int = 1 + while mro_t2 is not None and mro_t2[mi_t2] != 0 and looked_up_ty is None: + anc_t2: str = rawptr(mro_t2[mi_t2]) + if anc_t2 is not None and string.strcmp(anc_t2, class_name) != 0: + anc_sha1_t2: str = self.Gen.StructGen.get_struct_sha1(anc_t2) + looked_up_ty = self._LookupMethodRetTyFromTextLL(pool, anc_sha1_t2, anc_t2, method_name) + if looked_up_ty is not None: + ret_anc_name = anc_t2 + ret_anc_sha1 = anc_sha1_t2 + mi_t2 += 1 + if looked_up_ty is not None: call_ret_ty = looked_up_ty else: - # The method may be defined in an MRO ancestor (Mixin) module - # not yet translated; if any ancestor is unready, defer. - mro_p2: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name) - mi_p2: int = 1 - anc_missing_p2: int = 0 - while mro_p2 is not None and mro_p2[mi_p2] != 0: - anc_p2: str = rawptr(mro_p2[mi_p2]) - if anc_p2 is not None: - anc_sha1_p2: str = self.Gen.StructGen.get_struct_sha1(anc_p2) - if anc_sha1_p2 is None or self._dep_text_ll_exists(pool, anc_sha1_p2) == 0: - anc_missing_p2 = 1 - break - mi_p2 += 1 - if anc_missing_p2 != 0: - # Dependency not ready: keep default i32 (build_call(Void) - # returns None and breaks expression translation). - call_ret_ty = mlirlite.Int32(pool) - else: call_ret_ty = self._fail_method_ret_ty(method_name, cl, class_name, ret_anc_name, ret_anc_sha1) + # vpi 存根在 Phase A 前已为所有源文件预生成,因此"vpi 查不到"是真实错误, + # 不是"依赖尚未翻译"。静默退化成 i32 会把指针返回值截断,并在下游赋值处 + # 变成一个误导性的 CastError(现场:AssignGenMixin L390 map_builtin_type)。 + call_ret_ty = self._fail_method_ret_ty(method_name, cl, class_name, ret_anc_name, ret_anc_sha1, pool) # found_func missed but the return type resolved: build the cross-module # call name from the defining class to avoid undefined references. @@ -1193,6 +1229,11 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): mdf_defaults: rawptr = None mdf_default_count: int = 0 mdf_param_count: int = 0 + # 方法所属模块 SHA1:后面所有 defaults 查找都必须带上它,绝不跨模块按名搜 + # (`run`/`init` 这类方法名在多个模块里都有,裸名会拿错签名去补参)。 + vpi_sha1_p: str = None + if mc_entry is not None and mc_entry.ModuleSha1 is not None: vpi_sha1_p = mc_entry.ModuleSha1 + else: vpi_sha1_p = self.Gen.StructGen.get_struct_sha1(class_name) if trans is not None and trans._funcs is not None: mdf_entry: GenTypes.FuncEntry* = self.Gen.CallGenLeafKit.find_func_entry_in_table(trans._funcs, trans._func_count, lookup_name) if mdf_entry is not None: @@ -1201,19 +1242,26 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): mdf_param_count = mdf_entry.ParamCount # Always query the global default table: the snapshot-rebuild branch below # depends on mdf_gd (the only holder of stdlib constant snapshots). - mdf_gd: GenTypes.FuncDefaultsEntry* = self.Gen.CallGenLeafKit.find_func_defaults(lookup_name) - if mdf_defaults is None and mdf_gd is not None: + mdf_gd: GenTypes.FuncDefaultsEntry* = None + if vpi_sha1_p is not None: mdf_gd = self.Gen.CallGenLeafKit.find_func_defaults(lookup_name, vpi_sha1_p) + if mdf_gd is not None: mdf_defaults = mdf_gd.Defaults mdf_default_count = mdf_gd.DefaultCount mdf_param_count = mdf_gd.ParamCount + # SHA1 隔离兜底:表里没有该方法(或表里没有缺省值快照)时,直接读它所属模块 .vpi 的签名。 + # 只读 vpi_sha1_p 这一个模块,不跨模块搜索;同名多处定义时该查找返回未命中(不猜)。 + if (mdf_gd is None or mdf_param_count == 0) and vpi_sha1_p is not None: + mdf_gd = self.Gen.CallGen._defaults_entry_from_vpi(pool, vpi_sha1_p, lookup_name) + if mdf_gd is not None: + mdf_defaults = None + mdf_default_count = mdf_gd.DefaultCount + mdf_param_count = mdf_gd.ParamCount + # Cross-module stub fallback: when the dependency's vpi is not loaded the # table lacks ParamCount; fill it from the vpi text cache. vpi_pa: str = None if mdf_param_count == 0 and found_func is None: - vpi_sha1_p: str = None - if mc_entry is not None and mc_entry.ModuleSha1 is not None: vpi_sha1_p = mc_entry.ModuleSha1 - else: vpi_sha1_p = self.Gen.StructGen.get_struct_sha1(class_name) if vpi_sha1_p is not None: vpi_pa = self.Gen.CallGenBase._vpi_method_param_annot(pool, vpi_sha1_p, class_name, method_name) if vpi_pa is not None and vpi_pa[0] != '\0': @@ -1243,8 +1291,8 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): mdf_offset: int = mdf_param_count - mdf_default_count mdf_pi: int = arg_count_eff while mdf_pi < mdf_param_count: - # Prefer the defined default value. - if mdf_defaults is not None and mdf_default_count > 0 and mdf_pi >= mdf_offset: + # Prefer the defined default value(快照在 mdf_gd 里,AST 列表可能已被回收)。 + if (mdf_defaults is not None or mdf_gd is not None) and mdf_default_count > 0 and mdf_pi >= mdf_offset: mdf_di: int = mdf_pi - mdf_offset if mdf_di < mdf_default_count: # Rebuild from the stdlib constant snapshot (the pool AST may be @@ -1301,7 +1349,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): if seg_buf_v is not None: string.strncpy(seg_buf_v, vpi_pa + seg_start_v, seg_len_v) seg_buf_v[seg_len_v] = '\0' - rty_v: mlirlite.LLVMType* = self.Gen.CallGenBase._vpi_resolve_param_annot_ty(pool, seg_buf_v) + rty_v: mlirlite.LLVMType* = self.Gen.CallGenBase._vpi_resolve_param_annot_ty(pool, seg_buf_v, vpi_sha1_p) if rty_v is not None: pty_v = rty_v stdlib.free(seg_buf_v) zero_val_v: mlirlite.Value* = mlirlite.ConstZero(pool, pty_v) @@ -1312,6 +1360,11 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): total_count += 1 mdf_pi += 1 + # 补完仍少于声明形参:直接给 VPC Diag,不要放行到 mlir-opt 的 + # "incorrect number of operands"(那时已经丢失函数名与差几个实参的信息)。 + if found_func is None and mdf_param_count > 0 and (total_count - 1) < mdf_param_count: + self.Gen.TypeGenErr.fatal_error(cl, f"method call '{lookup_name}' passes {total_count - 1} argument(s) but the definition declares {mdf_param_count} parameter(s) (self excluded)", "TypeError") + # Virtual dispatch: if the class has a vtable and the method is in it, call # indirectly (does not depend on found_func; reuses the earlier mc_entry). mc_has_vt: int = 0 @@ -1671,7 +1724,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): sp_ty_sp: mlirlite.LLVMType* = self._special_method_ret_ty(pool, anc, method_name) if sp_ty_sp is not None: ret_ty_sp = sp_ty_sp else: - vpi_ret_sp: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, anc_sha1_sp, anc, method_name) + vpi_ret_sp: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, anc_sha1_sp, anc, method_name, trans) if vpi_ret_sp is not None: ret_ty_sp = vpi_ret_sp else: looked_sp: mlirlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, anc_sha1_sp, anc, method_name) diff --git a/App/lib/core/Gen/Call/CallGenLeafKit.vp b/App/lib/core/Gen/Call/CallGenLeafKit.vp index bdfa1b5..6c697fe 100644 --- a/App/lib/core/Gen/Call/CallGenLeafKit.vp +++ b/App/lib/core/Gen/Call/CallGenLeafKit.vp @@ -1023,8 +1023,22 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin): # 任一实参类型无法静态确定时,候选 >=2 直接 fail-fast(必须显式强转)。 # ============================================================ + # _overload_name_matches - 候选名匹配:exact 模式要求同名;prefix 模式接受 + # "键" 本身或 "键_v{hash}"(方法重载:类内同名方法的表键带签名后缀)。 + def _overload_name_matches(self, entry_name: str, key: str, prefix_mode: int) -> int: + """函数表项名是否属于该重载组;1=是""" + if entry_name is None or key is None: return 0 + if prefix_mode == 0: + if string.strcmp(entry_name, key) == 0: return 1 + return 0 + kn: sizet = string.strlen(key) + if string.strncmp(entry_name, key, kn) != 0: return 0 + if entry_name[kn] == '\0': return 1 + if entry_name[kn] == '_' and entry_name[kn + 1] == 'v': return 1 + return 0 + # count_owned_overloads - 统计某名字在本模块下的函数表项数 - def count_owned_overloads(self, funcs: GenTypes.FuncEntry*, func_count: int, name: str, owner_sha1: str) -> int: + def count_owned_overloads(self, funcs: GenTypes.FuncEntry*, func_count: int, name: str, owner_sha1: str, prefix_mode: int = 0) -> int: """同名且符号属于 owner_sha1 模块的函数表项个数""" if funcs is None or name is None: return 0 entry_size: sizet = GenTypes.FuncEntry.__sizeof__() @@ -1032,7 +1046,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin): for i in range(int(func_count)): entry: GenTypes.FuncEntry* = GenTypes.FuncEntry*(uint64(funcs) + uint64(i * entry_size)) if entry is None or entry.Name is None or entry.Func is None: continue - if string.strcmp(entry.Name, name) != 0: continue + if self._overload_name_matches(entry.Name, name, prefix_mode) == 0: continue if owner_sha1 is not None: fn_nm: str = mlirlite.function_get_name(entry.Func) if fn_nm is None: continue @@ -1214,8 +1228,10 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin): return better # resolve_overload_call - 在候选集中决议唯一的重载;歧义/无法定型时 fail-fast - def resolve_overload_call(self, pool: memhub.MemBuddy*, trans: HT.Translator*, funcs: GenTypes.FuncEntry*, func_count: int, name: str, owner_sha1: str, cargs: list[ast.AST*]*, can: sizet, call_node: ast.AST*) -> mlirlite.Function*: - """按实参静态类型选择唯一最优重载;返回 None 表示已报错""" + def resolve_overload_call(self, pool: memhub.MemBuddy*, trans: HT.Translator*, funcs: GenTypes.FuncEntry*, func_count: int, name: str, owner_sha1: str, cargs: list[ast.AST*]*, can: sizet, call_node: ast.AST*, prefix_mode: int = 0, param_base: int = 0) -> mlirlite.Function*: + """按实参静态类型选择唯一最优重载;返回 None 表示已报错。 + prefix_mode=1 用于方法(表键 "Class.m" / "Class.m_v{hash}"); + param_base=1 表示候选签名第 0 个形参是 self,需跳过。""" if funcs is None or name is None: return None entry_size: sizet = GenTypes.FuncEntry.__sizeof__() argv_n: int = int(can) @@ -1228,7 +1244,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin): for i in range(int(func_count)): entry: GenTypes.FuncEntry* = GenTypes.FuncEntry*(uint64(funcs) + uint64(i * entry_size)) if entry is None or entry.Name is None or entry.Func is None: continue - if string.strcmp(entry.Name, name) != 0: continue + if self._overload_name_matches(entry.Name, name, prefix_mode) == 0: continue if owner_sha1 is not None: fn_nm_c: str = mlirlite.function_get_name(entry.Func) if fn_nm_c is None: continue @@ -1295,7 +1311,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin): break r = 3 else: - p: TSpec.TypeSpec* = TSpec.SpecParamAt(e.Spec, ai) + p: TSpec.TypeSpec* = TSpec.SpecParamAt(e.Spec, param_base + ai) r = self._overload_rank((TSpec.TypeSpec*)(uint64(argspec[ai])), p) if r < 0: good = 0 diff --git a/App/lib/core/Gen/Class/ClassGenVTable.vp b/App/lib/core/Gen/Class/ClassGenVTable.vp index a71d8a7..f5637ee 100644 --- a/App/lib/core/Gen/Class/ClassGenVTable.vp +++ b/App/lib/core/Gen/Class/ClassGenVTable.vp @@ -6,6 +6,7 @@ import mlirlite import memhub import string import stdio +import stdlib import viperlib import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT @@ -16,7 +17,7 @@ import lib.core.Gen.GenApi as GenApi import lib.core.Gen.DebugInfoGen as DebugInfoGen -# Method metadata flag: TLS (@t.TLS decorator; local copy avoids a cross-module CDefine lookup). +# Method metadata flag: TLS (@t.TLS decorator; local copy avoids a cross-module DefineT lookup). define META_TLS = 32 # Vtable method tables are sized per class at collection time (parents' count + own methods); # there is deliberately no fixed VTBL_METHOD_MAX. @@ -53,8 +54,34 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): for i in name: if i == '.': return 1 return 0 - # _collect_vtable_methods - collect virtual methods (inherited first, then own) into the - # Vt* buffers and entry.VTableMethods/VTableMethodCount; returns 1 if any, else 0. + # _method_slot_name - 方法在函数表 / vtable 里的短名。 + # 类内同名方法 >=2 构成重载组时追加 "_v{参数签名hash}"(跳过 self);三者必须一致: + # 函数表键 "Class.m_v{hash}"、LLVM 符号 "{sha1}.Class.m_v{hash}"、vtable 槽名 "m_v{hash}"。 + def _method_slot_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, cd: ast.ClassDef*, fd: ast.FunctionDef*) -> str: + """方法短名(重载组带签名后缀);失败返回 None(已报错)""" + if fd is None or fd.name is None: return None + if cd is None or cd.children is None: return fd.name + if self.Gen.FuncGenDef._overload_name_count(cd.children, fd.name) < 2: return fd.name + if fd.type_params is not None: + self.Gen.TypeGenErr.fatal_error(fd, f"generic method '{fd.name}' cannot be overloaded (generic overloads are not supported)", "TypeError") + return None + # @staticmethod 没有 self;实例方法 / 类方法的第 0 个形参是 self,不参与签名 + skip_self: int = 1 + fm_s: int = self.Gen.ClassGenBase._extract_func_meta(fd.decorator_list) + if fm_s & GenTypes.META_STATIC != 0: skip_self = 0 + sig: str = self.Gen.FuncGenDef._overload_sig_text(pool, fd, trans, skip_self) + if sig is None: return None + h: str = TSpec.SpecSigHash16(sig) + stdlib.free(sig) + if h is None: return None + base_len: sizet = string.strlen(fd.name) + out: str = pool.alloc(base_len + 24) + if out is None: return None + viperlib.snprintf(out, base_len + 24, "%s_v%s", fd.name, h) + return out + + # _collect_vtable_methods - collect virtual methods (inherited first, then own) into the + # Vt* buffers and entry.VTableMethods/VTableMethodCount; returns 1 if any, else 0. def _collect_vtable_methods(self, trans: HT.Translator*, cd: ast.ClassDef*, class_name: str) -> int: """Collect virtual methods into entry.VTableMethods/VTableMethodCount.""" if trans is None or cd is None or class_name is None: return 0 @@ -98,6 +125,22 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): method_count: int = 0 + # 重载:先算出每个 def 的槽名(类内同名 >=2 时带 "_v{签名hash}")。 + # "继承槽覆盖判定"与"本类槽加入"共用这张表,两处名字必须完全一致, + # 否则重载方法要么被误判成覆盖、要么占两个槽。 + slot_names: sizet* = pool.alloc(8 * cn) + if slot_names is None: return 0 + string.memset(slot_names, 0, sizet(8 * cn)) + for si in range(int(cn)): + stmt_s: ast.AST* = children.get(si) + if stmt_s is None: continue + if stmt_s.kind() != ast.ASTKind.FunctionDef: continue + fd_s: ast.FunctionDef* = ast.FunctionDef*(stmt_s) + if fd_s is None or fd_s.name is None: continue + slot_nm_s: str = self._method_slot_name(pool, trans, cd, fd_s) + if slot_nm_s is None: return 0 + slot_names[si] = sizet(slot_nm_s) + # Inherit parent virtual methods first (stable slot order); overridden methods use the # subclass, others the parent; SHA1-aware lookup avoids cross-module same-name mismatches. vt_self_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1) @@ -132,8 +175,11 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): if stmt2 is None: continue if stmt2.kind() != ast.ASTKind.FunctionDef: continue fd2: ast.FunctionDef* = ast.FunctionDef*(stmt2) - if fd2 is not None and fd2.name is not None: - if string.strcmp(fd2.name, pmethod_name) == 0: + # 覆盖判定按"槽名"比较(重载组带签名后缀):只有签名相同的同名方法才算覆盖 + fd2_slot: str = None + if slot_names[ci2] != 0: fd2_slot = rawptr(slot_names[ci2]) + if fd2 is not None and fd2_slot is not None: + if string.strcmp(fd2_slot, pmethod_name) == 0: child_overrides = 1 # Check whether the overriding subclass method is marked @t.NoVTable. if fd2.decorator_list is not None: @@ -191,11 +237,13 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): # stay loud rather than writing past the PermPool array. self.Gen.TypeGenErr.fatal_error(cd, f"internal error: vtable of '{class_name}' exceeded its computed capacity {vt_cap}", "InternalCompilerError") - mname: str = fd.name - # Skip constructors and special methods (they must not enter the vtable). - if string.strcmp(mname, "__init__") == 0: continue - if string.strcmp(mname, "__before_init__") == 0: continue - if string.strcmp(mname, "__new__") == 0: continue + mname: str = rawptr(slot_names[ci]) + if mname is None: mname = fd.name + # Skip constructors and special methods (they must not enter the vtable). + # 用 fd.name 判断:重载组的槽名带后缀,用槽名会漏判把 __init__ 塞进 vtable。 + if string.strcmp(fd.name, "__init__") == 0: continue + if string.strcmp(fd.name, "__before_init__") == 0: continue + if string.strcmp(fd.name, "__new__") == 0: continue # Function-level decorator filter: decide whether the method belongs in the vtable. if self.Gen.ClassGenBase._should_method_be_virtual(cd, fd, trans) == 0: continue # Skip if already inherited (avoid duplicates). @@ -402,8 +450,8 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): # # 「带 virtual 标记」在本工程里没有独立的「虚类」位,统一走 entry.HasVTable==1: # - 该标记由 ClassGenDef 依据 ClassGenBase._detect_vtable_status 置位(ClassGenDef.vp:487); - # - _detect_vtable_status 覆盖:类级 @t.CVTable(`virtual class` 的语法糖,parser.vp:556-621 合成)、 - # 方法级 @t.CVTable(`virtual def` 同款合成)、以及继承链上父类 HasVTable==1 的推导 + # - _detect_vtable_status 覆盖:类级 @t.VTableT(`virtual class` 的语法糖,parser.vp:556-621 合成)、 + # 方法级 @t.VTableT(`virtual def` 同款合成)、以及继承链上父类 HasVTable==1 的推导 # (ClassGenBase.vp:883-916)。 # 即「显式 virtual 标记 或 由虚父类推导出的虚表」这一口径,且不含纯数据类。 # @@ -546,7 +594,14 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): elif func_meta & GenTypes.META_PROP_DELETER != 0: mn_len2: sizet = string.strlen(method_name_buf) if mn_len2 + 5 < 128: string.strcat(method_name_buf, "$del") - method_name = method_name_buf + method_name = method_name_buf + + # 重载:类内同名方法 >=2 时短名追加 "_v{签名hash}",使符号、函数表键与 vtable 槽名一致。 + # 属性 setter/deleter 已带 $set/$del 后缀,不参与重载。 + if func_meta & GenTypes.META_PROP_SETTER == 0 and func_meta & GenTypes.META_PROP_DELETER == 0: + slot_nm_m: str = self._method_slot_name(pool, trans, cd, fd) + if slot_nm_m is None: return 0 + method_name = slot_nm_m is_static_m: int = 0 is_class_m: int = 0 @@ -571,9 +626,9 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): self.Gen.TypeGenResolve.reject_ref_marker_position(fd.returns, imported_modules, "a method return type") ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0) if ret_ty is None and fd.returns is not None: - # t.State / t.CExport / t.CExtern markers mean Void: such annotations carry no real return + # t.State / t.ExportT / t.ExternT markers mean Void: such annotations carry no real return # type (written as " | t."); only the marker itself reaches here. - if self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0) != 0 or self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CExport", 0) != 0 or self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CExtern", 0) != 0: + if self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0) != 0 or self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "ExportT", 0) != 0 or self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "ExternT", 0) != 0: ret_ty = mlirlite.Void(pool) else: # [S4 fail-fast] return annotation failed to resolve: do not fall back to infer_return_type (silent mismatch). @@ -666,7 +721,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): self.Gen.TypeGenErr.fatal_error(arg, f"method '{class_name}.{method_name}' param '{arg.arg}' type annotation failed to resolve", "NameError") return 0 # Bare-class param -> Ptr(struct) (objects are pointers; by-value would dangle after append). - # CArray param -> element pointer ty* (C array decay, same as function params). + # ArrayT param -> element pointer ty* (C array decay, same as function params). match resolved: case mlirlite.LLVMType.Void(): # void is not a legal param type (only void* and typedef opaque structs); void* @@ -824,7 +879,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): self.Gen.TypeGenErr.fatal_error(arg2, f"method '{class_name}.{method_name}' argument '{arg2.arg}' failed to resolve type", "NameError") resolved2 = i32_ty # Unreachable (fatal_error already exits). if resolved2 is not None: - # Keep in sync with add_param: bare class -> Ptr(struct) (objects are pointers); CArray -> element pointer ty*. + # Keep in sync with add_param: bare class -> Ptr(struct) (objects are pointers); ArrayT -> element pointer ty*. match resolved2: case mlirlite.LLVMType.Void(): # void is not a legal param type (same rule as above). @@ -1046,7 +1101,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): self_val: mlirlite.Value* = mlirlite.SSAValue(pool, self_ptr_ty, "%self") # Zero-fill: store zeroinitializer into %self. Empty structs ({}) cannot use zeroinitializer - # (invalid type for null constant), so field-less classes skip it; @t.CVTable classes always have a slot. + # (invalid type for null constant), so field-less classes skip it; @t.VTableT classes always have a slot. struct_fcount_zi: int = mlirlite.get_struct_fcount(struct_ty) if struct_fcount_zi > 0: zero: mlirlite.Value* = mlirlite.ConstZero(pool, struct_ty) @@ -1122,7 +1177,11 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): fname_dbg_v: str = fe.Name if fname_dbg_v is None: fname_dbg_v = "?" viperlib.snprintf(arr_ctx, 192, "class %s field %s: ", class_name, fname_dbg_v) - self.Gen.AssignGen._init_global_array_from_list(func_builder, pool, mod, field_ptr_arr, fe.DefaultVal, trans, arr_ctx) + # arr_ty_opt = fe.Ty: the GEP result may carry no pointee type, and the helper + # used to bail out silently, leaving the field zero-initialized (sha1.state). + arr_init_rc: int = self.Gen.AssignGen._init_global_array_from_list(func_builder, pool, mod, field_ptr_arr, fe.DefaultVal, trans, arr_ctx, 0, fe.Ty) + if arr_init_rc != 0: + self.Gen.TypeGenErr.fatal_error(fe.DefaultVal, f"class {class_name} field {fe.Name}: array default initializer could not be emitted (the declared array type or the list literal was rejected)", "TypeError") continue # Translate the default-value expression. default_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(func_builder, pool, mod, fe.DefaultVal, None, 0, trans) diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index 6faaab5..4ba5908 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -102,7 +102,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase): # 用注解(而不是 LLVM 类型)生成:LLVM 没有符号性,int/uint 会撞;走 SpecFromLLVMType + # SpecWithSign 后别名展开为 i32/u32,指针是 p:i32,结构体是 st:{模块sha1}.类名 # (跨模块引用天然展开,不会同名相撞)。可变参数追加 ",..."。 - def _overload_sig_text(self, pool: memhub.MemBuddy*, fd: ast.FunctionDef*, trans: HT.Translator*) -> str: + def _overload_sig_text(self, pool: memhub.MemBuddy*, fd: ast.FunctionDef*, trans: HT.Translator*, skip_first: int = 0) -> str: """返回规范形参签名文本(stdlib 缓冲,调用方 free);失败时已报错并返回 None""" if pool is None or fd is None or trans is None: return None arena: TSpec.TypeSpecArena* = self.Gen.TypeGenBase.ensure_spec_arena() @@ -122,9 +122,13 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase): alist: list[ast.AST*]* = ags.args an: sizet = len(alist) ai: sizet = 0 + pi: int = 0 while ai < an: arg: ast.Arg* = ast.Arg*(alist.get(ai)) ai += 1 + pi += 1 + # 方法重载跳过 self(所有重载共享同一个 self,不参与签名) + if pi <= skip_first: continue if arg is None or arg.arg is None: continue ty: mlirlite.LLVMType* = None if arg.annotation is not None: