fix(diag): 泄漏点采集改为"确认报错后才跑",并保存/恢复扫描期状态
原先在调用点的位循环里就调 escape_leak_line:该函数会重扫被调者函数体并改 EscConstEnv/EscIndCount, 而循环里可能紧接着发现"该实参其实不是帧生命期"从而不报错 —— 等于白白跑一遍扫描、还留下脏状态。 现在改成把 (被调者, 形参位) 传给检查方,过了 LIFE_FRAME 判定才采集;escape_leak_line 内部 把 EscConstEnv/EscIndCount/EscLeakWant 先存后还。
This commit is contained in:
1 parent
2353dca8ec
commit
8881dea2e2
4 files changed
+24
-13
No files matched your search
@@ -716,7 +716,7 @@ novirtual class AssignGenMixin:
|
||||
|
||||
# fail_if_life_escapes - 汇点检查:被写出表达式的生命期为 LIFE_FRAME → LifetimeError。
|
||||
# 这是 GenApi.fail_if_stack_addr_escape 的唯一实现(语法判据已并入 life_of_expr)。
|
||||
def fail_if_life_escapes(self, node: ast.AST*, where: str, trans: HT.Translator*, target_ty: mlirlite.LLVMType*, leak_line: int = 0) -> int:
|
||||
def fail_if_life_escapes(self, node: ast.AST*, where: str, trans: HT.Translator*, target_ty: mlirlite.LLVMType*, leak_func: str = None, leak_param: int = -1) -> int:
|
||||
"""Fail fast (LifetimeError) when the written-out value is frame-lifetime storage"""
|
||||
if node is None or trans is None: return 0
|
||||
if self.life_of_expr(trans, node, target_ty) != GenTypes.LIFE_FRAME: return 0
|
||||
@@ -733,11 +733,15 @@ novirtual class AssignGenMixin:
|
||||
if n3.id is not None:
|
||||
nm_txt = n3.id
|
||||
nm_pre = "&"
|
||||
# 泄漏点(仅同模块调用点会传 leak_line>0):被调者那边**造成逃逸的写入点**在同一个源文件里,
|
||||
# 追加一帧 "File lifetime leak ..." 指出它,读者才知道值是从哪一行漏出去的。
|
||||
# 泄漏点(可选,只有"确实报错"才去算):被调者那边**造成逃逸的写入点**在同一源文件里,
|
||||
# 追加一帧 "File lifetime leak ..."。"先过 life 判定再采集"是刻意的:escape_leak_line
|
||||
# 会重扫被调者函数体并动 EscConstEnv/EscIndCount,不能对"其实不报错"的实参白白跑一遍。
|
||||
leak_f: str = None
|
||||
if leak_line > 0: leak_f = self.Gen.TypeGenResolve.get_current_file()
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local; using it at {where} hands out a dangling pointer once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).", "LifetimeError", 0, 0, leak_f, leak_line)
|
||||
leak_l: int = 0
|
||||
if leak_func is not None and leak_param >= 0:
|
||||
leak_l = self.Gen.StructGen.escape_leak_line(leak_func, leak_param)
|
||||
if leak_l > 0: leak_f = self.Gen.TypeGenResolve.get_current_file()
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local; using it at {where} hands out a dangling pointer once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).", "LifetimeError", 0, 0, leak_f, leak_l)
|
||||
return 0
|
||||
|
||||
# _SlifeDropOwned - 该槽已被登记为 slife 绑定时交还所有权:释放它当前拥有的对象、影子清 0。
|
||||
|
||||
@@ -196,9 +196,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
_dummy_ptr: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
for ai in range(can):
|
||||
if ((esc_mask >> int(ai)) & 1) != 0:
|
||||
# 泄漏点(**仅同模块**:EscFuncTable 按文件重置,跨模块返回 0 → 不打该帧)
|
||||
leak_ln: int = GenApi.escape_leak_line(func_name, int(ai))
|
||||
GenApi.fail_if_stack_addr_escape(cargs.get(ai), "an escaping callee parameter", trans, _dummy_ptr, leak_ln)
|
||||
# 泄漏点:把"被调者 + 形参位"交给检查方,确认真要报错后才去采集(同模块才有行号)
|
||||
GenApi.fail_if_stack_addr_escape(cargs.get(ai), "an escaping callee parameter", trans, _dummy_ptr, func_name, int(ai))
|
||||
# P2 willFree:查被调者的形参失效标注(本文件表 → 跨模块 .vpi 回退)。
|
||||
# 标记动作在**实参全部翻译完成之后**(见下方 "# Default-argument filling" 之前),
|
||||
# 否则 `stdlib.free(p)` 自己那次读取 p 会被误判为"使用已失效变量"。
|
||||
|
||||
@@ -733,13 +733,14 @@ def escape_fstring_copy(src: str) -> str:
|
||||
# (b) `name` — a bare local ARRAY used where a pointer is expected (array decay);
|
||||
# (c) propagated borrows — `s: str = l` gives Life(s)=FRAME, so `return s` is caught.
|
||||
# A bare name with a matching aggregate target is a value copy, never an escape.
|
||||
def fail_if_stack_addr_escape(node: ast.AST*, where: str, trans: HT.Translator*, target_ty: mlirlite.LLVMType* = None, leak_line: int = 0) -> int:
|
||||
def fail_if_stack_addr_escape(node: ast.AST*, where: str, trans: HT.Translator*, target_ty: mlirlite.LLVMType* = None, leak_func: str = None, leak_param: int = -1) -> int:
|
||||
"""Fail fast (LifetimeError) when the address of a stack object escapes the frame.
|
||||
|
||||
leak_line > 0 时附带"泄漏点"帧(被调者那边造成逃逸的写入点行号,见 escape_leak_line)。
|
||||
leak_func/leak_param 给出被调者与形参位:确认要报错后才去采集"泄漏点"帧
|
||||
(见 escape_leak_line);给 None/-1 则不采集。
|
||||
"""
|
||||
if node is None or trans is None or _g_gen is None: return 0
|
||||
return _g_gen.AssignGen.fail_if_life_escapes(node, where, trans, target_ty, leak_line)
|
||||
return _g_gen.AssignGen.fail_if_life_escapes(node, where, trans, target_ty, leak_func, leak_param)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -624,6 +624,10 @@ novirtual class StructGenMixin:
|
||||
box: sizet* = sizet*(uint64(g) + EC_BOX_SLOT * 8)
|
||||
# 与 compute_escape_summary 同序(先收 global 名单再扫),但**不需要**常量特化/标注叠加:
|
||||
# 这里只求"首个写入点行号",保守扫描已足够。
|
||||
# 扫描会动 EscConstEnv/EscIndCount/EscLeakWant,全部先存后还(调用点可能还要接着用)。
|
||||
prev_env: sizet* = self.EscConstEnv
|
||||
prev_ind: int = self.EscIndCount
|
||||
prev_want: int = self.EscLeakWant
|
||||
g[0] = 0
|
||||
self._CollectGlobalNames(fd.children, g, 0)
|
||||
self.EscConstEnv = None
|
||||
@@ -631,8 +635,11 @@ novirtual class StructGenMixin:
|
||||
self.EscLeakWant = want_pi
|
||||
self.EscLeakLine = 0
|
||||
self._ScanEscapes(fd, fd.children, g, box, 0)
|
||||
self.EscLeakWant = -1
|
||||
return self.EscLeakLine
|
||||
ln: int = self.EscLeakLine
|
||||
self.EscLeakWant = prev_want
|
||||
self.EscConstEnv = prev_env
|
||||
self.EscIndCount = prev_ind
|
||||
return ln
|
||||
|
||||
# ============================================================
|
||||
# 跨过程常量特化(P3 收尾):调用点用常量实参重算被调者的逃逸摘要
|
||||
|
||||
Reference in new issue
Block a user