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:
1 parent
c89a0d747c
commit
faea680116
4 files changed
+222
-90
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user