diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index 5a4fcfb..ff3294a 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -146,6 +146,19 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): if pool is None or builder is None or class_name is None or method_name is None: return None if self_ptr is None: return None + # 调用点生命期检查(M2.3 覆盖面扩展,构造器):`__init__` 里最常见的逃逸就是 + # `self.f = p`(_MarkEscapingStore 已把该形参置位)。实参链 = (self, 用户实参…), + # 故第 ai 个用户实参对应 bit (ai+1);默认值是**右对齐追加**在末尾,不影响前缀对应。 + # `__new__` 不查:其形参与 __init__ 共享且可能被裁剪,位置对应不可靠。 + if arg_nodes is not None and string.strcmp(method_name, "__init__") == 0: + esc_mask_ci: int = GenApi.get_param_escapes_method(pool, class_name, method_name) + if esc_mask_ci != 0: + dummy_ptr_ci: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool)) + can_ci: sizet = len(arg_nodes) + for ai_ci in range(can_ci): + if ((esc_mask_ci >> int(ai_ci + 1)) & 1) != 0: + GenApi.fail_if_stack_addr_escape(arg_nodes.get(ai_ci), "an argument bound to a constructor parameter that __init__ stores into a field (escapes)", trans, dummy_ptr_ci) + # Fetch the struct entry early (find_struct_by_type avoids cross-module # same-name collisions) for SHA1 lookup and vtable dispatch. cmop_entry: GenTypes.StructEntry* = None @@ -517,6 +530,19 @@ novirtual class CallGenCall(CallGenBase.CallGenBase): if pool is None or builder is None or class_name is None or method_name is None: return None if obj_ptr is None or cl is None: return None + # 调用点生命期检查(M2.3 覆盖面扩展):方法摘要键为 "Class.method"(含 MRO 回退)。 + # 摘要的形参下标含 self(bit 0),故用户第 ai 个实参对应 bit (ai+1)。语义与模块级 + # 函数那条检查一致:被调者"会逃逸或直接返回"的形参位上,实参不得交出本帧栈地址。 + cargs_mc: list[ast.AST*]* = cl.args + if cargs_mc is not None: + esc_mask_mc: int = GenApi.get_param_escapes_method(pool, class_name, method_name) + if esc_mask_mc != 0: + dummy_ptr_mc: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool)) + can_mc: sizet = len(cargs_mc) + for ai_mc in range(can_mc): + if ((esc_mask_mc >> int(ai_mc + 1)) & 1) != 0: + GenApi.fail_if_stack_addr_escape(cargs_mc.get(ai_mc), "an argument bound to a method parameter that the callee escapes or returns", trans, dummy_ptr_mc) + # list generic container method: dispatch to _translate_list_builtin_call inline # when the method is a list builtin; use the receiver's actual struct name. lb_src: str = class_name diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index 0b4fe13..61aedcf 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -298,6 +298,12 @@ def get_ret_borrow(name: str) -> int: return 0 +def get_param_escapes_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int: + """方法形参逃逸掩码:查 "Class.method" 并沿 MRO 回退,最后退裸名键;无记录返回 0""" + if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_method(pool, class_name, method_name) + return 0 + + # ---------------- find_struct_by_type (delegates to StructGenMixin.find_struct_by_type) ---------------- def find_struct_by_type(struct_ty: mlirlite.LLVMType*) -> GenTypes.StructEntry*: """Delegate to gen.find_struct_by_type""" diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index df2a065..00ba681 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -290,6 +290,16 @@ novirtual class StructGenMixin: # 生命期摘要(M2.3 v1b):表内只存 u64 名哈希——不存字符串指针, # 从根上消除"槽里是坏指针 → strcmp 崩在 0xffff…"这一整类问题。 self._RegisterFuncEscapeSummary(esc_pool, fd, fd.name) + # 嵌套函数用裸名键(与调用点 func_name 一致) + self._RegisterNestedFuncs(esc_pool, fd.children, 0) + + # M2.3 覆盖面扩展:类方法用 "Class.method" 键——此前方法与嵌套函数没有摘要, + # 它们的"存对象字段 / 返回形参"在调用点不会被拦截。 + for ci2 in range(cn): + child2: ast.AST* = ch.get(ci2) + if child2 is None: continue + if child2.kind() != ast.ASTKind.ClassDef: continue + self._RegisterClassMethods(esc_pool, ast.ClassDef*(child2)) # v2 传递闭包:反复重扫直到掩码不再变化(单调 OR-in 必收敛),上限 8 轮兜底。 # 原来固定 2 轮是近似,>2 层的链会漏;这里改为扫到不动点。 @@ -519,8 +529,148 @@ novirtual class StructGenMixin: if pi >= 0: box[0] = box[0] | (sizet(1) << pi) return 0 + # ---- M2.3 覆盖面扩展:类方法与嵌套函数的摘要 ---- + # 键约定(注册与查询必须一致): + # 模块级函数 / 嵌套函数 → **裸名**(与调用点的 func_name 一致); + # 类方法 → "Class.method"(调用点拿得到 class_name,沿 MRO 回退)。 + + def _EscKey2(self, pool: memhub.MemBuddy*, a: str, b: str) -> str: + """构造 "a.b" 键(池上字符串,仅供立即哈希,不常驻)""" + if pool is None or a is None or b is None: return None + klen: sizet = string.strlen(a) + string.strlen(b) + 2 + kb: str = pool.alloc(klen) + if kb is None: return None + viperlib.snprintf(kb, klen, "%s.%s", a, b) + return kb + + def _RegisterClassMethods(self, pool: memhub.MemBuddy*, cd: ast.ClassDef*) -> int: + """登记一个类里各方法的摘要(键 "Class.method"),并登记方法体内的嵌套函数""" + if cd is None or cd.name is None: return 0 + mch: list[ast.AST*]* = cd.children + if mch is None: return 0 + mn: sizet = len(mch) + for mi in range(mn): + m_c: ast.AST* = mch.get(mi) + if m_c is None: continue + if m_c.kind() != ast.ASTKind.FunctionDef: continue + mfd: ast.FunctionDef* = ast.FunctionDef*(m_c) + if mfd is None or mfd.name is None: continue + key_m: str = self._EscKey2(pool, cd.name, mfd.name) + if key_m is not None: self._RegisterFuncEscapeSummary(pool, mfd, key_m) + self._RegisterNestedFuncs(pool, mfd.children, 0) + return 0 + + def _RegisterNestedFuncs(self, pool: memhub.MemBuddy*, stmts: list[ast.AST*]*, depth: int) -> int: + """登记 stmts 中的嵌套 def(裸名键,与调用点一致),递归进其体与 if/while/for 块""" + if stmts is None or depth > 64: return 0 + n: sizet = len(stmts) + for i in range(n): + st: ast.AST* = stmts.get(i) + if st is None: continue + k: int = st.kind() + if k == ast.ASTKind.FunctionDef: + nfd: ast.FunctionDef* = ast.FunctionDef*(st) + if nfd is not None and nfd.name is not None: + self._RegisterFuncEscapeSummary(pool, nfd, nfd.name) + self._RegisterNestedFuncs(pool, nfd.children, depth + 1) + elif k == ast.ASTKind.If: + ifn: ast.If* = ast.If*(st) + self._RegisterNestedFuncs(pool, ifn.children, depth + 1) + self._RegisterNestedFuncs(pool, ifn.orelse, depth + 1) + elif k == ast.ASTKind.While: + whn: ast.While* = ast.While*(st) + self._RegisterNestedFuncs(pool, whn.children, depth + 1) + self._RegisterNestedFuncs(pool, whn.orelse, depth + 1) + elif k == ast.ASTKind.For: + frn: ast.For* = ast.For*(st) + self._RegisterNestedFuncs(pool, frn.children, depth + 1) + self._RegisterNestedFuncs(pool, frn.orelse, depth + 1) + return 0 + + def get_param_escapes_method(self, pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int: + """方法形参逃逸掩码:查 "Class.method",再沿 MRO 祖先查;无记录返回 0。 + + 刻意**不**回退裸名键:方法名与模块级函数名可能重名(如 error/init),回退会把 + 模块函数的掩码错用到方法调用上,产生误报。查不到即视为无摘要(保守放行)。""" + if method_name is None or class_name is None: return 0 + key_q: str = self._EscKey2(pool, class_name, method_name) + if key_q is not None: + m_q: int = self.get_param_escapes(key_q) + if m_q != 0: return m_q + mro: sizet* = self.get_mro_chain(pool, class_name) + mi2: int = 1 + while mro is not None and mro[mi2] != 0: + anc_m: str = rawptr(mro[mi2]) + if anc_m is not None and string.strcmp(anc_m, class_name) != 0: + key_a: str = self._EscKey2(pool, anc_m, method_name) + if key_a is not None: + m_a: int = self.get_param_escapes(key_a) + if m_a != 0: return m_a + mi2 += 1 + return 0 + + def _RescanOne(self, fd: ast.FunctionDef*, key: str) -> int: + """一次迭代中重扫单个函数:以既有掩码为起点做单调 OR-in""" + if fd is None or key is None or self.EscTable is None or self.EscScratch is None: return 1 + idx: int = self._EscLookup(key) + if idx < 0: return 0 + gset: sizet* = self.EscScratch + gset[0] = 0 + self._CollectGlobalNames(fd.children, gset, 0) + box: sizet* = sizet*(uint64(self.EscScratch) + 520) + box[0] = self.EscTable[idx * 3 + 1] + box[1] = self.EscTable[idx * 3 + 2] + self._ScanEscapes(fd, fd.children, gset, box, 0) + self.EscTable[idx * 3 + 1] = box[0] + self.EscTable[idx * 3 + 2] = box[1] + return 0 + + def _RescanNested(self, pool: memhub.MemBuddy*, stmts: list[ast.AST*]*, depth: int) -> int: + """重扫 stmts 里的嵌套 def(裸名键),递归进其体与 if/while/for""" + if stmts is None or depth > 64: return 0 + n: sizet = len(stmts) + for i in range(n): + st: ast.AST* = stmts.get(i) + if st is None: continue + k: int = st.kind() + if k == ast.ASTKind.FunctionDef: + nfd: ast.FunctionDef* = ast.FunctionDef*(st) + if nfd is not None and nfd.name is not None: + self._RescanOne(nfd, nfd.name) + self._RescanNested(pool, nfd.children, depth + 1) + elif k == ast.ASTKind.If: + ifn: ast.If* = ast.If*(st) + self._RescanNested(pool, ifn.children, depth + 1) + self._RescanNested(pool, ifn.orelse, depth + 1) + elif k == ast.ASTKind.While: + whn: ast.While* = ast.While*(st) + self._RescanNested(pool, whn.children, depth + 1) + self._RescanNested(pool, whn.orelse, depth + 1) + elif k == ast.ASTKind.For: + frn: ast.For* = ast.For*(st) + self._RescanNested(pool, frn.children, depth + 1) + self._RescanNested(pool, frn.orelse, depth + 1) + return 0 + + def _RescanClassMethods(self, pool: memhub.MemBuddy*, cd: ast.ClassDef*) -> int: + """重扫一个类的方法(键 "Class.method")与其方法体内的嵌套函数""" + if cd is None or cd.name is None: return 0 + mch: list[ast.AST*]* = cd.children + if mch is None: return 0 + mn: sizet = len(mch) + for mi in range(mn): + m_c: ast.AST* = mch.get(mi) + if m_c is None: continue + if m_c.kind() != ast.ASTKind.FunctionDef: continue + mfd: ast.FunctionDef* = ast.FunctionDef*(m_c) + if mfd is None or mfd.name is None: continue + key_m: str = self._EscKey2(pool, cd.name, mfd.name) + if key_m is not None: self._RescanOne(mfd, key_m) + self._RescanNested(pool, mfd.children, 0) + return 0 + def _RescanEscapes(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int: - """v2 传递闭包的一次迭代:以既有掩码为起点重扫本文件全部顶层函数(单调 OR-in)""" + """v2 传递闭包的一次迭代:重扫本文件全部函数(模块级 + 类方法 + 嵌套),单调 OR-in""" if pool is None or tree is None or self.EscTable is None: return 1 ch: list[ast.AST*]* = tree.children if ch is None: return 1 @@ -528,21 +678,15 @@ novirtual class StructGenMixin: for ci in range(cn): child: ast.AST* = ch.get(ci) if child is None: continue - if child.kind() != ast.ASTKind.FunctionDef: continue - fd: ast.FunctionDef* = ast.FunctionDef*(child) - if fd is None or fd.name is None: continue - idx: int = self._EscLookup(fd.name) - if idx < 0: continue - if self.EscScratch is None: continue - gset: sizet* = self.EscScratch - gset[0] = 0 - self._CollectGlobalNames(fd.children, gset, 0) - box: sizet* = sizet*(uint64(self.EscScratch) + 520) - box[0] = self.EscTable[idx * 3 + 1] - box[1] = self.EscTable[idx * 3 + 2] - self._ScanEscapes(fd, fd.children, gset, box, 0) - self.EscTable[idx * 3 + 1] = box[0] - self.EscTable[idx * 3 + 2] = box[1] + kc: int = child.kind() + if kc == ast.ASTKind.FunctionDef: + fd: ast.FunctionDef* = ast.FunctionDef*(child) + if fd is None or fd.name is None: continue + self._RescanOne(fd, fd.name) + self._RescanNested(pool, fd.children, 0) + elif kc == ast.ASTKind.ClassDef: + cd_r: ast.ClassDef* = ast.ClassDef*(child) + self._RescanClassMethods(pool, cd_r) return 0 def _EscMaskSum(self) -> sizet: