From bf5ecd7668b0832fbd2ac49166c0cf8616c119ad Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 9 Oct 2026 22:30:35 +0800 Subject: [PATCH] =?UTF-8?q?fix(lifetime):=20=E6=96=B9=E6=B3=95=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E8=A1=A5=E4=B8=8A"=E5=90=8C=E5=91=BD=E8=A3=81?= =?UTF-8?q?=E5=89=AA"=EF=BC=88=E6=AD=A4=E5=89=8D=E7=BC=BA=E5=A4=B1?= =?UTF-8?q?=EF=BC=89=E2=80=94=E2=80=94=20h.set=5Fq(l)=20=E4=B8=8D=E8=AF=A5?= =?UTF-8?q?=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现场:h: S* = new heap slife S()(生命期本就是 FRAME:作用域出口释放)+ l 是栈数组; h.set_q(l) 却报"escaping method parameter"。根因:同命裁剪只做在模块级函数路径,方法路径一直没接 (此前已明确告知"还差一半")。 新增 CallGenMixin._FrameArgsEscapeClearsMethod:位 0 = 接收者(self)、位 (i+1) = 用户第 i 个实参; 某位可清 ⇔ 该位对应实参是 FRAME/VALUE 且其余全部实参(含接收者)也在本帧。 CallGenCall 方法路径在 refine 之后、指令最终裁决之前套用;硬位仍用 get_param_escapes_hard_method 排除。 构造器路径暂未接(__init__ 的 self 是"正在构造的对象",此处拿不到接收者 AST 变量)。 --- App/lib/core/Gen/Call/CallGenCall.vp | 15 ++++++++++++- App/lib/core/Gen/Call/CallGenMixin.vp | 31 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index e2eb15a..68fb7ff 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -574,7 +574,20 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): # 跨过程常量特化(P3 收尾):同文件能拿到方法体时,用常量实参重算摘要(形参位含 self 偏移) if esc_mask_mc != 0: can_rf: sizet = len(cargs_mc) - esc_mask_mc = GenApi.refine_escape_mask_method(pool, class_name, method_name, cargs_mc, can_rf, esc_mask_mc) + esc_mask_mc = GenApi.refine_escape_mask_method(pool, class_name, method_name, cargs_mc, can_rf, esc_mask_mc) + # 同命裁剪(**方法路径,此前缺失**):只清"写进形参对象字段"那一类位 —— 若其余实参(含 + # 接收者)也都在本帧,则接收者与本帧同命、并不真的逃逸。现场:`h.set_q(l)`,h 是 + # `new heap slife S()`(生命期本就是 FRAME)、l 是栈数组 → 两都在本帧 → 不该报。 + if esc_mask_mc != 0: + hard_mc: int = GenApi.get_param_escapes_hard_method(pool, class_name, method_name) + fld_mc: int = esc_mask_mc & (~hard_mc) + if fld_mc != 0: + recv_mc: ast.AST* = None + if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + recv_mc = ast.Attribute*(cl.func).value + can_sf: sizet = len(cargs_mc) + cl_sfm: int = self.Gen.CallGen._FrameArgsEscapeClearsMethod(trans, recv_mc, cargs_mc, can_sf, fld_mc) + if cl_sfm != 0: esc_mask_mc = esc_mask_mc & (~cl_sfm) # 声明式指令最终裁决(@life(escape/noescape):unsafe 但强制),在推断/回退之后最后套用 f_mc: int = GenApi.get_param_escape_force_method(pool, class_name, method_name) n_mc: int = GenApi.get_param_noescape_force_method(pool, class_name, method_name) diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index c8bc623..209f687 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -60,6 +60,37 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase): return 0 + # _FrameArgsEscapeClearsMethod - 方法调用版"同命"裁剪(与函数版同口径,但位有 self 偏移) + # 位 0 = 接收者(self),位 (i+1) = 用户第 i 个实参。 + # 某位可清 ⇔ 该位对应的实参是帧生命期(FRAME/VALUE),且**其余全部实参(含接收者)也在本帧**。 + # 现场:`h.set_q(l)` 里 h = `new heap slife S()`(生命期本就是 FRAME:作用域出口释放)、 + # l 是栈数组 → 两都在本帧 → 不该报。 + def _FrameArgsEscapeClearsMethod(self, trans: HT.Translator*, recv_node: ast.AST*, cargs: list[ast.AST*]*, can: sizet, only_fld: int) -> int: + """返回方法调用中可安全清掉的位掩码(0 = 一位都清不掉)""" + if trans is None or only_fld == 0: return 0 + cleared: int = 0 + bi: int = 1 + while bi <= int(can): + if ((only_fld >> bi) & 1) == 0: + bi += 1 + continue + all_frame: int = 1 + if recv_node is not None: + lr: int = self.Gen.AssignGen.life_of_expr(trans, recv_node, None) + if lr != GenTypes.LIFE_FRAME and lr != GenTypes.LIFE_VALUE: all_frame = 0 + j: sizet = 0 + while j < can: + if all_frame == 0: break + if int(j) != bi - 1: + a_j: ast.AST* = cargs.get(j) + if a_j is not None: + lj: int = self.Gen.AssignGen.life_of_expr(trans, a_j, None) + if lj != GenTypes.LIFE_FRAME and lj != GenTypes.LIFE_VALUE: all_frame = 0 + j += 1 + if all_frame != 0: cleared = cleared | (1 << bi) + bi += 1 + return cleared + # _FrameArgsEscapeClears - "同命"精化(P3 同命规则在**调用点**的对应物): # 候选位 only_fld 只由"写进某个**形参对象**的字段"造成;若调用点交出的其它实参也都在本帧 # (LIFE_FRAME)或纯值语义(LIFE_VALUE),则对象与本帧同命、并不真的逃逸 → 可清。