feat(overload): 类方法与虚函数重载(符号/函数表键/vtable 槽名一致)

类内同名方法 >=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 换成带后缀的短名,使虚调用取到正确槽位。注:本提交含此前未提交的既有改动。
This commit is contained in:
TermiNexus committed 2026-10-24 17:02:33 +08:00
1 parent c89a0d747c
commit faea680116
4 files changed
+222 -90

No files matched your search

+117 -64
View File
@@ -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)
+22 -6
View File
@@ -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
+78 -19
View File
@@ -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 "<original> | t.<marker>"); 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)
+5 -1
View File
@@ -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: