feat(core): 栈地址逃逸检查(LifetimeError)与标量→聚合 fail-fast
- MemoryOps._IsFrameStackAddr 判据 + HandlesReturn/HandlesAssign 汇点挂钩:禁止栈地址逃逸(&local 与数组退让);新增 LifetimeError(FailFastError) 承载该错误类。 - MemoryOps._coerce_value:标量(整数/浮点)不得初始化/赋值给聚合类型,改为 FailFastError(旧行为让 _store 抛 TypeError,非 strict 下被静默吞掉,聚合未初始化)。
This commit is contained in:
1 parent
75aae26881
commit
5a0eecc22f
4 files changed
+81
-1
No files matched your search
@@ -372,6 +372,15 @@ class AssignHandle(BaseHandle):
|
||||
GVar.initializer = ir.Constant(_gv_type, None) if isinstance(_gv_type, ir.PointerType) else ir.Constant(_gv_type, 0)
|
||||
else:
|
||||
GVar = Gen.module.globals[VarName]
|
||||
# 栈地址逃逸:`G = &local`,或 `G = arr`(G 为指针时的数组退让)——禁止把本帧栈
|
||||
# 地址写入模块级全局(函数返回后悬垂);目标为同型聚合时按值拷贝,不在此列。
|
||||
TgtTy1: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None
|
||||
if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy1):
|
||||
from lib.core.LLVMCG.FuncGen import LifetimeError
|
||||
raise LifetimeError(
|
||||
f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName},"
|
||||
f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配"
|
||||
)
|
||||
Gen._store(Value, GVar)
|
||||
Gen.variables[VarName] = GVar
|
||||
if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
|
||||
@@ -441,6 +450,14 @@ class AssignHandle(BaseHandle):
|
||||
IsMainScope: bool = CurrentFuncName == 'main' or CurrentFuncName.endswith('.main')
|
||||
if VarName in Gen.module.globals and (VarName in Gen.global_vars or IsMainScope):
|
||||
GVar: ir.GlobalVariable = Gen.module.globals[VarName]
|
||||
# 栈地址逃逸:main 作用域下的全局写入同口径(含 `G = arr` 的数组退让)。
|
||||
TgtTy2: Any = GVar.type.pointee if isinstance(GVar.type, ir.PointerType) else None
|
||||
if Gen._IsFrameStackAddr(Node.value, Gen, TgtTy2):
|
||||
from lib.core.LLVMCG.FuncGen import LifetimeError
|
||||
raise LifetimeError(
|
||||
f"栈地址逃逸:不能把本帧栈上存储的地址写入模块级全局变量 {VarName},"
|
||||
f"函数返回后该指针悬垂;请返回值本身(按值拷贝),或改用堆分配"
|
||||
)
|
||||
Gen._store(Value, GVar, Node=Node.value)
|
||||
Gen.variables[VarName] = GVar
|
||||
elif _rhs_addr_form and isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.PointerType):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
if TYPE_CHECKING:
|
||||
from lib.core.translator import Translator
|
||||
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
|
||||
@@ -136,6 +136,16 @@ class ReturnHandle(BaseHandle):
|
||||
Gen.emit_return(result, Node.value)
|
||||
return
|
||||
if Node.value:
|
||||
# 栈地址逃逸:`return &local`(显式取址)与 `return arr`(返回类型为指针时的数组退让,
|
||||
# 如 `-> char*`)都交出本帧栈地址;`return arr` 且返回类型为同型聚合时按值拷贝,不在此列。
|
||||
RetTy: Any = (Gen.func.ftype.return_type
|
||||
if getattr(Gen, 'func', None) is not None and getattr(Gen.func, 'ftype', None) is not None else None)
|
||||
if Gen._IsFrameStackAddr(Node.value, Gen, RetTy):
|
||||
from lib.core.LLVMCG.FuncGen import LifetimeError
|
||||
raise LifetimeError(
|
||||
f"栈地址逃逸:return 交出了本帧栈上存储的地址(局部变量/数组或形参),函数返回后即悬垂;"
|
||||
f"请返回值本身(按值拷贝),或改用堆分配"
|
||||
)
|
||||
# 本函数返回类型即"使用处宽度":先取回返回类型再翻译值,供 CDefine 常量
|
||||
# 按该位宽物化(更宽 ⇒ 升宽物化;更窄 ⇒ fail-fast)。
|
||||
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
|
||||
|
||||
@@ -19,6 +19,14 @@ class FailFastError(Exception):
|
||||
"""
|
||||
|
||||
|
||||
class LifetimeError(FailFastError):
|
||||
"""生命周期/逃逸类编译错误(如栈地址逃逸:把本帧局部/形参的地址写到帧外)。
|
||||
|
||||
继承 FailFastError:既有 `isinstance(..., FailFastError)` 的捕获/上抛语义全部保持不变,
|
||||
仅让错误类型名独立出来(与 TransPyV 的 err_type "LifetimeError" 同口径)。
|
||||
"""
|
||||
|
||||
|
||||
class MemberLookupFailFast(KeyError):
|
||||
"""成员偏移查找失败的 fail-fast 异常(**继承 KeyError** 以保持既有兜底点可用)。
|
||||
|
||||
|
||||
@@ -388,6 +388,37 @@ class MemoryOpsMixin:
|
||||
return Node.value
|
||||
return Node
|
||||
|
||||
@staticmethod
|
||||
def _IsFrameStackAddr(Node: Any, Gen: Any, TargetType: Any = None) -> bool:
|
||||
"""AST 判据:Node 是否交出「当前帧栈上存储的地址」。
|
||||
|
||||
两种形态(与 TransPyV fail_if_stack_addr_escape 同口径):
|
||||
(a) `&local`:UnaryOp(AddrOf, Name),且该 Name 是本帧局部 alloca(含形参);
|
||||
(b) 数组退让:裸本帧**数组**名出现在**指针**目标位置(如 `return arr` with `-> char*`),
|
||||
交出的是首元素地址,同属栈地址。
|
||||
模块级全局在静态存储、函数在代码段,取址可以逃逸,故排除。裸聚合名 + 同型聚合目标
|
||||
是按值拷贝,不是逃逸,故不在 (b) 判据内。
|
||||
"""
|
||||
Vars: Any = getattr(Gen, 'variables', None)
|
||||
if isinstance(Node, ast.UnaryOp) and isinstance(Node.op, ast.AddrOf):
|
||||
Inner: Any = Node.operand
|
||||
if not isinstance(Inner, ast.Name) or Inner.id is None:
|
||||
return False
|
||||
Var: Any = Vars.get(Inner.id) if Vars is not None else None
|
||||
if isinstance(Var, ir.AllocaInstr):
|
||||
return True
|
||||
Module: Any = getattr(Gen, 'module', None)
|
||||
if Module is not None and Inner.id in getattr(Module, 'globals', {}):
|
||||
return False
|
||||
# 全局/函数之外的已知名(如未登记的形参)保守判为栈地址
|
||||
return True
|
||||
if isinstance(Node, ast.Name) and Node.id is not None and isinstance(TargetType, ir.PointerType):
|
||||
Var2: Any = Vars.get(Node.id) if Vars is not None else None
|
||||
if (isinstance(Var2, ir.AllocaInstr) and isinstance(Var2.type, ir.PointerType)
|
||||
and isinstance(Var2.type.pointee, ir.ArrayType)):
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _IsSameAggregate(A: Any, B: Any) -> bool:
|
||||
"""两个 LLVM 聚合类型是否指同一类型(对象同一,或 Identified 结构体 IR 名相同)。"""
|
||||
@@ -606,6 +637,20 @@ class MemoryOpsMixin:
|
||||
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
|
||||
if isinstance(val.type, ir.IntType):
|
||||
return val
|
||||
# 聚合目标(数组/结构体)不接受标量(整数/浮点)初始化或赋值:标量→聚合无
|
||||
# 转换语义,旧行为让本方法返回 None,_store 再抛 TypeError,而该 TypeError 在
|
||||
# 非 strict 模式被静默吞掉 → 聚合完全未初始化(历史现场:`l: [10]char = 0`)。
|
||||
# 直接 fail-fast(FailFastError 不被 TypeError 捕获,故能穿透调用点的 try)并
|
||||
# 提示正确写法。typedef 伪整数已在上方短路,不受影响。
|
||||
if (isinstance(target_type, (ir.ArrayType, ir.LiteralStructType, ir.IdentifiedStructType))
|
||||
and isinstance(val.type, (ir.IntType, ir.FloatType, ir.DoubleType))):
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||
raise FailFastError(
|
||||
f"标量(整数/浮点)不能初始化或赋值给聚合类型 {target_type}:"
|
||||
f"旧行为只写首元素字节或整体不初始化,聚合其余部分保持未初始化;"
|
||||
f"聚合零初始化请写 None,填充内容请用列表字面量 [...] 或逐元素赋值"
|
||||
f"{self._DescribeValueNode(Node)}"
|
||||
)
|
||||
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.IntType):
|
||||
if target_type.width < val.type.width:
|
||||
# 目标 i1:真值归一化(与 bool(x)/if 条件同一判据),不是 trunc 取最低位
|
||||
|
||||
Reference in new issue
Block a user