Files

1100 lines
56 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from stdint import *
import ast
import mlirlite
import memhub
import string
import stdlib
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.Translator as HT
import lib.core.Gen.Generator as Generator
# GenApi - module-level delegate entry for pipeline/reserved-file access to Gen
# (drops the Handles layer); generated from Gen sub-object methods via _g_gen.
# Global gen singleton (injected by main/BuildPipeline after creating gen)
_g_gen: Generator.Generator* = None
# ---------------- set_temp_dir (delegates to TypeGenBase.set_temp_dir) ----------------
def set_temp_dir(temp_dir: str) -> None:
"""Delegate to gen.set_temp_dir"""
if _g_gen is not None: _g_gen.TypeGenBase.set_temp_dir(temp_dir)
# ---------------- set_current_file_imports (delegates to ImportsGenMixin.set_current_file) ----------------
def set_current_file_imports(file_path: str) -> None:
"""Delegate to gen.set_current_file"""
if _g_gen is not None: _g_gen.ImportsGen.set_current_file(file_path)
# ---------------- set_current_file_type (delegates to TypeGenResolve.set_current_file) ----------------
def set_current_file_type(file_path: str) -> None:
"""Set the current file for attribution via gen, falling back to the Generator singleton."""
if _g_gen is not None:
_g_gen.TypeGenResolve.set_current_file(file_path)
return
gg: Generator.Generator* = Generator._g_gen
if gg is not None: gg.TypeGenResolve.set_current_file(file_path)
# ---------------- set_current_module_sha1 (delegates to TypeGenBase.set_current_module_sha1) ----------------
def set_current_module_sha1(sha1: str) -> None:
"""Delegate to gen.set_current_module_sha1"""
if _g_gen is not None: _g_gen.TypeGenBase.set_current_module_sha1(sha1)
# ---------------- clear_trace (delegates to TypeGenBase.clear_trace) ----------------
def clear_trace() -> None:
"""Delegate to gen.clear_trace"""
if _g_gen is not None: _g_gen.TypeGenBase.clear_trace()
# ---------------- clear_cdefine_constants (delegates to TypeGenCDef.clear_cdefine_constants) ----------------
def clear_cdefine_constants() -> None:
"""Delegate to gen.clear_cdefine_constants"""
if _g_gen is not None: _g_gen.TypeGenCDef.clear_cdefine_constants()
# ---------------- clear_ctypedefs (delegates to TypeGenCType.clear_ctypedefs) ----------------
def clear_ctypedefs() -> None:
"""Delegate to gen.clear_ctypedefs"""
if _g_gen is not None: _g_gen.TypeGenCType.clear_ctypedefs()
# ---------------- RegisterCTypedefFromSource (delegates to TypeGenCType.register_ctypedef_from_source) ----------------
def RegisterCTypedefFromSource(pool: memhub.MemBuddy*, src: str) -> int:
"""Delegate to gen.register_ctypedef_from_source"""
if _g_gen is not None: _g_gen.TypeGenCType.register_ctypedef_from_source(pool, src)
return 0
# ---------------- dump_parse_traceback (delegates to TypeGenErr.dump_parse_traceback) ----------------
def dump_parse_traceback(file_path: str) -> None:
"""Delegate to gen.dump_parse_traceback"""
if _g_gen is not None: _g_gen.TypeGenErr.dump_parse_traceback(file_path)
# ---------------- fatal_error (delegates to TypeGenErr.fatal_error) ----------------
def fatal_error(node: ast.AST*, msg: str, err_type: str = "SyntaxError") -> None:
"""Report an error through gen.TypeGenErr, falling back to the Generator singleton."""
if _g_gen is not None:
_g_gen.TypeGenErr.fatal_error(node, msg, err_type)
return
gg: Generator.Generator* = Generator._g_gen
if gg is not None: gg.TypeGenErr.fatal_error(node, msg, err_type)
# ---------------- map_builtin_type (delegates to TypeGenResolve.map_builtin_type) ----------------
def map_builtin_type(pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*:
"""Delegate to gen.map_builtin_type"""
if _g_gen is not None: _g_gen.TypeGenResolve.map_builtin_type(pool, type_name)
return None
# ---------------- resolve_typedef_or_map (delegates to TypeGenResolve.resolve_typedef_or_map) ----------------
def resolve_typedef_or_map(pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*:
"""Delegate to gen.resolve_typedef_or_map: CTypedef table first, then map_builtin_type."""
if _g_gen is not None: return _g_gen.TypeGenResolve.resolve_typedef_or_map(pool, name, imported_modules, from_imports, trans)
return None
# ---------------- infer_external_func_ret_ty (delegates to CallGenLib._infer_external_func_ret_ty) ----------------
def infer_external_func_ret_ty(pool: memhub.MemBuddy*, func_name: str) -> mlirlite.LLVMType*:
"""Infer an external function return type from the whitelist, defaulting to i32."""
if _g_gen is not None: return _g_gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, None, 0)
return None
# ---------------- get_current_file (delegates to TypeGenResolve.get_current_file) ----------------
def get_current_file() -> str:
"""Delegate to gen.get_current_file"""
if _g_gen is not None: return _g_gen.TypeGenResolve.get_current_file()
return None
# ---------------- _is_ctypedef_annotation (delegates to TypeGenCType._is_ctypedef_annotation) ----------------
def _is_ctypedef_annotation(annotation: ast.AST*) -> int:
"""Delegate to gen._is_ctypedef_annotation"""
if _g_gen is not None: return _g_gen.TypeGenCType._is_ctypedef_annotation(annotation)
return 0
# ---------------- resolve_annotation_type (delegates to TypeGenResolve.resolve_annotation_type) ----------------
def resolve_annotation_type(pool: memhub.MemBuddy*, node: ast.AST*, imported_modules: str, from_imports: str, trans: HT.Translator* = None, depth: int = 0) -> mlirlite.LLVMType*:
"""Delegate to gen.resolve_annotation_type"""
if _g_gen is not None: return _g_gen.TypeGenResolve.resolve_annotation_type(pool, node, imported_modules, from_imports, trans, depth)
return None
# ---------------- has_decorator_marker (delegates to TypeGenErr.has_decorator_marker) ----------------
def has_decorator_marker(node: ast.AST*, marker_name: str, depth: int = 0) -> int:
"""Delegate to gen.has_decorator_marker"""
if _g_gen is not None: return _g_gen.TypeGenErr.has_decorator_marker(node, marker_name, depth)
return 0
# ---------------- has_pointee_volatile_marker (delegates to TypeGenErr.has_pointee_volatile_marker) ----------------
def has_pointee_volatile_marker(node: ast.AST*) -> int:
"""Delegate to gen.has_pointee_volatile_marker (rawptr[t.CVolatile | T] field/var pointee)"""
if _g_gen is not None: return _g_gen.TypeGenErr.has_pointee_volatile_marker(node)
return 0
# ---------------- is_const_object_marker (delegates to TypeGenErr.is_const_object_marker) ----------------
def is_const_object_marker(node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
"""Delegate to gen.is_const_object_marker (t.CConst non-pointer object field/var)"""
if _g_gen is not None: return _g_gen.TypeGenErr.is_const_object_marker(node, var_ty)
return 0
# ---------------- _get_type_name_from_annotation (delegates to TypeGenResolve._get_type_name_from_annotation) ----------------
def _get_type_name_from_annotation(pool: memhub.MemBuddy*, node: ast.AST*) -> str:
"""Delegate to gen._get_type_name_from_annotation"""
if _g_gen is not None: return _g_gen.TypeGenResolve._get_type_name_from_annotation(pool, node)
return None
# ---------------- register_ctypedef (delegates to TypeGenCType.register_ctypedef) ----------------
def register_ctypedef(pool: memhub.MemBuddy*, name: str, type_str: str):
"""Delegate to gen.register_ctypedef"""
if _g_gen is not None: _g_gen.TypeGenCType.register_ctypedef(pool, name, type_str)
# ---------------- lookup_cdefine_constant (delegates to TypeGenCDef.lookup_cdefine_constant) ----------------
def lookup_cdefine_constant(name: str) -> int64:
"""Delegate to gen.lookup_cdefine_constant"""
if _g_gen is not None: return _g_gen.TypeGenCDef.lookup_cdefine_constant(name)
return 0
# ---------------- is_cdefine_found (delegates to TypeGenCDef.is_cdefine_found) ----------------
def is_cdefine_found() -> int:
"""Delegate to gen.is_cdefine_found"""
if _g_gen is not None: return _g_gen.TypeGenCDef.is_cdefine_found()
return 0
# ---------------- register_cdefine_constant (delegates to TypeGenCDef.register_cdefine_constant) ----------------
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0):
"""Delegate to gen.register_cdefine_constant (bits: 0=未知;8/16/32/64=已知位宽)"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value, bits)
# ---------------- cdefine_decl_bits (delegates to AssignGenMixin.cdefine_decl_bits) ----------------
def cdefine_decl_bits(pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.AST*) -> int:
"""define 声明的位宽裁决(注解 → 右侧显式强转 → 值的最小可存宽度);0=非整型/无法裁决。
跨模块(.vpi 重解析 define 的 AST)注册时必须用它,否则使用方位宽与定义方不一致。"""
if _g_gen is not None: return _g_gen.AssignGen.cdefine_decl_bits(pool, annot, value_node)
return 0
# ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ----------------
def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0):
"""Delegate to gen.register_cdefine_double (is_f32: 1=float 声明,0=double)"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value, is_f32)
# ---------------- cdefine_decl_float_bits (delegates to AssignGenMixin.cdefine_decl_float_bits) ----------------
def cdefine_decl_float_bits(pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
"""define 声明的**浮点**宽度裁决:注解显式写了 float → 1(f32),double/未写 → 0。
跨模块(.vpi 重解析 define 的 AST)注册必须用它,否则使用方把 f32 常量当 double 物化。"""
if _g_gen is not None: return _g_gen.AssignGen.cdefine_decl_float_bits(pool, annot)
return 0
# ---------------- is_cdefine_annotation (delegates to AssignGenMixin.is_cdefine_annotation) ----------------
def is_cdefine_annotation(annot: ast.AST*) -> int:
"""Delegate to gen.is_cdefine_annotation"""
if _g_gen is not None: return _g_gen.AssignGen.is_cdefine_annotation(annot)
return 0
# ---------------- extract_cdefine_int_value (delegates to AssignGenMixin.extract_cdefine_int_value) ----------------
def extract_cdefine_int_value(val_node: ast.AST*) -> int64:
"""Delegate to gen.extract_cdefine_int_value"""
if _g_gen is not None: return _g_gen.AssignGen.extract_cdefine_int_value(val_node)
return 0
# ---------------- push_trace (delegates to TypeGenBase.push_trace) ----------------
def push_trace(file_name: str, lineno: int, col_start: int = 0, col_end: int = 0):
"""Delegate to gen.push_trace"""
if _g_gen is not None: _g_gen.TypeGenBase.push_trace(file_name, lineno, col_start, col_end)
# ---------------- pop_trace (delegates to TypeGenBase.pop_trace) ----------------
def pop_trace():
"""Delegate to gen.pop_trace"""
if _g_gen is not None: _g_gen.TypeGenBase.pop_trace()
# ---------------- get_temp_dir (delegates to TypeGenBase.get_temp_dir) ----------------
def get_temp_dir() -> str:
"""Delegate to gen.get_temp_dir"""
if _g_gen is not None: return _g_gen.TypeGenBase.get_temp_dir()
return None
# ---------------- reset_visible_structs (delegates to StructGenMixin.reset_visible_structs) ----------------
def reset_visible_structs(pool: memhub.MemBuddy*, strict: int):
"""Delegate to gen.reset_visible_structs"""
if _g_gen is not None: _g_gen.StructGen.reset_visible_structs(pool, strict)
# ---------------- set_stdlib_alloc (delegates to StructGenMixin.set_stdlib_alloc) ----------------
def set_stdlib_alloc(flag: int):
"""Delegate to gen.set_stdlib_alloc"""
if _g_gen is not None: _g_gen.StructGen.set_stdlib_alloc(flag)
# ---------------- get_struct_count (delegates to StructGenMixin.get_struct_count) ----------------
def get_struct_count() -> int:
"""Delegate to gen.get_struct_count"""
if _g_gen is not None: return _g_gen.StructGen.get_struct_count()
return 0
# ---------------- reset_module_methods_emitted (delegates to StructGenMixin.reset_module_methods_emitted) ----------------
def reset_module_methods_emitted(sha1: str) -> int:
"""Delegate to gen.reset_module_methods_emitted"""
if _g_gen is not None: return _g_gen.StructGen.reset_module_methods_emitted(sha1)
return 0
# ---------------- get_struct_type (delegates to StructGenMixin.get_struct_type) ----------------
def get_struct_type(class_name: str) -> mlirlite.LLVMType*:
"""Delegate to gen.get_struct_type"""
if _g_gen is not None: return _g_gen.StructGen.get_struct_type(class_name)
return None
# ---------------- get_enum_base_type (delegates to StmtGenDeclMixin.get_enum_base_type) ----------------
def get_enum_base_type(enum_name: str) -> mlirlite.LLVMType*:
"""Delegate to gen.get_enum_base_type, falling back to the enum table's base type."""
if _g_gen is not None: return _g_gen.StmtGenDecl.get_enum_base_type(enum_name)
return None
# ---------------- add_visible_struct (delegates to StructGenMixin.add_visible_struct) ----------------
def add_visible_struct(pool: memhub.MemBuddy*, name: str):
"""Delegate to gen.add_visible_struct"""
if _g_gen is not None: _g_gen.StructGen.add_visible_struct(pool, name)
# ---------------- find_struct_by_module (delegates to StructGenMixin.find_struct_by_module) ----------------
def find_struct_by_module(name: str, sha1: str) -> GenTypes.StructEntry*:
"""Delegate to gen.find_struct_by_module"""
if _g_gen is not None: return _g_gen.StructGen.find_struct_by_module(name, sha1)
return None
# ---------------- mark_module_class (delegates to StructGenMixin.mark_module_class) ----------------
def mark_module_class(pool: memhub.MemBuddy*, sha1: str, name: str) -> None:
"""Register "{sha1}.{class}" for this module and point the current definition fields at it."""
if _g_gen is not None: _g_gen.StructGen.mark_module_class(pool, sha1, name)
# ---------------- register_file_classes (delegates to StructGenMixin.register_file_classes) ----------------
def register_file_classes(pool: memhub.MemBuddy*, sha1: str, tree: ast.AST*) -> None:
"""Pre-register top-level ClassDef names with this file's module SHA1."""
if _g_gen is not None: _g_gen.StructGen.register_file_classes(pool, sha1, tree)
# ---------------- register_file_funcs / is_file_func (delegates to StructGenMixin) ----------------
def register_file_funcs(pool: memhub.MemBuddy*, tree: ast.AST*) -> None:
"""Pre-register this file's top-level FunctionDef names to guard its own extern declarations."""
if _g_gen is not None: _g_gen.StructGen.register_file_funcs(pool, tree)
# ---------------- reset_file_name_window / register_file_func_names ----------------
# "文件级名字窗口"的唯一开启入口 + vpi 落盘路径的轻量重建(契约见 StructGenMixin 两处注释)。
# 任何文件级入口(翻译 / vpi 落盘 / …)都必须先 reset_file_name_window、再重建对应表。
def reset_file_name_window() -> None:
"""清空四张按文件重建的名字表(FileClassNames/FileModuleSha1/FileFuncNames/FileVarNames)"""
if _g_gen is not None: _g_gen.StructGen.reset_file_name_window()
def register_file_func_names(pool: memhub.MemBuddy*, tree: ast.AST*) -> None:
"""只重建"本文件顶层函数名"表(供 vpi 落盘路径的 is_file_func 使用;须先 reset_file_name_window)"""
if _g_gen is not None: _g_gen.StructGen.register_file_func_names(pool, tree)
def is_file_func(name: str) -> int:
"""Return 1 if name is a top-level function declared in this file, else 0."""
if _g_gen is not None: return _g_gen.StructGen.is_file_func(name)
return 0
def is_file_var(name: str) -> int:
"""本文件是否顶层声明过该模块级变量名(AnnAssign/Assign 目标);1=是 / 0=否。"""
if _g_gen is not None: return _g_gen.StructGen.is_file_var(name)
return 0
# ---------------- 生命期摘要查询(M2.3 v1b, delegates to StructGenMixin) ----------------
def get_param_escapes(name: str) -> int:
"""形参逃逸掩码(第 k 位=第 k 个形参逃逸);无记录返回 0"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes(name)
return 0
def get_ret_borrow(name: str) -> int:
"""返回借用标记(1=该函数直接返回某形参);无记录返回 0"""
if _g_gen is not None: return _g_gen.StructGen.get_ret_borrow(name)
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
def compute_escape_summary(pool: memhub.MemBuddy*, fd: ast.AST*, gset: sizet*, box: sizet*) -> int:
"""计算函数逃逸摘要(box[0]=形参逃逸掩码,box[1]=返回借用位);.vpi 生成时落盘用"""
if _g_gen is not None: return _g_gen.StructGen.compute_escape_summary(pool, ast.FunctionDef*(fd), gset, box)
return 1
# ---------------- 生命期标注 @life(willFree(...)) / @willFree(...)(P2) ----------------
# 返回值 = 形参位掩码:第 k 位 = 第 k 个形参所指对象在**本调用返回后失效**(被调用者释放/接管)。
# 语义见 includes/stdlib.vp 的 free 与 Debugger/ownership-and-new.md §12-P2。
# 非法标注一律 fail-fast(LifetimeError),不静默忽略。
def compute_willfree_mask(fd: ast.AST*) -> int:
"""解析函数定义的 @life(willFree(...)) / @willFree(...) 标注并返回形参位掩码(0=无标注)"""
fn: ast.FunctionDef* = ast.FunctionDef*(fd)
if fn is None or fn.decorator_list is None: return 0
dl: list[ast.AST*]* = fn.decorator_list
dn: sizet = len(dl)
if dn == 0: return 0
mask: int = 0
for i in range(int(dn)):
deco: ast.AST* = dl.get(i)
if deco is None or deco.kind() != ast.ASTKind.Call: continue
dc: ast.Call* = ast.Call*(deco)
if dc.func is None or dc.func.kind() != ast.ASTKind.Name: continue
fname: ast.Name* = ast.Name*(dc.func)
if fname.id is None: continue
if string.strcmp(fname.id, "life") == 0:
# @life(<tag>, <tag>, ...):逐个标签并入
if dc.args is not None:
an: sizet = len(dc.args)
for j in range(int(an)):
mask = _WillFreeMergeTag(fn, dc.args.get(j), mask)
elif string.strcmp(fname.id, "willFree") == 0:
# 兼容形式:@willFree(0) 直接叠加(与 @life 等价、可混用)
mask = _WillFreeMergeTag(fn, deco, mask)
return mask
def _WillFreeMergeTag(fn: ast.FunctionDef*, tag: ast.AST*, mask: int) -> int:
"""校验一个标签调用 willFree(<int>|<name>) 并并入掩码;未知标签/非法实参 → fail-fast"""
if tag is None or tag.kind() != ast.ASTKind.Call:
fatal_error(tag, "@life(...) accepts only tag calls such as willFree(0) or willFree(p)", "LifetimeError")
return mask
tc: ast.Call* = ast.Call*(tag)
tnm: str = None
if tc.func is not None:
if tc.func.kind() == ast.ASTKind.Name:
tn: ast.Name* = ast.Name*(tc.func)
tnm = tn.id
elif tc.func.kind() == ast.ASTKind.Attribute:
ta: ast.Attribute* = ast.Attribute*(tc.func)
tnm = ta.attr
if tnm is None:
fatal_error(tag, "@life(...) accepts only `willFree(<param>)` / `escape(<param>)` / `noescape(<param>)` tags", "LifetimeError")
return mask
is_wf: int = string.strcmp(tnm, "willFree")
is_esc: int = string.strcmp(tnm, "escape")
is_nesc: int = string.strcmp(tnm, "noescape")
# A1(§14):stores ≡ escape("确定存进长命存储"的强声明)、borrows ≡ noescape(借出口径)。
# 二者复用同一对契约位,位本身由 compute_escape_directives 收。
is_st: int = string.strcmp(tnm, "stores")
is_bw: int = string.strcmp(tnm, "borrows")
# A3 的标签(owns)先**显式**报"未实现",不做静默忽略(静默 = 用户以为标注生效了)。
if string.strcmp(tnm, "owns") == 0:
fatal_error(tag, "@life tag `owns(...)` is not implemented yet (planned A3); use willFree/escape/noescape/stores/borrows for now", "LifetimeError")
return mask
# A2 的 `ret(...)` 由 compute_life_anno 收集与校验(返回值生命期契约,不进 willFree 掩码)。
if string.strcmp(tnm, "ret") == 0:
return mask
if is_wf != 0 and is_esc != 0 and is_nesc != 0 and is_st != 0 and is_bw != 0:
fatal_error(tag, "@life(...) accepts only `willFree(...)` / `escape(...)` / `noescape(...)` / `stores(...)` / `borrows(...)` tags", "LifetimeError")
return mask
if tc.args is None or len(tc.args) == 0:
fatal_error(tag, f"@life tag `{tnm}(...)` needs at least one parameter name or 0-based position", "LifetimeError")
return mask
# 一个标签可以带多个形参:`@life(noescape(x, s))`(逐个并入)
an_w: sizet = len(tc.args)
k_w: sizet = 0
while k_w < an_w:
idx: int = _WillFreeArgIndex(fn, tc.args.get(k_w))
k_w += 1
if idx < 0: continue
# escape/noescape/stores/borrows 的位由 compute_escape_directives 收,这里只负责校验合法性;
# **只有 `willFree` 才置 willFree 位**。
# ⚠ 这里的判据必须是 `is_wf == 0`:`string.strcmp` **相等返回 0**,`is_wf != 0` 的含义是
# "不是 willFree"。写反的后果(现场):
# · 真正标了 willFree 的(如 includes/stdlib.vp 的 free)**位永远不置** → `wf_mask=0`
# → .vpi 不写 `wf=`(实测 free 那行没有尾注)→ 调用点无从"标记实参失效" → P2 全面漏检
# (NewLifetimeWillFreeTest / WillFreeKwarg / WillFreeMethod / WillFreeAddr 都静默通过);
# · 别的标签(noescape 等)反而把 willFree 位置 1(实测 EscapeOkTest 的 StoreNS 写出
# `# wf=1 escn=1`)→ 反向误标。
# 回归来源:fc797da(A1 把三元 guard 扩成五元时把这行一起改反了)。
if is_wf == 0: mask = mask | (1 << idx)
return mask
# compute_escape_directives - 收 `@life(escape(...), noescape(...))` 的方向性契约。
# out[0] = 强制**逃逸**位(无条件,不受数据流/常量折叠影响)、out[1] = 强制**不逃逸**位。
# 只认 `@life(...)` 里的标签(独立的 `@willFree(...)` 叠加形式里不放这两个)。
# 同一形参同时标 escape 与 noescape → fail-fast(矛盾契约,绝不猜一边)。
def compute_escape_directives(fd: ast.AST*, out: sizet*) -> int:
"""解析 @life(...) 里的 escape/noescape 标签到 out[0]/out[1];0=完成 / 1=入参非法"""
if out is None: return 1
out[0] = 0
out[1] = 0
fn: ast.FunctionDef* = ast.FunctionDef*(fd)
if fn is None or fn.decorator_list is None: return 0
dl: list[ast.AST*]* = fn.decorator_list
dn: sizet = len(dl)
i: sizet = 0
while i < dn:
deco: ast.AST* = dl.get(i)
i += 1
if deco is None or deco.kind() != ast.ASTKind.Call: continue
dc: ast.Call* = ast.Call*(deco)
if dc.func is None or dc.func.kind() != ast.ASTKind.Name: continue
fname: ast.Name* = ast.Name*(dc.func)
if fname.id is None or string.strcmp(fname.id, "life") != 0: continue
if dc.args is None: continue
an: sizet = len(dc.args)
j: sizet = 0
while j < an:
tg: ast.AST* = dc.args.get(j)
j += 1
if tg is None or tg.kind() != ast.ASTKind.Call: continue
tcl: ast.Call* = ast.Call*(tg)
tnm2: str = None
if tcl.func is not None and tcl.func.kind() == ast.ASTKind.Name:
tn2: ast.Name* = ast.Name*(tcl.func)
tnm2 = tn2.id
if tnm2 is None: continue
is_esc2: int = string.strcmp(tnm2, "escape")
is_nesc2: int = string.strcmp(tnm2, "noescape")
# A1(§14):stores 归到"强制逃逸"位、borrows 归到"强制不逃逸"位(复用同一对契约位)
if string.strcmp(tnm2, "stores") == 0: is_esc2 = 0
if string.strcmp(tnm2, "borrows") == 0: is_nesc2 = 0
if is_esc2 != 0 and is_nesc2 != 0: continue
if tcl.args is None or len(tcl.args) == 0:
fatal_error(tg, f"@life tag `{tnm2}(...)` needs at least one parameter name or 0-based position", "LifetimeError")
continue
an4: sizet = len(tcl.args)
k4: sizet = 0
while k4 < an4:
idx2: int = _WillFreeArgIndex(fn, tcl.args.get(k4))
k4 += 1
if idx2 < 0: continue
if is_esc2 == 0:
if ((int(out[1]) >> idx2) & 1) != 0:
fatal_error(tg, f"contradictory lifetime tags: parameter #{idx2} is marked both escape/stores(...) and noescape/borrows(...)", "LifetimeError")
continue
out[0] = out[0] | (sizet(1) << idx2)
else:
if ((int(out[0]) >> idx2) & 1) != 0:
fatal_error(tg, f"contradictory lifetime tags: parameter #{idx2} is marked both escape/stores(...) and noescape/borrows(...)", "LifetimeError")
continue
out[1] = out[1] | (sizet(1) << idx2)
return 0
# ---- A2(§14)`@life(ret(...))`:返回值生命期契约的编码 ----
# 该编码同时用于 StructGenMixin.EscDirTable 的第 4 槽与 .vpi 行尾 `escr=`。
define RET_KIND_NONE = 0 # 无声明(= 回落到现有推断)
define RET_KIND_FRAME = 1 # `ret(frame)`:返回值可能指向**本帧**存储
define RET_KIND_HEAP = 2 # `ret(heap)`:返回值是自由生命期(可自由传递)
define RET_KIND_STATIC = 3 # `ret(static)`:返回值指向静态存储
define RET_KIND_NONFRAME = 4 # `ret(borrows=none)`:不借用任何形参 → 非本帧
define RET_KIND_BORROW_BASE = 8 # `ret(borrows=x)`:值 = RET_KIND_BORROW_BASE + 形参下标
# compute_life_anno - 收 `@life(ret(...))` 的返回值生命期契约到 out[0](ret_kind 编码)。
# 与 compute_escape_directives 同构:只认 `@life(...)` 里的 `ret` 标签;矛盾/未知一律 fail-fast。
# 语法(§14.1):`ret(frame)` / `ret(heap)` / `ret(static)` / `ret(borrows=<形参名或序号>)` / `ret(borrows=none)`。
def compute_life_anno(fd: ast.AST*, out: sizet*) -> int:
"""解析 @life(ret(...)) 到 out[0](ret_kind 编码);0=完成 / 1=入参非法"""
if out is None: return 1
out[0] = 0
fn: ast.FunctionDef* = ast.FunctionDef*(fd)
if fn is None or fn.decorator_list is None: return 0
dl: list[ast.AST*]* = fn.decorator_list
dn: sizet = len(dl)
i: sizet = 0
while i < dn:
deco: ast.AST* = dl.get(i)
i += 1
if deco is None or deco.kind() != ast.ASTKind.Call: continue
dc: ast.Call* = ast.Call*(deco)
if dc.func is None or dc.func.kind() != ast.ASTKind.Name: continue
fname: ast.Name* = ast.Name*(dc.func)
if fname.id is None or string.strcmp(fname.id, "life") != 0: continue
if dc.args is None: continue
an: sizet = len(dc.args)
j: sizet = 0
while j < an:
tg: ast.AST* = dc.args.get(j)
j += 1
if tg is None or tg.kind() != ast.ASTKind.Call: continue
tcl: ast.Call* = ast.Call*(tg)
tnm: str = None
if tcl.func is not None and tcl.func.kind() == ast.ASTKind.Name:
tn: ast.Name* = ast.Name*(tcl.func)
tnm = tn.id
if tnm is None or string.strcmp(tnm, "ret") != 0: continue
rk: int = _RetKindFromTag(fn, tcl)
if rk < 0: continue # 已 fail-fast
if int(out[0]) != 0 and int(out[0]) != rk:
fatal_error(tg, "contradictory @life(ret(...)) declarations on the same function", "LifetimeError")
continue
out[0] = sizet(rk)
return 0
def _RetKindFromTag(fn: ast.FunctionDef*, tcl: ast.Call*) -> int:
"""把一个 `ret(...)` 标签解析成 ret_kind 编码;非法即 fail-fast 并返回 -1"""
# 形式一:ret(borrows=x) / ret(borrows=none)
if tcl.keywords is not None and len(tcl.keywords) > 0:
if len(tcl.keywords) != 1:
fatal_error(tcl, "@life(ret(...)) accepts exactly one keyword: `borrows=<param>` or `borrows=none`", "LifetimeError")
return -1
if tcl.args is not None and len(tcl.args) > 0:
fatal_error(tcl, "@life(ret(...)): cannot mix positional arguments with `borrows=`", "LifetimeError")
return -1
kw: ast.AST* = tcl.keywords.get(0)
if kw is None or kw.kind() != ast.ASTKind.Keyword:
fatal_error(tcl, "@life(ret(...)) accepts only `ret(frame|heap|static)` or `ret(borrows=<param>|none)`", "LifetimeError")
return -1
ka: ast.Keyword* = ast.Keyword*(kw)
if ka.arg is None or string.strcmp(ka.arg, "borrows") != 0:
fatal_error(tcl, "@life(ret(...)): the only supported keyword is `borrows`", "LifetimeError")
return -1
val: ast.AST* = ka.value
if val is None:
fatal_error(tcl, "@life(ret(borrows=...)) requires a parameter name or `none`", "LifetimeError")
return -1
# `borrows=none`:返回值不借用任何形参(非本帧)。
# 其余一律交给 _WillFreeArgIndex:它同时认**形参名**与**0 起序号**(与 willFree(...) 同口径,
# 见 includes/stdlib.vp 的注释:"写法二选一:形参名 willFree(p) 或形参位置序号 willFree(0)"),
# 非法实参(非名非整数/越界/不是形参名)在那边 fail-fast。
if val.kind() == ast.ASTKind.Name:
vn: ast.Name* = ast.Name*(val)
if vn.id is None: return -1
if string.strcmp(vn.id, "none") == 0: return RET_KIND_NONFRAME
elif val.kind() == ast.ASTKind.Constant:
vc: ast.Constant* = ast.Constant*(val)
if vc is not None and vc.const_kind == ast.CONST_NONE: return RET_KIND_NONFRAME
else:
fatal_error(tcl, "@life(ret(borrows=...)) accepts only a parameter name (or its 0-based position) or `none`", "LifetimeError")
return -1
idx: int = _WillFreeArgIndex(fn, val)
if idx < 0: return -1 # 已 fail-fast
return RET_KIND_BORROW_BASE + idx
# 形式二:ret(frame) / ret(heap) / ret(static)
if tcl.args is None or len(tcl.args) != 1:
fatal_error(tcl, "@life(ret(...)) expects exactly one of `frame` / `heap` / `static`, or `borrows=<param>` / `borrows=none`", "LifetimeError")
return -1
a0: ast.AST* = tcl.args.get(0)
if a0 is None or a0.kind() != ast.ASTKind.Name:
fatal_error(tcl, "@life(ret(...)) expects exactly one of `frame` / `heap` / `static`", "LifetimeError")
return -1
an: ast.Name* = ast.Name*(a0)
if an.id is None: return -1
if string.strcmp(an.id, "frame") == 0: return RET_KIND_FRAME
if string.strcmp(an.id, "heap") == 0: return RET_KIND_HEAP
if string.strcmp(an.id, "static") == 0: return RET_KIND_STATIC
fatal_error(a0, f"@life(ret({an.id})): expected `frame` / `heap` / `static`, or `borrows=<param>` / `borrows=none`", "LifetimeError")
return -1
def get_ret_kind(name: str) -> int:
"""本文件函数的 `@life(ret(...))` 契约编码;0 = 无声明"""
if _g_gen is not None: return _g_gen.StructGen.get_ret_kind(name)
return 0
def get_ret_kind_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版 `@life(ret(...))` 契约:查 "Class.method" 并沿 MRO 回退;0 = 无声明"""
if _g_gen is not None: return _g_gen.StructGen.get_ret_kind_method(pool, class_name, method_name)
return 0
def get_ret_kind_by_func(fd: ast.AST*) -> int:
"""**定义端**按 FunctionDef* 取本函数的 `@life(ret(...))` 契约(顶层/方法/嵌套都覆盖);0 = 无声明"""
if _g_gen is not None: return _g_gen.StructGen.get_ret_kind_by_func(fd)
return 0
def lookup_ret_kind_via_vpi(cls_sha1: str, class_name: str, func_name: str) -> int:
"""跨模块给被调者的 `@life(ret(...))` 契约;0 = 无声明(含该模块未加载)"""
if _g_gen is not None: return _g_gen.CallGenBase.lookup_ret_kind_via_vpi(cls_sha1, class_name, func_name)
return 0
def _WillFreeArgIndex(fn: ast.FunctionDef*, arg: ast.AST*) -> int:
"""标签实参 → 形参下标(0 起,self/cls 计入);非法即报错并返回 -1。
被 `willFree` / `escape|stores` / `noescape|borrows` / `ret(borrows=...)` 共用,
故报文统一用"@life 标签"口径。"""
if arg is None:
fatal_error(arg, "an @life(...) tag requires a parameter name or its 0-based position", "LifetimeError")
return -1
# FunctionDef.args 的字段静态类型是 AST*(见 includes/ast/stmts.vp),必须先转成 Arguments*
# 才能再取 .args 列表;直接写 fn.args.args 会去 AST 类查 'args' 触发 MemberLookupFailFast。
if fn.args is None:
fatal_error(arg, "an @life(...) tag cannot be used here: the definition has no parameter list", "LifetimeError")
return -1
fargs: ast.Arguments* = ast.Arguments*(fn.args)
if fargs is None or fargs.args is None:
fatal_error(arg, "an @life(...) tag cannot be used here: the definition has no parameter list", "LifetimeError")
return -1
pl: list[ast.AST*]* = fargs.args
pn: sizet = len(pl)
if arg.kind() == ast.ASTKind.Name:
nma: ast.Name* = ast.Name*(arg)
if nma.id is None: return -1
for k in range(int(pn)):
pa: ast.AST* = pl.get(k)
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
aa: ast.Arg* = ast.Arg*(pa)
if aa.arg is not None and string.strcmp(aa.arg, nma.id) == 0: return int(k)
fatal_error(arg, f"@life tag argument '{nma.id}': not a parameter of this function", "LifetimeError")
return -1
if arg.kind() != ast.ASTKind.Constant:
fatal_error(arg, "an @life(...) tag accepts only a parameter name or an integer position", "LifetimeError")
return -1
ca: ast.Constant* = ast.Constant*(arg)
if ca.const_kind != ast.CONST_INT:
fatal_error(arg, "an @life(...) tag accepts only a parameter name or an integer position", "LifetimeError")
return -1
idx: int = int(ca.int_val)
if idx < 0 or idx >= int(pn):
fatal_error(arg, f"@life tag position {idx}: out of range (the function has {int(pn)} parameter(s))", "LifetimeError")
return -1
return idx
def get_param_escapes_hard(name: str) -> int:
"""本文件函数"硬逃逸位"掩码(全局写 / 返回形参 / 传递闭包 / base 未定的字段写);0 = 无记录"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard(name)
return 0
def get_param_escapes_hard_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版硬逃逸位:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard_method(pool, class_name, method_name)
return 0
# ---- 声明式指令(@life(escape/noescape))的最终裁决掩码 ----
# 契约表与推断表分开:调用点在所有推断/裁剪之后最后套用,任何推导都不能推翻。
def get_param_escape_force(name: str) -> int:
"""`@life(escape(...))` 强制逃逸位掩码;0 = 无该标注"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escape_force(name)
return 0
def get_param_noescape_force(name: str) -> int:
"""`@life(noescape(...))` 强制不逃逸位掩码;0 = 无该标注"""
if _g_gen is not None: return _g_gen.StructGen.get_param_noescape_force(name)
return 0
def get_param_escape_force_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版 `@life(escape(...))` 掩码:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escape_force_method(pool, class_name, method_name)
return 0
def get_param_noescape_force_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版 `@life(noescape(...))` 掩码:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_noescape_force_method(pool, class_name, method_name)
return 0
# escape_leak_line - **错误路径专用**:被调者第 want_pi 号形参的"首个逃逸写入点"行号。
# 只在 fail-fast 报错时调用(会重扫一次被调者函数体);跨模块拿不到 AST 时返回 0。
def escape_leak_line(name: str, want_pi: int) -> int:
"""形参 want_pi 的逃逸点行号;0 = 未知(含跨模块)"""
if _g_gen is not None: return _g_gen.StructGen.escape_leak_line(name, want_pi)
return 0
def get_param_willfree(name: str) -> int:
"""本文件函数的 willFree 形参掩码(第 k 位=第 k 个形参在本调用后失效);无记录返回 0"""
if _g_gen is not None: return _g_gen.StructGen.get_param_willfree(name)
return 0
def get_param_willfree_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法 willFree 掩码:查 "Class.method" 并沿 MRO 回退;无记录返回 0"""
if _g_gen is not None: return _g_gen.StructGen.get_param_willfree_method(pool, class_name, method_name)
return 0
# ---------------- 跨过程常量特化(P3 收尾):调用点用常量实参重算逃逸掩码 ----------------
def refine_escape_mask(pool: memhub.MemBuddy*, name: str, param_base: int, cargs: list[ast.AST*]*, can: sizet, mask: int) -> int:
"""普通函数:实参含编译期常量时把常量代进被调者体重算逃逸掩码;不适用时原样返回 mask"""
if _g_gen is not None: return _g_gen.StructGen.refine_escape_mask(pool, name, param_base, cargs, can, mask)
return mask
def refine_escape_mask_method(pool: memhub.MemBuddy*, class_name: str, method_name: str, cargs: list[ast.AST*]*, can: sizet, mask: int) -> int:
"""方法:同上,键走 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.refine_escape_mask_method(pool, class_name, method_name, cargs, can, mask)
return mask
def compute_escape_clears(fd: ast.FunctionDef*, out_packed: sizet*, max: int) -> int:
"""算出可落 .vpi 的"常量守卫"记录条数(打包值写进 out_packed);0 = 没有"""
if _g_gen is not None: return _g_gen.StructGen.compute_escape_clears(None, fd, out_packed, max)
return 0
def build_const_env(cargs: list[ast.AST*]*, can: sizet, param_base: int) -> int:
"""把实参字面量装进常量环境;1 = 装了至少一个(供跨模块守卫套用)"""
if _g_gen is not None: return _g_gen.StructGen.build_const_env(None, cargs, can, param_base)
return 0
def apply_escape_clears(packed: sizet*, n: int, mask: int) -> int:
"""按常量环境套用守卫记录(清位);返回精化后的掩码"""
if _g_gen is not None: return _g_gen.StructGen.apply_escape_clears(packed, n, mask)
return mask
def const_env_ptr() -> sizet*:
"""取常量环境区指针(`build_const_env` 装填的那块,2 槽/形参);None = 不可用"""
if _g_gen is not None: return _g_gen.StructGen.const_env_ptr()
return None
# ---------------- 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"""
if _g_gen is not None: return _g_gen.StructGen.find_struct_by_type(struct_ty)
return None
# ---------------- register_struct (delegates to StructGenMixin.register_struct) ----------------
def register_struct(pool: memhub.MemBuddy*, name: str, struct_ty: mlirlite.LLVMType*, sha1: str = None) -> GenTypes.StructEntry*:
"""Delegate to gen.register_struct"""
if _g_gen is not None: return _g_gen.StructGen.register_struct(pool, name, struct_ty, sha1)
return None
# ---------------- add_field (delegates to StructGenMixin.add_field) ----------------
def add_field(pool: memhub.MemBuddy*, struct_entry: GenTypes.StructEntry*, field_name: str, field_ty: mlirlite.LLVMType*) -> int:
"""Delegate to gen.add_field"""
if _g_gen is not None: return _g_gen.StructGen.add_field(pool, struct_entry, field_name, field_ty)
return -1
# ---------------- register_struct_alias (delegates to StructGenMixin.register_struct_alias) ----------------
def register_struct_alias(pool: memhub.MemBuddy*, name: str, sha1: str, src_entry: GenTypes.StructEntry*) -> GenTypes.StructEntry*:
"""Delegate to gen.StructGen.register_struct_alias for package re-export aliases."""
if _g_gen is not None: return _g_gen.StructGen.register_struct_alias(pool, name, sha1, src_entry)
return None
# ---------------- get_stdlib_alloc (delegates to StructGenMixin.get_stdlib_alloc) ----------------
def get_stdlib_alloc() -> int:
"""Delegate to gen.get_stdlib_alloc"""
if _g_gen is not None: return _g_gen.StructGen.get_stdlib_alloc()
return 0
# ---------------- compute_package_from_relpath (delegates to ImportsGenMixin.compute_package_from_relpath) ----------------
def compute_package_from_relpath(pool: memhub.MemBuddy*, rel_path: str) -> str:
"""Delegate to gen.compute_package_from_relpath"""
if _g_gen is not None: return _g_gen.ImportsGen.compute_package_from_relpath(pool, rel_path)
return None
# ---------------- is_module_imported (delegates to ImportsGenMixin.is_module_imported) ----------------
def is_module_imported(imported_modules: str, name: str) -> int:
"""Delegate to gen.is_module_imported"""
if _g_gen is not None: return _g_gen.ImportsGen.is_module_imported(imported_modules, name)
return 0
# Single source of truth for SHA1-prefix recognition: sha1_prefix_len(name) returns
# 17 for "{16 lowercase hex}.Class", else 0; is_sha1_hex16(seg) checks a bare segment.
def sha1_prefix_len(name: str) -> int:
"""Return 17 if name starts with "{16 lowercase hex}.", otherwise 0."""
if name is None: return 0
if string.strlen(name) < 17: return 0
if name[16] != '.': return 0
i: sizet
for i in range(16):
c: int = name[i]
# 48-57 = '0'-'9', 97-102 = 'a'-'f'
if not ((c >= 48 and c <= 57) or (c >= 97 and c <= 102)): return 0
return 17
def is_sha1_hex16(seg: str) -> int:
"""Return 1 if seg is exactly 16 lowercase hex chars, else 0."""
if seg is None: return 0
if string.strlen(seg) != 16: return 0
i: sizet
for i in range(16):
c: int = seg[i]
if not ((c >= 48 and c <= 57) or (c >= 97 and c <= 102)): return 0
return 1
# ---------------- add_imported_module (delegates to ImportsGenMixin.add_imported_module) ----------------
def add_imported_module(pool: memhub.MemBuddy*, imported_modules: str, name: str) -> str:
"""Delegate to gen.add_imported_module"""
if _g_gen is not None: return _g_gen.ImportsGen.add_imported_module(pool, imported_modules, name)
return None
# ---------------- translate_import (delegates to ImportsGenMixin.translate_import) ----------------
def translate_import(trans: HT.Translator*, node: ast.AST*) -> int:
"""Delegate to gen.translate_import"""
if _g_gen is not None: return _g_gen.ImportsGen.translate_import(trans, node)
return 0
# ---------------- translate_import_from_module (delegates to ImportsGenMixin.translate_import_from_module) ----------------
def translate_import_from_module(trans: HT.Translator*, node: ast.AST*) -> int:
"""Delegate to gen.translate_import_from_module"""
if _g_gen is not None: return _g_gen.ImportsGen.translate_import_from_module(trans, node)
return 0
# ---------------- translate_import_from_names (delegates to ImportsGenMixin.translate_import_from_names) ----------------
def translate_import_from_names(trans: HT.Translator*, node: ast.AST*) -> int:
"""Delegate to gen.translate_import_from_names"""
if _g_gen is not None: return _g_gen.ImportsGen.translate_import_from_names(trans, node)
return 0
# ---------------- _get_parent_class (delegates to ClassGenBase._get_parent_class) ----------------
def _get_parent_class(cd: ast.ClassDef*, trans: HT.Translator* = None) -> str:
"""Delegate to gen._get_parent_class"""
if _g_gen is not None: return _g_gen.ClassGenBase._get_parent_class(cd, trans)
return None
# ---------------- _register_generic_template (delegates to ClassGenGeneric._register_generic_template) ----------------
def _register_generic_template(pool: memhub.MemBuddy*, cd: ast.ClassDef*, module_sha1: str):
"""Delegate to gen._register_generic_template"""
if _g_gen is not None: _g_gen.ClassGenGeneric._register_generic_template(pool, cd, module_sha1)
# ---------------- _register_generic_func_template (delegates to ClassGenGeneric._register_generic_func_template) ----------------
def _register_generic_func_template(pool: memhub.MemBuddy*, fd: ast.FunctionDef*, module_sha1: str):
"""Delegate to gen.ClassGenGeneric._register_generic_func_template for scan pre-registration."""
if _g_gen is not None: _g_gen.ClassGenGeneric._register_generic_func_template(pool, fd, module_sha1)
# ---------------- _mangle_name_with_sha1 (delegates to FuncGenBase._mangle_name_with_sha1) ----------------
def _mangle_name_with_sha1(sha1: str, name: str) -> str:
"""Delegate to gen._mangle_name_with_sha1"""
if _g_gen is not None: return _g_gen.FuncGenBase._mangle_name_with_sha1(sha1, name)
return None
# ---------------- forward_declare_functions (delegates to FuncGenDef.forward_declare_functions) ----------------
def forward_declare_functions(trans: HT.Translator*, tree: ast.AST*) -> int:
"""Delegate to gen.forward_declare_functions"""
if _g_gen is not None: return _g_gen.FuncGenDef.forward_declare_functions(trans, tree)
return 0
# f-string escape rules: results point at the current function's stack buffer, so
# escaping positions fail-fast; use escape_fstring_copy to copy explicitly.
def fail_if_fstring_escape(node: ast.AST*, where: str) -> int:
"""Fail fast when an f-string sits in an escaping position such as a return value."""
if node is None: return 0
hit: int = 0
k: int = node.kind()
if k == ast.ASTKind.JoinedStr or k == ast.ASTKind.FormattedValue: hit = 1
elif k == ast.ASTKind.Tuple:
tp: ast.Tuple* = ast.Tuple*(node)
if tp.elts is not None:
en: sizet = len(tp.elts)
for i in range(int(en)):
e: ast.AST* = tp.elts.get(i)
if e is not None:
ek: int = e.kind()
if ek == ast.ASTKind.JoinedStr or ek == ast.ASTKind.FormattedValue:
hit = 1
break
if hit == 0: return 0
if _g_gen is not None:
_g_gen.TypeGenErr.fatal_error(node, f"f-string result cannot be used in {where} (escape): f-string points at **the stack buffer of the current function**, " f"which becomes invalid once the function returns; carrying this pointer out of the function reads an already-popped frame" f"(historical incident: ASan stack-buffer-underflow in vsnprintf). " f"when the value must outlive the function, allocate and copy explicitly: " f"tmp: str = f\"...\"; out: str = GenApi.escape_fstring_copy(tmp); return out.", "TypeError")
return 0
def escape_fstring_copy(src: str) -> str:
"""Copy an f-string result onto the stdlib heap and return it; the only escape path."""
if src is None: return None
n: sizet = string.strlen(src)
out: str = stdlib.malloc(n + 1)
if out is None: return None
string.strcpy(out, src)
return out
# Stack-escape rule: the address of a stack object must not outlive the current frame.
# The judgement is now the unified **life-tag** mechanism (GenTypes define LIFE_*); the
# implementation lives in AssignGen.fail_if_life_escapes. It covers:
# (a) `&name` — explicit address of a local variable / parameter;
# (b) `name` — a bare local ARRAY used where a pointer is expected (array decay);
# (c) propagated borrows — `s: str = l` gives Life(s)=FRAME, so `return s` is caught.
# 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, leak_func: str = None, leak_param: int = -1) -> int:
"""Fail fast (LifetimeError) when the address of a stack object escapes the frame.
leak_func/leak_param 给出被调者与形参位:确认要报错后才去采集"泄漏点"帧
(见 escape_leak_line);给 None/-1 则不采集。
"""
if node is None or trans is None or _g_gen is None: return 0
return _g_gen.AssignGen.fail_if_life_escapes(node, where, trans, target_ty, leak_func, leak_param)
# ---------------- 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"""
if _g_gen is not None: return _g_gen.TranslatorGen.create_wrapper_main(trans, tree, i32_ty)
return 0
# ---------------- handle_module_level_var (delegates to TranslatorGenMixin.handle_module_level_var) ----------------
def handle_module_level_var(trans: HT.Translator*, node: ast.AST*) -> int:
"""Delegate to gen.handle_module_level_var"""
if _g_gen is not None: return _g_gen.TranslatorGen.handle_module_level_var(trans, node)
return 0
# ---------------- translate_children (delegates to TranslatorGenMixin.translate_children) ----------------
def translate_children(trans: HT.Translator*, tree: ast.AST*) -> int:
"""Delegate to gen.translate_children"""
if _g_gen is not None: return _g_gen.TranslatorGen.translate_children(trans, tree)
return 0
# ---------------- init_symbol_table (delegates to VarGenMixin.init_symbol_table) ----------------
def init_symbol_table(pool: memhub.MemBuddy*) -> GenTypes.SymbolTable*:
"""Delegate to gen.init_symbol_table"""
if _g_gen is not None: return _g_gen.VarGen.init_symbol_table(pool)
return None
# ---------------- SetTempDir (delegates to CallGenBase.SetTempDir) ----------------
def SetTempDir(temp_dir: str):
"""Delegate to gen.SetTempDir"""
if _g_gen is not None: _g_gen.CallGenBase.SetTempDir(temp_dir)
# ---------------- find_func_in_module (delegates to CallGenBase.find_func_in_module) ----------------
def find_func_in_module(mod: mlirlite.LLVMModule*, name: str) -> mlirlite.Function*:
"""Delegate to gen.find_func_in_module"""
if _g_gen is not None: return _g_gen.CallGenBase.find_func_in_module(mod, name)
return None
# ---------------- add_func_to_table (delegates to CallGenLeafKit.add_func_to_table) ----------------
def add_func_to_table(funcs: GenTypes.FuncEntry*, func_count: int, name: str, func: mlirlite.Function*, max_count: int, defaults: t.CPtr = None, default_count: int = 0, param_count: int = 0, pool: memhub.MemBuddy* = None, def_sha1: str = None) -> int:
"""Delegate to gen.add_func_to_table; def_sha1 is the defining module's SHA1."""
if _g_gen is not None: return _g_gen.CallGenLeafKit.add_func_to_table(funcs, func_count, name, func, max_count, defaults, default_count, param_count, pool, def_sha1)
return 0
# ---------------- init_func_table (delegates to CallGenLeafKit.init_func_table) ----------------
def init_func_table(pool: memhub.MemBuddy*, max_count: int) -> GenTypes.FuncEntry*:
"""Delegate to gen.init_func_table"""
if _g_gen is not None: return _g_gen.CallGenLeafKit.init_func_table(pool, max_count)
return None
# ---------------- translate_call (delegates to CallGenMixin.translate_call) ----------------
def translate_call(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Delegate to gen.translate_call"""
if _g_gen is not None: return _g_gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
return None
# ---------------- translate_printf_call (delegates to CallGenLeafKit.translate_printf_call) ----------------
def translate_printf_call(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator* = None):
"""Delegate to gen.translate_printf_call"""
if _g_gen is not None: _g_gen.CallGenLeafKit.translate_printf_call(pool, builder, mod, cl, trans)
# ---------------- translate_print_call (delegates to CallGenLeafKit.translate_print_call) ----------------
def translate_print_call(pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, trans: HT.Translator* = None):
"""Delegate to gen.translate_print_call"""
if _g_gen is not None: _g_gen.CallGenLeafKit.translate_print_call(pool, builder, mod, cl, trans)
# ---------------- add_global_name (delegates to TranslatorGenMixin.add_global_name) ----------------
def add_global_name(trans: HT.Translator*, name: str):
"""Delegate to gen.TranslatorGen.add_global_name"""
if _g_gen is not None: _g_gen.TranslatorGen.add_global_name(trans, name)
# ---------------- is_global_name (delegates to TranslatorGenMixin.is_global_name) ----------------
def is_global_name(trans: HT.Translator*, name: str) -> int:
"""Delegate to gen.TranslatorGen.is_global_name"""
if _g_gen is not None: return _g_gen.TranslatorGen.is_global_name(trans, name)
return 0
# ---------------- add_nonlocal_name (delegates to TranslatorGenMixin.add_nonlocal_name) ----------------
def add_nonlocal_name(trans: HT.Translator*, name: str, var_ty: mlirlite.LLVMType* = None):
"""Delegate to gen.TranslatorGen.add_nonlocal_name"""
if _g_gen is not None: _g_gen.TranslatorGen.add_nonlocal_name(trans, name, var_ty)
# ---------------- get_nonlocal_type (delegates to TranslatorGenMixin.get_nonlocal_type) ----------------
def get_nonlocal_type(trans: HT.Translator*, name: str) -> mlirlite.LLVMType*:
"""Delegate to gen.TranslatorGen.get_nonlocal_type"""
if _g_gen is not None: return _g_gen.TranslatorGen.get_nonlocal_type(trans, name)
return None
# ---------------- is_nonlocal_name (delegates to TranslatorGenMixin.is_nonlocal_name) ----------------
def is_nonlocal_name(trans: HT.Translator*, name: str) -> int:
"""Delegate to gen.TranslatorGen.is_nonlocal_name"""
if _g_gen is not None: return _g_gen.TranslatorGen.is_nonlocal_name(trans, name)
return 0
# ---------------- get_nonlocal_index (delegates to TranslatorGenMixin.get_nonlocal_index) ----------------
def get_nonlocal_index(trans: HT.Translator*, name: str) -> int:
"""Delegate to gen.TranslatorGen.get_nonlocal_index"""
if _g_gen is not None: return _g_gen.TranslatorGen.get_nonlocal_index(trans, name)
return 0
# ---------------- clear_scope_names (delegates to TranslatorGenMixin.clear_scope_names) ----------------
def clear_scope_names(trans: HT.Translator*):
"""Delegate to gen.TranslatorGen.clear_scope_names"""
if _g_gen is not None: _g_gen.TranslatorGen.clear_scope_names(trans)
# ---------------- update_global_func_spec (delegates to CallGenLeafKit.update_global_func_spec) ----------------
def update_global_func_spec(name: str, func: mlirlite.Function*) -> int:
"""Delegate to gen.CallGenLeafKit.update_global_func_spec."""
if _g_gen is not None: return _g_gen.CallGenLeafKit.update_global_func_spec(name, func)
return 0