feat(gen): 入口 main 内豁免栈上结构体指针的逃逸检查
memhub.MemBuddy(...) 是裸构造(无 __new__),life_of_expr 判定为 LIFE_FRAME;main 里 argparse._mbuddy = mb 把帧内结构体指针写进模块级字段即触发 P3 拦截。入口 main 的帧生命期=进程生命期,其栈上结构体不会被本帧返回后使用,故在 fail_if_life_escapes 放行。仅豁免'结构体指针'(新增 _CurFuncIsEntryMain/_TypeIsPtrToStruct/_ExprIsStructPtr):标量指针(&local、栈数组退让、ret(frame) 返回 int*)仍受检,保持 NewLifetimeFieldEscapeTest / NewLifetimeAnnFieldEscapeTest 等负向工程不回归。
This commit is contained in:
1 parent
034293df59
commit
87e0bce5ee
1 file changed
+45
@@ -830,11 +830,56 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.VarGen.set_var_life(trans.SymTab, tnm.id, life)
|
||||
return 0
|
||||
|
||||
# _CurFuncIsEntryMain - 当前正在翻译的函数是否为**入口 main**(1=是 / 0=否)。
|
||||
# 嵌套函数不算:它们的帧不是进程生命期,不能享受豁免。
|
||||
def _CurFuncIsEntryMain(self, trans: HT.Translator*) -> int:
|
||||
"""当前函数是否为入口 main(按 llirlite 函数名判,与 FuncGenDef 对 main 不改名同口径)"""
|
||||
if trans is None or trans._cur_func is None: return 0
|
||||
nm: str = mlirlite.function_get_name(trans._cur_func)
|
||||
if nm is None: return 0
|
||||
return 1 if string.strcmp(nm, "main") == 0 else 0
|
||||
|
||||
# _TypeIsPtrToStruct - 该 LLVM 类型是否为"指向结构体的指针"(1=是 / 0=否)。
|
||||
def _TypeIsPtrToStruct(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""ty 是否为"指针且 pointee 是已登记的结构体"(标量指针如 i8*/i32* 返回 0)"""
|
||||
if ty is None: return 0
|
||||
if self.Gen.ExprGenBase.is_ptr_type(ty) == 0: return 0
|
||||
po: mlirlite.LLVMType* = ty.Pointee
|
||||
if po is None: return 0
|
||||
return 1 if self.Gen.StructGen.find_struct_by_type(po) is not None else 0
|
||||
|
||||
# _ExprIsStructPtr - 该右值表达式是否为"指向结构体的指针"(1=是 / 0=否/判不定)。
|
||||
# 覆盖两条常见来源:① 变量名(按其槽的值类型 `Ptr(StructTy)` 判);
|
||||
# ② 裸构造 `T(...)`(T 是已登记结构体 → 结果类型即 T*)。
|
||||
def _ExprIsStructPtr(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""右值是否为结构体指针(供 main 豁免判据用;判不定一律 0=照常受检)"""
|
||||
if trans is None or node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None: return 0
|
||||
slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
if slot is None: slot = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id)
|
||||
if slot is None or slot.Ty is None: return 0
|
||||
return self._TypeIsPtrToStruct(slot.Ty.Pointee)
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
cn: str = self.Gen.ExprGenBase.get_func_name(cl.func)
|
||||
if cn is None: return 0
|
||||
return 1 if self.Gen.StructGen.get_struct_type(cn) is not None else 0
|
||||
return 0
|
||||
|
||||
# fail_if_life_escapes - 汇点检查:被写出表达式的生命期为 LIFE_FRAME → LifetimeError。
|
||||
# 这是 GenApi.fail_if_stack_addr_escape 的唯一实现(语法判据已并入 life_of_expr)。
|
||||
# 例外(main 栈上结构体):入口 main 的帧生命期 = 进程生命期,其栈上的**结构体对象**
|
||||
# 在本帧返回(= 进程退出)后不可能再被使用,故 main 内把结构体指针写进长命存储不报
|
||||
# (如 `argparse._mbuddy = mb`,mb 是裸构造 `MemBuddy(...)` 的帧内结构体)。
|
||||
# **只豁免结构体指针**:标量指针(`&local` / 栈数组退让 / ret(frame) 返回 int*)仍受检,
|
||||
# 以保留既有负向工程(NewLifetimeFieldEscapeTest / NewLifetimeAnnFieldEscapeTest 等)。
|
||||
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._CurFuncIsEntryMain(trans) != 0 and self._ExprIsStructPtr(trans, node) != 0: return 0
|
||||
if self.life_of_expr(trans, node, target_ty) != GenTypes.LIFE_FRAME: return 0
|
||||
# 报文里给出名字(&x / x),便于定位;前缀单独存放,避免 str 相加(str 是 i8*,无 '+' 语义)
|
||||
nm_txt: str = "expression"
|
||||
|
||||
Reference in new issue
Block a user