diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index 56d7b10..a67f0da 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -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 diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index 366cf83..e73093e 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -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 diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index 0e6b8b6..1e20b37 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -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) diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index 2f124ba..b2e4b6c 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -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""" diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 32cbd6b..40e127b 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -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