From eea8111e61bad759c380fc2f4366dc8acd57b6f9 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 10 Oct 2026 14:10:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen):=20willFree=20=E5=A4=B1=E6=95=88?= =?UTF-8?q?=E6=A7=BD=E8=A1=A8=E8=A1=A5=E6=8E=A7=E5=88=B6=E6=B5=81=E5=90=88?= =?UTF-8?q?=E6=B5=81=EF=BC=8C=E4=BF=AE=E5=A4=8D=E5=88=86=E6=94=AF=E5=86=85?= =?UTF-8?q?=20free=20=E6=B3=84=E6=BC=8F=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E8=AF=AF=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 失效表原为平坦单调全局表,无分支合流语义:if 分支体内 free(x) 会把失效态泄漏到合流之后,于是 fileio.vp 的 if ...: free(buf); return None 之后 return buf 误报 LifetimeError(1784eaf 修好 free 的 wf 位后才暴露)。新增 freed_capture/freed_restore/freed_join(pool 快照 + 求交),并在 if(结合 then/else 是否到达合流)与 while/for 的既有 life_* 同点位接入:只有所有到达合流的路径都失效才算失效。 --- App/lib/core/Gen/Stmt/StmtGenCifMixin.vp | 17 +++++++ App/lib/core/Gen/Stmt/StmtGenMixin.vp | 6 +++ App/lib/core/Gen/VarGenMixin.vp | 58 ++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp index a7b909a..320e43b 100644 --- a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp @@ -719,6 +719,8 @@ novirtual class StmtGenCifMixin: # 生命期汇合:进入分支前快照(else 分支需从该状态重新开始,见 §11 控制流 Meet) life_snap0: sizet* = self.Gen.VarGen.life_capture(trans) + # willFree 失效槽表同点位快照(分支体内的 free 不得泄漏到合流之后) + freed_snap0: sizet* = self.Gen.VarGen.freed_capture(trans) # 5. translate the then body; reset the merge-reachable flag first (nested control flow # overwrites it, so the branch-tail check must not see a stale value from before this If). @@ -744,12 +746,14 @@ novirtual class StmtGenCifMixin: # then 末状态快照(供两端 Meet:先复位到 if 前状态,再与 then 末状态合并) life_snap1: sizet* = self.Gen.VarGen.life_capture(trans) + freed_snap1: sizet* = self.Gen.VarGen.freed_capture(trans) # 6. translate the else body (if any) else_branches_merge: int = 0 if has_else == 1: # 复位到 if 前状态,让 else 分支从同一基线出发(否则会看到 then 分支写入的 Life) self.Gen.VarGen.life_restore(life_snap0) + self.Gen.VarGen.freed_restore(freed_snap0) # as in then: reset the flag to avoid stale values from the then branch trans._ctrl_merge_reachable = 1 mlirlite.position_at_end(builder, else_bb) @@ -769,6 +773,19 @@ novirtual class StmtGenCifMixin: if has_else == 1: self.Gen.VarGen.life_join(life_snap1) else: self.Gen.VarGen.life_join(life_snap0) + # willFree 失效槽表合流:只有"到达合流的路径"参与(否则会把"分支体内 free 后 return" + # 的失效态错误地带到合流之后——fileio.vp 的 `if …: free(buf); return None` 即此现场)。 + # 两分支都到达 → 求交(curr=else 末态 ∩ then 末态); + # 仅 then 到达 → 取 then 末态; + # 仅 else/无 else → 取 else 末态(无 else 时即 if 前状态)。 + if has_else == 1: + if then_branches_merge != 0 and else_branches_merge != 0: + self.Gen.VarGen.freed_join(freed_snap1) + elif then_branches_merge != 0: + self.Gen.VarGen.freed_restore(freed_snap1) + else: + self.Gen.VarGen.freed_restore(freed_snap0) + # 7. merge reachability (used by tail-flow checks): without else cond_br always reaches # merge; with else, any branch br reaching merge suffices; only all-return is unreachable. trans._ctrl_merge_reachable = 1 diff --git a/App/lib/core/Gen/Stmt/StmtGenMixin.vp b/App/lib/core/Gen/Stmt/StmtGenMixin.vp index 5ebc0e8..119a8ce 100644 --- a/App/lib/core/Gen/Stmt/StmtGenMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenMixin.vp @@ -66,15 +66,21 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin): elif k == ast.ASTKind.While: # 循环生命期汇合:循环可能 0 次执行,出口状态须与进入前状态 Meet(见 §11 控制流 Meet) snap_wh: sizet* = self.Gen.VarGen.life_capture(trans) + # willFree 失效槽表:同点位快照,出口与进入前状态**求交**(循环体可能 0 次执行, + # 故体内 free 不得被当成"循环后已失效") + freed_snap_wh: sizet* = self.Gen.VarGen.freed_capture(trans) rc_wh: int = self.Gen.StmtGenCif.translate_while(trans, node) self.Gen.VarGen.life_join(snap_wh) + self.Gen.VarGen.freed_join(freed_snap_wh) return rc_wh elif k == ast.ASTKind.AugAssign: return self.Gen.AssignGen.translate_augassign(trans, node) elif k == ast.ASTKind.For: # 同上:for 的各迭代形态(range/指针/数组/容器/解糖)统一在此收口 snap_fr: sizet* = self.Gen.VarGen.life_capture(trans) + freed_snap_fr: sizet* = self.Gen.VarGen.freed_capture(trans) rc_fr: int = self.Gen.StmtGenCif.translate_for(trans, node) self.Gen.VarGen.life_join(snap_fr) + self.Gen.VarGen.freed_join(freed_snap_fr) return rc_fr elif k == ast.ASTKind.ClassDef: return self.Gen.ClassGenDef.translate_class_def(trans, node) elif k == ast.ASTKind.Import: return self.Gen.ImportsGen.translate_import(trans, node) diff --git a/App/lib/core/Gen/VarGenMixin.vp b/App/lib/core/Gen/VarGenMixin.vp index 494e64a..6beea47 100644 --- a/App/lib/core/Gen/VarGenMixin.vp +++ b/App/lib/core/Gen/VarGenMixin.vp @@ -367,6 +367,64 @@ novirtual class VarGenMixin: if symtab is None or name is None: return 0 return self._FreedClearSlot(self.lookup_var(symtab, name)) + # ---- P2 willFree:失效槽表的控制流汇合(与 life_capture/life_restore/life_join 同点位) ---- + # 背景:失效表是"平坦、单调"的全局表(_FreedMark 只插入),原本无分支合流语义, + # 于是"分支体内 free(x)"会把失效态泄漏到合流之后 → `if c: free(p); return` 之后的 + # `return p` 误报。这里给 if/while/for 补上快照/复位/求交,口径 = "只有所有到达合流的 + # 路径都失效,才算失效"(求交),与 life_* 的 Meet 同点位、同生命周期(pool 快照)。 + def freed_capture(self, trans: HT.Translator*) -> sizet*: + """快照当前失效槽表:buf[0]=count,其后每项一个失效槽指针;失败返回 None""" + if trans is None or trans.Pool is None: return None + pool: memhub.MemBuddy* = trans.Pool + n: sizet = sizet(_g_freed_count) + buf: sizet* = sizet*(pool.alloc((n + 1) * 8)) + if buf is None: return None + buf[0] = n + i: sizet = 0 + while i < n: + buf[1 + i] = _g_freed_slots[i] + i += 1 + return buf + + def freed_restore(self, buf: sizet*) -> int: + """把失效槽表复位到快照(分支出口 / 单侧到达合流时用);0=完成 / 1=无快照""" + global _g_freed_count + if buf is None: return 1 + n: sizet = buf[0] + if n > 0: + if _g_freed_slots is None: + _g_freed_slots = sizet*(stdlib.malloc(_FREED_MAX * 8)) + if _g_freed_slots is None: return 1 + i: sizet = 0 + while i < n: + _g_freed_slots[i] = buf[1 + i] + i += 1 + _g_freed_count = int(n) + return 0 + + def freed_join(self, buf: sizet*) -> int: + """合流:当前失效槽表与快照**求交**(只有两路都失效才算失效);0=完成 / 1=无快照""" + global _g_freed_count + if buf is None: return 1 + n: sizet = buf[0] + out: int = 0 + i: sizet = 0 + while i < sizet(_g_freed_count): + if _g_freed_slots is None: break + s: sizet = _g_freed_slots[i] + j: sizet = 0 + keep: int = 0 + while j < n: + if buf[1 + j] == s: keep = 1 + if keep != 0: break + j += 1 + if keep != 0: + _g_freed_slots[out] = s + out += 1 + i += 1 + _g_freed_count = out + return 0 + # _spec_of_var_alloca - build a TypeSpec from the alloca's value type (S3 dual write). # alloca.Ty is Ptr(var_ty), so the pointee is the value type; None means failure or an unsupported type. def _spec_of_var_alloca(self, alloca: mlirlite.Value*) -> TSpec.TypeSpec*: