feat(gen): willFree 覆盖关键字实参、方法调用与取地址三条路径

关键字/混合实参:CallGenMixin._MarkWillFreeKwargs 按形参序(位置实参占前 can 个形参,其余按关键字名匹配)标记。方法调用:CallGenCall._translate_method_call 的 kwargs 与位置两条循环,实参链含 self 故用户第 ai 个实参对应 bit (ai+1);新增 GenApi/StructGenMixin.get_param_willfree_method(查 Class.method 并沿 MRO 回退)。取地址:ExprGenOps 的 AddrOf 分支对已失效变量 fail-fast。
This commit is contained in:
TermiNexus committed 2026-10-08 21:39:53 +08:00
1 parent d921dcd9e7
commit b60ce06335
5 files changed
+97 -1

No files matched your search

+30
View File
@@ -526,6 +526,20 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if uint64(self_ptr.Ty) != uint64(cmop_i8_ptr_ty): cmop_bar_ptr = mlirlite.build_bitcast(builder, self_ptr, cmop_i8_ptr_ty)
if cmop_bar_ptr is not None: mlirlite.build_load(builder, cmop_i8_ty, cmop_bar_ptr, 1)
return call_res_cmop
def _MarkWillFreeArgName(self, trans: HT.Translator*, arg_node: ast.AST*) -> int:
"""P2 willFree:把实参变量槽标为"已失效";只处理简单变量名(1=标记过 / 0=未处理)。
与模块级调用同一口径(VarEntry.Freed):该槽其后被读 / 取地址 / 再传时,由
ExprGenName.translate_name_value 或 ExprGenOps 的 AddrOf 分支报 LifetimeError。"""
if trans is None or arg_node is None: return 0
if arg_node.kind() != ast.ASTKind.Name: return 0
nm: ast.Name* = ast.Name*(arg_node)
if nm.id is None: return 0
ent: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if ent is None: return 0
ent.Freed = 1
return 1
# _translate_method_call - translate obj.method(args) into a call with the
# object pointer (obj_ptr, an alloca of Ptr(StructTy)) as the self argument.
@@ -553,6 +567,16 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if ((esc_mask_mc >> int(ai_mc + 1)) & 1) != 0:
GenApi.fail_if_stack_addr_escape(cargs_mc.get(ai_mc), "an argument bound to a method parameter that the callee escapes or returns", trans, dummy_ptr_mc)
# P2 willFree:方法级标注查询(本文件表 → 跨模块 .vpi 回退)。
# 实参链含 self(bit 0),故用户第 ai 个实参对应形参位 (ai+1);
# 标记动作在**实参翻译之后**(见下方 kwargs / 位置实参两条循环)。
wf_mask_mc: int = 0
if cargs_mc is not None:
wf_mask_mc = GenApi.get_param_willfree_method(pool, class_name, method_name)
if wf_mask_mc == 0:
sha_wf_mc: str = self.Gen.StructGen.get_struct_sha1(class_name)
wf_mask_mc = self.Gen.CallGenBase.lookup_willfree_via_vpi(pool, sha_wf_mc, class_name, method_name)
# list generic container method: dispatch to _translate_list_builtin_call inline
# when the method is a list builtin; use the receiver's actual struct name.
lb_src: str = class_name
@@ -1048,6 +1072,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
mlirlite.value_set_next(tail, arg_val_k)
tail = arg_val_k
arg_count_eff += 1
# P2 willFree:该形参位带标注且实参是变量名 → 标记失效(实参已翻译完)
if wf_mask_mc != 0 and arg_node_k is not None and ((wf_mask_mc >> (pi_k + 1)) & 1) != 0:
self._MarkWillFreeArgName(trans, arg_node_k)
pi_k += 1
cur_param_k = mlirlite.param_get_next(cur_param_k)
else:
@@ -1100,6 +1127,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
mlirlite.value_set_next(tail, arg_val)
tail = arg_val
arg_count_eff += 1
# P2 willFree:该形参位带标注且实参是变量名 → 标记失效(实参已翻译完)
if wf_mask_mc != 0 and ((wf_mask_mc >> int(ai + 1)) & 1) != 0:
self._MarkWillFreeArgName(trans, arg_node)
total_count: int = 1 + arg_count_eff
+35 -1
View File
@@ -61,6 +61,36 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# translate_call - translate Call(func, args) (module-level version).
def _MarkWillFreeKwargs(self, trans: HT.Translator*, wf_mask: int, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, found_func: mlirlite.Function*) -> int:
"""P2 willFree:关键字(或"位置+关键字"混合)实参路径的失效标记。
形参序必须与 _translate_call_with_kwargs 的遍历一致:位置实参占前 can 个形参,
其余按关键字名匹配到形参位。调用方必须在本函数**之后**——那时实参才翻译完。"""
if trans is None or found_func is None or wf_mask == 0: return 0
param_idx: sizet = 0
cur_param: mlirlite.Param* = mlirlite.function_get_param_head(found_func)
while cur_param is not None:
arg_node_kw: ast.AST* = None
if param_idx < can:
if cargs is not None: arg_node_kw = cargs.get(param_idx)
else:
pname_kw: str = mlirlite.param_get_name(cur_param)
if pname_kw is not None and keywords is not None:
ki_kw: sizet
for ki_kw in range(kw_count):
kw_node: ast.AST* = keywords.get(ki_kw)
if kw_node is None: continue
kw_el: ast.Keyword* = ast.Keyword*(kw_node)
if kw_el is None or kw_el.arg is None or kw_el.value is None: continue
if self.Gen.CallGenCall._kwarg_name_matches(kw_el.arg, pname_kw) == 1:
arg_node_kw = kw_el.value
break
if arg_node_kw is not None and ((wf_mask >> int(param_idx)) & 1) != 0:
self.Gen.CallGenCall._MarkWillFreeArgName(trans, arg_node_kw)
param_idx += 1
cur_param = mlirlite.param_get_next(cur_param)
return 0
def translate_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Translate a Call node and return the result Value."""
cl: ast.Call* = ast.Call*(node)
@@ -798,7 +828,11 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# Translate and link arguments; with keyword args and a known definition, match by parameter name.
if kw_count > 0 and found_func is not None and found_params is not None:
return self.Gen.CallGenLib._translate_call_with_kwargs(pool, builder, mod, func_name, found_func, cargs, can, keywords, kw_count, trans)
kw_res: mlirlite.Value* = self.Gen.CallGenLib._translate_call_with_kwargs(pool, builder, mod, func_name, found_func, cargs, can, keywords, kw_count, trans)
# P2 willFree:关键字实参路径——实参已在上面的调用里翻译完,此刻按形参序标记失效
if wf_mask != 0 and trans is not None:
self._MarkWillFreeKwargs(trans, wf_mask, cargs, can, keywords, kw_count, found_func)
return kw_res
head: mlirlite.Value* = None
tail: mlirlite.Value* = None
+4
View File
@@ -584,6 +584,10 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if uo.operand.kind() == ast.ASTKind.Name:
nm_ao: ast.Name* = ast.Name*(uo.operand)
if nm_ao.id is not None:
# P2 willFree:对已失效变量取地址 → fail-fast(与读取路径同一口径)
ve_ao: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_ao.id)
if ve_ao is not None and ve_ao.Freed != 0:
self.Gen.TypeGenErr.fatal_error(node, f"cannot take the address of '{nm_ao.id}': its value was released by a call annotated with @life(willFree(...)) and is no longer valid", "LifetimeError")
# self/cls object reference: return the object pointer itself (Ptr(Struct)), not the pointer variable's address (Ptr(Ptr(Struct))).
if string.strcmp(nm_ao.id, "self") == 0 or string.strcmp(nm_ao.id, "cls") == 0:
se_ao: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_ao.id)
+6
View File
@@ -418,6 +418,12 @@ def get_param_willfree(name: str) -> int:
return 0
def get_param_willfree_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法 willFree 掩码:查 "Class.method" 并沿 MRO 回退;无记录返回 0"""
if _g_gen is not None: return _g_gen.StructGen.get_param_willfree_method(pool, class_name, method_name)
return 0
# ---------------- find_struct_by_type (delegates to StructGenMixin.find_struct_by_type) ----------------
def find_struct_by_type(struct_ty: mlirlite.LLVMType*) -> GenTypes.StructEntry*:
"""Delegate to gen.find_struct_by_type"""
+22
View File
@@ -435,6 +435,28 @@ novirtual class StructGenMixin:
if idx < 0: return 0
return int(self.WfTable[idx * 2 + 1])
def get_param_willfree_method(self, pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法 willFree 掩码:查 "Class.method",再沿 MRO 祖先查;无记录返回 0。
与 get_param_escapes_method 同口径:刻意**不**回退裸名键——方法名与模块级函数名可能
重名,回退会把模块函数的掩码错用到方法调用上(产生误报)。查不到即视为无标注。"""
if method_name is None or class_name is None: return 0
key_q: str = self._EscKey2(pool, class_name, method_name)
if key_q is not None:
m_q: int = self.get_param_willfree(key_q)
if m_q != 0: return m_q
mro: sizet* = self.get_mro_chain(pool, class_name)
mi2: int = 1
while mro is not None and mro[mi2] != 0:
anc_m: str = rawptr(mro[mi2])
if anc_m is not None and string.strcmp(anc_m, class_name) != 0:
key_a: str = self._EscKey2(pool, anc_m, method_name)
if key_a is not None:
m_a: int = self.get_param_willfree(key_a)
if m_a != 0: return m_a
mi2 += 1
return 0
def _RegisterFuncWillFree(self, fd: ast.FunctionDef*, key: str) -> int:
"""登记函数的 willFree 掩码(无标注也登记 0,避免同名重复计算);1=失败 / 0=成功"""
if fd is None or key is None or self.WfTable is None: return 1