fix(gen): 跨模块 vpi 查询只在加载成功时记"已加载"备忘(修漏检回归)

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(既有同款隐患,一并修)。
This commit is contained in:
TermiNexus committed 2026-10-10 11:25:43 +08:00
1 parent a3a627f513
commit 948e1855a6
1 file changed
+9 -2
+9 -2
View File
@@ -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)