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(, , ...):逐个标签并入 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(|) 并并入掩码;未知标签/非法实参 → 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()` / `escape()` / `noescape()` 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=` 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=|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=` / `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=` / `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