diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 1dec661..c140722 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -721,8 +721,22 @@ novirtual class StructGenMixin: al[0] = c + 1 return 0 + def _NllListMentions(self, lst: list[ast.AST*]*, al: sizet*) -> int: + """列表里任一元素提及别名 → 1""" + if lst is None: return 0 + n: sizet = len(lst) + i: sizet = 0 + while i < n: + if self._NllMentions(lst.get(i), al) != 0: return 1 + i += 1 + return 0 + def _NllMentions(self, node: ast.AST*, al: sizet*) -> int: - """表达式/子树里是否出现别名集里的变量(保守:拿不到子树就算"出现")""" + """表达式/子树里是否出现别名集里的变量 → 1。 + + **必须按字段显式递归**:AST 里 `_set_parent_list` 只设父指针、**不填 `children`**, + 所以表达式节点的 `children` 基本是 None(New 已确认如此),用 children 做通用遍历会把 + `new stack [2]char` 这类整块判成"提及"。认不出的种类一律保守算"提及"。""" if node is None: return 0 k: int = node.kind() if k == ast.ASTKind.Name: @@ -730,21 +744,38 @@ novirtual class StructGenMixin: if nm.id is not None and self._NllInSet(al, nm.id) != 0: return 1 return 0 if k == ast.ASTKind.Constant: return 0 - # 属性访问:变量只可能出现在基对象里(`.attr` 是静态字符串),必须显式只递归 .value。 - # 否则若该节点的 children 不含 value,就会落进下面的"拿不到子树 = 算提及"分支 → - # 把 `stdio.printf(...)` 之类误判成提及/不安全,整条 NLL 判据被否掉(现场:该误判 - # 使 `h = new heap S()` 永不降级,g(l, h) 照旧报逃逸)。 if k == ast.ASTKind.Attribute: - at: ast.Attribute* = ast.Attribute*(node) - return self._NllMentions(at.value, al) + # 变量只可能在基对象里(`.attr` 是静态字符串) + return self._NllMentions(ast.Attribute*(node).value, al) + if k == ast.ASTKind.Subscript: + sb: ast.Subscript* = ast.Subscript*(node) + if self._NllMentions(sb.value, al) != 0: return 1 + return self._NllMentions(sb.slice, al) + if k == ast.ASTKind.Call: + cl: ast.Call* = ast.Call*(node) + if self._NllMentions(cl.func, al) != 0: return 1 + return self._NllListMentions(cl.args, al) + if k == ast.ASTKind.New: + nn: ast.New* = ast.New*(node) + if self._NllMentions(nn.func, al) != 0: return 1 + if self._NllMentions(nn.memgr, al) != 0: return 1 + return self._NllListMentions(nn.args, al) + if k == ast.ASTKind.UnaryOp: + return self._NllMentions(ast.UnaryOp*(node).operand, al) + if k == ast.ASTKind.BinOp: + bo: ast.BinOp* = ast.BinOp*(node) + if self._NllMentions(bo.left, al) != 0: return 1 + return self._NllMentions(bo.right, al) + if k == ast.ASTKind.IfExp: + ie: ast.IfExp* = ast.IfExp*(node) + if self._NllMentions(ie.test, al) != 0: return 1 + if self._NllMentions(ie.body, al) != 0: return 1 + return self._NllMentions(ie.orelse, al) + if k == ast.ASTKind.Tuple or k == ast.ASTKind.List or k == ast.ASTKind.Set: + return self._NllListMentions(ast.Tuple*(node).elts, al) ch: list[ast.AST*]* = node.children - if ch is None: return 1 - n: sizet = len(ch) - i: sizet = 0 - while i < n: - if self._NllMentions(ch.get(i), al) != 0: return 1 - i += 1 - return 0 + if ch is not None: return self._NllListMentions(ch, al) + return 1 # 认不出的种类:保守算"提及" def _NllAliasSource(self, value: ast.AST*, al: sizet*) -> int: """RHS 是否"恰好是一个别名变量"(`w = v`)→ 1""" @@ -765,10 +796,28 @@ novirtual class StructGenMixin: if self.has_param_escapes(fnm.id) == 0: return -1 return self.get_param_escapes(fnm.id) + def _NllListUnsafe(self, lst: list[ast.AST*]*, al: sizet*) -> int: + """列表里任一元素处于不安全位置 → 1""" + if lst is None: return 0 + n: sizet = len(lst) + i: sizet = 0 + while i < n: + if self._NllExprUnsafe(lst.get(i), al) != 0: return 1 + i += 1 + return 0 + def _NllExprUnsafe(self, node: ast.AST*, al: sizet*) -> int: - """表达式里别名是否被放到"不安全位置"→ 1(取址 / 当被调者 / 交给会逃逸的形参位)""" + """表达式里别名是否被放到"不安全位置"→ 1。 + + 不安全位置 = 取址 / 当被调者 / 交给会逃逸的形参位 / **认不出的表达式种类**(保守)。 + 裸读(Name)、读字段或元素(Attribute/Subscript)、常量都不是不安全位置。 + 同样按字段显式递归(AST 的 `children` 对表达式基本为空,见 _NllMentions 注释)。""" if node is None: return 0 k: int = node.kind() + if k == ast.ASTKind.Name: return 0 + if k == ast.ASTKind.Constant: return 0 + if k == ast.ASTKind.Attribute: return 0 + if k == ast.ASTKind.Subscript: return 0 if k == ast.ASTKind.UnaryOp: # 取址(以及任何一元运算):保守视为不安全 uo: ast.UnaryOp* = ast.UnaryOp*(node) @@ -791,14 +840,21 @@ novirtual class StructGenMixin: if mask < 0: return 1 if ((mask >> idx) & 1) != 0: return 1 return 0 + if k == ast.ASTKind.New: + # 构造实参里的调用同样要按不安全位置检查 + return self._NllListUnsafe(ast.New*(node).args, al) + if k == ast.ASTKind.BinOp: + bo: ast.BinOp* = ast.BinOp*(node) + if self._NllExprUnsafe(bo.left, al) != 0: return 1 + return self._NllExprUnsafe(bo.right, al) + if k == ast.ASTKind.IfExp: + ie: ast.IfExp* = ast.IfExp*(node) + if self._NllExprUnsafe(ie.test, al) != 0: return 1 + if self._NllExprUnsafe(ie.body, al) != 0: return 1 + return self._NllExprUnsafe(ie.orelse, al) ch: list[ast.AST*]* = node.children - if ch is None: return 0 - n: sizet = len(ch) - i: sizet = 0 - while i < n: - if self._NllExprUnsafe(ch.get(i), al) != 0: return 1 - i += 1 - return 0 + if ch is not None: return self._NllListUnsafe(ch, al) + return 1 # 认不出的表达式种类:保守按"不安全"处理 def _NllStmts(self, stmts: list[ast.AST*]*, al: sizet*, def_node: ast.AST*, depth: int) -> int: """遍历一个块;1=块内所有别名使用都帧内安全 / 0=发现外泄(放弃降级) @@ -869,9 +925,9 @@ novirtual class StructGenMixin: if self._NllStmts(fr.children, al, def_node, depth + 1) == 0: return 0 if self._NllStmts(fr.orelse, al, def_node, depth + 1) == 0: return 0 else: - # 认不出的语句(pass/assert/del/嵌套 def 等):整棵子树不提及别名就放行, - # 提及则保守拒绝(嵌套 def 捕获闭包变量走这条)。 - if self._NllMentions(st, al) != 0: return 0 + # 认不出的语句(pass/assert/del/嵌套 def 等):其子节点(若有)按不安全位置检查; + # 无子节点 ⇒ 不可能外泄(`pass` 之类放行)。嵌套 def 捕获闭包变量会被判不安全。 + if self._NllListUnsafe(st.children, al) != 0: return 0 return 1 def NllFrameConfined(self, trans: HT.Translator*, var_name: str, def_node: ast.AST*) -> int: