From 948e1855a62ee6ab1a78399e54b7eb75ba9a630c Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 10 Oct 2026 11:25:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen):=20=E8=B7=A8=E6=A8=A1=E5=9D=97=20vpi?= =?UTF-8?q?=20=E6=9F=A5=E8=AF=A2=E5=8F=AA=E5=9C=A8=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E6=97=B6=E8=AE=B0"=E5=B7=B2=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD"=E5=A4=87=E5=BF=98=EF=BC=88=E4=BF=AE=E6=BC=8F?= =?UTF-8?q?=E6=A3=80=E5=9B=9E=E5=BD=92=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A2 引入的 lookup_ret_kind_via_vpi 会在 life_of_expr(判右值生命期,可能在极早的翻译阶段) 里触发对方模块 vpi 的按需加载,但它沿用 _vpi_escape_packed 的旧写法:**无论加载成功与否** 都把该模块记入 _vpi_escape_module_loaded 备忘。于是当那个时刻 vpi 还没生成/打不开时, 该模块被毒化成"已加载",之后所有共用这条备忘的跨模块查询(逃逸摘要 esc=、willFree wf=) 都跳过真正的加载 → 缓存恒空 → 判据静默失效。 现场:EscapeXmodTest(跨模块 esc)与 NewLifetimeWillFreeTest(跨模块 wf)双双漏检, 即"应拒绝却通过"。同模块用例不受影响,故更隐蔽。 修法:两处加载点都改为 _ensure_vpi_ret_annot_loaded 返回非 0(= 真加载/已缓存)才 _vpi_escape_mark_module;失败直接返回 0,留给后续查询重试。 - lookup_ret_kind_via_vpi(新代码,回归根源) - _vpi_escape_packed(既有同款隐患,一并修)。 --- App/lib/core/Gen/Call/CallGenBase.vp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/App/lib/core/Gen/Call/CallGenBase.vp b/App/lib/core/Gen/Call/CallGenBase.vp index e9fd05d..13e3196 100644 --- a/App/lib/core/Gen/Call/CallGenBase.vp +++ b/App/lib/core/Gen/Call/CallGenBase.vp @@ -802,7 +802,9 @@ novirtual class CallGenBase: if sha1 is None or method_name is None: return 0 if VpiReader._vpi_escape_module_loaded(sha1) == 0: if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0 - VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir) + # ⚠ 同 lookup_ret_kind_via_vpi:**加载成功才记备忘**。失败也记会把该模块毒化成"已加载", + # 使之后所有跨模块查询(含 willFree 的 _vpi_willfree,它共用这条备忘)永远查空。 + if VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir) == 0: return 0 VpiReader._vpi_escape_mark_module(sha1) return VpiReader._lookup_vpi_escape_packed(sha1, cls_name, method_name) @@ -823,7 +825,12 @@ novirtual class CallGenBase: if func_name is None or cls_sha1 is None: return 0 if VpiReader._vpi_escape_module_loaded(cls_sha1) == 0: if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0 - VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir) + # ⚠ 只有**加载成功**才能记入"已加载"备忘。_ensure 失败(该模块的 vpi 此刻还没生成 / + # 打不开)时若也记上,后续所有跨模块查询(逃逸摘要 / willFree / ret)都会**跳过真正的 + # 加载** → 缓存恒空 → 判据静默失效。现场:本查询可能在极早的翻译阶段先跑(life_of_expr + # 判右值生命期),把尚无 vpi 的模块毒化成"已加载" → EscapeXmodTest(esc)/ + # NewLifetimeWillFreeTest(wf)双双漏检。 + if VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir) == 0: return 0 VpiReader._vpi_escape_mark_module(cls_sha1) return VpiReader._lookup_vpi_escape_dir(cls_sha1, class_name, func_name, 2)