diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index bbb6f5b..1caa760 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -687,12 +687,12 @@ novirtual class AssignGenMixin: # _SlifeDropOwned - 该槽已被登记为 slife 绑定时交还所有权:释放它当前拥有的对象、影子清 0。 # 用于所有"槽里不再是编译器拥有的对象"的赋值(覆盖赋值 / 别名拷贝 / 外来指针 / None)。 # 未登记 → no-op(该槽从未被 `new heap slife` 绑定过,编译器不负责它)。 - def _SlifeDropOwned(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_slot: mlirlite.Value*) -> int: - """释放该槽当前拥有的对象并置"不拥有";1=处理过 / 0=未登记""" + def _SlifeDropOwned(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, target_slot: mlirlite.Value*) -> int: + """释放该槽当前拥有的对象并置"不拥有"(先按 §3.6 调度 `__del__`);1=处理过 / 0=未登记""" if target_slot is None: return 0 sh: mlirlite.Value* = self.Gen.VarGen._SlifeShadow(target_slot) if sh is None: return 0 - self.Gen.VarGen._SlifeEmitGuardedFree(pool, builder, mod, target_slot, sh) + self.Gen.VarGen._SlifeEmitGuardedFree(pool, builder, mod, trans, target_slot, sh) self.Gen.VarGen._SlifeNoteStore(pool, builder, sh, 0) return 1 @@ -710,7 +710,7 @@ novirtual class AssignGenMixin: if self.Gen.CallGenCtor._new_is_owned_heap(nn) != 0: sh: mlirlite.Value* = self.Gen.VarGen._SlifeEnsureOwned(pool, builder, target_slot) if sh is None: return 0 - self._SlifeDropOwned(pool, builder, mod, target_slot) + self._SlifeDropOwned(pool, builder, mod, trans, target_slot) self.Gen.VarGen._SlifeNoteStore(pool, builder, sh, 1) return 1 # 自赋值(p = p):保持原状;否则会"先释放再放弃所有权",把对象漏掉 @@ -719,7 +719,7 @@ novirtual class AssignGenMixin: if rn.id is not None: src_slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, rn.id) if src_slot is not None and uint64(src_slot) == uint64(target_slot): return 0 - self._SlifeDropOwned(pool, builder, mod, target_slot) + self._SlifeDropOwned(pool, builder, mod, trans, target_slot) return 0 def _is_addrlike_call(self, node: ast.AST*) -> int: diff --git a/App/lib/core/Gen/Class/ClassGenVTable.vp b/App/lib/core/Gen/Class/ClassGenVTable.vp index 4c46b74..ba80a85 100644 --- a/App/lib/core/Gen/Class/ClassGenVTable.vp +++ b/App/lib/core/Gen/Class/ClassGenVTable.vp @@ -832,7 +832,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): if ret_none_cn_m.const_kind == ast.CONST_NONE: _is_void = 1 if _is_void != 0: # M3:隐式收尾也是函数出口,先释放本方法登记的 slife - self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans) mlirlite.build_ret_void(func_builder) elif last_is_ctrl != 0 and trans._ctrl_merge_reachable == 0: # Non-void with a control-flow last statement and a structurally unreachable merge -> @@ -844,7 +844,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): _m_zero_val: mlirlite.Value* = mlirlite.ConstZero(pool, ret_ty) if _m_zero_val is not None: # M3:隐式收尾出口,先释放 slife 再返回 - self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans) mlirlite.build_ret(func_builder, _m_zero_val) else: self.Gen.TypeGenErr.fatal_error(fd, "method flow falls off the end without an explicit return", "TypeError") else: self.Gen.TypeGenErr.fatal_error(fd, "method flow falls off the end without an explicit return", "TypeError") diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index 4b59882..0395551 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -820,7 +820,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase): if ret_none_cn.const_kind == ast.CONST_NONE: _is_void = 1 if _is_void != 0: # M3:隐式收尾也是一个函数出口,先释放本函数登记的 slife 再返回 - self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans) mlirlite.build_ret_void(func_builder) elif last_is_ctrl != 0 and trans._ctrl_merge_reachable == 0: # A non-void function whose last statement is control flow with a structurally unreachable merge (all @@ -832,7 +832,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase): zero_val: mlirlite.Value* = mlirlite.ConstZero(pool, ret_ty) if zero_val is not None: # M3:隐式收尾出口,先释放 slife 再返回 - self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans) mlirlite.build_ret(func_builder, zero_val) else: self.Gen.TypeGenErr.fatal_error(node, "function flow falls off the end without an explicit return", "TypeError") else: self.Gen.TypeGenErr.fatal_error(node, "function flow falls off the end without an explicit return", "TypeError") diff --git a/App/lib/core/Gen/Func/FuncGenMixin.vp b/App/lib/core/Gen/Func/FuncGenMixin.vp index 0e460ed..f87b305 100644 --- a/App/lib/core/Gen/Func/FuncGenMixin.vp +++ b/App/lib/core/Gen/Func/FuncGenMixin.vp @@ -224,7 +224,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase): if last_stmt is not None and last_stmt.kind() == ast.ASTKind.Return: last_is_return = 1 if last_is_return == 0: # M3:隐式收尾也是函数出口,先释放本嵌套函数登记的 slife - self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans) zero_val: mlirlite.Value* = mlirlite.const_int32(pool, 0) mlirlite.build_ret(func_builder, zero_val) diff --git a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp index 2fceaf5..6a6ebfc 100644 --- a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp @@ -535,17 +535,17 @@ novirtual class StmtGenCifMixin: is_float_ret = 1 if is_void_ret != 0: # M3 owned slife(§4.2):每个 return 都是函数出口,先释放本函数登记的 slife - self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans) mlirlite.build_ret_void(builder) return 0 if is_ptr_ret != 0: null_val: mlirlite.Value* = mlirlite.ConstNull(pool, ret_ty, "null") - self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans) mlirlite.build_ret(builder, null_val) return 0 if is_float_ret != 0: zero_f: mlirlite.Value* = mlirlite.ConstFloat(pool, ret_ty, 0.0) - self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans) mlirlite.build_ret(builder, zero_f) return 0 if ret_ty is None: @@ -569,7 +569,7 @@ novirtual class StmtGenCifMixin: case mlirlite.LLVMType.Void(): rv_void = 1 if rv_void != 0: - self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans) mlirlite.build_ret_void(builder) return 0 if ret_ty_rt is not None: @@ -633,7 +633,7 @@ novirtual class StmtGenCifMixin: val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node, trans) # M3 owned slife(§4.2):返回值已算完,此处是常规出口——先释放 slife 再 ret # (必须在 val 求值之后:`return p.x` 之类若先释放 p 再读就成了 use-after-free)。 - self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod) + self.Gen.VarGen._SlifeEmitFrees(pool, builder, mod, trans) mlirlite.build_ret(builder, val) return 0 diff --git a/App/lib/core/Gen/Stmt/StmtGenMixin.vp b/App/lib/core/Gen/Stmt/StmtGenMixin.vp index bdaef0e..1072b7b 100644 --- a/App/lib/core/Gen/Stmt/StmtGenMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenMixin.vp @@ -100,6 +100,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin): if tgt is None: continue # del obj -> __delete__ (matches TPC HandlesDelete.py; no-op if undefined) if tgt.kind() == ast.ASTKind.Name: + # §2.4 判据5:slife 对象由编译器独占(出口自动 __del__ + free),手动 del 会让析构跑两次 + nm_del: ast.Name* = ast.Name*(tgt) + if nm_del is not None and nm_del.id is not None: + del_slot: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_del.id) + if self.Gen.VarGen._SlifeOwned(del_slot) != 0: + self.Gen.TypeGenErr.fatal_error(tgt, f"cannot `del {nm_del.id}`: it is a `slife` object owned by the compiler (its destructor `__del__` runs and its storage is freed automatically at the end of the scope); a manual del would destroy it twice", "LifetimeError") del_obj: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, tgt, None, 0, trans) if del_obj is not None: del_st_nm: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(del_obj) diff --git a/App/lib/core/Gen/VarGenMixin.vp b/App/lib/core/Gen/VarGenMixin.vp index e37e339..7696170 100644 --- a/App/lib/core/Gen/VarGenMixin.vp +++ b/App/lib/core/Gen/VarGenMixin.vp @@ -10,6 +10,7 @@ import mlirlite import memhub import string import stdlib +import viperlib # VarGenMixin - variable/scope symbol table Mixin @@ -493,13 +494,60 @@ novirtual class VarGenMixin: mlirlite.build_store(builder, v, sh) return 1 - def _SlifeEmitGuardedFree(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, alloca: mlirlite.Value*, sh: mlirlite.Value*) -> int: - """`free(select(sh != 0, load 槽, null))`:只释放该槽**确实拥有**的对象。 + def _SlifeEmitDtorBranch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, alloca: mlirlite.Value*, sh: mlirlite.Value*) -> int: + """§3.6 析构调度:拥有该对象时先调 `__del__`(对齐 TPC MemoryOps._emit_local_heap_frees:先析构、再 free)。 - 用 icmp + select 而不是分支块:释放点可能插在任意位置(每个 return、覆盖赋值处), - 建块要处理支配关系与合流,select 无分支、无合流,安全得多。sh=0 时等价 free(NULL)。""" + 用**条件分支**而不是 select:未拥有的路径上必须完全不进析构体(析构体里会解引用 self)。 + 类(含 MRO / 跨模块 stub)没定义 `__del__` 时**不加任何分支**,直接返回——常见路径零开销。 + 析构名的选择:与 TPC 的**释放**路径一致用 `__del__`(`del obj` 那条语句才是 `__delete__`)。""" + if pool is None or builder is None or mod is None or trans is None: return 0 + if alloca is None or sh is None: return 0 + if alloca.Ty is None or alloca.Ty.Pointee == 0: return 0 + elem: mlirlite.LLVMType* = alloca.Ty.Pointee # 变量类型(T*) + if elem is None or self.Gen.ExprGenBase.is_ptr_type(elem) == 0: return 0 + struct_ty: mlirlite.LLVMType* = elem.Pointee # T + if struct_ty is None or struct_ty.kind() != mlirlite.LLVMType.Struct: return 0 + cls_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty) + if cls_nm is None: return 0 + if self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, cls_nm, "__del__", trans, struct_ty) is None: return 0 + func: mlirlite.Function* = builder.Func + if func is None: return 0 + zero8: mlirlite.Value* = mlirlite.const_int(pool, 8, 0) + if zero8 is None: return 0 + shv: mlirlite.Value* = mlirlite.build_load(builder, mlirlite.Int8(pool), sh) + if shv is None: return 0 + cond: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_NE, shv, zero8) + if cond is None: return 0 + cur: mlirlite.Value* = mlirlite.build_load(builder, alloca.Ty.Pointee, alloca) + if cur is None: return 0 + # 唯一标签:用 trans._label_counter(与 translate_if 同源,避免同函数内重名标签) + cnt: int = trans._label_counter + trans._label_counter = cnt + 1 + name_buf: str = pool.alloc(32) + if name_buf is None: return 0 + viperlib.snprintf(name_buf, 32, "slife.dtor.%d", cnt) + dtor_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf) + viperlib.snprintf(name_buf, 32, "slife.dtor.end.%d", cnt) + merge_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf) + if dtor_bb is None or merge_bb is None: return 0 + mlirlite.build_cond_br(builder, cond, dtor_bb, merge_bb) + mlirlite.position_at_end(builder, dtor_bb) + # `%cur` 定义在分支块之前,支配 dtor_bb 与 merge_bb 两边 ✓ + self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, cur, "__del__", trans, None, 0) + mlirlite.build_br(builder, merge_bb) + mlirlite.position_at_end(builder, merge_bb) + return 1 + + def _SlifeEmitGuardedFree(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, alloca: mlirlite.Value*, sh: mlirlite.Value*) -> int: + """释放该槽**确实拥有**的对象:先按 §3.6 调度析构 `__del__`(若类定义了),再 `free`。 + + 释放本体用 icmp + select 而不是分支块:释放点可能插在任意位置(每个 return、覆盖赋值处), + select 无分支、无合流,安全得多;sh=0 时取 null,等价 free(NULL),槽里的外来指针/未初始化 + 值绝不参与释放。析构那一步另有条件分支(见 _SlifeEmitDtorBranch)。""" if pool is None or builder is None or alloca is None or sh is None: return 0 if alloca.Ty is None or alloca.Ty.Pointee == 0: return 0 + # §3.6:先析构,再 free + self._SlifeEmitDtorBranch(pool, builder, mod, trans, alloca, sh) zero8: mlirlite.Value* = mlirlite.const_int(pool, 8, 0) if zero8 is None: return 0 shv: mlirlite.Value* = mlirlite.build_load(builder, mlirlite.Int8(pool), sh) @@ -536,7 +584,7 @@ novirtual class VarGenMixin: j += 1 return 1 - def _SlifeEmitFrees(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*) -> int: + def _SlifeEmitFrees(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*) -> int: """函数出口(所有 return / 隐式收尾):逆序释放**本函数**登记且仍拥有的 slife 对象。 防御性去重:同一变量槽若在表里出现多次(登记路径异常 / 旧条目残留),只发一次 free—— @@ -547,7 +595,7 @@ novirtual class VarGenMixin: slot: uint64 = uint64(_g_slife_owned[i * 2]) sh: mlirlite.Value* = mlirlite.Value*(_g_slife_owned[i * 2 + 1]) if sh is not None and self._SlifeSlotFirstSeen(i, slot) != 0: - self._SlifeEmitGuardedFree(pool, builder, mod, mlirlite.Value*(_g_slife_owned[i * 2]), sh) + self._SlifeEmitGuardedFree(pool, builder, mod, trans, mlirlite.Value*(_g_slife_owned[i * 2]), sh) i -= 1 return 0