feat(gen): 栈地址逃逸检查(LifetimeError)、按值返回聚合、标量→聚合 fail-fast
- GenApi.fail_if_stack_addr_escape:禁止把本帧栈地址写出帧外(显式 &local,以及局部数组名退让到指针目标,如 return arr with '-> char*');裸聚合名 + 同型聚合目标是值拷贝,不在其列;err_type 记为 LifetimeError。 - StmtGenCifMixin.translate_return:按值返回聚合时,对被指类型与返回聚合类型同一的指针补 load(与多返回值 struct 的 ret 同形),修复 return 局部数组报 CastError。 - ExprGenBase.coerce_to_type:聚合目标不接受标量(旧行为落成 store i32 0, ptr,只写首元素字节并抬高对齐),改为编译期 fail-fast。
This commit is contained in:
1 parent
2ab6342d3c
commit
7070bc1608
4 files changed
+83
-2
No files matched your search
@@ -457,6 +457,42 @@ def escape_fstring_copy(src: str) -> str:
|
||||
return out
|
||||
|
||||
|
||||
# Stack-escape rule: the address of a stack object must not outlive the current frame.
|
||||
# Two forms hand out this frame's stack address:
|
||||
# (a) `&name` — the explicit address of a local variable / parameter;
|
||||
# (b) `name` — a bare local ARRAY used where a pointer is expected (array decay,
|
||||
# e.g. `def f() -> char*: ...; return arr`).
|
||||
# A bare name with a matching aggregate target is a value copy, never an escape.
|
||||
def fail_if_stack_addr_escape(node: ast.AST*, where: str, trans: HT.Translator*, target_ty: mlirlite.LLVMType* = None) -> int:
|
||||
"""Fail fast when the address of a stack object escapes the current frame."""
|
||||
if node is None or trans is None or _g_gen is None: return 0
|
||||
nm_sa: str = None
|
||||
if node.kind() == ast.ASTKind.UnaryOp:
|
||||
uo_sa: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uo_sa.op == ast.OpKind.AddrOf and uo_sa.operand is not None and uo_sa.operand.kind() == ast.ASTKind.Name:
|
||||
nm0_sa: ast.Name* = ast.Name*(uo_sa.operand)
|
||||
if nm0_sa.id is not None: nm_sa = nm0_sa.id
|
||||
elif node.kind() == ast.ASTKind.Name and target_ty is not None:
|
||||
# 数组退让(array decay):本帧局部数组名出现在指针目标位置 ⇒ 交出首元素地址(栈地址)
|
||||
if _g_gen.ExprGenBase.is_ptr_type(target_ty) != 0:
|
||||
nm1_sa: ast.Name* = ast.Name*(node)
|
||||
if nm1_sa.id is not None:
|
||||
v1_sa: mlirlite.Value* = _g_gen.VarGen.lookup_var(trans.SymTab, nm1_sa.id)
|
||||
if v1_sa is not None and v1_sa.Ty is not None and _g_gen.ExprGenBase.is_ptr_type(v1_sa.Ty) != 0:
|
||||
inner1_sa: mlirlite.LLVMType* = v1_sa.Ty.Pointee
|
||||
if inner1_sa is not None and inner1_sa.kind() == mlirlite.LLVMType.Array: nm_sa = nm1_sa.id
|
||||
if nm_sa is None: return 0
|
||||
# Nonlocal captures are heap-promoted and may outlive the frame.
|
||||
if is_nonlocal_name(trans, nm_sa) != 0: return 0
|
||||
# Only a local alloca of this frame is stack storage; module globals / function names
|
||||
# do not resolve in the local scope and are allowed to escape.
|
||||
if _g_gen.VarGen.lookup_var(trans.SymTab, nm_sa) is None: return 0
|
||||
_g_gen.TypeGenErr.fatal_error(node, f"stack address escapes: `{nm_sa}` refers to storage of the current frame (local variable/array or parameter); using it at {where} hands out a stack address, which becomes invalid once the function returns (dangling pointer). Return the value itself (a value copy) or allocate on the heap (`new heap hlife ...`).", "LifetimeError")
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
|
||||
# ---------------- create_wrapper_main (delegates to TranslatorGenMixin.create_wrapper_main) ----------------
|
||||
def create_wrapper_main(trans: HT.Translator*, tree: ast.AST*, i32_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Delegate to gen.create_wrapper_main"""
|
||||
|
||||
Reference in new issue
Block a user