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:
1 parent
d921dcd9e7
commit
b60ce06335
5 files changed
+97
-1
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user