From 87e0bce5eea585dc3ca8d73ca36821867b800ca9 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 10 Oct 2026 14:45:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(gen):=20=E5=85=A5=E5=8F=A3=20main=20?= =?UTF-8?q?=E5=86=85=E8=B1=81=E5=85=8D=E6=A0=88=E4=B8=8A=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BD=93=E6=8C=87=E9=92=88=E7=9A=84=E9=80=83=E9=80=B8=E6=A3=80?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 等负向工程不回归。 --- App/lib/core/Gen/AssignGenMixin.vp | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 3ec3f1b..acee9e0 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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"