refactor(callgen): annotate string values as char* (mechanical pass)

One-token annotation change across the call generators: str -> char*.
No logic changes; the same pass also rewrote literal "string" in comments,
which is why this is committed as a checkpoint rather than as behaviour.
This commit is contained in:
TermiNexus committed 2026-10-31 16:50:55 +08:00
1 parent cd78edd702
commit 84f11a69e7
8 files changed
+570 -570

No files matched your search

+107 -107
View File
@@ -273,7 +273,7 @@ novirtual class CallGenBase:
# appended by the caller) are preserved. Idempotent: _apply_auto_addr_arg passes an already-pointer
# argument through, so a caller that also ran translate_call_arg does not double-address.
def _apply_auto_addr_to_call_args(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*,
trans: HT.Translator*, callee_name: str, arg_nodes: list[ast.AST*]*,
trans: HT.Translator*, callee_name: char*, arg_nodes: list[ast.AST*]*,
head: mlirlite.Value*, count: int, start_idx: int) -> mlirlite.Value*:
"""Apply value-reference auto-address (&T / &temp T) to a call's user args; returns the chain head."""
if head is None or count <= 0: return head
@@ -305,7 +305,7 @@ novirtual class CallGenBase:
mlirlite.value_set_next(new_tail, cur)
return new_head
def _lookup_module_sha1(self, mod_name: str) -> str:
def _lookup_module_sha1(self, mod_name: char*) -> str:
"""Resolve a module name (or an already-resolved SHA1) to a SHA1; None if absent.
Re-export entries may store the defining SHA1 directly, so a 16-hex input is
@@ -323,11 +323,11 @@ novirtual class CallGenBase:
if is_hex != 0: return mod_name
return StubMergerStore.LookupSha1ByModName(mod_name)
def _lookup_module_sha1_suffix(self, mod_suffix: str) -> str:
def _lookup_module_sha1_suffix(self, mod_suffix: char*) -> str:
"""Look up a module SHA1 by suffix (e.g. "__types" matches "mlirlite.__types") for relative imports, via StubMerger.LookupSha1ByModNameSuffix."""
return StubMergerStore.LookupSha1ByModNameSuffix(mod_suffix)
def _module_package_from_sha1(self, pool: memhub.MemBuddy*, mod_sha1: str) -> str:
def _module_package_from_sha1(self, pool: memhub.MemBuddy*, mod_sha1: char*) -> str:
"""Compute the module's package name (directory part, '/' to '.') from the SHA1 store's rel_path"""
if pool is None or mod_sha1 is None: return None
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
@@ -338,7 +338,7 @@ novirtual class CallGenBase:
sidx: sizet = sizet(i) * 17
if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0: continue
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
rel: str = rel_arr_ptr + ridx
rel: char* = rel_arr_ptr + ridx
# Includes are registered with a workspace-relative path ("includes/ast/__init__.vp")
# because the scan root is the workspace, while the module name drops that prefix
# ("ast", "ast.lexer"). Dropping it here too keeps re-export resolution (`from .lexer
@@ -346,11 +346,11 @@ novirtual class CallGenBase:
rel_off: sizet = 0
if string.strncmp(rel, "includes/", 9) == 0: rel_off = 9
rel = rel + rel_off
last_slash: str = string.strrchr(rel, 47) # 47 = ord('/')
last_slash: char* = string.strrchr(rel, 47) # 47 = ord('/')
if last_slash is None: return None
pkg_len: sizet = sizet(uint64(last_slash) - uint64(rel))
if pkg_len == 0: return None
pkg_buf: str = pool.alloc(pkg_len + 1)
pkg_buf: char* = pool.alloc(pkg_len + 1)
if pkg_buf is None: return None
for pi in range(int(pkg_len)):
if rel[pi] == '/': pkg_buf[pi] = '.'
@@ -360,7 +360,7 @@ novirtual class CallGenBase:
return None
def _lookup_reexport_from_vpi(self, pool: memhub.MemBuddy*, mod_sha1: str, func_name: str, allow_star: int = 1) -> str:
def _lookup_reexport_from_vpi(self, pool: memhub.MemBuddy*, mod_sha1: char*, func_name: char*, allow_star: int = 1) -> str:
"""On a re-export table miss, parse the module's .vpi from-import statements to locate the defining module (the table may be empty since it is only populated during full translation).
allow_star=0 skips `from X import *` candidates: they are unverified hints (the caller must
check them), and a CLASS owner must never be taken from one -- nearly every file star-imports
@@ -368,12 +368,12 @@ novirtual class CallGenBase:
forward-declared as "stdint.TypeGenErr" this way, then every vpi/text.ll lookup missed)."""
if pool is None or mod_sha1 is None or func_name is None: return None
# accumulate star-import candidates (space-separated); caller validates each
star_cands: str = None
star_cands: char* = None
star_cands_len: sizet = 0
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
temp_dir: char* = self.Gen.TypeGenBase.get_temp_dir()
if temp_dir is None: return None
td_len: sizet = string.strlen(temp_dir)
vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, mod_sha1, "vpi")
vpi_path: char* = StubMergerCollect._sliced_path(temp_dir, td_len, mod_sha1, "vpi")
if vpi_path is None: return None
READ_SZ: sizet = 1048576
buf: bytes = stdlib.malloc(READ_SZ)
@@ -436,7 +436,7 @@ novirtual class CallGenBase:
if allow_star == 0: continue
star_dot: int = 0
if buf[mod_start] == '.': star_dot = 1
star_buf: str = pool.alloc(mod_end - mod_start + 1)
star_buf: char* = pool.alloc(mod_end - mod_start + 1)
if star_buf is not None:
si2: sizet = 0
while mod_start + si2 < mod_end:
@@ -446,16 +446,16 @@ novirtual class CallGenBase:
while star_buf[0] == '.': star_buf = star_buf + 1
if string.strlen(star_buf) > 0:
if star_dot != 0:
spkg: str = self._module_package_from_sha1(pool, mod_sha1)
spkg: char* = self._module_package_from_sha1(pool, mod_sha1)
if spkg is not None:
srl: sizet = string.strlen(spkg) + string.strlen(star_buf) + 2
sres: str = pool.alloc(srl)
sres: char* = pool.alloc(srl)
if sres is not None:
viperlib.snprintf(sres, srl, "%s.%s", spkg, star_buf)
star_buf = sres
# accumulate candidates (space-separated)
sbl: sizet = string.strlen(star_buf)
new_cands: str = pool.alloc(star_cands_len + sbl + 2)
new_cands: char* = pool.alloc(star_cands_len + sbl + 2)
if new_cands is not None:
if star_cands is not None and star_cands_len > 0:
string.strcpy(new_cands, star_cands)
@@ -492,7 +492,7 @@ novirtual class CallGenBase:
# hit: parse module name (leading dot -> relative, add pkg prefix)
has_dot: int = 0
if buf[mod_start] == '.': has_dot = 1
mod_buf: str = pool.alloc(mod_end - mod_start + 1)
mod_buf: char* = pool.alloc(mod_end - mod_start + 1)
if mod_buf is None:
stdlib.free(buf)
return None
@@ -506,12 +506,12 @@ novirtual class CallGenBase:
stdlib.free(buf)
return None
if has_dot != 0:
pkg: str = self._module_package_from_sha1(pool, mod_sha1)
pkg: char* = self._module_package_from_sha1(pool, mod_sha1)
if pkg is None:
stdlib.free(buf)
return None
rl: sizet = string.strlen(pkg) + string.strlen(mod_buf) + 2
res: str = pool.alloc(rl)
res: char* = pool.alloc(rl)
if res is None:
stdlib.free(buf)
return None
@@ -530,7 +530,7 @@ novirtual class CallGenBase:
return None
def register_cexport_func(self, sha1: str, func_name: str) -> int:
def register_cexport_func(self, sha1: char*, func_name: char*) -> int:
"""Register a ExportT function into the global table (single source of truth in Gen.CallGenBase)"""
if self.Gen is None or sha1 is None or func_name is None: return 0
if self.Gen.CallGenBase.CexportSha1Arr is None:
@@ -552,7 +552,7 @@ novirtual class CallGenBase:
return 0
def is_cexport_func(self, sha1: str, func_name: str) -> int:
def is_cexport_func(self, sha1: char*, func_name: char*) -> int:
"""Whether (sha1, func_name) is a ExportT function (reads the Gen.CallGenBase source of truth), falling back to the vpi return annotation on a registry miss."""
if self.Gen is None or sha1 is None or func_name is None: return 0
if self.Gen.CallGenBase.CexportSha1Arr is not None:
@@ -566,7 +566,7 @@ novirtual class CallGenBase:
# RegisterStaticSymbol / IsStaticSymbol - static symbol registry: the four
# syntaxes all normalize to t.StaticT; meaning = no cross-module use.
def RegisterStaticSymbol(self, sha1: str, name: str) -> int:
def RegisterStaticSymbol(self, sha1: char*, name: char*) -> int:
"""Register a static symbol (defining module SHA1 + symbol name); 1=registered (idempotent) / 0=failure"""
if self.Gen is None or sha1 is None or name is None: return 0
if self.Gen.CallGenBase.StaticSha1Arr is None:
@@ -594,7 +594,7 @@ novirtual class CallGenBase:
return 0
def IsStaticSymbol(self, sha1: str, name: str) -> int:
def IsStaticSymbol(self, sha1: char*, name: char*) -> int:
"""Whether (sha1, name) is a registered static symbol, the cross-module reference blocker; pairs (sha1, name) exactly to avoid same-name mistakes."""
if self.Gen is None or sha1 is None or name is None: return 0
if self.Gen.CallGenBase.StaticSha1Arr is None: return 0
@@ -606,7 +606,7 @@ novirtual class CallGenBase:
return 0
def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int:
def _is_cexport_func_from_vpi(self, sha1: char*, func_name: char*) -> int:
"""Whether the vpi declares a bare-name function (`export def` / `extern def` / legacy ExportT)."""
if sha1 is None or func_name is None: return 0
if self.Gen is None or self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen <= 0: return 0
@@ -614,45 +614,45 @@ novirtual class CallGenBase:
# New vpi carries the storage marker as the `export`/`extern` prefix, not in the
# return annotation; without this, cross-module calls mangle the name and link fails.
if VpiReader._lookup_vpi_bare(sha1, None, func_name) != 0: return 1
cexp_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
cexp_annot: char* = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
if cexp_annot is None: return 0
if string.strstr(cexp_annot, "ExportT") is not None: return 1
return 0
def _is_state_func_from_vpi(self, pool: memhub.MemBuddy*, sha1: str, func_name: str) -> int:
def _is_state_func_from_vpi(self, pool: memhub.MemBuddy*, sha1: char*, func_name: char*) -> int:
"""Whether the vpi declares a bare-name external C function (`extern def` / legacy State)."""
if pool is None or sha1 is None or func_name is None: return 0
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
# `extern def` prefix (current vpi) => bare C symbol name.
if VpiReader._lookup_vpi_bare(sha1, None, func_name) != 0: return 1
state_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
state_annot: char* = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
if state_annot is None: return 0
# ret annotation contains "State" -> external C function
if string.strstr(state_annot, "State") is not None: return 1
return 0
def _lookup_mod_sha1(self, pool: memhub.MemBuddy*, from_imports: str, class_name: str, allow_star_fallback: int = 1) -> str:
def _lookup_mod_sha1(self, pool: memhub.MemBuddy*, from_imports: char*, class_name: char*, allow_star_fallback: int = 1) -> str:
"""Look up the SHA1 of the module owning a class found in from_imports; None if not found. allow_star_fallback=0 disables star-import fallback."""
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, class_name, allow_star_fallback)
mod_name_raw: char* = self.Gen.ImportsGen.lookup_from_import(from_imports, class_name, allow_star_fallback)
if mod_name_raw is None: return None
return self._lookup_sha1_from_modname_raw(pool, mod_name_raw)
def _lookup_sha1_from_modname_raw(self, pool: memhub.MemBuddy*, mod_name_raw: str) -> str:
def _lookup_sha1_from_modname_raw(self, pool: memhub.MemBuddy*, mod_name_raw: char*) -> str:
"""Look up SHA1 from a raw module name; None if not found"""
# find module-name end (space/null/':' alias sep), copy into a new buffer
mod_len: sizet = 0
while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':': mod_len += 1
mod_name_buf: str = pool.alloc(mod_len + 1)
mod_name_buf: char* = pool.alloc(mod_len + 1)
if mod_name_buf is None: return None
for mi in range(int(mod_len)): mod_name_buf[mi] = mod_name_raw[mi]
mod_name_buf[mod_len] = '\0'
# strip leading dots of relative import
base_mod: str = mod_name_buf
base_mod: char* = mod_name_buf
while base_mod[0] == '.': base_mod = base_mod + 1
sha1: str = self._lookup_module_sha1(base_mod)
sha1: char* = self._lookup_module_sha1(base_mod)
if sha1 is None: sha1 = self._lookup_module_sha1_suffix(base_mod)
return sha1
@@ -675,7 +675,7 @@ novirtual class CallGenBase:
p_cur = mlirlite.param_get_next(p_cur)
return mlirlite.Func(pool, f_ref.RetTy, pn_head, p_cnt)
def find_func_in_module(self, mod: mlirlite.LLVMModule*, name: str, owner_sha1: str = None) -> mlirlite.Function*:
def find_func_in_module(self, mod: mlirlite.LLVMModule*, name: char*, owner_sha1: char* = None) -> mlirlite.Function*:
"""Find a function by name in the module's function list: exact then suffix match, honoring owner_sha1 for same-module priority."""
if mod is None or name is None: return None
name_len: sizet = string.strlen(name)
@@ -686,7 +686,7 @@ novirtual class CallGenBase:
declare_hit: mlirlite.Function* = None
any_hit: mlirlite.Function* = None
while cur is not None:
cur_name: str = mlirlite.function_get_name(cur)
cur_name: char* = mlirlite.function_get_name(cur)
if cur_name is not None:
# fast path: skip strcmp when first chars differ (mangled names start
# with sha1 hex, bare names with letters/underscore)
@@ -725,7 +725,7 @@ novirtual class CallGenBase:
# _ensure_method_declare: create a declare in the current module when the stub is missing
def _ensure_method_declare(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, call_name: str, ret_ty: mlirlite.LLVMType*, param_head: mlirlite.Value*, param_count: int):
def _ensure_method_declare(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, call_name: char*, ret_ty: mlirlite.LLVMType*, param_head: mlirlite.Value*, param_count: int):
"""Create a declare for a cross-module method call, appending missing params to an existing shorter declare (defines are left untouched)."""
if pool is None or mod is None or call_name is None: return
# list-walk sentinel (fail-fast): an unbounded walk would loop forever if a
@@ -786,9 +786,9 @@ novirtual class CallGenBase:
func.IsDeclared = 1
# Prefer definition-side param types (from {sha1}.{cls}.{method} vpi
# annotations): wider ints win, pointers always use the definition side.
sha1_d: str = None
cls_d: str = None
meth_d: str = None
sha1_d: char* = None
cls_d: char* = None
meth_d: char* = None
if call_name is not None:
# SHA1 prefix via single source GenApi.sha1_prefix_len (no hardcoded 17/16)
sha1_pfx_d: int = GenApi.sha1_prefix_len(call_name)
@@ -799,9 +799,9 @@ novirtual class CallGenBase:
string.strncpy(sha1_buf_d, call_name, pfx_len_d - 1)
sha1_buf_d[pfx_len_d - 1] = '\0'
sha1_d = sha1_buf_d
rest_d: str = call_name + pfx_len_d
rest_d: char* = call_name + pfx_len_d
# take the last '.': method has no dot; the first would truncate module-qualified args
dot2_d: str = string.strrchr(rest_d, '.')
dot2_d: char* = string.strrchr(rest_d, '.')
if dot2_d is not None:
cls_len_d: sizet = uint64(dot2_d) - uint64(rest_d)
if cls_len_d > 0:
@@ -812,7 +812,7 @@ novirtual class CallGenBase:
cls_d = cls_buf_d
meth_d = dot2_d + 1
else: meth_d = rest_d
prm_annots_d: str = None
prm_annots_d: char* = None
if sha1_d is not None and meth_d is not None: prm_annots_d = self._vpi_method_param_annot(pool, sha1_d, cls_d, meth_d)
i32_ty_d: mlirlite.LLVMType* = mlirlite.Int32(pool)
i8_ptr_d: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
@@ -853,8 +853,8 @@ novirtual class CallGenBase:
def _call_ty_equal(self, pool: memhub.MemBuddy*, a: mlirlite.LLVMType*, b: mlirlite.LLVMType*) -> int:
"""1 when both types have the same LLVM text (the canonical form used for emission)."""
if a is None or b is None: return 0
ta: str = self.Gen.ExprGenBase._type_text(pool, a)
tb: str = self.Gen.ExprGenBase._type_text(pool, b)
ta: char* = self.Gen.ExprGenBase._type_text(pool, a)
tb: char* = self.Gen.ExprGenBase._type_text(pool, b)
if ta is None or tb is None: return 0
if string.strcmp(ta, tb) == 0: return 1
return 0
@@ -862,11 +862,11 @@ novirtual class CallGenBase:
# _path_tail_is - 1 when the last `.`-separated segment of path equals name.
# Discriminates `mod.Class.func()` (path tail = the class) from `mod.func()` (path tail = the
# module) when a suffix lookup finds a dotted method symbol.
def _path_tail_is(self, path: str, name: str) -> int:
def _path_tail_is(self, path: char*, name: char*) -> int:
"""1 when the last segment of path equals name."""
if path is None or name is None: return 0
dot: str = string.strrchr(path, 46)
tail: str = path
dot: char* = string.strrchr(path, 46)
tail: char* = path
if dot is not None: tail = dot + 1
if tail[0] == '\0': return 0
if string.strcmp(tail, name) == 0: return 1
@@ -880,7 +880,7 @@ novirtual class CallGenBase:
if func is None: return 0
p: mlirlite.Param* = mlirlite.function_get_param_head(func)
if p is None: return 0
nm: str = mlirlite.param_get_name(p)
nm: char* = mlirlite.param_get_name(p)
if nm is None: return 1
if string.strcmp(nm, "self") == 0: return 1
if string.strcmp(nm, "cls") == 0: return 1
@@ -894,7 +894,7 @@ novirtual class CallGenBase:
# not accept, and only the MLIR verifier notices. Once the declare/definition exists it is
# the authority: coerce each argument to its parameter type, use its return type, and fail
# fast when a cross-module call site resolved a conflicting return type.
def _call_symbol_ret_ty(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, call_name: str,
def _call_symbol_ret_ty(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, call_name: char*,
ret_ty: mlirlite.LLVMType*, node: ast.AST* = None) -> mlirlite.LLVMType*:
"""Return type to emit for this call: the callee symbol's, validated against ret_ty."""
if mod is None or call_name is None: return ret_ty
@@ -922,7 +922,7 @@ novirtual class CallGenBase:
# Positional alignment over the fixed params; a variadic callee's extra arguments (already
# promoted to the C ABI) are left untouched.
def _conform_call_args(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*,
call_name: str, head: mlirlite.Value*, node: ast.AST* = None,
call_name: char*, head: mlirlite.Value*, node: ast.AST* = None,
trans: HT.Translator* = None) -> mlirlite.Value*:
"""Rebuild the argument chain with every fixed argument coerced to its parameter type."""
if head is None or call_name is None or mod is None: return head
@@ -962,7 +962,7 @@ novirtual class CallGenBase:
# `X*` / `X**` / `module.Class` / 裸类名 / 内建名 / typedef。
# 只用最基础的字符串操作,不依赖 VpiReader 的多分支分段解析器:自举时该解析器可能
# 误编译,让一个已注册的类解析失败。无法判定时返回 None,由后续完整解析器继续处理。
def _resolve_vpi_annot_direct(self, pool: memhub.MemBuddy*, annot: str) -> mlirlite.LLVMType*:
def _resolve_vpi_annot_direct(self, pool: memhub.MemBuddy*, annot: char*) -> mlirlite.LLVMType*:
"""解析 vpi 注解里的指针写法:`X*` / `X**` / `const X*` / `module.Class`。
不递归:先把 `const` / `volatile` 前缀剥掉,再统计尾部 `*` 的层数,
最后只做一次类/枚举/typedef 查找并按层数包 Ptr。"""
@@ -991,8 +991,8 @@ novirtual class CallGenBase:
if nm_buf is None: return None
string.strncpy(nm_buf, annot + st_i, end_i - st_i)
nm_buf[end_i - st_i] = '\0'
dot_n: str = string.strrchr(nm_buf, '.')
cls_n: str = nm_buf
dot_n: char* = string.strrchr(nm_buf, '.')
cls_n: char* = nm_buf
if dot_n is not None: cls_n = dot_n + 1
base_ty: mlirlite.LLVMType* = GenApi.get_struct_type(cls_n)
if base_ty is None: base_ty = GenApi.get_enum_base_type(cls_n)
@@ -1006,7 +1006,7 @@ novirtual class CallGenBase:
# (`GSList[Param]*`, `dict[str, X]*`) by specializing it on demand. VpiReader's string
# resolver handles markers/named types only, so without this a generic return type falls
# through to the i32 placeholder (现场: mlirlite.function_get_params -> GSList[Param]*).
def _resolve_generic_annot_str(self, trans: HT.Translator*, pool: memhub.MemBuddy*, annot: str) -> mlirlite.LLVMType*:
def _resolve_generic_annot_str(self, trans: HT.Translator*, pool: memhub.MemBuddy*, annot: char*) -> mlirlite.LLVMType*:
"""Specialize `Base[Arg, ...]` (+ trailing '*') from an annotation string; None if not applicable."""
if trans is None or pool is None or annot is None: return None
a_len: sizet = string.strlen(annot)
@@ -1031,7 +1031,7 @@ novirtual class CallGenBase:
if lb >= end_i - 1: return None
base_len: sizet = lb - st_i
if base_len == 0: return None
base_buf: str = pool.alloc(base_len + 1)
base_buf: char* = pool.alloc(base_len + 1)
if base_buf is None: return None
string.strncpy(base_buf, annot + st_i, base_len)
base_buf[base_len] = '\0'
@@ -1047,7 +1047,7 @@ novirtual class CallGenBase:
while seg_s < seg_e and annot[seg_s] == ' ': seg_s += 1
while seg_e > seg_s and annot[seg_e - 1] == ' ': seg_e -= 1
if seg_e > seg_s:
ab: str = pool.alloc(seg_e - seg_s + 1)
ab: char* = pool.alloc(seg_e - seg_s + 1)
if ab is None: return None
string.strncpy(ab, annot + seg_s, seg_e - seg_s)
ab[seg_e - seg_s] = '\0'
@@ -1057,7 +1057,7 @@ novirtual class CallGenBase:
elif annot[ai] == ']': depth_g -= 1
ai += 1
if len(ta_list) == 0: return None
spec: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, base_buf, ta_list)
spec: char* = self.Gen.ClassGenGeneric._specialize_generic_class(trans, base_buf, ta_list)
if spec is None: return None
ty: mlirlite.LLVMType* = GenApi.get_struct_type(spec)
if ty is None: ty = GenApi.get_enum_base_type(spec)
@@ -1068,7 +1068,7 @@ novirtual class CallGenBase:
k_l += 1
return ty
def _lookup_method_ret_ty_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str, trans: HT.Translator* = None, depth: int = 0) -> mlirlite.LLVMType*:
def _lookup_method_ret_ty_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: char*, cls_name: char*, method_name: char*, trans: HT.Translator* = None, depth: int = 0) -> mlirlite.LLVMType*:
"""Get a method return type from the vpi stub annotation cache; None on miss"""
if pool is None or cls_sha1 is None or method_name is None: return None
# lazy import inside the function: VpiReader imports HandlesExprCall, a top-level import would cycle
@@ -1077,7 +1077,7 @@ novirtual class CallGenBase:
# on-demand load: read {temp_dir}/{sha1[0:2]}/{sha1}.vpi on cache miss,
# covering method calls whose stub is not yet injected.
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
annot: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name)
annot: char* = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name)
# A re-export facade such as `io.vp` (`from _io import *`) has no own `def`;
# the annotation is stored in the defining module's vpi. Follow the re-export
# entry to that module before giving up.
@@ -1098,7 +1098,7 @@ novirtual class CallGenBase:
if ret_ty is None: ret_ty = self._resolve_generic_annot_str(trans, pool, annot)
return ret_ty
def _vpi_module_defines(self, mod_sha1: str, func_name: str) -> int:
def _vpi_module_defines(self, mod_sha1: char*, func_name: char*) -> int:
"""Return 1 when the module's .vpi declares a module-level function with this name."""
if mod_sha1 is None or func_name is None: return 0
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
@@ -1106,7 +1106,7 @@ novirtual class CallGenBase:
if VpiReader._lookup_vpi_ret_annot(mod_sha1, None, func_name) is not None: return 1
return 0
def _is_reexport_owner(self, pool: memhub.MemBuddy*, mod_sha1: str, func_name: str, owner_full_name: str) -> int:
def _is_reexport_owner(self, pool: memhub.MemBuddy*, mod_sha1: char*, func_name: char*, owner_full_name: char*) -> int:
"""Return 1 when owner_full_name belongs to a re-export source of mod_sha1.
A module-qualified call on a facade (e.g. `io.ExpandEnvironmentPath`)
@@ -1122,7 +1122,7 @@ novirtual class CallGenBase:
osuf: sizet = own_len - fn_len
if owner_full_name[osuf - 1] != '.': return 0
if string.strcmp(owner_full_name + osuf, func_name) != 0: return 0
rr_src: str = self.Gen.ImportsGen.lookup_reexport_call(mod_sha1, func_name)
rr_src: char* = self.Gen.ImportsGen.lookup_reexport_call(mod_sha1, func_name)
if rr_src is None: rr_src = self._lookup_reexport_from_vpi(pool, mod_sha1, func_name)
if rr_src is None: return 0
total: sizet = string.strlen(rr_src)
@@ -1134,17 +1134,17 @@ novirtual class CallGenBase:
while ri < total and rr_src[ri] != ' ': ri += 1
seg_len: sizet = ri - seg_start
if seg_len == 0: continue
seg_buf: str = pool.alloc(seg_len + 1)
seg_buf: char* = pool.alloc(seg_len + 1)
if seg_buf is None: continue
string.strncpy(seg_buf, rr_src + seg_start, seg_len)
seg_buf[seg_len] = '\0'
cand_sha1: str = self._lookup_module_sha1(seg_buf)
cand_sha1: char* = self._lookup_module_sha1(seg_buf)
if cand_sha1 is None: cand_sha1 = self._lookup_module_sha1_suffix(seg_buf)
if cand_sha1 is not None:
if string.strncmp(owner_full_name, cand_sha1, 16) == 0 and own_len > 17 and owner_full_name[16] == '.': return 1
return 0
def _lookup_reexport_ret_ty(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str, trans: HT.Translator* = None, depth: int = 0) -> mlirlite.LLVMType*:
def _lookup_reexport_ret_ty(self, pool: memhub.MemBuddy*, cls_sha1: char*, cls_name: char*, method_name: char*, trans: HT.Translator* = None, depth: int = 0) -> mlirlite.LLVMType*:
"""Resolve a module-level function's return type through re-export entries.
The facade vpi has no `def`; the annotation belongs to the defining module.
@@ -1154,7 +1154,7 @@ novirtual class CallGenBase:
"""
if depth >= 4 or pool is None or cls_sha1 is None or method_name is None: return None
if cls_name is not None: return None
rr_src: str = self.Gen.ImportsGen.lookup_reexport_call(cls_sha1, method_name)
rr_src: char* = self.Gen.ImportsGen.lookup_reexport_call(cls_sha1, method_name)
if rr_src is None: rr_src = self._lookup_reexport_from_vpi(pool, cls_sha1, method_name)
if rr_src is None: return None
total: sizet = string.strlen(rr_src)
@@ -1166,18 +1166,18 @@ novirtual class CallGenBase:
while ri < total and rr_src[ri] != ' ': ri += 1
seg_len: sizet = ri - seg_start
if seg_len == 0: continue
seg_buf: str = pool.alloc(seg_len + 1)
seg_buf: char* = pool.alloc(seg_len + 1)
if seg_buf is None: continue
string.strncpy(seg_buf, rr_src + seg_start, seg_len)
seg_buf[seg_len] = '\0'
rr_sha1: str = self._lookup_module_sha1(seg_buf)
rr_sha1: char* = self._lookup_module_sha1(seg_buf)
if rr_sha1 is None: rr_sha1 = self._lookup_module_sha1_suffix(seg_buf)
if rr_sha1 is not None:
ty: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, rr_sha1, cls_name, method_name, trans, depth + 1)
if ty is not None: return ty
return None
def _vpi_escape_packed(self, sha1: str, cls_name: str, method_name: str) -> int:
def _vpi_escape_packed(self, sha1: char*, cls_name: char*, method_name: char*) -> int:
"""跨模块逃逸摘要查询(v3):按 (定义模块 sha1, Class?, method) 查 .vpi 缓存;0=无摘要。
用"本模块已加载"备忘包住 _ensure_vpi_ret_annot_loaded(其内部是 O(N) 前缀判定):
@@ -1194,7 +1194,7 @@ novirtual class CallGenBase:
# lookup_escape_dir_via_vpi - 跨模块声明式指令契约(@life(escape(...)) / @life(noescape(...)))。
# 本文件指令表按文件重置,跨模块调用点必为空,只能靠 .vpi 行尾的 esce=/escn= 传递;
# noescape != 0 取"强制不逃逸位",否则取"强制逃逸位"。0 = 无该标注(含该模块未加载过)。
def lookup_escape_dir_via_vpi(self, cls_sha1: str, class_name: str, func_name: str, noescape: int) -> int:
def lookup_escape_dir_via_vpi(self, cls_sha1: char*, class_name: char*, func_name: char*, noescape: int) -> int:
"""对方模块 .vpi 里落的契约掩码;0 = 无该标注"""
if func_name is None or cls_sha1 is None: return 0
return VpiReader._lookup_vpi_escape_dir(cls_sha1, class_name, func_name, noescape)
@@ -1203,7 +1203,7 @@ novirtual class CallGenBase:
# 与 escape/noescape 同一条 .vpi 通道(行尾 escr=);0 = 无声明(含该模块未加载过)。
# 注意:本查询可能在调用点**之前**(life_of_expr 判右值生命期)发生,不能假定调用点的
# 逃逸查询已把对方 vpi 载入,故这里自己走一次"本模块已加载"备忘 + 按需加载。
def lookup_ret_kind_via_vpi(self, cls_sha1: str, class_name: str, func_name: str) -> int:
def lookup_ret_kind_via_vpi(self, cls_sha1: char*, class_name: char*, func_name: char*) -> int:
"""对方模块 .vpi 里落的 ret_kind;0 = 无声明"""
if func_name is None or cls_sha1 is None: return 0
if VpiReader._vpi_escape_module_loaded(cls_sha1) == 0:
@@ -1217,7 +1217,7 @@ novirtual class CallGenBase:
VpiReader._vpi_escape_mark_module(cls_sha1)
return VpiReader._lookup_vpi_escape_dir(cls_sha1, class_name, func_name, 2)
def lookup_escape_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> int:
def lookup_escape_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: char*, cls_name: char*, method_name: char*) -> int:
"""跨模块逃逸摘要(v3):本类 + MRO 祖先各自的模块 sha1 依次查 .vpi,返回打包值(0=无摘要)。
本文件的摘要表按文件重置,跨模块调用点上必为空,只能靠 .vpi 传递;
@@ -1230,16 +1230,16 @@ novirtual class CallGenBase:
mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, cls_name)
mi: int = 1
while mro is not None and mro[mi] != 0:
anc: str = rawptr(mro[mi])
anc: char* = rawptr(mro[mi])
if anc is not None and string.strcmp(anc, cls_name) != 0:
sha_a: str = self.Gen.StructGen.get_struct_sha1(anc)
sha_a: char* = self.Gen.StructGen.get_struct_sha1(anc)
if sha_a is not None:
pa: int = self._vpi_escape_packed(sha_a, anc, method_name)
if pa != 0: return pa
mi += 1
return 0
def _vpi_willfree(self, sha1: str, cls_name: str, method_name: str) -> int:
def _vpi_willfree(self, sha1: char*, cls_name: char*, method_name: char*) -> int:
"""跨模块 willFree 标注查询(P2):按 (定义模块 sha1, Class?, method) 查 .vpi 缓存;0=无标注。
与逃逸摘要共用"本模块已加载"备忘:_ensure_vpi_ret_annot_loaded 的单一入口会把
@@ -1251,7 +1251,7 @@ novirtual class CallGenBase:
VpiReader._vpi_escape_mark_module(sha1)
return VpiReader._lookup_vpi_willfree(sha1, cls_name, method_name)
def lookup_escape_clears_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, class_name: str, func_name: str, cargs: list[ast.AST*]*, can: sizet, param_base: int) -> int:
def lookup_escape_clears_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: char*, class_name: char*, func_name: char*, cargs: list[ast.AST*]*, can: sizet, param_base: int) -> int:
"""跨模块常量特化:把实参字面量装进常量环境,再向 vpi 缓存要"该清掉的位掩码";0 = 无命中
跨模块拿不到被调者函数体(只有 .vpi),所以用落盘的守卫表(`escg=`)而不是重算摘要。
@@ -1262,7 +1262,7 @@ novirtual class CallGenBase:
if fenv is None: return 0
return VpiReader._lookup_vpi_escape_clears(cls_sha1, class_name, func_name, fenv)
def lookup_willfree_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> int:
def lookup_willfree_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: char*, cls_name: char*, method_name: char*) -> int:
"""跨模块 willFree 掩码(P2):本类 + MRO 祖先各自的模块 sha1 依次查 .vpi;0=无标注"""
if method_name is None: return 0
if cls_sha1 is not None:
@@ -1272,9 +1272,9 @@ novirtual class CallGenBase:
mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, cls_name)
mi: int = 1
while mro is not None and mro[mi] != 0:
anc: str = rawptr(mro[mi])
anc: char* = rawptr(mro[mi])
if anc is not None and string.strcmp(anc, cls_name) != 0:
sha_a: str = self.Gen.StructGen.get_struct_sha1(anc)
sha_a: char* = self.Gen.StructGen.get_struct_sha1(anc)
if sha_a is not None:
pa: int = self._vpi_willfree(sha_a, anc, method_name)
if pa != 0: return pa
@@ -1282,7 +1282,7 @@ novirtual class CallGenBase:
return 0
# _vpi_method_param_annot: on-demand lookup of the vpi param annotation string
def _vpi_method_param_annot(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> str:
def _vpi_method_param_annot(self, pool: memhub.MemBuddy*, cls_sha1: char*, cls_name: char*, method_name: char*) -> str:
"""Load and query the vpi parameter annotation string on demand; None on miss"""
if pool is None or cls_sha1 is None or cls_name is None or method_name is None: return None
# on-demand load: read {temp_dir}/{sha1[0:2]}/{sha1}.vpi on cache miss
@@ -1290,7 +1290,7 @@ novirtual class CallGenBase:
return VpiReader._lookup_vpi_param_annots(cls_sha1, cls_name, method_name)
# _vpi_resolve_param_annot_ty: parse a vpi param annotation segment into LLVMType
def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy*, seg: str, src_sha1: str = None) -> mlirlite.LLVMType*:
def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy*, seg: char*, src_sha1: char* = None) -> mlirlite.LLVMType*:
"""Parse a single vpi parameter annotation segment into an LLVMType; None on failure"""
if pool is None or seg is None: return None
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
@@ -1301,7 +1301,7 @@ novirtual class CallGenBase:
# _special_method_ret_ty: fixed return types for compiler built-in special methods
# ============================================================
def _special_method_ret_ty(self, pool: memhub.MemBuddy*, class_name: str, method_name: str) -> mlirlite.LLVMType*:
def _special_method_ret_ty(self, pool: memhub.MemBuddy*, class_name: char*, method_name: char*) -> mlirlite.LLVMType*:
"""Fixed return type for compiler builtin special methods; None for non-special ones"""
if pool is None or method_name is None: return None
if string.strcmp(method_name, "__before_init__") == 0 or string.strcmp(method_name, "__init__") == 0: return mlirlite.Void(pool)
@@ -1315,7 +1315,7 @@ novirtual class CallGenBase:
# _fail_method_ret_ty: ERROR + abort when the method return type cannot be resolved
# ============================================================
def _fail_method_ret_ty(self, method_name: str, node: ast.AST* = None, cls: str = None, anc: str = None, sha1: str = None, pool: memhub.MemBuddy* = None) -> mlirlite.LLVMType*:
def _fail_method_ret_ty(self, method_name: char*, node: ast.AST* = None, cls: char* = None, anc: char* = None, sha1: char* = None, pool: memhub.MemBuddy* = None) -> mlirlite.LLVMType*:
"""Fail fast when a method return type cannot be resolved precisely (module function, text.ll define, then vpi cache); never default to i32."""
# NOTE: VLogger.error terminates the process in batch mode (sys.exit(1)), so the whole
# report must be a SINGLE call -- anything after it is dead code (this is why a separate
@@ -1324,30 +1324,30 @@ novirtual class CallGenBase:
# Reports: receiver class, its registered struct sha1, the class/sha1 actually used for
# the lookup (MRO fallback candidate), whether that module's text.ll exists, and the raw
# vpi return-annotation string if the cache holds one.
c_cls: str = cls
c_cls: char* = cls
if c_cls is None: c_cls = "?"
c_anc: str = anc
c_anc: char* = anc
if c_anc is None: c_anc = "?"
# Probe with the sha1 actually used for the lookup, falling back to the receiver class's
# own registered sha1 when the caller passed none (so the dump is informative everywhere).
probe_sha: str = sha1
probe_sha: char* = sha1
if probe_sha is None and anc is not None: probe_sha = self.Gen.StructGen.get_struct_sha1(anc)
if probe_sha is None and cls is not None: probe_sha = self.Gen.StructGen.get_struct_sha1(cls)
c_sha: str = probe_sha
c_sha: char* = probe_sha
if c_sha is None: c_sha = "?"
c_st_sha: str = "?"
c_st_sha: char* = "?"
if cls is not None:
st_sha_d: str = self.Gen.StructGen.get_struct_sha1(cls)
st_sha_d: char* = self.Gen.StructGen.get_struct_sha1(cls)
if st_sha_d is not None: c_st_sha = st_sha_d
c_tll: int = 0
c_vpi: str = "-"
c_tail_ty: str = "-"
c_annot_res: str = "-"
c_vpi: char* = "-"
c_tail_ty: char* = "-"
c_annot_res: char* = "-"
if probe_sha is not None:
c_tll = self._dep_text_ll_exists(None, probe_sha)
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_ret_annot_loaded(probe_sha, self.Gen.CallGenBase.TempDir)
annot_d: str = VpiReader._lookup_vpi_ret_annot(probe_sha, cls, method_name)
annot_d: char* = VpiReader._lookup_vpi_ret_annot(probe_sha, cls, method_name)
if annot_d is not None:
c_vpi = annot_d
# Probe the string resolver here (caller's pool when available, same annotation)
@@ -1363,12 +1363,12 @@ novirtual class CallGenBase:
# Annotate the tail class of the raw annotation: "missing" = the class was never
# registered (upstream bug); "struct"/"enum" = registered but the string resolver
# still failed (resolver bug). Distinguishes the two without guessing.
dot_d: str = string.strrchr(annot_d, '.')
dot_d: char* = string.strrchr(annot_d, '.')
if dot_d is not None:
tail_len_d: sizet = string.strlen(dot_d + 1)
tb_d: bytes = stdlib.malloc(tail_len_d + 1)
if tb_d is not None:
rb_d: str = string.strchr(dot_d + 1, ']')
rb_d: char* = string.strchr(dot_d + 1, ']')
if rb_d is not None: tail_len_d = sizet(uint64(rb_d) - uint64(dot_d + 1))
string.strncpy(tb_d, dot_d + 1, tail_len_d)
tb_d[tail_len_d] = '\0'
@@ -1379,11 +1379,11 @@ novirtual class CallGenBase:
# Same-named struct entries: find_struct returns the FIRST exact match, so an earlier bogus
# registration (alias/forward/stale, different ModuleSha1) silently shadows the real one.
dup_n_d: int = 0
dup_last_d: str = "-"
dup_last_d: char* = "-"
# The entry's LLVM type name embeds "{sha1}.{Class}", i.e. the sha1 used when the entry
# was first created (registration / forward-declare / stub injection) -- it reveals
# provenance even if a later path overwrote ModuleSha1.
dup_ty_nm_d: str = "-"
dup_ty_nm_d: char* = "-"
sc_n_d: int = self.Gen.StructGen.get_struct_count()
sc_i_d: int = 0
while sc_i_d < sc_n_d:
@@ -1393,15 +1393,15 @@ novirtual class CallGenBase:
dup_n_d += 1
if e_d.ModuleSha1 is not None: dup_last_d = e_d.ModuleSha1
if e_d.Ty is not None:
nm_d: str = mlirlite.get_struct_name(e_d.Ty)
nm_d: char* = mlirlite.get_struct_name(e_d.Ty)
if nm_d is not None: dup_ty_nm_d = nm_d
sc_i_d += 1
# Current-file / defining-module sha1 at the moment of failure: distinguishes
# "wrong sha1 captured during (vpi injection) forward-declare" from "correct sha1
# overwritten later".
cur_file_d: str = self.Gen.StructGen.get_file_module_sha1()
cur_file_d: char* = self.Gen.StructGen.get_file_module_sha1()
if cur_file_d is None: cur_file_d = "-"
cur_def_d: str = self.Gen.StructGen.CurDefModuleSha1
cur_def_d: char* = self.Gen.StructGen.CurDefModuleSha1
if cur_def_d is None: cur_def_d = "-"
VLogger.error(f"method return type cannot be obtained precisely (function/text.ll/vpi stub all missed): {method_name} | [diag] cls={c_cls} struct_sha1={c_st_sha} lookup_anc={c_anc} lookup_sha1={c_sha} text_ll={c_tll} vpi_annot={c_vpi} tail_ty={c_tail_ty} annot_res={c_annot_res} same_name_entries={dup_n_d} last_same_sha1={dup_last_d} entry_ty={dup_ty_nm_d} cur_file_sha1={cur_file_d} cur_def_sha1={cur_def_d}", "HandlesExprCall")
self.Gen.TypeGenErr.fatal_error(node, f"method return type cannot be obtained precisely; defaulting to i32 would truncate pointers/i64: {method_name}", "TypeError")
@@ -1410,7 +1410,7 @@ novirtual class CallGenBase:
# SetTempDir: set the temp dir path (called before Phase A to read dependency text.ll)
# ============================================================
def SetTempDir(self, temp_dir: str) -> None:
def SetTempDir(self, temp_dir: char*) -> None:
"""Set the temp directory path (single source of truth in Gen.CallGenBase)"""
if self.Gen is None: return
self.Gen.CallGenBase.TempDir = temp_dir
@@ -1690,7 +1690,7 @@ novirtual class CallGenBase:
return None
# _LookupCachedRetTyText: lookup the persistent "dependency text.ll return type" cache
def _LookupCachedRetTyText(self, class_sha1: str, class_name: str, method_name: str, use_bare: int) -> str:
def _LookupCachedRetTyText(self, class_sha1: char*, class_name: char*, method_name: char*, use_bare: int) -> str:
"""Look up the return-type text cache: return the text on a hit, None on a miss"""
if self.Gen is None or class_sha1 is None or method_name is None: return None
ht: hashtable.HashTable* = self.Gen.CallGenBase.MethodRetTyMap
@@ -1713,7 +1713,7 @@ novirtual class CallGenBase:
return rawptr(v)
# _CacheModuleRetTyTexts: one pass to register all define lines and find the first match
def _CacheModuleRetTyTexts(self, class_sha1: str, text_buf: bytes, text_len: sizet, pool: memhub.MemBuddy*, search_buf: str, search_str_len: sizet) -> mlirlite.LLVMType*:
def _CacheModuleRetTyTexts(self, class_sha1: char*, text_buf: bytes, text_len: sizet, pool: memhub.MemBuddy*, search_buf: char*, search_str_len: sizet) -> mlirlite.LLVMType*:
"""Single-pass register and lookup over all text.ll define lines (lookup only if table allocation fails)"""
if text_buf is None or text_len == 0: return None
# Merge the former two passes (register all, then scan again) into one
@@ -1819,8 +1819,8 @@ novirtual class CallGenBase:
rt_len = 5 # '#' + "none"
else: rt_len = 1 + (ret_e - ret_s)
else: rt_len = at_pos - (ls + 7)
kcopy: str = perm.alloc(name_len + 1)
tcopy: str = perm.alloc(rt_len + 1)
kcopy: char* = perm.alloc(name_len + 1)
tcopy: char* = perm.alloc(rt_len + 1)
if kcopy is not None and tcopy is not None:
string.strncpy(kcopy, text_buf + name_start, name_len)
kcopy[name_len] = '\0'
@@ -1849,7 +1849,7 @@ novirtual class CallGenBase:
# _LookupMethodRetTyFromTextLL: read {temp_dir}/{sha1[0:2]}/{sha1}.text.ll and find the define line
# ============================================================
def _LookupMethodRetTyFromTextLL(self, pool: memhub.MemBuddy*, class_sha1: str, class_name: str, method_name: str, use_bare: int = 0) -> mlirlite.LLVMType*:
def _LookupMethodRetTyFromTextLL(self, pool: memhub.MemBuddy*, class_sha1: char*, class_name: char*, method_name: char*, use_bare: int = 0) -> mlirlite.LLVMType*:
"""Look up a method return type in a dependency module's text.ll; use_bare=1 searches a module-level "{sha1}.{method_name}" define."""
if pool is None or class_sha1 is None or method_name is None: return None
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return None
@@ -1858,7 +1858,7 @@ novirtual class CallGenBase:
if sha1_len < 2: return None
# cache hit: O(1) text retrieval, rebuilt with the caller pool; miss falls through
cached_text: str = self._LookupCachedRetTyText(class_sha1, class_name, method_name, use_bare)
cached_text: char* = self._LookupCachedRetTyText(class_sha1, class_name, method_name, use_bare)
if cached_text is not None:
# '#' prefix = MLIR type text (form marker written by _CacheModuleRetTyTexts)
if cached_text[0] == '#': return self._ParseMlirRetTyStr(pool, cached_text, 1, string.strlen(cached_text))
@@ -1931,7 +1931,7 @@ novirtual class CallGenBase:
# _dep_text_ll_exists: probe whether {temp_dir}/{sha1[0:2]}/{sha1}.text.ll exists
# ============================================================
def _dep_text_ll_exists(self, pool: memhub.MemBuddy*, sha1: str) -> int:
def _dep_text_ll_exists(self, pool: memhub.MemBuddy*, sha1: char*) -> int:
"""Probe whether {temp_dir}/{sha1[0:2]}/{sha1}.text.ll exists, 1=yes"""
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0 or sha1 is None: return 0
sha1_len: sizet = string.strlen(sha1)
+59 -59
View File
@@ -37,7 +37,7 @@ define LLVMIR_TEMPLATE_BUF = 4096
novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
"""Translate c.* builtins, inline asm and raw LLVM IR."""
def _asm_append_cstr(self, dst: str, dst_size: sizet, src: str, what: str = "inline asm"):
def _asm_append_cstr(self, dst: char*, dst_size: sizet, src: char*, what: char* = "inline asm"):
"""Append the C string src to the end of dst; fail-fast when it does not fit.
Silent truncation corrupts the generated text (a template cut in the middle of an
@@ -58,12 +58,12 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
dst[dlen + i] = '\0'
def _asm_mangle_global_symbols(self, pool: memhub.MemBuddy*, asm_text: str, asm_size: sizet, mod: mlirlite.LLVMModule*) -> str:
def _asm_mangle_global_symbols(self, pool: memhub.MemBuddy*, asm_text: char*, asm_size: sizet, mod: mlirlite.LLVMModule*) -> str:
"""Fail-fast when inline asm references a SHA1-mangled module global by bare name."""
if asm_text is None or mod is None: return asm_text
src_len: sizet = string.strlen(asm_text)
if src_len == 0: return asm_text
out: str = pool.alloc(asm_size * 2)
out: char* = pool.alloc(asm_size * 2)
if out is None: return asm_text
out[0] = '\0'
out_pos: sizet = 0
@@ -72,10 +72,10 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
matched: int = 0
cur: mlirlite.GlobalVariable* = mod.GlobalHead
while cur is not None:
gname: str = cur.Name
gname: char* = cur.Name
if gname is not None and string.strchr(gname, 46) is not None:
dot_g: str = string.strchr(gname, 46)
bare_g: str = dot_g + 1
dot_g: char* = string.strchr(gname, 46)
bare_g: char* = dot_g + 1
bare_len: sizet = string.strlen(bare_g)
if bare_len > 0 and i + bare_len <= src_len:
if string.strncmp(asm_text + i, bare_g, bare_len) == 0:
@@ -108,11 +108,11 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
return out
def _asm_escape_for_ir(self, pool: memhub.MemBuddy*, src: str) -> str:
def _asm_escape_for_ir(self, pool: memhub.MemBuddy*, src: char*) -> str:
"""Escape asm text for an LLVM IR string literal."""
if src is None: return None
slen: sizet = string.strlen(src)
buf: str = pool.alloc(slen * 4 + 8)
buf: char* = pool.alloc(slen * 4 + 8)
if buf is None: return None
pos: sizet = 0
for ch in src:
@@ -144,7 +144,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
return buf
def _resolve_register(self, attr_name: str) -> str:
def _resolve_register(self, attr_name: char*) -> str:
"""Map an ASM_DESCR attribute name to its constraint string."""
if attr_name is None: return ""
@@ -248,11 +248,11 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if bop.op == ast.OpKind.BitOr:
left_str: str = self._resolve_asm_descr(pool, bop.left)
right_str: str = self._resolve_asm_descr(pool, bop.right)
left_str: char* = self._resolve_asm_descr(pool, bop.left)
right_str: char* = self._resolve_asm_descr(pool, bop.right)
llen: sizet = string.strlen(left_str)
rlen: sizet = string.strlen(right_str)
buf: str = pool.alloc(llen + rlen + 1)
buf: char* = pool.alloc(llen + rlen + 1)
if buf is None: return ""
string.strcpy(buf, left_str)
for i in range(int(rlen)): buf[llen + i] = right_str[i]
@@ -274,11 +274,11 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if k == ast.ASTKind.Name:
nm_b: ast.Name* = ast.Name*(node)
if nm_b.id is not None:
rs_b: str = self._resolve_register(nm_b.id)
rs_b: char* = self._resolve_register(nm_b.id)
if rs_b is not None and rs_b[0] != '\0': return rs_b
return ""
# Constant: string constant
# Constant: char*ing constant
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_STR: return cn.str_val
@@ -308,10 +308,10 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
return None
def _asm_name_is_mangled(self, name: str) -> int:
def _asm_name_is_mangled(self, name: char*) -> int:
"""Return 1 when the LLVM name carries a SHA1-mangled prefix."""
if name is None: return 0
n: str = name
n: char* = name
nlen: sizet = string.strlen(n)
# Strip the '@"..."' wrapper (reference name)
if nlen >= 3 and n[0] == '@' and n[1] == '"' and n[nlen - 1] == '"': n = n + 2
@@ -320,7 +320,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
return 0
def _asm_add_operand(self, operands: GenTypes.AsmOperand*, count: int, val: mlirlite.Value*, constraint: str, is_output: int) -> int:
def _asm_add_operand(self, operands: GenTypes.AsmOperand*, count: int, val: mlirlite.Value*, constraint: char*, is_output: int) -> int:
"""Append an operand, dedup by value and direction, and return the new count."""
# fail-fast: the operand array is fixed at ASM_MAX_OPERANDS slots; dropping the
# excess operand here would silently change the generated constraint/argument lists.
@@ -367,7 +367,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
string.memset(clobbers, 0, 8 * ASM_MAX_CLOBBERS)
# Allocate the asm text buffer
asm_text: str = pool.alloc(ASM_TEXT_BUF_SIZE)
asm_text: char* = pool.alloc(ASM_TEXT_BUF_SIZE)
if asm_text is None: return None
asm_text[0] = '\0'
@@ -409,7 +409,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
fexpr: ast.AST* = fv.value
if fexpr is None or fexpr.kind() != ast.ASTKind.Call: continue
fcall: ast.Call* = ast.Call*(fexpr)
attr_nm: str = self._asm_is_c_call(fcall)
attr_nm: char* = self._asm_is_c_call(fcall)
if attr_nm is None: continue
is_out: int = 0
@@ -421,7 +421,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if fargs is None or len(fargs) < 1: continue
val_node: ast.AST* = fargs.get(0)
cons_str: str = ""
cons_str: char* = ""
if len(fargs) >= 2: cons_str = self._resolve_asm_descr(pool, fargs.get(1))
if is_out != 0:
@@ -501,12 +501,12 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
oelt: ast.AST* = out_elts.get(oi)
if oelt is None or oelt.kind() != ast.ASTKind.Call: continue
ocall: ast.Call* = ast.Call*(oelt)
oattr: str = self._asm_is_c_call(ocall)
oattr: char* = self._asm_is_c_call(ocall)
if oattr is None or string.strcmp(oattr, "AsmOut") != 0: continue
oargs: list[ast.AST*]* = ocall.args
if oargs is None or len(oargs) < 1: continue
oval_node: ast.AST* = oargs.get(0)
ocons: str = ""
ocons: char* = ""
if len(oargs) >= 2: ocons = self._resolve_asm_descr(pool, oargs.get(1))
if oval_node is None or oval_node.kind() != ast.ASTKind.Name: continue
onm: ast.Name* = ast.Name*(oval_node)
@@ -530,12 +530,12 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
ielt: ast.AST* = in_elts.get(ii)
if ielt is None or ielt.kind() != ast.ASTKind.Call: continue
icall: ast.Call* = ast.Call*(ielt)
iattr: str = self._asm_is_c_call(icall)
iattr: char* = self._asm_is_c_call(icall)
if iattr is None or string.strcmp(iattr, "AsmInp") != 0: continue
iargs: list[ast.AST*]* = icall.args
if iargs is None or len(iargs) < 1: continue
ival_node: ast.AST* = iargs.get(0)
icons: str = ""
icons: char* = ""
if len(iargs) >= 2: icons = self._resolve_asm_descr(pool, iargs.get(1))
ival2: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ival_node, None, 0, trans)
if ival2 is None: continue
@@ -551,7 +551,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
for ci in range(int(clen)):
celt: ast.AST* = cl_elts.get(ci)
if celt is None: continue
cstr: str = self._resolve_asm_descr(pool, celt)
cstr: char* = self._resolve_asm_descr(pool, celt)
if cstr is None or cstr[0] == '\0': continue
# fail-fast: the clobber slot array is fixed at ASM_MAX_CLOBBERS;
# dropping the excess would silently weaken the clobber list.
@@ -559,7 +559,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
self.Gen.TypeGenErr.fatal_error(celt, f"inline asm has more than {ASM_MAX_CLOBBERS} clobbers (compiler limit); remove duplicates or split the asm block", "TypeError")
return None
cl_addr: uint64 = uint64(clobbers) + clobber_count * 8
cl_ptr: str* = (str*)(cl_addr)
cl_ptr: char** = (str*)(cl_addr)
cl_ptr[0] = cstr
clobber_count += 1
@@ -574,13 +574,13 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
for pi in range(int(plen)):
pelt: ast.AST* = pos_elts.get(pi)
if pelt is None: continue
pstr: str = self._resolve_asm_descr(pool, pelt)
pstr: char* = self._resolve_asm_descr(pool, pelt)
if pstr is None or pstr[0] == '\0': continue
if clobber_count >= ASM_MAX_CLOBBERS:
self.Gen.TypeGenErr.fatal_error(pelt, f"inline asm has more than {ASM_MAX_CLOBBERS} clobbers (compiler limit); remove duplicates or split the asm block", "TypeError")
return None
pcl_addr: uint64 = uint64(clobbers) + clobber_count * 8
pcl_ptr: str* = (str*)(pcl_addr)
pcl_ptr: char** = (str*)(pcl_addr)
pcl_ptr[0] = pstr
clobber_count += 1
@@ -700,7 +700,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if fv_fi < fv_count:
orig_idx2: int = fv_orig_indices[fv_fi]
new_idx2: int = orig_to_new[orig_idx2]
ph_buf2: str = pool.alloc(16)
ph_buf2: char* = pool.alloc(16)
if ph_buf2 is not None:
viperlib.snprintf(ph_buf2, 16, "$%d", new_idx2)
self._asm_append_cstr(asm_text, ASM_TEXT_BUF_SIZE, ph_buf2)
@@ -714,7 +714,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
# ~300 bytes, so the old fixed 512-byte cap could silently cut it. Overflow of this
# (or any asm text buffer) is fail-fast in _asm_append_cstr.
const_cap: sizet = ASM_CONST_BUF_SIZE + sizet(operand_count + clobber_count + 1) * 64
const_buf: str = pool.alloc(const_cap)
const_buf: char* = pool.alloc(const_cap)
if const_buf is None: return None
const_buf[0] = '\0'
@@ -722,7 +722,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
for ci2 in range(int(operand_count)):
orig_i: int = new_order[ci2]
cop: GenTypes.AsmOperand* = self._asm_get_operand(operands, orig_i)
cons_val: str = cop.Constraint
cons_val: char* = cop.Constraint
if cons_val is None: cons_val = "r"
if first_const == 0: self._asm_append_cstr(const_buf, const_cap, ",")
@@ -737,7 +737,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
# Append clobber constraints ~{name}
for cidx in range(int(clobber_count)):
cl_addr2: uint64 = uint64(clobbers) + cidx * 8
cname: str = (str*)(cl_addr2)[0]
cname: char* = (str*)(cl_addr2)[0]
if cname is None or cname[0] == '\0': continue
if first_const == 0: self._asm_append_cstr(const_buf, const_cap, ",")
first_const = 0
@@ -750,7 +750,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
# buffer from that worst case instead of underestimating it (544 bytes/operand
# silently cut wide inlined named structs).
asm_arg_cap: sizet = sizet(operand_count + 1) * 4224 + 64
args_buf: str = pool.alloc(asm_arg_cap)
args_buf: char* = pool.alloc(asm_arg_cap)
if args_buf is None: return None
args_buf[0] = '\0'
@@ -766,7 +766,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
first_arg = 0
# Type text
ty_buf: str = pool.alloc_tagged(4096, memhub.DETSITE_CALLGENCBUILTIN_1150)
ty_buf: char* = pool.alloc_tagged(4096, memhub.DETSITE_CALLGENCBUILTIN_1150)
if ty_buf is not None:
ty_buf[0] = '\0'
mlirlite.TypePrint(ty_buf, 4096, aval.Ty, pool)
@@ -776,15 +776,15 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
# 7. Escape the asm text. First replace bare symbols with mangled names
# (lea rax, [rip + gdt] -> [rip + 054f5e56cbefcaa4.gdt]).
mangled_asm: str = self._asm_mangle_global_symbols(pool, asm_text, ASM_TEXT_BUF_SIZE, mod)
mangled_asm: char* = self._asm_mangle_global_symbols(pool, asm_text, ASM_TEXT_BUF_SIZE, mod)
if mangled_asm is None: mangled_asm = asm_text
escaped_asm: str = self._asm_escape_for_ir(pool, mangled_asm)
escaped_asm: char* = self._asm_escape_for_ir(pool, mangled_asm)
if escaped_asm is None: return None
# 8. Generate and emit the IR line
# The line buffer is sized after ret_ty_buf is ready (see line_cap below)
ssa_name: str = None
ssa_name: char* = None
if is_void == 0:
ssa_name = pool.alloc(16)
if ssa_name is not None:
@@ -792,7 +792,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
builder.Counter += 1
# Get the return type text
ret_ty_buf: str = pool.alloc_tagged(4096, memhub.DETSITE_CALLGENCBUILTIN_1176)
ret_ty_buf: char* = pool.alloc_tagged(4096, memhub.DETSITE_CALLGENCBUILTIN_1176)
if ret_ty_buf is not None:
ret_ty_buf[0] = '\0'
mlirlite.TypePrint(ret_ty_buf, 4096, ret_ty, pool)
@@ -801,7 +801,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
line_cap: sizet = 512 + string.strlen(escaped_asm) + string.strlen(args_buf)
if const_buf is not None: line_cap += string.strlen(const_buf)
if ret_ty_buf is not None: line_cap += string.strlen(ret_ty_buf)
line: str = pool.alloc(line_cap)
line: char* = pool.alloc(line_cap)
if line is None: return None
if is_void == 1:
@@ -823,13 +823,13 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if output_count > 1:
# Multi-output: extractvalue index out_emit_idx, then store
if oo.Value is not None and oo.Value.Ty is not None and oo.Value.Ty.Pointee != 0:
ev_name: str = pool.alloc(32)
ev_name: char* = pool.alloc(32)
# Size the line from its actual parts: ret_ty_buf alone can be ~4096
# bytes, so the old fixed cap could cut the instruction in half.
ev_cap: sizet = ASM_LINE_BUF_SIZE + 64
if ret_ty_buf is not None: ev_cap += string.strlen(ret_ty_buf)
if ssa_name is not None: ev_cap += string.strlen(ssa_name)
ev_line: str = pool.alloc(ev_cap)
ev_line: char* = pool.alloc(ev_cap)
if ev_name is not None and ev_line is not None:
# ssa_name is "%N"; build a unique "%asm.N.e{i}" (letter-
# initial) so %N.e{i} is not parsed as id plus a bad suffix.
@@ -870,7 +870,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
elif tk == ast.ASTKind.Constant:
cn_only: ast.Constant* = ast.Constant*(first_arg)
if cn_only.str_val is not None: cap += string.strlen(cn_only.str_val)
text: str = pool.alloc(cap)
text: char* = pool.alloc(cap)
if text is None: return None
text[0] = '\0'
@@ -894,7 +894,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if fexpr is None: continue
if fexpr.kind() == ast.ASTKind.Call:
fcall: ast.Call* = ast.Call*(fexpr)
attr_nm: str = self._asm_is_c_call(fcall)
attr_nm: char* = self._asm_is_c_call(fcall)
if attr_nm is not None:
fargs: list[ast.AST*]* = fcall.args
if fargs is None or len(fargs) < 1: continue
@@ -909,7 +909,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
self.Gen.TypeGenErr.fatal_error(fcall, f"raw IR has more than {LLVMIR_MAX_OPS} input operands (compiler limit); split the block", "TypeError")
return None
in_vals[counts[0]] = sizet(inp_val)
pname: str = pool.alloc(32)
pname: char* = pool.alloc(32)
if pname is not None:
viperlib.snprintf(pname, 32, "%%in%d", counts[0])
self._asm_append_cstr(text, cap, pname, "raw IR")
@@ -931,7 +931,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
self.Gen.TypeGenErr.fatal_error(fcall, f"raw IR has more than {LLVMIR_MAX_OPS} output operands (compiler limit); split the block", "TypeError")
return None
out_vals[counts[1]] = sizet(alloca)
rname: str = pool.alloc(32)
rname: char* = pool.alloc(32)
if rname is not None:
viperlib.snprintf(rname, 32, "%%vpc_out%d", counts[1])
self._asm_append_cstr(text, cap, rname, "raw IR")
@@ -948,7 +948,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
return text
def _raw_ir_emit_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, name: str, in_vals: sizet*, in_count: int, out_vals: sizet*, out_count: int) -> mlirlite.Value*:
def _raw_ir_emit_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, name: char*, in_vals: sizet*, in_count: int, out_vals: sizet*, out_count: int) -> mlirlite.Value*:
"""Emit the call to the generated raw function (inputs, then output pointers)."""
total: int = in_count + out_count
head: mlirlite.Value* = None
@@ -965,14 +965,14 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
void_ty: mlirlite.LLVMType* = mlirlite.Void(pool)
return mlirlite.build_call(builder, name, head, total, void_ty, 0)
def _raw_ir_func_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, tag: str) -> str:
def _raw_ir_func_name(self, pool: memhub.MemBuddy*, trans: HT.Translator*, tag: char*) -> str:
"""Unique symbol name for one generated raw function."""
seq: int = self.Gen.CallGenBase.LLVMIRRegSeq + 1
self.Gen.CallGenBase.LLVMIRRegSeq = seq
sha1: str = trans.ModuleSha1
sha1: char* = trans.ModuleSha1
if sha1 is None: sha1 = "vpc"
cap: sizet = string.strlen(sha1) + string.strlen(tag) + 48
name: str = pool.alloc(cap)
name: char* = pool.alloc(cap)
if name is None: return None
viperlib.snprintf(name, cap, "%s.%s_%d", sha1, tag, seq)
return name
@@ -994,19 +994,19 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
counts[0] = 0
counts[1] = 0
body: str = self._raw_ir_template(pool, builder, mod, cl, trans, in_vals, out_vals, counts)
body: char* = self._raw_ir_template(pool, builder, mod, cl, trans, in_vals, out_vals, counts)
if body is None: return None
in_count: int = counts[0]
out_count: int = counts[1]
name: str = self._raw_ir_func_name(pool, trans, "__vpc_llvmir")
name: char* = self._raw_ir_func_name(pool, trans, "__vpc_llvmir")
if name is None: return None
# Sidecar definition: linkonce_odr + alwaysinline so llvm-link merges it with the
# declaration and opt -passes=always-inline restores inline semantics.
cap: sizet = string.strlen(body) + 256 + sizet(in_count + out_count) * 4352
fn_buf: str = pool.alloc(cap)
ty_buf: str = pool.alloc(4096)
fn_buf: char* = pool.alloc(cap)
ty_buf: char* = pool.alloc(4096)
if fn_buf is None or ty_buf is None: return None
fn_buf[0] = '\0'
viperlib.snprintf(fn_buf, cap, "define linkonce_odr void @\"%s\"(", name)
@@ -1082,17 +1082,17 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
counts[0] = 0
counts[1] = 0
body: str = self._raw_ir_template(pool, builder, mod, cl, trans, in_vals, out_vals, counts)
body: char* = self._raw_ir_template(pool, builder, mod, cl, trans, in_vals, out_vals, counts)
if body is None: return None
in_count: int = counts[0]
out_count: int = counts[1]
name: str = self._raw_ir_func_name(pool, trans, "__vpc_mlir")
name: char* = self._raw_ir_func_name(pool, trans, "__vpc_mlir")
if name is None: return None
fn: mlirlite.Function* = mlirlite.create_function(pool, mod, name, mlirlite.Void(pool))
if fn is None: return None
pn: str = pool.alloc(32)
pn: char* = pool.alloc(32)
for k in range(in_count):
v: mlirlite.Value* = mlirlite.Value*(in_vals[k])
if v is None or v.Ty is None: return None
@@ -1108,9 +1108,9 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
fn.Attrs = "alwaysinline"
cap: sizet = string.strlen(body) + 128 + sizet(out_count) * 8448
raw: str = pool.alloc(cap)
ty_buf: str = pool.alloc(4096)
ptr_buf: str = pool.alloc(4096)
raw: char* = pool.alloc(cap)
ty_buf: char* = pool.alloc(4096)
ptr_buf: char* = pool.alloc(4096)
if raw is None or ty_buf is None or ptr_buf is None: return None
string.strcpy(raw, body)
pos: sizet = string.strlen(raw)
+115 -115
View File
@@ -20,7 +20,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# (GEP vtable ptr -> load method slot -> bitcast -> call).
# ============================================================
def _do_virtual_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, self_ptr: mlirlite.Value*, args_head: mlirlite.Value*, args_count: int, found_func: mlirlite.Function*, ret_ty: mlirlite.LLVMType*, vtable_base: mlirlite.Value* = None) -> mlirlite.Value*:
def _do_virtual_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: char*, method_name: char*, self_ptr: mlirlite.Value*, args_head: mlirlite.Value*, args_count: int, found_func: mlirlite.Function*, ret_ty: mlirlite.LLVMType*, vtable_base: mlirlite.Value* = None) -> mlirlite.Value*:
"""Call a virtual method indirectly through the vtable; returns None for void."""
if pool is None or builder is None or class_name is None or method_name is None: return None
if self_ptr is None or args_head is None: return None
@@ -55,7 +55,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
for mi in range(int(vc_entry.VTableMethodCount)):
mname_addr: sizet = methods_arr[mi]
if mname_addr == 0: continue
mname: str = rawptr(mname_addr)
mname: char* = rawptr(mname_addr)
if mname is not None and string.strcmp(mname, method_name) == 0:
method_index = mi
break
@@ -146,7 +146,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# first argument (goes through virtual dispatch if the method is in the vtable).
# ============================================================
def _call_method_on_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, self_ptr: mlirlite.Value*, extra_args: sizet*, extra_count: int, trans: HT.Translator*, node: ast.AST* = None, is_volatile: int = 0, arg_nodes: list[ast.AST*]* = None, recv_life: int = -1) -> mlirlite.Value*:
def _call_method_on_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: char*, method_name: char*, self_ptr: mlirlite.Value*, extra_args: sizet*, extra_count: int, trans: HT.Translator*, node: ast.AST* = None, is_volatile: int = 0, arg_nodes: list[ast.AST*]* = None, recv_life: int = -1) -> mlirlite.Value*:
"""Call a method on a struct pointer; adds a volatile barrier for constructors."""
if pool is None or builder is None or class_name is None or method_name is None: return None
if self_ptr is None: return None
@@ -157,7 +157,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# `__new__` 不查:其形参与 __init__ 共享且可能被裁剪,位置对应不可靠。
if arg_nodes is not None and string.strcmp(method_name, "__init__") == 0:
esc_mask_ci: int = GenApi.get_param_escapes_method(pool, class_name, method_name)
sha_ci: str = None
sha_ci: char* = None
if esc_mask_ci == 0:
# 跨模块回退(v3):构造器常定义在别的模块(self.f = p 写在那边)
sha_ci = self.Gen.StructGen.get_struct_sha1(class_name)
@@ -201,7 +201,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if cmop_struct_ty is None and cmop_entry is not None: cmop_struct_ty = cmop_entry.Ty
# Build the lookup name: ClassName.method_name.
lookup_name: str = pool.alloc(128)
lookup_name: char* = pool.alloc(128)
if lookup_name is None: return None
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, method_name)
@@ -214,13 +214,13 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if arg_nodes is not None: m_argc2 = len(arg_nodes)
ovl_m2: mlirlite.Function* = self.Gen.CallGenLeafKit.resolve_overload_call(pool, trans, trans._funcs, trans._func_count, lookup_name, trans.ModuleSha1, arg_nodes, m_argc2, node, 1, 1)
if ovl_m2 is None: return None
fn_sym2: str = mlirlite.function_get_name(ovl_m2)
fn_sym2: char* = mlirlite.function_get_name(ovl_m2)
if fn_sym2 is not None:
pre_len2: sizet = 17 + string.strlen(lookup_name)
if string.strlen(fn_sym2) > pre_len2:
sfx2: str = fn_sym2 + pre_len2
sfx2: char* = fn_sym2 + pre_len2
short_cap2: sizet = string.strlen(method_name) + string.strlen(sfx2) + 1
short_buf2: str = pool.alloc(short_cap2)
short_buf2: char* = pool.alloc(short_cap2)
if short_buf2 is not None:
viperlib.snprintf(short_buf2, short_cap2, "%s%s", method_name, sfx2)
method_name = short_buf2
@@ -229,14 +229,14 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if m_ovl_done2 == 0: found_func = self.find_func_in_module(mod, lookup_name)
# Determine the call name.
call_name: str = lookup_name
call_name: char* = lookup_name
if found_func is not None:
found_name: str = mlirlite.function_get_name(found_func)
found_name: char* = mlirlite.function_get_name(found_func)
if found_name is not None: call_name = found_name
else:
# found_func is None (stub not injected): build the call name from the
# class module SHA1, preferring the entry's SHA1.
cls_sha1: str = None
cls_sha1: char* = None
if cmop_entry is not None: cls_sha1 = cmop_entry.ModuleSha1
else: cls_sha1 = self.Gen.StructGen.get_struct_sha1(class_name)
if cls_sha1 is None and trans is not None:
@@ -245,15 +245,15 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if cls_sha1 is None:
# [D6 truncation] Allocate by exact length instead of a fixed 256
# buffer so long class/method names are not silently truncated.
msg_fmt: str = "Class '%s' method '%s': module SHA1 not found"
msg_fmt: char* = "Class '%s' method '%s': module SHA1 not found"
msg_size: sizet = string.strlen(msg_fmt) + string.strlen(class_name) + string.strlen(method_name) - 4 + 1
err_msg: str = pool.alloc(msg_size)
err_msg: char* = pool.alloc(msg_size)
if err_msg is not None:
viperlib.snprintf(err_msg, msg_size, msg_fmt, class_name, method_name)
self.Gen.TypeGenErr.fatal_error(node, err_msg)
self.Gen.TypeGenErr.fatal_error(node, "Module SHA1 not found")
# Build the call name: {cls_sha1}.{lookup_name}.
mangled2: str = pool.alloc(string.strlen(cls_sha1) + string.strlen(lookup_name) + 2)
mangled2: char* = pool.alloc(string.strlen(cls_sha1) + string.strlen(lookup_name) + 2)
if mangled2 is not None:
viperlib.snprintf(mangled2, string.strlen(cls_sha1) + string.strlen(lookup_name) + 2, "%s.%s", cls_sha1, lookup_name)
call_name = mangled2
@@ -267,9 +267,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
elif string.strcmp(method_name, "__getitem__") == 0 or string.strcmp(method_name, "__new__") == 0:
# fail-fast: __getitem__/__new__ must return a pointer; a missing
# return type means a broken stub declare (silent ptr truncation).
msg_fmt1: str = "Cannot determine return type of method '%s' (stub declare return type missing)"
msg_fmt1: char* = "Cannot determine return type of method '%s' (stub declare return type missing)"
msg_size1: sizet = string.strlen(msg_fmt1) + string.strlen(method_name) - 2 + 1
err_cm1: str = pool.alloc(msg_size1)
err_cm1: char* = pool.alloc(msg_size1)
if err_cm1 is not None:
viperlib.snprintf(err_cm1, msg_size1, msg_fmt1, method_name)
self.Gen.TypeGenErr.fatal_error(node, err_cm1)
@@ -284,18 +284,18 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if cmop_struct_ty is not None: call_ret_ty = mlirlite.Ptr(pool, cmop_struct_ty)
else:
# [D6 truncation] Use exact length instead of a fixed 256 buffer.
msg_fmt2: str = "Unknown self struct type of method '%s' (field type degraded, dependency module not preregistered?)"
msg_fmt2: char* = "Unknown self struct type of method '%s' (field type degraded, dependency module not preregistered?)"
msg_size2: sizet = string.strlen(msg_fmt2) + string.strlen(method_name) - 2 + 1
err_cm2: str = pool.alloc(msg_size2)
err_cm2: char* = pool.alloc(msg_size2)
if err_cm2 is not None:
viperlib.snprintf(err_cm2, msg_size2, msg_fmt2, method_name)
self.Gen.TypeGenErr.fatal_error(node, err_cm2)
self.Gen.TypeGenErr.fatal_error(node, "Unknown self struct type")
elif string.strcmp(method_name, "__getitem__") == 0:
# [D6 truncation] Use exact length instead of a fixed 256 buffer.
msg_fmt3: str = "Method '%s.__getitem__': stub declare not injected (dependency module vpi missing or wrong topological order); cannot determine return type"
msg_fmt3: char* = "Method '%s.__getitem__': stub declare not injected (dependency module vpi missing or wrong topological order); cannot determine return type"
msg_size3: sizet = string.strlen(msg_fmt3) + string.strlen(class_name) - 2 + 1
err_cm3: str = pool.alloc(msg_size3)
err_cm3: char* = pool.alloc(msg_size3)
if err_cm3 is not None:
viperlib.snprintf(err_cm3, msg_size3, msg_fmt3, class_name)
self.Gen.TypeGenErr.fatal_error(node, err_cm3)
@@ -307,7 +307,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# is_local_mod exemption: when the target module is the one being
# translated, take the return type from the current IR, not text.ll.
if cls_sha1 is not None and trans is not None and trans.ModuleSha1 is not None and string.strncmp(cls_sha1, trans.ModuleSha1, 16) == 0:
local_lk_cm2: str = pool.alloc(string.strlen(class_name) + string.strlen(method_name) + 2)
local_lk_cm2: char* = pool.alloc(string.strlen(class_name) + string.strlen(method_name) + 2)
if local_lk_cm2 is not None:
viperlib.snprintf(local_lk_cm2, string.strlen(class_name) + string.strlen(method_name) + 2, "%s.%s", class_name, method_name)
local_fn_cm2: mlirlite.Function* = self.find_func_in_module(mod, local_lk_cm2)
@@ -331,7 +331,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# returns None and breaks expression translation).
call_ret_ty = mlirlite.Int32(pool)
else:
err_cm4: str = pool.alloc(256)
err_cm4: char* = pool.alloc(256)
if err_cm4 is not None:
viperlib.snprintf(err_cm4, 256, "Cannot determine return type of method '%s.%s' (neither text.ll nor vpi hit, cross-module stub not injected)", class_name, method_name)
self.Gen.TypeGenErr.fatal_error(node, err_cm4)
@@ -387,7 +387,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# depends on mdf_gd (the only holder of stdlib constant snapshots).
# 必须带方法所属模块 SHA1:裸名跨模块会命中同名方法(`run`/`init`)的签名。
mdf_gd: GenTypes.FuncDefaultsEntry* = None
cmop_sha: str = self.Gen.StructGen.get_struct_sha1(class_name)
cmop_sha: char* = self.Gen.StructGen.get_struct_sha1(class_name)
if cmop_sha is not None: mdf_gd = self.Gen.CallGenLeafKit.find_func_defaults(lookup_name, cmop_sha)
if mdf_defaults is None and mdf_gd is not None:
mdf_defaults = mdf_gd.Defaults
@@ -396,9 +396,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Cross-module stub fallback: when found_func is None and the table has no
# ParamCount, fill it from the vpi cache to align declare/call with define.
vpi_pa: str = None
vpi_pa: char* = None
if mdf_param_count == 0 and found_func is None:
vpi_sha1_p: str = None
vpi_sha1_p: char* = None
if cmop_entry is not None and cmop_entry.ModuleSha1 is not None: vpi_sha1_p = cmop_entry.ModuleSha1
else: vpi_sha1_p = self.Gen.StructGen.get_struct_sha1(class_name)
if vpi_sha1_p is not None:
@@ -417,7 +417,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
ppcnt_f2: int = 0
ppn_f2: mlirlite.Param* = mlirlite.function_get_param_head(found_func)
while ppn_f2 is not None:
fp_nm_f2: str = mlirlite.param_get_name(ppn_f2)
fp_nm_f2: char* = mlirlite.param_get_name(ppn_f2)
if fp_nm_f2 is None or (string.strcmp(fp_nm_f2, "%__eh_msg_out__") != 0 and string.strcmp(fp_nm_f2, "%__eh_code_out__") != 0):
ppcnt_f2 += 1
ppn_f2 = mlirlite.param_get_next(ppn_f2)
@@ -555,7 +555,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
for cmop_mi in range(int(cmop_entry.VTableMethodCount)):
cmop_mname_addr: sizet = cmop_methods_arr[cmop_mi]
if cmop_mname_addr == 0: continue
cmop_mname: str = rawptr(cmop_mname_addr)
cmop_mname: char* = rawptr(cmop_mname_addr)
if cmop_mname is not None and string.strcmp(cmop_mname, method_name) == 0:
cmop_method_idx = cmop_mi
break
@@ -600,14 +600,14 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# land on a procedure. A generic instance (Class[T]) is emitted in this module only after all
# bodies, so the call site cannot read its signature; the template method is the only source.
# No return annotation = procedure = void, matching the unified body pipeline.
def _predicted_generic_method_is_void(self, pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
def _predicted_generic_method_is_void(self, pool: memhub.MemBuddy*, class_name: char*, method_name: char*) -> int:
"""1 when the template method (of a Class[T] instance) has no return annotation."""
if class_name is None or method_name is None: return 0
br: str = string.strchr(class_name, '[')
br: char* = string.strchr(class_name, '[')
if br is None: return 0
bare_len: sizet = sizet(uint64(br) - uint64(class_name))
if bare_len == 0: return 0
bare: str = pool.alloc(bare_len + 1)
bare: char* = pool.alloc(bare_len + 1)
if bare is None: return 0
string.strncpy(bare, class_name, bare_len)
bare[bare_len] = '\0'
@@ -642,7 +642,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# object pointer (obj_ptr, an alloca of Ptr(StructTy)) as the self argument.
# ============================================================
def _translate_method_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, obj_ptr: mlirlite.Value*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_method_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: char*, method_name: char*, obj_ptr: mlirlite.Value*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
"""Translate a method call; returns the call result Value."""
if pool is None or builder is None or class_name is None or method_name is None: return None
if obj_ptr is None or cl is None: return None
@@ -653,7 +653,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
cargs_mc: list[ast.AST*]* = cl.args
if cargs_mc is not None:
esc_mask_mc: int = GenApi.get_param_escapes_method(pool, class_name, method_name)
sha_mc: str = None
sha_mc: char* = None
if esc_mask_mc == 0:
# 跨模块回退(v3):本文件摘要表按文件重置,跨模块只能查 .vpi 落盘的摘要
sha_mc = self.Gen.StructGen.get_struct_sha1(class_name)
@@ -702,19 +702,19 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if cargs_mc is not None:
wf_mask_mc = GenApi.get_param_willfree_method(pool, class_name, method_name)
if wf_mask_mc == 0:
sha_wf_mc: str = self.Gen.StructGen.get_struct_sha1(class_name)
sha_wf_mc: char* = self.Gen.StructGen.get_struct_sha1(class_name)
wf_mask_mc = self.Gen.CallGenBase.lookup_willfree_via_vpi(pool, sha_wf_mc, class_name, method_name)
# list generic container method: dispatch to _translate_list_builtin_call inline
# when the method is a list builtin; use the receiver's actual struct name.
lb_src: str = class_name
lb_src: char* = class_name
if lb_src is None or lb_src[0] == '\0':
lb_sty_s: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(obj_ptr)
if lb_sty_s is not None:
lb_nm_s: str = mlirlite.get_struct_name(lb_sty_s)
lb_nm_s: char* = mlirlite.get_struct_name(lb_sty_s)
if lb_nm_s is not None: lb_src = lb_nm_s
if method_name is not None and lb_src is not None:
lb_cls: str = lb_src
lb_cls: char* = lb_src
# Strip the cross-module specialization sha1 prefix "{16hex}." using
# the single source GenApi.sha1_prefix_len.
lb_pfx_d: int = GenApi.sha1_prefix_len(lb_cls)
@@ -744,7 +744,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# [D25 fail-fast] A non-builtin method call on a bare generic template base
# (e.g. "list") has an unknown element type; fail fast rather than assume it.
cm_bare: str = None
cm_bare: char* = None
if lb_src is not None:
cm_pfx: int = GenApi.sha1_prefix_len(lb_src)
cm_bare = lb_src + cm_pfx if cm_pfx != 0 else lb_src
@@ -762,13 +762,13 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# [D25] Generic container receivers use the actual struct name (the IR-level
# truth); resolve via CallGenProp._resolve_generic_receiver_name.
mc_act_d: str = self.Gen.CallGenProp._resolve_generic_receiver_name(pool, mc_struct_ty, method_name, cl)
mc_act_d: char* = self.Gen.CallGenProp._resolve_generic_receiver_name(pool, mc_struct_ty, method_name, cl)
if mc_act_d is not None: class_name = mc_act_d
# Build the lookup name; allocate by exact length ([D6]) since the
# specialized class name can be long (e.g. list[rawptr[sha.AST]]).
lk_cap: sizet = string.strlen(class_name) + string.strlen(method_name) + 2
lookup_name: str = pool.alloc(lk_cap)
lookup_name: char* = pool.alloc(lk_cap)
if lookup_name is None: return None
viperlib.snprintf(lookup_name, lk_cap, "%s.%s", class_name, method_name)
@@ -784,13 +784,13 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if m_args_o is not None: m_argc_o = len(m_args_o)
ovl_m: mlirlite.Function* = self.Gen.CallGenLeafKit.resolve_overload_call(pool, trans, trans._funcs, trans._func_count, lookup_name, trans.ModuleSha1, m_args_o, m_argc_o, cl.func, 1, 1)
if ovl_m is None: return None
fn_sym_o: str = mlirlite.function_get_name(ovl_m)
fn_sym_o: char* = mlirlite.function_get_name(ovl_m)
if fn_sym_o is not None:
pre_len_o: sizet = 17 + string.strlen(lookup_name)
if string.strlen(fn_sym_o) > pre_len_o:
sfx_o: str = fn_sym_o + pre_len_o
sfx_o: char* = fn_sym_o + pre_len_o
short_cap: sizet = string.strlen(method_name) + string.strlen(sfx_o) + 1
short_buf_o: str = pool.alloc(short_cap)
short_buf_o: char* = pool.alloc(short_cap)
if short_buf_o is not None:
viperlib.snprintf(short_buf_o, short_cap, "%s%s", method_name, sfx_o)
method_name = short_buf_o
@@ -804,20 +804,20 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# ({sha1}.list[int].get), so match by suffix using mc_entry.Name.
if found_func is None and mc_entry is not None and mc_entry.Name is not None:
if string.strcmp(mc_entry.Name, class_name) != 0:
spec_lookup: str = pool.alloc(160)
spec_lookup: char* = pool.alloc(160)
if spec_lookup is not None:
viperlib.snprintf(spec_lookup, 160, "%s.%s", mc_entry.Name, method_name)
found_func = self.find_func_in_module(mod, spec_lookup)
# Walk the MRO ancestor chain for the parent method (first parent wins).
mc_anc: str = None # the method's defining class (for multi-inheritance this-adjust)
mc_anc: char* = None # the method's defining class (for multi-inheritance this-adjust)
if found_func is None:
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi: int = 1
while mro_ptr is not None and mro_ptr[mi] != 0:
anc: str = rawptr(mro_ptr[mi])
anc: char* = rawptr(mro_ptr[mi])
if anc is not None:
parent_lookup: str = pool.alloc(128)
parent_lookup: char* = pool.alloc(128)
if parent_lookup is not None:
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, method_name)
parent_func: mlirlite.Function* = self.find_func_in_module(mod, parent_lookup)
@@ -832,7 +832,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if found_func is None:
sib_recv_local: int = 1
if trans is not None and trans.ModuleSha1 is not None:
sib_recv_sha1: str = None
sib_recv_sha1: char* = None
if mc_entry is not None and mc_entry.ModuleSha1 is not None: sib_recv_sha1 = mc_entry.ModuleSha1
else: sib_recv_sha1 = self.Gen.StructGen.get_struct_sha1(class_name)
if sib_recv_sha1 is not None:
@@ -844,12 +844,12 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
while sc_i < sc_n and found_func is None:
cand_entry: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(sc_i)
if cand_entry is not None and cand_entry.Name is not None:
cand_name: str = cand_entry.Name
cand_name: char* = cand_entry.Name
if string.strcmp(cand_name, class_name) != 0:
# Specialized generic class name contains '[': not a Mixin candidate.
if string.strchr(cand_name, '[') is None:
if self._mixin_sibling_same_composite(pool, class_name, cand_name) != 0:
cand_lookup: str = pool.alloc(128)
cand_lookup: char* = pool.alloc(128)
if cand_lookup is not None:
viperlib.snprintf(cand_lookup, 128, "%s.%s", cand_name, method_name)
cand_func: mlirlite.Function* = self.find_func_in_module(mod, cand_lookup)
@@ -860,9 +860,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Determine the call name (default "ClassName.method"; when not found, build
# it along the MRO using the defining class's SHA1).
call_name: str = lookup_name
call_name: char* = lookup_name
if found_func is not None:
found_name: str = mlirlite.function_get_name(found_func)
found_name: char* = mlirlite.function_get_name(found_func)
if found_name is not None: call_name = found_name
# Determine the return type.
@@ -878,8 +878,8 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
call_ret_ty = sp_ty2
# Special methods (e.g. __before_init__) also need the sha1 prefix on
# cross-module calls, else the bare name links to an undefined symbol.
sp_anc_name: str = class_name
sp_anc_sha1: str = None
sp_anc_name: char* = class_name
sp_anc_sha1: char* = None
if mc_entry is not None: sp_anc_sha1 = mc_entry.ModuleSha1
else: sp_anc_sha1 = self.Gen.StructGen.get_struct_sha1(class_name)
if sp_anc_sha1 is None and mc_struct_ty is not None:
@@ -889,7 +889,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Only extract sha1 when the type name is "{16hex}.{Class}"
# (via GenApi.sha1_prefix_len), avoiding false positives.
if GenApi.sha1_prefix_len(type_name_sp) != 0:
sb_sp: str = pool.alloc(17)
sb_sp: char* = pool.alloc(17)
if sb_sp is not None:
string.strncpy(sb_sp, type_name_sp, 16)
sb_sp[16] = '\0'
@@ -898,15 +898,15 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
pass
if sp_anc_sha1 is not None:
ml_sp: sizet = string.strlen(sp_anc_sha1) + string.strlen(sp_anc_name) + string.strlen(method_name) + 3
mangled_sp: str = pool.alloc(ml_sp)
mangled_sp: char* = pool.alloc(ml_sp)
if mangled_sp is not None:
viperlib.snprintf(mangled_sp, ml_sp, "%s.%s.%s", sp_anc_sha1, sp_anc_name, method_name)
call_name = mangled_sp
else:
# The method's defining class (source of cross-module name / return
# type); defaults to class_name and updates on MRO fallback.
ret_anc_name: str = class_name
ret_anc_sha1: str = None
ret_anc_name: char* = class_name
ret_anc_sha1: char* = None
if mc_entry is not None: ret_anc_sha1 = mc_entry.ModuleSha1
else: ret_anc_sha1 = self.Gen.StructGen.get_struct_sha1(class_name)
if ret_anc_sha1 is None:
@@ -914,7 +914,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
mro_s0: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi_s0: int = 1
while mro_s0 is not None and mro_s0[mi_s0] != 0 and ret_anc_sha1 is None:
anc_s0: str = rawptr(mro_s0[mi_s0])
anc_s0: char* = rawptr(mro_s0[mi_s0])
if anc_s0 is not None: ret_anc_sha1 = self.Gen.StructGen.get_struct_sha1(anc_s0)
mi_s0 += 1
@@ -922,15 +922,15 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# registered yet; trigger it (idempotent), then re-lookup.
gen_done: int = 0
if class_name is not None:
lbkt_g: str = string.strchr(class_name, '[')
lbkt_g: char* = string.strchr(class_name, '[')
if lbkt_g is not None:
rbkt_g: str = string.strrchr(class_name, ']')
rbkt_g: char* = string.strrchr(class_name, ']')
if rbkt_g is not None and rbkt_g > lbkt_g:
base_len_g: sizet = sizet(uint64(lbkt_g) - uint64(class_name))
arg_len_g: sizet = sizet(uint64(rbkt_g) - uint64(lbkt_g) - 1)
if base_len_g > 0 and arg_len_g > 0:
base_buf_g: str = pool.alloc(base_len_g + 1)
arg_buf_g: str = pool.alloc(arg_len_g + 1)
base_buf_g: char* = pool.alloc(base_len_g + 1)
arg_buf_g: char* = pool.alloc(arg_len_g + 1)
if base_buf_g is not None and arg_buf_g is not None:
string.strncpy(base_buf_g, class_name, base_len_g)
base_buf_g[base_len_g] = '\0'
@@ -939,14 +939,14 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
ta_list_g: list[str]* = list[str](pool, 8)
if ta_list_g is not None:
# Comma-separated multiple type args (list[X] / dict[K, V]).
cur_arg_g: str = arg_buf_g
cur_arg_g: char* = arg_buf_g
while cur_arg_g is not None:
comma_g: str = string.strchr(cur_arg_g, ',')
comma_g: char* = string.strchr(cur_arg_g, ',')
if comma_g is None:
ta_list_g.append(cur_arg_g)
break
seg_len_g: sizet = sizet(uint64(comma_g) - uint64(cur_arg_g))
seg_buf_g: str = pool.alloc(seg_len_g + 1)
seg_buf_g: char* = pool.alloc(seg_len_g + 1)
if seg_buf_g is not None:
string.strncpy(seg_buf_g, cur_arg_g, seg_len_g)
seg_buf_g[seg_len_g] = '\0'
@@ -954,16 +954,16 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
cur_arg_g = comma_g + 1
# base_buf_g may carry a template module SHA1 prefix
# ({tpl}.GSList); strip it before template lookup.
base_clean_g: str = base_buf_g
base_clean_g: char* = base_buf_g
# [C10] SHA1 prefix detection uses the single source;
# take the cut length from its return value.
base_pfx_g: int = GenApi.sha1_prefix_len(base_buf_g)
if base_pfx_g != 0: base_clean_g = base_buf_g + sizet(base_pfx_g)
spec_name_g: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, base_clean_g, ta_list_g)
spec_name_g: char* = self.Gen.ClassGenGeneric._specialize_generic_class(trans, base_clean_g, ta_list_g)
if spec_name_g is not None:
# Re-lookup the specialized function after specialization;
# find_func_in_module matches both bare and tpl-prefixed names.
spec_lk_g: str = pool.alloc(string.strlen(class_name) + string.strlen(method_name) + 2)
spec_lk_g: char* = pool.alloc(string.strlen(class_name) + string.strlen(method_name) + 2)
if spec_lk_g is not None:
viperlib.snprintf(spec_lk_g, string.strlen(class_name) + string.strlen(method_name) + 2, "%s.%s", class_name, method_name)
spec_found_g: mlirlite.Function* = self.find_func_in_module(mod, spec_lk_g)
@@ -977,7 +977,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# is_local_mod exemption: when the method is defined in the module
# being translated, take the return type from the current IR.
if trans is not None and ret_anc_sha1 is not None and trans.ModuleSha1 is not None and string.strncmp(ret_anc_sha1, trans.ModuleSha1, 16) == 0:
local_lk_lm: str = pool.alloc(string.strlen(ret_anc_name) + string.strlen(method_name) + 2)
local_lk_lm: char* = pool.alloc(string.strlen(ret_anc_name) + string.strlen(method_name) + 2)
if local_lk_lm is not None:
viperlib.snprintf(local_lk_lm, string.strlen(ret_anc_name) + string.strlen(method_name) + 2, "%s.%s", ret_anc_name, method_name)
local_fn_lm: mlirlite.Function* = self.find_func_in_module(mod, local_lk_lm)
@@ -999,9 +999,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
mro_v2: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi_v2: int = 1
while mro_v2 is not None and mro_v2[mi_v2] != 0 and vpi_ret_ty is None:
anc_v2: str = rawptr(mro_v2[mi_v2])
anc_v2: char* = rawptr(mro_v2[mi_v2])
if anc_v2 is not None and string.strcmp(anc_v2, class_name) != 0:
anc_sha1_v2: str = self.Gen.StructGen.get_struct_sha1(anc_v2)
anc_sha1_v2: char* = self.Gen.StructGen.get_struct_sha1(anc_v2)
vpi_ret_ty = self._lookup_method_ret_ty_via_vpi(pool, anc_sha1_v2, anc_v2, method_name, trans)
if vpi_ret_ty is not None:
ret_anc_name = anc_v2
@@ -1018,9 +1018,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
mro_t2: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
mi_t2: int = 1
while mro_t2 is not None and mro_t2[mi_t2] != 0 and looked_up_ty is None:
anc_t2: str = rawptr(mro_t2[mi_t2])
anc_t2: char* = rawptr(mro_t2[mi_t2])
if anc_t2 is not None and string.strcmp(anc_t2, class_name) != 0:
anc_sha1_t2: str = self.Gen.StructGen.get_struct_sha1(anc_t2)
anc_sha1_t2: char* = self.Gen.StructGen.get_struct_sha1(anc_t2)
looked_up_ty = self._LookupMethodRetTyFromTextLL(pool, anc_sha1_t2, anc_t2, method_name)
if looked_up_ty is not None:
ret_anc_name = anc_t2
@@ -1045,7 +1045,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Only extract sha1 when the type name is "{16hex}.{Class}"
# (via the single source GenApi.sha1_prefix_len).
if GenApi.sha1_prefix_len(type_name_st) != 0:
sha1_from_ty: str = pool.alloc(17)
sha1_from_ty: char* = pool.alloc(17)
if sha1_from_ty is not None:
string.strncpy(sha1_from_ty, type_name_st, 16)
sha1_from_ty[16] = '\0'
@@ -1054,7 +1054,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
pass
if ret_anc_name is not None and ret_anc_sha1 is not None:
ml_anc: sizet = string.strlen(ret_anc_sha1) + string.strlen(ret_anc_name) + string.strlen(method_name) + 3
mangled_anc: str = pool.alloc(ml_anc)
mangled_anc: char* = pool.alloc(ml_anc)
if mangled_anc is not None:
viperlib.snprintf(mangled_anc, ml_anc, "%s.%s.%s", ret_anc_sha1, ret_anc_name, method_name)
call_name = mangled_anc
@@ -1082,17 +1082,17 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
is_first_parent_th: int = 0
mro_fp_th: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
if mro_fp_th is not None and mro_fp_th[1] != 0:
first_parent_th: str = rawptr(mro_fp_th[1])
first_parent_th: char* = rawptr(mro_fp_th[1])
if first_parent_th is not None and string.strcmp(first_parent_th, mc_anc) == 0: is_first_parent_th = 1
anc_entry_th: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(mc_anc)
# Parent with no fields (FieldCount==0): the method body reads no layout
# fields, so this needs no byte adjust; pass it through unchanged.
if anc_entry_th is not None and anc_entry_th.FieldCount > 0:
anc_first_name_th: str = None
anc_first_name_th: char* = None
for afi_th in range(int(anc_entry_th.FieldCount)):
afe_th: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(anc_entry_th, afi_th)
if afe_th is not None:
afn_th: str = self.Gen.StructGen.get_field_name_ptr(afe_th)
afn_th: char* = self.Gen.StructGen.get_field_name_ptr(afe_th)
if afn_th is not None and string.strcmp(afn_th, "__vtable__") != 0:
anc_first_name_th = afn_th
break
@@ -1174,7 +1174,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
arg_node_k: ast.AST* = None
if pi_k < can: arg_node_k = cargs.get(pi_k)
else:
pname_k: str = mlirlite.param_get_name(cur_param_k)
pname_k: char* = mlirlite.param_get_name(cur_param_k)
if pname_k is not None:
ki_k: int
for ki_k in range(int(kwc)):
@@ -1187,7 +1187,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if arg_node_k is not None:
# Fetch the param type first (char literal -> str param needs a materialized string).
pty_k0: mlirlite.LLVMType* = mlirlite.param_get_ty(cur_param_k)
ao_name_k: str = None
ao_name_k: char* = None
if found_func is not None: ao_name_k = mlirlite.function_get_name(found_func)
arg_val_k = self.Gen.CallGenBase.translate_call_arg(builder, pool, mod, trans, ao_name_k, pi_k, arg_node_k, pty_k0, None)
if arg_val_k is None:
@@ -1234,7 +1234,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
pi += 1
if pp is not None: pty = mlirlite.param_get_ty(pp)
# Value-reference params (&T / &temp T): auto-address (self is param 0, arg ai -> param ai+1).
ao_name_p: str = None
ao_name_p: char* = None
if found_func is not None: ao_name_p = mlirlite.function_get_name(found_func)
arg_val: mlirlite.Value* = self.Gen.CallGenBase.translate_call_arg(builder, pool, mod, trans, ao_name_p, int(ai) + 1, arg_node, pty, None)
if arg_val is None:
@@ -1275,7 +1275,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
mdf_param_count: int = 0
# 方法所属模块 SHA1:后面所有 defaults 查找都必须带上它,绝不跨模块按名搜
# (`run`/`init` 这类方法名在多个模块里都有,裸名会拿错签名去补参)。
vpi_sha1_p: str = None
vpi_sha1_p: char* = None
if mc_entry is not None and mc_entry.ModuleSha1 is not None: vpi_sha1_p = mc_entry.ModuleSha1
else: vpi_sha1_p = self.Gen.StructGen.get_struct_sha1(class_name)
if trans is not None and trans._funcs is not None:
@@ -1304,7 +1304,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Cross-module stub fallback: when the dependency's vpi is not loaded the
# table lacks ParamCount; fill it from the vpi text cache.
vpi_pa: str = None
vpi_pa: char* = None
if mdf_param_count == 0 and found_func is None:
if vpi_sha1_p is not None:
vpi_pa = self.Gen.CallGenBase._vpi_method_param_annot(pool, vpi_sha1_p, class_name, method_name)
@@ -1322,7 +1322,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
ppcnt_f: int = 0
ppn_f: mlirlite.Param* = mlirlite.function_get_param_head(found_func)
while ppn_f is not None:
fp_nm_f: str = mlirlite.param_get_name(ppn_f)
fp_nm_f: char* = mlirlite.param_get_name(ppn_f)
if fp_nm_f is None or (string.strcmp(fp_nm_f, "%__eh_msg_out__") != 0 and string.strcmp(fp_nm_f, "%__eh_code_out__") != 0):
ppcnt_f += 1
ppn_f = mlirlite.param_get_next(ppn_f)
@@ -1420,7 +1420,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
for mc_mi in range(int(mc_entry.VTableMethodCount)):
mc_mname_addr: sizet = mc_methods_arr[mc_mi]
if mc_mname_addr == 0: continue
mc_mname: str = rawptr(mc_mname_addr)
mc_mname: char* = rawptr(mc_mname_addr)
if mc_mname is not None and string.strcmp(mc_mname, method_name) == 0:
mc_method_idx = mc_mi
break
@@ -1447,7 +1447,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# _translate_list_builtin_call - inline translation of list builtin methods
# (layout: __data__(0) __count__(8) __elem_size__(32) ...); lm_obj is Ptr(list-struct).
def _translate_list_builtin_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, lm_name: str, lm_obj: mlirlite.Value*, cargs: list[ast.AST*]*, can: sizet, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_list_builtin_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, lm_name: char*, lm_obj: mlirlite.Value*, cargs: list[ast.AST*]*, can: sizet, trans: HT.Translator*) -> mlirlite.Value*:
"""Translate a list builtin method call; returns None for void methods."""
if pool is None or builder is None or lm_obj is None or lm_name is None: return None
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
@@ -1456,7 +1456,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Element type T parsed from the list specialization name (list[int]->i32,
# list[str]->i8*; unknown/pointer elements -> i8*).
lm_esty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lm_obj)
lm_ename: str = None
lm_ename: char* = None
if lm_esty is not None: lm_ename = mlirlite.get_struct_name(lm_esty)
lm_elem_ty: mlirlite.LLVMType* = None
if lm_ename is not None: lm_elem_ty = self.Gen.ExprGenName._list_elem_type_from_name(pool, lm_ename)
@@ -1552,7 +1552,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# reallocation, so use the _list.py implementation.
elif string.strcmp(lm_name, "append") == 0:
if can >= 1 and cargs is not None:
ap_sha1: str = self._lookup_module_sha1("_list")
ap_sha1: char* = self._lookup_module_sha1("_list")
if ap_sha1 is None:
# Dependency module not compiled (_list not in the build chain): fail
# fast, never silently skip append.
@@ -1560,21 +1560,21 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if ap_sha1 is not None:
# Extract the specialized class name: list.append's symbol is
# {sha1}.list[AST*].append, not {sha1}.list.append.
ap_spec_nm: str = None
ap_spec_nm: char* = None
ap_struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lm_obj)
if ap_struct_ty is not None: ap_spec_nm = self.Gen.StructGen.get_class_name_by_type(pool, ap_struct_ty)
if ap_spec_nm is None: ap_spec_nm = "list"
# Trigger generic specialization translation (idempotent).
if trans is not None and ap_spec_nm is not None:
lbkt_ap: str = string.strchr(ap_spec_nm, '[')
lbkt_ap: char* = string.strchr(ap_spec_nm, '[')
if lbkt_ap is not None:
rbkt_ap: str = string.strrchr(ap_spec_nm, ']')
rbkt_ap: char* = string.strrchr(ap_spec_nm, ']')
if rbkt_ap is not None and rbkt_ap > lbkt_ap:
base_len_ap: sizet = sizet(uint64(lbkt_ap) - uint64(ap_spec_nm))
arg_len_ap: sizet = sizet(uint64(rbkt_ap) - uint64(lbkt_ap) - 1)
if base_len_ap > 0 and arg_len_ap > 0:
base_buf_ap: str = pool.alloc(base_len_ap + 1)
arg_buf_ap: str = pool.alloc(arg_len_ap + 1)
base_buf_ap: char* = pool.alloc(base_len_ap + 1)
arg_buf_ap: char* = pool.alloc(arg_len_ap + 1)
if base_buf_ap is not None and arg_buf_ap is not None:
string.strncpy(base_buf_ap, ap_spec_nm, base_len_ap)
base_buf_ap[base_len_ap] = '\0'
@@ -1583,14 +1583,14 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
args_ap: list[str]* = list[str](pool, 8)
if args_ap is not None:
# Comma-separated multiple type args (list[X] / dict[K, V]).
cur_ap: str = arg_buf_ap
cur_ap: char* = arg_buf_ap
while cur_ap is not None:
comma_ap: str = string.strchr(cur_ap, ',')
comma_ap: char* = string.strchr(cur_ap, ',')
if comma_ap is None:
args_ap.append(cur_ap)
break
seg_len_ap: sizet = sizet(uint64(comma_ap) - uint64(cur_ap))
seg_buf_ap: str = pool.alloc(seg_len_ap + 1)
seg_buf_ap: char* = pool.alloc(seg_len_ap + 1)
if seg_buf_ap is not None:
string.strncpy(seg_buf_ap, cur_ap, seg_len_ap)
seg_buf_ap[seg_len_ap] = '\0'
@@ -1600,7 +1600,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Call name "{sha1}.{spec}.append":
# sha1(16) + '.'(1) + spec + '.append'(7) + NUL(1) = spec + 25
ap_mlen: sizet = string.strlen(ap_sha1) + string.strlen(ap_spec_nm) + 26
ap_mangled: str = pool.alloc(ap_mlen)
ap_mangled: char* = pool.alloc(ap_mlen)
if ap_mangled is not None:
viperlib.snprintf(ap_mangled, ap_mlen, "%s.%s.append", ap_sha1, ap_spec_nm)
ap_item_node: ast.AST* = cargs.get(0)
@@ -1622,7 +1622,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# current class from self, first MRO ancestor defining the method, no virtual.
# ============================================================
def _translate_super_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, method_name: str, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_super_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, method_name: char*, trans: HT.Translator*) -> mlirlite.Value*:
"""Translate super().method(args) as a static parent-class method call."""
if pool is None or builder is None or cl is None or method_name is None: return None
if trans is None: return None
@@ -1633,18 +1633,18 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if self_entry is None:
self.Gen.TypeGenErr.fatal_error(cl, "super(): no self variable in the current scope (unavailable inside a static method)", "TypeError")
return None
cur_class: str = self.Gen.VarGen.var_annot_class_name(self_entry)
cur_class: char* = self.Gen.VarGen.var_annot_class_name(self_entry)
if cur_class is None:
self.Gen.TypeGenErr.fatal_error(cl, "super() can only be used inside a class method (self has no annotated class name)")
return None
# 2. Walk the MRO, skipping self, for the first ancestor defining the method.
anc: str = None
anc: char* = None
mro_sp: sizet* = self.Gen.StructGen.get_mro_chain(pool, cur_class)
mi_sp: int = 1
while mro_sp is not None and mro_sp[mi_sp] != 0:
anc_sp: str = rawptr(mro_sp[mi_sp])
anc_sp: char* = rawptr(mro_sp[mi_sp])
if anc_sp is not None:
probe_sp: str = pool.alloc(160)
probe_sp: char* = pool.alloc(160)
if probe_sp is not None:
viperlib.snprintf(probe_sp, 160, "%s.%s", anc_sp, method_name)
if self.Gen.CallGenLeafKit.find_func_global(probe_sp) is not None:
@@ -1652,7 +1652,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
break
mi_sp += 1
if anc is None:
err_sp: str = pool.alloc(256)
err_sp: char* = pool.alloc(256)
if err_sp is not None:
viperlib.snprintf(err_sp, 256, "super(): the parent chain of current class %s does not define method %s", cur_class, method_name)
self.Gen.TypeGenErr.fatal_error(cl, err_sp, "TypeError")
@@ -1679,11 +1679,11 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if string.strcmp(anc, cur_class) != 0:
anc_entry_sp: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(anc)
if anc_entry_sp is not None and anc_entry_sp.FieldCount > 0:
first_name_sp: str = None
first_name_sp: char* = None
for afi_sp in range(int(anc_entry_sp.FieldCount)):
afe_sp: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(anc_entry_sp, afi_sp)
if afe_sp is not None:
afn_sp: str = self.Gen.StructGen.get_field_name_ptr(afe_sp)
afn_sp: char* = self.Gen.StructGen.get_field_name_ptr(afe_sp)
if afn_sp is not None and string.strcmp(afn_sp, "__vtable__") != 0:
first_name_sp = afn_sp
break
@@ -1709,19 +1709,19 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
mlirlite.module_ensure_opaque_for_type(mod, pool, anc_ptr_sp)
self_arg = mlirlite.build_bitcast(builder, adj_sp, anc_ptr_sp)
# 4. Find the parent method function and build the call name (SHA1-mangled).
anc_lookup: str = pool.alloc(160)
anc_lookup: char* = pool.alloc(160)
if anc_lookup is None: return None
viperlib.snprintf(anc_lookup, 160, "%s.%s", anc, method_name)
found_func: mlirlite.Function* = self.find_func_in_module(mod, anc_lookup)
call_name: str = anc_lookup
anc_sha1_sp: str = self.Gen.StructGen.get_struct_sha1(anc)
call_name: char* = anc_lookup
anc_sha1_sp: char* = self.Gen.StructGen.get_struct_sha1(anc)
if anc_sha1_sp is None: anc_sha1_sp = trans.ModuleSha1
if found_func is not None:
fname_sp: str = mlirlite.function_get_name(found_func)
fname_sp: char* = mlirlite.function_get_name(found_func)
if fname_sp is not None: call_name = fname_sp
else:
if anc_sha1_sp is not None:
mangled_sp: str = self.Gen.FuncGenBase._mangle_name_with_sha1(anc_sha1_sp, anc_lookup)
mangled_sp: char* = self.Gen.FuncGenBase._mangle_name_with_sha1(anc_sha1_sp, anc_lookup)
if mangled_sp is not None: call_name = mangled_sp
# 5. Translate args and coerce to params (self is param 0, arg ai maps to ai+1).
cargs_sp: list[ast.AST*]* = cl.args
@@ -1793,7 +1793,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
return mlirlite.build_call(builder, call_name, head, arg_count_sp, ret_ty_sp, is_variadic_sp)
# _recv_is_mixin_base - is the receiver used as a parent of a registered class?
# Only a real direct parent can have its field area in a composite class layout.
def _recv_is_mixin_base(self, class_name: str) -> int:
def _recv_is_mixin_base(self, class_name: char*) -> int:
"""Is the receiver class a direct parent of any registered class; 1=yes / 0=no."""
if class_name is None: return 0
if self.Gen is None: return 0
@@ -1803,14 +1803,14 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if e is not None and e.Name is not None:
pc: int = self.Gen.StructGen.get_parent_count(e.Name)
for pj in range(int(pc)):
pn: str = self.Gen.StructGen.get_parent_name_at(e.Name, pj)
pn: char* = self.Gen.StructGen.get_parent_name_at(e.Name, pj)
if pn is not None:
if string.strcmp(pn, class_name) == 0: return 1
return 0
# _mixin_sibling_same_composite - do both classes share a composite class's MRO?
# A registered class K whose MRO contains both is the necessary condition.
def _mixin_sibling_same_composite(self, pool: memhub.MemBuddy*, class_name: str, cand_name: str) -> int:
def _mixin_sibling_same_composite(self, pool: memhub.MemBuddy*, class_name: char*, cand_name: char*) -> int:
"""Do both classes share a registered composite class's MRO; 1=yes / 0=no."""
if class_name is None or cand_name is None or pool is None: return 0
if self.Gen is None: return 0
@@ -1824,7 +1824,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
has_cand: int = 0
mi: int = 0
while mro[mi] != 0:
nm: str = rawptr(mro[mi])
nm: char* = rawptr(mro[mi])
if nm is not None:
if string.strcmp(nm, class_name) == 0: has_self = 1
elif string.strcmp(nm, cand_name) == 0: has_cand = 1
@@ -1868,7 +1868,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
return 0
# _kwarg_name_matches - check whether a keyword arg name matches a param name.
# Param names carry a "%" prefix in IR; compare after skipping it.
def _kwarg_name_matches(self, kw_arg: str, param_name: str) -> int:
def _kwarg_name_matches(self, kw_arg: char*, param_name: char*) -> int:
"""Check whether kwarg name kw_arg matches param param_name (skips '%' prefix)."""
if kw_arg is None or param_name is None: return 0
start: sizet = 0
+41 -41
View File
@@ -54,7 +54,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# _apply_struct_defaults - apply struct field default values after zeroinitializer and
# before explicit args (which later override); translate + store each field with a default.
def _apply_struct_defaults(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, struct_ty: mlirlite.LLVMType*, class_name: str, tmp: mlirlite.Value*, trans: HT.Translator*, is_volatile: int = 0):
def _apply_struct_defaults(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, struct_ty: mlirlite.LLVMType*, class_name: char*, tmp: mlirlite.Value*, trans: HT.Translator*, is_volatile: int = 0):
"""Apply struct field default values; is_volatile=1 keeps ctor writes volatile"""
# locate the entry by type pointer to avoid cross-module same-name find_struct mistakes
entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
@@ -82,9 +82,9 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
zero_arr2: mlirlite.Value* = mlirlite.ConstZero(pool, fe.Ty)
if zero_arr2 is not None: mlirlite.build_store(builder, zero_arr2, field_ptr_arr2, is_volatile)
# owner_ctx only serves the empty-element fail-fast message (class + field + index)
sd_ctx: str = pool.alloc(192)
sd_ctx: char* = pool.alloc(192)
if sd_ctx is not None:
fname_sd: str = fe.Name
fname_sd: char* = fe.Name
if fname_sd is None: fname_sd = "?"
viperlib.snprintf(sd_ctx, 192, "Class %s field %s (struct ctor defaults):", class_name, fname_sd)
# arr_ty_opt = fe.Ty: same reason as ClassGenVTable (the GEP value may carry no pointee type)
@@ -108,7 +108,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# _translate_struct_ctor - struct constructor Point(10, 20): alloca, zeroinit, per-field
# GEP + store (or __new__/__before_init__/__init__ for OOP structs), then load.
def _translate_struct_ctor(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, struct_ty: mlirlite.LLVMType*, class_name: str, cargs: list[ast.AST*]*, can: sizet, trans: HT.Translator*, is_volatile: int = 0) -> mlirlite.Value*:
def _translate_struct_ctor(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, struct_ty: mlirlite.LLVMType*, class_name: char*, cargs: list[ast.AST*]*, can: sizet, trans: HT.Translator*, is_volatile: int = 0) -> mlirlite.Value*:
"""Translate a struct constructor; stores args by field; is_volatile=1 marks writes volatile"""
# Create the temporary alloca, ensuring the full cross-module struct definition is
# available in this module.
@@ -142,7 +142,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# Cross-module OOP inference: IsOOP may be unset (Phase1 cache skipped
# _translate_oop_methods). Check for ClassName.__init__ in the module or global defaults table.
if is_oop == 0 and class_name is not None:
init_lookup_name: str = pool.alloc(128)
init_lookup_name: char* = pool.alloc(128)
if init_lookup_name is not None:
viperlib.snprintf(init_lookup_name, 128, "%s.__init__", class_name)
# option 1: check the current module's function declarations
@@ -205,7 +205,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if ctor_entry is not None and ctor_entry.IsOOP != 0: bi_ok = 1
if bi_ok == 0 and ctor_entry is not None and ctor_entry.HasInit != 0: bi_ok = 1
if bi_ok == 0 and class_name is not None:
bi_lk: str = pool.alloc(160)
bi_lk: char* = pool.alloc(160)
if bi_lk is not None:
viperlib.snprintf(bi_lk, 160, "%s.__init__", class_name)
if self.find_func_in_module(mod, bi_lk) is not None: bi_ok = 1
@@ -215,7 +215,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
bi_mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
bi_mi: int = 1
while bi_mro is not None and bi_mro[bi_mi] != 0:
bi_anc: str = rawptr(bi_mro[bi_mi])
bi_anc: char* = rawptr(bi_mro[bi_mi])
if bi_anc is not None and string.strcmp(bi_anc, class_name) != 0:
bi_ae: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(bi_anc)
if bi_ae is not None and bi_ae.HasInit != 0:
@@ -267,7 +267,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
for fi in range(int(ctor_entry.FieldCount)):
cand_fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(ctor_entry, fi)
if cand_fe is None: continue
cand_name: str = self.Gen.StructGen.get_field_name_ptr(cand_fe)
cand_name: char* = self.Gen.StructGen.get_field_name_ptr(cand_fe)
if cand_name is not None and string.strcmp(cand_name, "__vtable__") == 0: continue
if target_idx == ai:
fe = cand_fe
@@ -335,7 +335,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# _translate_renum_construct - REnum variant ctor (ClassName.Variant(args...)): alloca,
# zeroinit, store __tag and payload fields, return the pointer.
def _translate_renum_construct(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, variant_name: str, tag_value: int, cargs: list[ast.AST*]*, can: sizet, trans: HT.Translator*, is_volatile: int = 0) -> mlirlite.Value*:
def _translate_renum_construct(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: char*, variant_name: char*, tag_value: int, cargs: list[ast.AST*]*, can: sizet, trans: HT.Translator*, is_volatile: int = 0) -> mlirlite.Value*:
"""Build a REnum variant instance; returns a pointer to the alloca"""
if pool is None or builder is None or class_name is None: return None
@@ -369,7 +369,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
tag_field: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(renum_entry, 0)
tag_field_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
if tag_field is not None and tag_field.Ty is not None: tag_field_ty = tag_field.Ty
tag_name_buf: str = pool.alloc(32)
tag_name_buf: char* = pool.alloc(32)
if tag_name_buf is not None: viperlib.snprintf(tag_name_buf, 32, "%d", tag_value)
tag_const: mlirlite.Value* = mlirlite.ConstInt(pool, tag_field_ty, tag_value, tag_name_buf if tag_name_buf is not None else "0")
tag_ptr: mlirlite.Value* = mlirlite.build_gep_struct(builder, renum_ty, tag_field_ty, tmp, 0)
@@ -408,7 +408,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# stack(含 stack slife)→ 帧内 alloca,随帧消失,不产生 free;
# memgr(x) slife → 归还池(D6 待定),本阶段不接管。
# _new_is_nll_demotable - §13 NLL:可降级为"帧对象"的 `new heap`(判据见 Debugger/ownership-and-new.md §13.2)
def _new_is_nll_demotable(self, trans: HT.Translator*, rhs: ast.AST*, var_name: str) -> int:
def _new_is_nll_demotable(self, trans: HT.Translator*, rhs: ast.AST*, var_name: char*) -> int:
"""1 = 该 `new` 可当帧对象处理(生命期按 FRAME、出口释放复用 slife 通道);0 = 不必/判不定。
只降级"堆且**未显式**指定生命期"的:显式 `hlife` 是用户对自由生命期的要求,不动;
@@ -467,7 +467,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if src == ast.NEW_SRC_STACK and life == ast.NEW_LIFE_HLIFE:
self.Gen.TypeGenErr.fatal_error(new_node, "`new stack hlife` is contradictory: stack storage cannot outlive its frame; use `new stack slife`, or `heap`/`memgr(x)`", "LifetimeError")
return None
class_name: str = self.Gen.ExprGenBase.get_func_name(new_node.func)
class_name: char* = self.Gen.ExprGenBase.get_func_name(new_node.func)
# REnum 变体构造:`new [desc] Qual.Variant(args)`(如 new memgr(pool) LLVMType.Int(32))。
# callee 是 Attribute 且限定名 Qual 为已注册 REnum 类、Variant 命中该类的变体表时,把
# class_name 归一到 **Qual(REnum 类名)**:下方 get_struct_type 命中的正是 REnum 的
@@ -476,7 +476,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# 不归一(Qual 非 REnum / 变体缺失)时保持原样,由下方基本类型路径定夺 / fail-fast。
if class_name is not None and new_node.func is not None and new_node.func.kind() == ast.ASTKind.Attribute:
_rn_at: ast.Attribute* = ast.Attribute*(new_node.func)
_rn_qual: str = None
_rn_qual: char* = None
if _rn_at.value is not None:
if _rn_at.value.kind() == ast.ASTKind.Name:
_rn_qual = ast.Name*(_rn_at.value).id
@@ -558,7 +558,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# heap/memgr → 按 sizeof(T) 分配(复用既有 memgr 通道)
# 有实参的标量(`new stack int(5)`)在这里 fail-fast:本路径刻意只做"取存储",
# 不引入"标量构造"的第二套语义(要初值就 `new … int` 之后赋值)。
def _translate_new_primitive(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, new_node: ast.New*, class_name: str, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_new_primitive(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, new_node: ast.New*, class_name: char*, trans: HT.Translator*) -> mlirlite.Value*:
"""`new` 基本类型(标量):取存储 + 零初始化;返回 T*(失败返回 None,均 fail-fast)"""
prim_ty: mlirlite.LLVMType* = None
if new_node.func is not None:
@@ -578,7 +578,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
elem_ty = elem_a
case _:
pass
nm: str = class_name
nm: char* = class_name
if nm is None: nm = "?"
if is_scalar == 0:
if class_name is None:
@@ -642,7 +642,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if mv is None:
self.Gen.TypeGenErr.fatal_error(mgr_node, "`memgr(x)`: cannot translate the manager expression", "TypeError")
return None
mgr_cls: str = None
mgr_cls: char* = None
mty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(mv)
if mty is not None: mgr_cls = self.Gen.StructGen.get_class_name_by_type(pool, mty)
if mgr_cls is None:
@@ -656,7 +656,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# _translate_struct_ctor_kw - struct constructor with keyword args: Point(x=10, y=20) or
# Point(10, y=20); positional args map by field index, keywords by field name.
def _translate_struct_ctor_kw(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, struct_ty: mlirlite.LLVMType*, class_name: str, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, trans: HT.Translator*, is_volatile: int = 0) -> mlirlite.Value*:
def _translate_struct_ctor_kw(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, struct_ty: mlirlite.LLVMType*, class_name: char*, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, trans: HT.Translator*, is_volatile: int = 0) -> mlirlite.Value*:
"""Struct constructor for keyword arguments; is_volatile=1 marks writes volatile"""
# Create the temporary alloca, ensuring the full cross-module struct definition is
# available in this module.
@@ -691,7 +691,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# Cross-module OOP inference: IsOOP may be unset (Phase1 cache skipped
# _translate_oop_methods). Check for ClassName.__init__ in the module or global defaults table.
if is_oop == 0 and class_name is not None:
kw_init_lookup: str = pool.alloc(128)
kw_init_lookup: char* = pool.alloc(128)
if kw_init_lookup is not None:
viperlib.snprintf(kw_init_lookup, 128, "%s.__init__", class_name)
# option 1: check the current module's function declarations
@@ -818,7 +818,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# Handle keyword args (lookup by field name).
# Get the SHA1 from kw_entry (avoid cross-module same-name find_struct mistakes).
kw_sha1: str = None
kw_sha1: char* = None
if kw_entry is not None: kw_sha1 = kw_entry.ModuleSha1
for ki in range(int(kw_count)):
kw: ast.Keyword* = ast.Keyword*(keywords.get(ki))
@@ -897,15 +897,15 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# Generic-instantiation helper: extract type-argument names from a Subscript slice.
# _get_type_name_from_annotation now lives in HandlesType.py (supports BinOp/Attribute).
def _extract_type_args_from_slice(self, pool: memhub.MemBuddy*, slice_node: ast.AST*, from_imports: str = None) -> list[str]*:
def _extract_type_args_from_slice(self, pool: memhub.MemBuddy*, slice_node: ast.AST*, from_imports: char* = None) -> list[str]*:
"""Extract type-argument names from a Subscript.slice; normalised when from_imports is set"""
if slice_node is None or pool is None: return None
# elem_size=8: str is an i8* pointer (8 bytes); with 4, append writes 8 bytes at a
# elem_size=8: char* is an i8* pointer (8 bytes); with 4, append writes 8 bytes at a
# 4-byte stride and the second element overwrites the first -> get returns a junk pointer.
result: list[str]* = list[str](pool, 8)
k: int = slice_node.kind()
tn_raw: str = None
tn: str = None
tn_raw: char* = None
tn: char* = None
if k == ast.ASTKind.Tuple:
tp: ast.Tuple* = ast.Tuple*(slice_node)
elts: list[ast.AST*]* = tp.elts
@@ -927,10 +927,10 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# _ptr_layer_type_arg - add the pointer layer (`*`) to inferred type args so the ctor side
# matches the annotation-side canonical form `{sha}.Type*`; non-class names pass through.
def _ptr_layer_type_arg(self, pool: memhub.MemBuddy*, cls_nm: str, ptr_level: int, from_imports: str) -> str:
def _ptr_layer_type_arg(self, pool: memhub.MemBuddy*, cls_nm: char*, ptr_level: int, from_imports: char*) -> str:
"""Map an annotation class name to a type-arg text; add `*` per PtrLevel, non-class names unchanged"""
if cls_nm is None: return None
res_pl: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, cls_nm, from_imports)
res_pl: char* = self.Gen.TypeGenResolve._norm_type_arg_name(pool, cls_nm, from_imports)
if res_pl is None: res_pl = cls_nm
if ptr_level <= 0: return res_pl
# non-class names (no SHA1 prefix, e.g. typedef "VOIDPTR" / bare marker "PtrT") keep the pointer layer
@@ -944,7 +944,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
lvl_pl -= 1
return res_pl
def _infer_generic_ctor_type_arg(self, pool: memhub.MemBuddy*, arg_node: ast.AST*, class_name: str, trans: HT.Translator*) -> str:
def _infer_generic_ctor_type_arg(self, pool: memhub.MemBuddy*, arg_node: ast.AST*, class_name: char*, trans: HT.Translator*) -> str:
"""Infer a generic type-arg name from a ctor arg AST (static AST only, no IR)"""
if pool is None or arg_node is None:
self.Gen.TypeGenErr.fatal_error(None, f"Cannot infer the type argument of generic class '{class_name}' from the argument; annotate explicitly, e.g. {class_name}[Type](...)", "TypeError")
@@ -969,12 +969,12 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if cty_c is not None:
# Signedness comes from the callee's source text (LLVM iN/uN are identical),
# reusing spec_sign_of_annot (whitelist + TypedefT chain fallback).
callee_txt_c: str = None
callee_txt_c: char* = None
if fk_c == ast.ASTKind.Name: callee_txt_c = ast.Name*(cl_f.func).id
else: callee_txt_c = ast.Attribute*(cl_f.func).attr
sign_c: int = TSpec.TS_SIGN_UNKNOWN
if callee_txt_c is not None: sign_c = self.Gen.TypeGenCType.spec_sign_of_annot(callee_txt_c, 0)
ta_c: str = self.Gen.ClassGenGeneric.infer_scalar_type_arg_from_llvm_ty(pool, cty_c, sign_c)
ta_c: char* = self.Gen.ClassGenGeneric.infer_scalar_type_arg_from_llvm_ty(pool, cty_c, sign_c)
if ta_c is not None: return ta_c
# A cast call whose callee does not resolve to a scalar type: fail-fast
# (return None -> caller fail-fasts).
@@ -989,7 +989,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if ve_i is not None:
# Route the annotation class name through _ptr_layer_type_arg: after
# _norm_type_arg_name, add `*` when the value type is a pointer (Spec.PtrLevel>0).
cn_nm_i: str = self.Gen.VarGen.var_annot_class_name(ve_i)
cn_nm_i: char* = self.Gen.VarGen.var_annot_class_name(ve_i)
pl_nm_i: int = 0
if ve_i.Spec is not None: pl_nm_i = ve_i.Spec.PtrLevel
return self._ptr_layer_type_arg(pool, cn_nm_i, pl_nm_i, trans._from_imports)
@@ -999,16 +999,16 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if k == ast.ASTKind.Attribute:
# Same: add `*` when the field value type is a pointer (FieldEntry.Spec.PtrLevel>0).
# _resolve_attr_field_entry is read-only, supports one level; deeper chains keep the old behaviour.
cn_at_i: str = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, arg_node)
cn_at_i: char* = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, arg_node)
pl_at_i: int = 0
fe_at_i: GenTypes.FieldEntry* = self.Gen.ExprGenAttr._resolve_attr_field_entry(pool, trans, arg_node)
if fe_at_i is not None and fe_at_i.Spec is not None: pl_at_i = fe_at_i.Spec.PtrLevel
fi_at_i: str = None
fi_at_i: char* = None
if trans is not None: fi_at_i = trans._from_imports
return self._ptr_layer_type_arg(pool, cn_at_i, pl_at_i, fi_at_i)
return None
def _annotation_refers_type_param(self, node: ast.AST*, tp_name: str) -> int:
def _annotation_refers_type_param(self, node: ast.AST*, tp_name: char*) -> int:
"""Whether an annotation refers to type parameter tp_name (1=yes / 0=no)"""
if node is None or tp_name is None: return 0
k: int = node.kind()
@@ -1022,7 +1022,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
return self._annotation_refers_type_param(bop_a.right, tp_name)
return 0
def _get_ctor_init_params(self, class_name: str) -> list[ast.AST*]*:
def _get_ctor_init_params(self, class_name: char*) -> list[ast.AST*]*:
"""Return the generic template __init__ parameter list (including self); None if unavailable"""
if class_name is None: return None
template_cd: ast.ClassDef* = self.Gen.ClassGenGeneric._find_generic_template(class_name)
@@ -1041,7 +1041,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
return fargs.args
return None
def _find_ctor_type_param_param_index(self, class_name: str, tp_name: str) -> int:
def _find_ctor_type_param_param_index(self, class_name: char*, tp_name: char*) -> int:
"""Parameter index in the template __init__ annotated with tp_name (incl. self, 0-based); -1 if not found"""
if class_name is None or tp_name is None: return -1
plist: list[ast.AST*]* = self._get_ctor_init_params(class_name)
@@ -1057,13 +1057,13 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if self._annotation_refers_type_param(ag.annotation, tp_name) != 0: return int(pi)
return -1
def _find_ctor_type_param_arg_index(self, class_name: str, tp_name: str) -> int:
def _find_ctor_type_param_arg_index(self, class_name: char*, tp_name: char*) -> int:
"""Positional-arg index for the param annotated with tp_name in the template __init__; -1 if not found"""
pi_arg: int = self._find_ctor_type_param_param_index(class_name, tp_name)
if pi_arg < 0: return -1
return pi_arg - 1
def _ctor_init_arg_slot_by_name(self, class_name: str, kw_name: str) -> int:
def _ctor_init_arg_slot_by_name(self, class_name: char*, kw_name: char*) -> int:
"""Map a keyword arg name to its non-self param slot; -1 if the template or name is missing"""
if class_name is None or kw_name is None: return -1
plist_sb: list[ast.AST*]* = self._get_ctor_init_params(class_name)
@@ -1079,7 +1079,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if ag_sb.arg is not None and string.strcmp(ag_sb.arg, kw_name) == 0: return int(pi_sb) - 1
return -1
def _find_ctor_type_param_kw_arg_index(self, class_name: str, tp_name: str, keywords: list[ast.AST*]*, kw_count: sizet, pos_count: int) -> int:
def _find_ctor_type_param_kw_arg_index(self, class_name: char*, tp_name: char*, keywords: list[ast.AST*]*, kw_count: sizet, pos_count: int) -> int:
"""Keyword-only construction: keyword-arg index for type param tp_name; -1 if not found"""
if class_name is None or tp_name is None or keywords is None: return -1
pi_kw: int = self._find_ctor_type_param_param_index(class_name, tp_name)
@@ -1100,7 +1100,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if string.strcmp(kw_tp.arg, ag_kw.arg) == 0: return int(ki_kw)
return -1
def _infer_ctor_type_args_from_ctx(self, pool: memhub.MemBuddy*, trans: HT.Translator*, class_name: str, tp_count: int, cl: ast.Call*) -> list[str]*:
def _infer_ctor_type_args_from_ctx(self, pool: memhub.MemBuddy*, trans: HT.Translator*, class_name: char*, tp_count: int, cl: ast.Call*) -> list[str]*:
"""Infer generic ctor type args from the assignment-target or return annotation context; None if N/A"""
if pool is None or trans is None or class_name is None or cl is None: return None
if tp_count <= 0: return None
@@ -1130,7 +1130,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if len(type_args_ctx) != sizet(tp_count): return None
return type_args_ctx
def _translate_generic_ctor_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, spec_name: str, cl: ast.Call*, trans: HT.Translator*, tmpl_name: str = None) -> mlirlite.Value*:
def _translate_generic_ctor_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, spec_name: char*, cl: ast.Call*, trans: HT.Translator*, tmpl_name: char* = None) -> mlirlite.Value*:
"""Translate a generic-class ctor call: __new__ + __before_init__ + __init__"""
if pool is None or builder is None or mod is None or spec_name is None or cl is None: return None
@@ -1249,7 +1249,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if gi_has_init == 0:
# 特化类的 HasInit 可能没被传播:再按非泛型路径同口径查"该方法是否真的存在"
# (模块内声明 或 全局默认参数表),避免有 __init__ 的泛型类被漏掉。
init_lk: str = pool.alloc(256)
init_lk: char* = pool.alloc(256)
if init_lk is not None:
viperlib.snprintf(init_lk, 256, "%s.__init__", spec_name)
if self.find_func_in_module(mod, init_lk) is not None: gi_has_init = 1
@@ -1272,7 +1272,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
for fi_sf in range(int(struct_entry.FieldCount)):
cand_sf: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(struct_entry, fi_sf)
if cand_sf is None: continue
cn_sf: str = self.Gen.StructGen.get_field_name_ptr(cand_sf)
cn_sf: char* = self.Gen.StructGen.get_field_name_ptr(cand_sf)
if cn_sf is not None and string.strcmp(cn_sf, "__vtable__") == 0: continue
if tidx_sf == ai_sf:
fe_sf = cand_sf
+94 -94
View File
@@ -27,14 +27,14 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# _tc_h0_generic_func - generic function call: infer/specialize type args, then emit the call.
# Must run before normal function/constructor dispatch; hit_box marks handled void calls.
def _tc_h0_generic_func(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
def _tc_h0_generic_func(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
if cl is None or trans is None: return None
if self.Gen.ClassGenGeneric.GenericFuncKeys is None: return None
if cl.func is None: return None
# ---- Classify the generic call shape + template name / explicit type args ----
tpl_name: str = None
tpl_name: char* = None
explicit_slice: ast.AST* = None
tpl_mod_sha1: str = None
tpl_mod_sha1: char* = None
is_attr_call: int = 0
fk: int = cl.func.kind()
if fk == ast.ASTKind.Subscript:
@@ -57,20 +57,20 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
tpl_name = at.attr
if tpl_name is None: return None
# ---- Resolve template key "{sha1}.{fname}": module-qualified > local > from_imports ----
tpl_key: str = None
tpl_key: char* = None
if is_attr_call != 0:
# Module-qualified call: only accept the key under that module's SHA1
if tpl_mod_sha1 is not None:
cand_q: str = self._generic_func_key(pool, tpl_mod_sha1, tpl_name)
cand_q: char* = self._generic_func_key(pool, tpl_mod_sha1, tpl_name)
if self.Gen.ClassGenGeneric._find_generic_func_template(cand_q) is not None: tpl_key = cand_q
else:
if trans.ModuleSha1 is not None:
cand_l: str = self._generic_func_key(pool, trans.ModuleSha1, tpl_name)
cand_l: char* = self._generic_func_key(pool, trans.ModuleSha1, tpl_name)
if self.Gen.ClassGenGeneric._find_generic_func_template(cand_l) is not None: tpl_key = cand_l
if tpl_key is None and trans._from_imports is not None:
imp_sha: str = self._lookup_mod_sha1(pool, trans._from_imports, tpl_name, 0)
imp_sha: char* = self._lookup_mod_sha1(pool, trans._from_imports, tpl_name, 0)
if imp_sha is not None:
cand_f: str = self._generic_func_key(pool, imp_sha, tpl_name)
cand_f: char* = self._generic_func_key(pool, imp_sha, tpl_name)
if self.Gen.ClassGenGeneric._find_generic_func_template(cand_f) is not None: tpl_key = cand_f
if tpl_key is None: return None
template_fd: ast.FunctionDef* = self.Gen.ClassGenGeneric._find_generic_func_template(tpl_key)
@@ -94,8 +94,8 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if type_args is None: return None
tpi: sizet
for tpi in range(int(tp_count)):
tp_name: str = tp_names.get(tpi)
ta: str = None
tp_name: char* = tp_names.get(tpi)
ta: char* = None
slot: int = self.Gen.ClassGenGeneric._find_generic_func_param_slot(template_fd, tp_name)
if slot >= 0 and sizet(slot) < can: ta = self.Gen.ClassGenGeneric._infer_generic_func_type_arg(pool, cargs.get(sizet(slot)), tpl_name, trans)
if ta is None and slot >= 0:
@@ -110,18 +110,18 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
type_args.append(ta)
# ---- Specialize ----
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_function(trans, tpl_key, type_args)
spec_name: char* = self.Gen.ClassGenGeneric._specialize_generic_function(trans, tpl_key, type_args)
if spec_name is None:
self.Gen.TypeGenErr.fatal_error(cl, f"Generic function '{tpl_name}': specialization failed", "TypeError")
return None
# ---- Emit call ----
return self._emit_generic_func_call(pool, builder, mod, trans, cl, spec_name, cargs, can, hit_box)
def _generic_func_key(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> str:
def _generic_func_key(self, pool: memhub.MemBuddy*, sha1: char*, name: char*) -> str:
"""Build the generic function template key "{sha1}.{name}" in a new buffer."""
if sha1 is None or name is None: return None
klen: sizet = string.strlen(sha1) + string.strlen(name) + 2
kbuf: str = pool.alloc(klen)
kbuf: char* = pool.alloc(klen)
if kbuf is None: return None
viperlib.snprintf(kbuf, klen, "%s.%s", sha1, name)
return kbuf
@@ -147,7 +147,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if string.strcmp(kw.arg, ag.arg) == 0: return int(ki)
return -1
def _emit_generic_func_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, cl: ast.Call*, spec_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
def _emit_generic_func_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, cl: ast.Call*, spec_name: char*, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Emit the specialized-function call, coercing args to the specialized param types."""
func: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, spec_name)
if func is None:
@@ -188,7 +188,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# reads its first field as a string and emits illegal IR. Use the looked-up symbol name.
return mlirlite.build_call(builder, spec_name, head, int(can), ret_ty, 0)
def _tc_h1_super_ctraits(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
def _tc_h1_super_ctraits(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Compile-time super()/ctraits detection; sets hit_box on interception, else returns None."""
# super().method(args) -> static call to the parent method. Must intercept before attribute
# dispatch, else Call(super) is treated as a plain function call and lookup hits self/vtable.
@@ -220,7 +220,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return mlirlite.const_int32(pool, 0)
return None
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Special __asm__/__llvmir__ calls; marks hit_box for void calls, else returns None."""
# Retired c.* pointer builtins: fail fast with the replacement syntax.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
@@ -292,7 +292,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return self.Gen.AsyncGen.translate_coro_gather(builder, pool, mod, cl, trans)
return None
def _tc_h3_stdarg_cast(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
def _tc_h3_stdarg_cast(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""stdarg sugar and str/int/bool built-in casts; returns None when unmatched."""
# stdarg sugar (va_arg / arg / va_start / va_end): these are declared as pass bodies in
# stdarg.vp and handled here at translation time by emitting LLVM va_arg / intrinsic calls.
@@ -306,10 +306,10 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
elif string.strcmp(func_name, "va_end") == 0:
return self.Gen.CallGenVa._translate_va_end_sugar(pool, builder, mod, cargs, can, funcs_ptr, func_count, trans)
# Builtin casts: str(x)/bytes(x) -> i8*, int(x) -> i32, equivalent to (str)(x)/(int32)(x).
# Builtin casts: char*(x)/bytes(x) -> i8*, int(x) -> i32, equivalent to (str)(x)/(int32)(x).
# In generic contexts T(x) first resolves T to a concrete type, then follows this path.
if func_name is not None and can == 1:
cast_fn_name: str = self.Gen.TypeGenBase.resolve_generic_type_name(func_name)
cast_fn_name: char* = self.Gen.TypeGenBase.resolve_generic_type_name(func_name)
if string.strcmp(cast_fn_name, "str") == 0 or string.strcmp(cast_fn_name, "bytes") == 0:
# str(x) / bytes(x) → i8* (bitcast)
cast_arg: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
@@ -354,7 +354,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
return None
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""Builtin / typedef type casts; returns None when unmatched."""
# Module-qualified typedef cast (posix.mode_t(mode), win32.win32base.LPCWSTR(v), ...): the
# callee is Attribute(<module path>, typedef); non-typedef names still go to the module call.
@@ -362,7 +362,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
mt_at: ast.Attribute* = ast.Attribute*(cl.func)
if mt_at.attr is not None:
# Module path supports both `mod.T` and nested `pkg.mod.T`.
mt_mod_path: str = None
mt_mod_path: char* = None
if mt_at.value is not None and mt_at.value.kind() == ast.ASTKind.Name:
mt_mod_nm: ast.Name* = ast.Name*(mt_at.value)
mt_mod_path = mt_mod_nm.id
@@ -371,23 +371,23 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if mt_mod_path is not None:
mod_td_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_builtin_type(pool, func_name)
if mod_td_ty is None:
mod_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(func_name)
mod_td_str: char* = self.Gen.TypeGenCType.lookup_ctypedef(func_name)
if mod_td_str is None or self.Gen.TypeGenCType.is_ctypedef_found() == 0:
# Library typedef closure may not be injected yet: scan the module source.
mod_td_str = self.Gen.TypeGenResolve._lookup_library_typedef(pool, mt_mod_path, func_name)
if mod_td_str is not None:
mod_td_ast: ast.AST* = self.Gen.TypeGenResolve._parse_type_string(pool, mod_td_str, 0)
if mod_td_ast is not None:
mod_imp_td: str = None
mod_fi_td: str = None
mod_imp_td: char* = None
mod_fi_td: char* = None
if trans is not None:
mod_imp_td = trans._imported_modules
mod_fi_td = trans._from_imports
mod_td_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, mod_td_ast, mod_imp_td, mod_fi_td, trans, 0)
if mod_td_ty is None:
# Final fallback: the shared typedef resolver also scans stdint/win32base.
mod_imp_fb: str = None
mod_fi_fb: str = None
mod_imp_fb: char* = None
mod_fi_fb: char* = None
if trans is not None:
mod_imp_fb = trans._imported_modules
mod_fi_fb = trans._from_imports
@@ -399,7 +399,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Bare typedef casts (BYTEPTR, HANDLE, CHARPTR, ...): all TypedefT names and builtin primitives
# are castable. In generic contexts T(x) first resolves T, then goes through resolve_typedef_or_map.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
bare_fn_name: str = self.Gen.TypeGenBase.resolve_generic_type_name(func_name)
bare_fn_name: char* = self.Gen.TypeGenBase.resolve_generic_type_name(func_name)
bare_ty: mlirlite.LLVMType* = None
# Builtin names resolved from generic context (T->"double"/"float"): float must map to
# f32, not f64.
@@ -408,8 +408,8 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
else:
# Prefer the TypedefT dynamic table (library typedef casts); on miss fall back to
# map_builtin_type.
bare_imp_c: str = None
bare_fi_c: str = None
bare_imp_c: char* = None
bare_fi_c: char* = None
if trans is not None:
bare_imp_c = trans._imported_modules
bare_fi_c = trans._from_imports
@@ -419,8 +419,8 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if bare_ty is None and string.strcmp(bare_fn_name, func_name) != 0:
gp_ast: ast.AST* = self.Gen.TypeGenResolve._parse_type_string(pool, bare_fn_name, 0)
if gp_ast is not None:
imp_gp: str = None
fi_gp: str = None
imp_gp: char* = None
fi_gp: char* = None
if trans is not None:
imp_gp = trans._imported_modules
fi_gp = trans._from_imports
@@ -457,7 +457,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
def _tc_h5_sizeof_len_isinst(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
def _tc_h5_sizeof_len_isinst(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""__sizeof__/len/isinstance built-ins; returns None when unmatched."""
# Detect ClassName.__sizeof__() and return the struct size constant (i64); it is a builtin,
# not a cross-module call. Supports e.g. Argument.__sizeof__() and module-qualified classes.
@@ -465,7 +465,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
sz_at: ast.Attribute* = ast.Attribute*(cl.func)
if sz_at.attr is not None and string.strcmp(sz_at.attr, "__sizeof__") == 0:
if sz_at.value is not None and can == 0:
sz_cls_name: str = None
sz_cls_name: char* = None
sz_vk: int = sz_at.value.kind()
if sz_vk == ast.ASTKind.Name:
sz_cls_nm2: ast.Name* = ast.Name*(sz_at.value)
@@ -480,7 +480,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
sz_inner_nm: ast.Name* = ast.Name*(sz_inner_at.value)
if sz_inner_nm.id is not None:
sz_full_len: sizet = string.strlen(sz_inner_nm.id) + 1 + string.strlen(sz_inner_at.attr) + 1
sz_full_buf: str = pool.alloc(sz_full_len)
sz_full_buf: char* = pool.alloc(sz_full_len)
if sz_full_buf is not None:
sz_full_buf[0] = '\0'
string.strcat(sz_full_buf, sz_inner_nm.id)
@@ -488,10 +488,10 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
string.strcat(sz_full_buf, sz_inner_at.attr)
sz_cls_name = sz_full_buf
elif sz_inner_at.value.kind() == ast.ASTKind.Attribute:
sz_mod_path: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, sz_inner_at.value)
sz_mod_path: char* = self.Gen.ExprGenAttr._attr_module_path_str(pool, sz_inner_at.value)
if sz_mod_path is not None:
sz_full_len2: sizet = string.strlen(sz_mod_path) + 1 + string.strlen(sz_inner_at.attr) + 1
sz_full_buf2: str = pool.alloc(sz_full_len2)
sz_full_buf2: char* = pool.alloc(sz_full_len2)
if sz_full_buf2 is not None:
sz_full_buf2[0] = '\0'
string.strcat(sz_full_buf2, sz_mod_path)
@@ -500,10 +500,10 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
sz_cls_name = sz_full_buf2
# Resolve the struct type: type-name hit -> generic specialization (Stack[T]) -> receiver type (self/obj)
sz_resolved_ty: mlirlite.LLVMType* = None
sz_resolved_name: str = sz_cls_name
sz_resolved_name: char* = sz_cls_name
if sz_cls_name is not None:
# Replace generic type parameter T: T.__sizeof__() -> int.__sizeof__()
sz_orig: str = sz_cls_name
sz_orig: char* = sz_cls_name
sz_cls_name = self.Gen.TypeGenBase.resolve_generic_type_name(sz_cls_name)
# Fallback when the TypeGenBase context is empty (template tp_names expired
# across phases): replace T using trans's generic mapping.
@@ -511,9 +511,9 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
gp_n: sizet = len(trans.GenericTypeParamNames)
gp_i: sizet
for gp_i in range(int(gp_n)):
gpn: str = trans.GenericTypeParamNames.get(gp_i)
gpn: char* = trans.GenericTypeParamNames.get(gp_i)
if gpn is not None and string.strcmp(sz_orig, gpn) == 0:
gta: str = trans.GenericTypeArgs.get(gp_i)
gta: char* = trans.GenericTypeArgs.get(gp_i)
if gta is not None: sz_cls_name = gta
break
sz_resolved_name = sz_cls_name
@@ -528,10 +528,10 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if _sz_len_s > 0 and sz_cls_name[_sz_len_s - 1] == '*': return mlirlite.const_int64(pool, 8)
# Save the full name (with module prefix) for exact module matching: stripping it
# lets get_struct_type globally match a same-named class from another module.
sz_full_name: str = sz_cls_name
sz_full_name: char* = sz_cls_name
# Strip the module prefix: the Viper parser yields Name(id="int") rather than
# Attribute, so manually take the last class-name component.
dot_pos: str = string.strrchr(sz_cls_name, '.')
dot_pos: char* = string.strrchr(sz_cls_name, '.')
if dot_pos is not None: sz_cls_name = dot_pos + 1
# [Phase 3] Unified size convention: resolve library typedefs first, only then
# use the hardcoded table. Pass trans's imports so from-import alias chains resolve.
@@ -567,16 +567,16 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Module-qualified classes match the defining module exactly, preventing a
# same-named class from another module from being matched globally.
if sz_full_name is not None:
szf_dot: str = string.strrchr(sz_full_name, '.')
szf_dot: char* = string.strrchr(sz_full_name, '.')
if szf_dot is not None:
szf_mod_len: sizet = sizet(uint64(szf_dot) - uint64(sz_full_name))
szf_mod: str = pool.alloc(szf_mod_len + 1)
szf_mod: char* = pool.alloc(szf_mod_len + 1)
if szf_mod is not None:
string.strncpy(szf_mod, sz_full_name, szf_mod_len)
szf_mod[szf_mod_len] = '\0'
# Module-qualified builtin names are already handled by the builtin/size table.
if szf_mod is not None:
szf_sha1: str = self.Gen.StructGen._resolve_module_sha1(szf_mod, trans._from_imports if trans is not None else None)
szf_sha1: char* = self.Gen.StructGen._resolve_module_sha1(szf_mod, trans._from_imports if trans is not None else None)
if szf_sha1 is not None:
szf_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(sz_cls_name, szf_sha1)
if szf_entry is not None and szf_entry.Ty is not None: sz_resolved_ty = szf_entry.Ty
@@ -595,9 +595,9 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
gai_sz: sizet = len(gen_args_sz)
gai2_sz: sizet = 0
for gai2_sz in range(int(gai_sz)):
gres_sz: str = self.Gen.TypeGenBase.resolve_generic_type_name(gen_args_sz.get(gai2_sz))
gres_sz: char* = self.Gen.TypeGenBase.resolve_generic_type_name(gen_args_sz.get(gai2_sz))
gargs_resolved.append(gres_sz)
spec_sz: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_nm_sz.id, gargs_resolved)
spec_sz: char* = self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_nm_sz.id, gargs_resolved)
if spec_sz is not None:
sz_resolved_name = spec_sz
sz_resolved_ty = self.Gen.StructGen.get_struct_type(spec_sz)
@@ -686,7 +686,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# strlen on the first field). list containers inline-read __count__; plain classes call {Class}.__len__.
len_struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(len_val)
if len_struct_ty is not None:
len_cls_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, len_struct_ty)
len_cls_nm: char* = self.Gen.StructGen.get_class_name_by_type(pool, len_struct_ty)
if len_cls_nm is None: self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type inference failed (unknown struct class name)", "TypeError")
# list builtin __len__: read __count__ (GEP offset 1, i64). Narrow len_arg_node
# to ast.Call* explicitly, since passing a base pointer to a derived param is banned.
@@ -696,13 +696,13 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
idx_one_lb: mlirlite.Value* = mlirlite.const_int64(pool, 1)
cnt_ptr_lb: mlirlite.Value* = mlirlite.build_gep(builder, i64_ty_lb, len_val, idx_one_lb)
if cnt_ptr_lb is not None: return mlirlite.build_load(builder, i64_ty_lb, cnt_ptr_lb)
len_lookup: str = pool.alloc(128)
len_lookup: char* = pool.alloc(128)
if len_lookup is not None:
viperlib.snprintf(len_lookup, 128, "%s.__len__", len_cls_nm)
len_fn2: mlirlite.Function* = self.find_func_in_module(mod, len_lookup)
if len_fn2 is not None:
len_ret_ty2: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(len_fn2)
len_fn_name2: str = mlirlite.function_get_name(len_fn2)
len_fn_name2: char* = mlirlite.function_get_name(len_fn2)
if len_ret_ty2 is not None and len_fn_name2 is not None:
# Align the self parameter type (bitcast to the method's first param)
len_self_arg2: mlirlite.Value* = len_val
@@ -714,12 +714,12 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return mlirlite.build_call(builder, len_fn_name2, len_self_arg2, 1, len_ret_ty2, 0)
# len_fn2 miss (cross-module method): build the exact name {sha1}.{cls}.__len__
# from the struct's module sha1, resolve the return type from text.ll/vpi, then call.
len_cls_sha1_x: str = self.Gen.StructGen.get_struct_sha1(len_cls_nm)
len_cls_sha1_x: char* = self.Gen.StructGen.get_struct_sha1(len_cls_nm)
if len_cls_sha1_x is not None:
# Buffer must fit "{sha1}.{cls}.__len__": 2 dots + "__len__"(7) + NUL = 10;
# +8 truncates the last two chars and breaks the link.
len_ml_x: sizet = string.strlen(len_cls_sha1_x) + string.strlen(len_cls_nm) + 10
len_mangled_x: str = pool.alloc(len_ml_x)
len_mangled_x: char* = pool.alloc(len_ml_x)
if len_mangled_x is not None:
viperlib.snprintf(len_mangled_x, len_ml_x, "%s.%s.__len__", len_cls_sha1_x, len_cls_nm)
len_ret_x: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, len_cls_sha1_x, len_cls_nm, "__len__")
@@ -734,7 +734,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# fail-fast: the class has no __len__ (compile-time error, matching TPC
# HandlesExprBuiltin._FailFastLenUnsupported wording).
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.ArrayT[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
# Non-struct: str (Ptr(Int8)) uses strlen; other types fail fast
# Non-struct: char* (Ptr(Int8)) uses strlen; other types fail fast
len_arg_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(len_val)
len_is_str: int = 0
if len_arg_ty is not None and self.Gen.ExprGenBase.is_ptr_type(len_arg_ty) != 0:
@@ -778,7 +778,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
def _tc_h6_method_dispatch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
def _tc_h6_method_dispatch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Static class/method/receiver/self.field/list built-ins; sets hit_box, else returns None."""
# REnum.kind() compiles to a __tag read (offset 0, type from the field declaration): bitcast obj
# to __tag* + load. Four criteria detect REnum, incl. pointee __tag and X.field annotations.
@@ -789,7 +789,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if rk_obj is not None and rk_obj.Ty is not None:
rk_ty: mlirlite.LLVMType* = rk_obj.Ty
rk_load: int = 0
rk_cls: str = None
rk_cls: char* = None
rk_pointee: mlirlite.LLVMType* = None
# rk_is_rn: whether already determined to be a REnum, decoupled from rk_cls.
rk_is_rn: int = 0
@@ -820,17 +820,17 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
rk_nm: ast.Name* = ast.Name*(rk_at.value)
if rk_nm.id is not None:
rk_ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, rk_nm.id)
rk_annot_raw: str = self.Gen.VarGen.var_annot_class_name(rk_ve)
rk_annot_raw: char* = self.Gen.VarGen.var_annot_class_name(rk_ve)
if rk_annot_raw is not None:
# Truncate the union/generic suffix (" | rawptr" / "[...]")
rk_ae: sizet = 0
while rk_annot_raw[rk_ae] != '\0' and rk_annot_raw[rk_ae] != ' ' and rk_annot_raw[rk_ae] != '|' and rk_annot_raw[rk_ae] != '[': rk_ae += 1
rk_ab: str = pool.alloc(rk_ae + 1)
rk_ab: char* = pool.alloc(rk_ae + 1)
if rk_ab is not None:
string.strncpy(rk_ab, rk_annot_raw, rk_ae)
rk_ab[rk_ae] = '\0'
# Strip the module prefix ("mlirlite.LLVMType" -> "LLVMType")
rk_ad: str = string.strrchr(rk_ab, '.')
rk_ad: char* = string.strrchr(rk_ab, '.')
if rk_ad is not None and rk_ad[1] != '\0': rk_cls = rk_ad + 1
else: rk_cls = rk_ab
# Same criteria as above (table or struct with __tag), not just the variant table
@@ -850,17 +850,17 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
rk_bnm: ast.Name* = ast.Name*(rk_bat.value)
if rk_bnm.id is not None:
rk_bve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, rk_bnm.id)
rk_bannot: str = self.Gen.VarGen.var_annot_class_name(rk_bve)
rk_bannot: char* = self.Gen.VarGen.var_annot_class_name(rk_bve)
if rk_bannot is not None:
rk_blen: sizet = 0
while rk_bannot[rk_blen] != '\0' and rk_bannot[rk_blen] != ' ' and rk_bannot[rk_blen] != '|' and rk_bannot[rk_blen] != '[': rk_blen += 1
rk_bbuf: str = pool.alloc(rk_blen + 1)
rk_bbuf: char* = pool.alloc(rk_blen + 1)
if rk_bbuf is not None:
string.strncpy(rk_bbuf, rk_bannot, rk_blen)
rk_bbuf[rk_blen] = '\0'
# Strip the module prefix ("mlirlite.Value" -> "Value")
rk_bdot: str = string.strrchr(rk_bbuf, '.')
rk_bcls: str = rk_bbuf
rk_bdot: char* = string.strrchr(rk_bbuf, '.')
rk_bcls: char* = rk_bbuf
if rk_bdot is not None and rk_bdot[1] != '\0': rk_bcls = rk_bdot + 1
rk_bst: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(rk_bcls)
if rk_bst is not None:
@@ -871,7 +871,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
rk_fpt = rk_fpt.Pointee
if rk_fpt is not None and self.Gen.ExprGenBase.is_ptr_type(rk_fpt) != 0: rk_fpt = rk_fpt.Pointee
if rk_fpt is not None:
rk_fcls: str = self.Gen.StructGen.get_class_name_by_type(pool, rk_fpt)
rk_fcls: char* = self.Gen.StructGen.get_class_name_by_type(pool, rk_fpt)
if rk_fcls is not None:
if self.Gen.ClassGenEnum._is_renum_class_name(rk_fcls) != 0 or self.Gen.ClassGenEnum._is_renum_by_entry(rk_fcls) != 0:
rk_is_rn = 1
@@ -954,7 +954,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# The struct value is inline in the slot: the slot address is the object address (self)
rp_val = self.Gen.VarGen.repl_try_addr(trans, rp_nm.id, cl)
else: rp_val = self.Gen.VarGen.repl_try_read(trans, rp_nm.id, cl)
rp_cls: str = None
rp_cls: char* = None
if rp_val is not None and rp_val.Ty is not None:
rp_sty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(rp_val)
if rp_sty is not None: rp_cls = self.Gen.StructGen.get_class_name_by_type(pool, rp_sty)
@@ -983,7 +983,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
recv_ty_c = recv_ty_c.Pointee
# Heap objects (@t.Object) are double pointers: deref once more to get the struct
if self.Gen.ExprGenBase.is_ptr_type(recv_ty_c) != 0: recv_ty_c = recv_ty_c.Pointee
recv_cls_c: str = self.Gen.StructGen.get_class_name_by_type(pool, recv_ty_c)
recv_cls_c: char* = self.Gen.StructGen.get_class_name_by_type(pool, recv_ty_c)
if recv_cls_c is not None:
if self.Gen.CallGenProp._is_list_builtin_receiver(pool, recv_val_c, recv_cls_c, method_at.attr, cl) == 0:
obj_ptr_c: mlirlite.Value* = recv_val_c
@@ -1014,7 +1014,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if nl_slot is not None: nl_deref = nl_slot
nl_struct: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(nl_deref)
if nl_struct is not None:
nl_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, nl_struct)
nl_cls: char* = self.Gen.StructGen.get_class_name_by_type(pool, nl_struct)
if nl_cls is not None:
if self.Gen.CallGenProp._is_list_builtin_receiver(pool, nl_deref, nl_cls, func_name, cl) == 0:
if hit_box is not None: hit_box[0] = 1
@@ -1028,15 +1028,15 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Identify list generic container annotations early (shared by the class branches below): rawptr[list[rawptr[AST]]] is Ptr(Ptr(Struct('list[...]'))), and the list specialty struct is not registered as a normal class, so unwrap one rawptr and flag is_list_annot_builtin for the inlined list branch.
var_entry: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, method_obj_nm.id)
is_list_annot_builtin: int = 0
la_buf: str = self.Gen.VarGen.var_annot_class_name(var_entry)
la_buf: char* = self.Gen.VarGen.var_annot_class_name(var_entry)
if la_buf is not None:
la_tail: str = la_buf
la_tail: char* = la_buf
if string.strncmp(la_buf, "rawptr[", 7) == 0: la_tail = la_buf + 7
if string.strncmp(la_tail, "list", 4) == 0:
if self.Gen.CallGenProp._is_list_builtin_receiver(pool, obj_var, la_tail, func_name, cl) != 0: is_list_annot_builtin = 1
match obj_var.Ty:
case mlirlite.LLVMType.Ptr(obj_struct_ty):
class_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, obj_struct_ty)
class_nm: char* = self.Gen.StructGen.get_class_name_by_type(pool, obj_struct_ty)
if class_nm is not None:
# list builtin container methods (append/get/set/pop/clear/__iter__/...) are
# inlined by the list builtin branch below; skip _translate_method_call for them.
@@ -1047,7 +1047,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Ptr(Struct) before lookup.
match obj_struct_ty:
case mlirlite.LLVMType.Ptr(inner_struct_ty):
inner_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, inner_struct_ty)
inner_nm: char* = self.Gen.StructGen.get_class_name_by_type(pool, inner_struct_ty)
if inner_nm is not None:
# Skip list builtin methods here too (same as the Ptr(Struct) branch);
# inner_nm may also be misread as the inner element class.
@@ -1063,12 +1063,12 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if fd_name_res is not None: return fd_name_res
# AnnotClassName fallback: for unions simplified to Ptr(i8), look up the struct by the
# original annotation's class name. Skip when is_list_annot_builtin is set.
annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry)
annot_val: char* = self.Gen.VarGen.var_annot_class_name(var_entry)
if annot_val is not None and is_list_annot_builtin == 0:
# Parse the module-qualified name "memhub.MemBuddy" -> mod_nm="memhub", cls_nm="MemBuddy"
dot_ptr: str = string.strrchr(annot_val, '.')
cls_nm2: str = annot_val
mod_nm2: str = None
dot_ptr: char* = string.strrchr(annot_val, '.')
cls_nm2: char* = annot_val
mod_nm2: char* = None
if dot_ptr is not None:
cls_nm2 = dot_ptr + 1
total_len_av: sizet = string.strlen(annot_val)
@@ -1099,12 +1099,12 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if fd_res is not None: return fd_res
# Struct unregistered (cross-module dependency not translated): look up
# the module SHA1 to build the method call name.
sha1_fc: str = None
sha1_fc: char* = None
if mod_nm2 is not None: sha1_fc = self._lookup_module_sha1(mod_nm2)
if sha1_fc is not None:
# Build the "{sha1}.{cls_nm2}.{func_name}" call name
mlen3: sizet = string.strlen(sha1_fc) + string.strlen(cls_nm2) + string.strlen(func_name) + 3
mangled3: str = pool.alloc(mlen3)
mangled3: char* = pool.alloc(mlen3)
if mangled3 is not None:
viperlib.snprintf(mangled3, mlen3, "%s.%s.%s", sha1_fc, cls_nm2, func_name)
# load obj_ptr (self)
@@ -1161,7 +1161,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
expr_root: ast.AST* = expr_method_at.value
while expr_root is not None and expr_root.kind() == ast.ASTKind.Attribute: expr_root = ast.Attribute*(expr_root).value
if expr_root is not None and expr_root.kind() == ast.ASTKind.Name:
expr_root_nm: str = ast.Name*(expr_root).id
expr_root_nm: char* = ast.Name*(expr_root).id
if expr_root_nm is not None:
if trans._imported_modules is not None:
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, expr_root_nm) != 0: is_mod_path_expr = 1
@@ -1169,7 +1169,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Translate the receiver expression (the result should be Ptr(Struct))
expr_obj: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, expr_method_at.value, funcs_ptr, func_count, trans)
if expr_obj is not None and expr_obj.Ty is not None:
expr_cls_nm: str = None
expr_cls_nm: char* = None
# Recursively unwrap Ptr to handle the Ptr(Ptr(Struct)) of X* unions (e.g. Vec3**);
# otherwise the method call is treated as a bare @dot and links undefined.
expr_struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(expr_obj)
@@ -1212,7 +1212,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Translate self.__pool__ to get the field value (an already-loaded object pointer)
field_obj: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, attr_method_at.value, funcs_ptr, func_count, trans)
if field_obj is not None and field_obj.Ty is not None:
field_cls_nm: str = None
field_cls_nm: char* = None
# Path 1: get the class name from the field value's LLVM type
match field_obj.Ty:
case mlirlite.LLVMType.Ptr(field_struct_ty):
@@ -1228,9 +1228,9 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
case mlirlite.LLVMType.Ptr(field_owner_struct_ty):
field_info: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(field_owner_struct_ty, inner_attr.attr)
if field_info is not None:
field_annot: str = self.Gen.StructGen.get_field_annot_class_name(field_info)
field_annot: char* = self.Gen.StructGen.get_field_annot_class_name(field_info)
if field_annot is not None:
dot_p: str = string.strrchr(field_annot, 46)
dot_p: char* = string.strrchr(field_annot, 46)
if dot_p is not None: field_cls_nm = dot_p + 1
else: field_cls_nm = field_annot
if field_cls_nm is not None:
@@ -1253,7 +1253,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
lm_at: ast.Attribute* = ast.Attribute*(cl.func)
if lm_at is not None and lm_at.attr is not None and lm_at.value is not None:
lm_name: str = lm_at.attr
lm_name: char* = lm_at.attr
# Check whether this is a list method name
lm_is_list: int = 0
if string.strcmp(lm_name, "get") == 0: lm_is_list = 1
@@ -1278,7 +1278,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Confirm the object really is a list (the branch matches by method name only, and
# get/set are not list-specific); skip otherwise to avoid wrong GEP offsets.
lm_conf_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lm_obj)
lm_conf_nm: str = None
lm_conf_nm: char* = None
if lm_conf_ty is not None: lm_conf_nm = mlirlite.get_struct_name(lm_conf_ty)
# [S4] Compare the container base name exactly: the LLVM name carries a module sha1
# prefix; SpecTypeNameBaseIs strips/truncates before comparing, avoiding substring false hits.
@@ -1292,7 +1292,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Element type T is resolved from the list specialization name (list[int]->i32,
# list[str]->i8*); using a fixed i8* would corrupt adjacent elements in set/pop.
lm_esty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lm_obj)
lm_ename: str = None
lm_ename: char* = None
if lm_esty is not None: lm_ename = mlirlite.get_struct_name(lm_esty)
lm_elem_ty: mlirlite.LLVMType* = None
if lm_ename is not None: lm_elem_ty = self.Gen.ExprGenName._list_elem_type_from_name(pool, lm_ename)
@@ -1382,7 +1382,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# memory reallocation handled in the _list implementation.
elif string.strcmp(lm_name, "append") == 0:
if can >= 1:
ap_sha1: str = self._lookup_module_sha1("_list")
ap_sha1: char* = self._lookup_module_sha1("_list")
if ap_sha1 is None:
# Dependency module not compiled (_list not in the chain): fail fast, never
# silently skip append (elements would never be added).
@@ -1390,22 +1390,22 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if ap_sha1 is not None:
# The append symbol is {sha1}.list[AST*].append (specialized name), not
# {sha1}.list.append; derive the specialized name from lm_obj's type.
ap_spec_nm: str = None
ap_spec_nm: char* = None
ap_struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lm_obj)
if ap_struct_ty is not None: ap_spec_nm = self.Gen.StructGen.get_class_name_by_type(pool, ap_struct_ty)
if ap_spec_nm is None: ap_spec_nm = "list"
# Trigger generic specialization translation (idempotent): the append define is
# emitted when the specialty class is translated, otherwise the symbol is missing.
if trans is not None and ap_spec_nm is not None:
lbkt_ap: str = string.strchr(ap_spec_nm, '[')
lbkt_ap: char* = string.strchr(ap_spec_nm, '[')
if lbkt_ap is not None:
rbkt_ap: str = string.strrchr(ap_spec_nm, ']')
rbkt_ap: char* = string.strrchr(ap_spec_nm, ']')
if rbkt_ap is not None and rbkt_ap > lbkt_ap:
base_len_ap: sizet = sizet(uint64(lbkt_ap) - uint64(ap_spec_nm))
arg_len_ap: sizet = sizet(uint64(rbkt_ap) - uint64(lbkt_ap) - 1)
if base_len_ap > 0 and arg_len_ap > 0:
base_buf_ap: str = pool.alloc(base_len_ap + 1)
arg_buf_ap: str = pool.alloc(arg_len_ap + 1)
base_buf_ap: char* = pool.alloc(base_len_ap + 1)
arg_buf_ap: char* = pool.alloc(arg_len_ap + 1)
if base_buf_ap is not None and arg_buf_ap is not None:
string.strncpy(base_buf_ap, ap_spec_nm, base_len_ap)
base_buf_ap[base_len_ap] = '\0'
@@ -1414,14 +1414,14 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
args_ap: list[str]* = list[str](pool, 8)
if args_ap is not None:
# Comma-separated multiple type args (list[X] / dict[K, V])
cur_ap: str = arg_buf_ap
cur_ap: char* = arg_buf_ap
while cur_ap is not None:
comma_ap: str = string.strchr(cur_ap, ',')
comma_ap: char* = string.strchr(cur_ap, ',')
if comma_ap is None:
args_ap.append(cur_ap)
break
seg_len_ap: sizet = sizet(uint64(comma_ap) - uint64(cur_ap))
seg_buf_ap: str = pool.alloc(seg_len_ap + 1)
seg_buf_ap: char* = pool.alloc(seg_len_ap + 1)
if seg_buf_ap is not None:
string.strncpy(seg_buf_ap, cur_ap, seg_len_ap)
seg_buf_ap[seg_len_ap] = '\0'
@@ -1431,7 +1431,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# Call name "{sha1}.{spec}.append": length is spec + 25 (16+1+7+1), so the old +8
# truncated the trailing 'd' and produced an undefined symbol.
ap_mlen: sizet = string.strlen(ap_sha1) + string.strlen(ap_spec_nm) + 26
ap_mangled: str = pool.alloc(ap_mlen)
ap_mangled: char* = pool.alloc(ap_mlen)
if ap_mangled is not None:
viperlib.snprintf(ap_mangled, ap_mlen, "%s.%s.append", ap_sha1, ap_spec_nm)
ap_item_node: ast.AST* = cargs.get(0)
+17 -17
View File
@@ -14,7 +14,7 @@ from . import CallGenBase
# CallGenFd - split from CallGenMixin (stage B): FakeDuck / requires field injection / provider stack
novirtual class CallGenFd(CallGenBase.CallGenBase):
"""FakeDuck / requires field injection / provider stack"""
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, method_name: str, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_fakeduck_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: char*, method_name: char*, cl: ast.Call*, trans: HT.Translator*) -> mlirlite.Value*:
"""FakeDuck: resolve a str variable's method call to extension class _Type/_str, return the call result or None"""
if pool is None or builder is None or cl is None or trans is None: return None
if method_name is None: return None
@@ -27,11 +27,11 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
if string.strcmp(fd_at.attr, method_name) != 0: return None
# Candidate extension classes: _Type first (a.type_id()), then _str (a.upper() etc.)
fd_func: mlirlite.Function* = None
fd_cls: str = None
fd_key: str = pool.alloc(160)
fd_cls: char* = None
fd_key: char* = pool.alloc(160)
if fd_key is not None:
for fd_ci in range(2):
fd_cn: str = "_Type"
fd_cn: char* = "_Type"
if fd_ci == 1: fd_cn = "_str"
viperlib.snprintf(fd_key, 160, "%s.%s", fd_cn, method_name)
fd_func = self.find_func_in_module(mod, fd_key)
@@ -53,7 +53,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
fd_onm: ast.Name* = ast.Name*(fd_at.value)
if fd_onm.id is not None:
# Pool-persistent buffer (an f-string stack buffer would expire after return)
fd_mn: str = pool.alloc(256)
fd_mn: char* = pool.alloc(256)
if fd_mn is not None: viperlib.snprintf(fd_mn, 256, "%s__meta__", fd_onm.id)
fd_meta: mlirlite.Value* = self.Gen.VarGen.lookup_current(trans.SymTab, fd_mn)
if fd_meta is not None and fd_has_fields != 0: fd_self = fd_meta
@@ -99,7 +99,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
if fd_arg.kind() == ast.ASTKind.Constant:
fd_ac: ast.Constant* = ast.Constant*(fd_arg)
if fd_ac.const_kind == ast.CONST_CHAR:
ch_buf: str = pool.alloc(2)
ch_buf: char* = pool.alloc(2)
if ch_buf is not None:
ch_buf[0] = char(fd_ac.int_val)
ch_buf[1] = '\0'
@@ -111,11 +111,11 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
# Return type and call name (from the function definition hit by find_func_in_module)
fd_ret_ty: mlirlite.LLVMType* = mlirlite.function_get_ret_ty(fd_func)
if fd_ret_ty is None: fd_ret_ty = mlirlite.Int32(pool)
fd_fname: str = mlirlite.function_get_name(fd_func)
fd_fname: char* = mlirlite.function_get_name(fd_func)
if fd_fname is None: return None
# Refine return type: cross-module declare may simplify a list[T] specialization return type to Ptr(i8), degrading the caller's alloca type -> parts[0] takes the char-index path instead of list inline __getitem__ (printf %s crash). Authority is the defining module's vpi (fallback text.ll); extract the module sha1 only when the name is "{16hex}.{func}" (via GenApi.sha1_prefix_len)
if GenApi.sha1_prefix_len(fd_fname) != 0:
fd_sha1: str = pool.alloc(17)
fd_sha1: char* = pool.alloc(17)
if fd_sha1 is not None:
string.strncpy(fd_sha1, fd_fname, 16)
fd_sha1[16] = '\0'
@@ -131,7 +131,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
# For each class __requires__ field (registered by fd1), call _find_provider(field) at runtime; if non-NULL bitcast and store it (conditional inject); skip when _find_provider is unregistered.
# ============================================================
def _inject_requires_fields(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, meta_ptr: mlirlite.Value*, class_name: str) -> int:
def _inject_requires_fields(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, meta_ptr: mlirlite.Value*, class_name: char*) -> int:
"""Inject requires fields from the with context, return the injected field count"""
if trans is None or pool is None or builder is None or mod is None: return 0
if meta_ptr is None or class_name is None: return 0
@@ -139,7 +139,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
# (matches TPC L4048-4050); otherwise non-with projects would emit undefined {sha1}._find_provider calls.
fd_fn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_find_provider")
if fd_fn_probe is None: return 0
fd_fname: str = mlirlite.function_get_name(fd_fn_probe)
fd_fname: char* = mlirlite.function_get_name(fd_fn_probe)
if fd_fname is None: return 0
cnt: int = self.Gen.ClassGenBase.class_meta_count(GenTypes.CLASS_META_REQUIRES, class_name)
if cnt <= 0: return 0
@@ -153,7 +153,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
if cur_func is None: return 0
injected: int = 0
for ji in range(int(cnt)):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
fname: char* = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
if fname is None: continue
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
if fe is None or fe.Ty is None: continue
@@ -173,7 +173,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
# conflict -> llc "Terminator found in the middle of a basic block").
vtc2: int = trans._label_counter
trans._label_counter = vtc2 + 1
fd_bb_buf2: str = pool.alloc(64)
fd_bb_buf2: char* = pool.alloc(64)
if fd_bb_buf2 is None: continue
viperlib.snprintf(fd_bb_buf2, 64, "fd.skip.%d", vtc2)
fd_skip_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, cur_func, fd_bb_buf2)
@@ -196,7 +196,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
# Matches TPC HandlesWith.py L138-174: push _push_provider(field, ctx_i8) on entry and equal _pop_provider() on exit.
# ============================================================
def _emit_provider_push(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str, ctx_val: mlirlite.Value*) -> int:
def _emit_provider_push(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: char*, ctx_val: mlirlite.Value*) -> int:
"""Emit a _push_provider(field, ctx_i8) call per provided field, return the field count"""
if trans is None or pool is None or builder is None or mod is None: return 0
if cls_name is None or ctx_val is None: return 0
@@ -206,7 +206,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
# skip when this module does not import _withcontent, to avoid undefined symbol).
fd_pfn_probe: mlirlite.Function* = self.find_func_in_module(mod, "_push_provider")
if fd_pfn_probe is None: return 0
fd_pfn: str = mlirlite.function_get_name(fd_pfn_probe)
fd_pfn: char* = mlirlite.function_get_name(fd_pfn_probe)
if fd_pfn is None: return 0
i8_ty_p: mlirlite.LLVMType* = mlirlite.Int8(pool)
if i8_ty_p is None: return 0
@@ -218,7 +218,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
fd_ctx_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
if fd_ctx_i8 is None: return 0
for pi in range(int(fd_pc)):
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
fname: char* = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
if fname is None: continue
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
if fd_fstr is None: continue
@@ -227,7 +227,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
self._ensure_method_declare(pool, mod, fd_pfn, ret_i32_p, fd_fstr, 2)
mlirlite.build_call(builder, fd_pfn, fd_fstr, 2, ret_i32_p, 0)
return fd_pc
def _emit_provider_pop(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: str) -> int:
def _emit_provider_pop(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cls_name: char*) -> int:
"""Emit a _pop_provider() call per provided field, return the field count"""
if trans is None or pool is None or builder is None or mod is None: return 0
if cls_name is None: return 0
@@ -237,7 +237,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
# skip when this module does not import _withcontent, to avoid undefined symbol).
fd_pon_probe: mlirlite.Function* = self.find_func_in_module(mod, "_pop_provider")
if fd_pon_probe is None: return 0
fd_pon: str = mlirlite.function_get_name(fd_pon_probe)
fd_pon: char* = mlirlite.function_get_name(fd_pon_probe)
if fd_pon is None: return 0
ret_i32_q: mlirlite.LLVMType* = mlirlite.Int32(pool)
if ret_i32_q is None: return 0
+22 -22
View File
@@ -17,7 +17,7 @@ from . import CallGenBase
novirtual class CallGenLib(CallGenBase.CallGenBase):
"""C-lib declarations / return-type inference / kwargs call helpers (extends Base)"""
def _ensure_c_lib_declare(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str) -> mlirlite.Function*:
def _ensure_c_lib_declare(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: char*) -> mlirlite.Function*:
"""Create a declare for a known C-lib function"""
if pool is None or mod is None or name is None: return None
@@ -94,7 +94,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
mlirlite.add_param(pool, fn, i64_ty, "n")
return fn
def _promote_clib_call_args(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: str, head: mlirlite.Value*, count: int) -> mlirlite.Value*:
def _promote_clib_call_args(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, call_name: char*, head: mlirlite.Value*, count: int) -> mlirlite.Value*:
"""Promote bare-name C-lib call arguments to their standard signature widths"""
if pool is None or builder is None or call_name is None or head is None: return head
# bare names only (no '.' -> no sha1 prefix); prefixed ones use vpi definition-side parsing
@@ -148,16 +148,16 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
# _coerce_args_to_def_params: coerce cross-module call args to vpi definition-side param
# types so call/declare/define widths match; bare C-lib names use the C-lib path instead.
def _coerce_args_to_def_params(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, call_name: str, head: mlirlite.Value*, count: int, cargs: list[ast.AST*]* = None, self_off: int = 0, trans: HT.Translator* = None) -> mlirlite.Value*:
def _coerce_args_to_def_params(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, call_name: char*, head: mlirlite.Value*, count: int, cargs: list[ast.AST*]* = None, self_off: int = 0, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Coerce cross-module call args to vpi definition-side param types; returns the rebuilt chain"""
if pool is None or builder is None or call_name is None or head is None: return head
if self._is_sha1_mangled_call(call_name) == 0: return head
# split {sha1}.{rest}: if rest has '.', it is {cls}.{method}, else {func}
rest: str = call_name + 17
cls_nm: str = None
meth_nm: str = rest
rest: char* = call_name + 17
cls_nm: char* = None
meth_nm: char* = rest
# take the last '.': the method has none, and the first '.' would truncate qualified type args
dot_r: str = string.strrchr(rest, 46)
dot_r: char* = string.strrchr(rest, 46)
if dot_r is not None:
cls_len: sizet = uint64(dot_r) - uint64(rest)
if cls_len <= 0: return head
@@ -178,7 +178,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
sha1_buf[16] = '\0'
# ensure-load + direct cache lookup (not _vpi_method_param_annot, which misses module funcs)
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir)
prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
prm_annots: char* = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
# generic-specialized class: annotations live on the template class, so fall back to template lookup
tpl_src: int = 0
if prm_annots is None:
@@ -206,7 +206,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
# Value-reference params (&T / &temp T): auto-address the argument (cross-module callee).
# The mode comes from the definition-side vpi annotation segment (shared resolver); if the
# arg is not a pointer it is addressed (ref: lvalue) or copied into a temp slot (temp).
vpi_cur_sha: str = None
vpi_cur_sha: char* = None
if trans is not None: vpi_cur_sha = trans.ModuleSha1
mode_cm: int = self.Gen.CallGenBase._vpi_auto_addr_mode(call_name, vpi_cur_sha, idx)
if mode_cm != 0 and trans is not None and trans.Module is not None:
@@ -291,14 +291,14 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
pass
return val
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy*, func_name: str, node: ast.AST*, fatal_if_unknown: int) -> mlirlite.LLVMType*:
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy*, func_name: char*, node: ast.AST*, fatal_if_unknown: int) -> mlirlite.LLVMType*:
"""Infer the return type of an external includes function; a whitelist miss may fail fast"""
if pool is None or func_name is None: return mlirlite.Int32(pool)
# extract the bare function name by stripping the SHA1 prefix
# (16 hex digits + '.'), e.g. "240a9a4157959a9f.parse" -> "parse".
bare_name: str = func_name
dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.')
bare_name: char* = func_name
dot_pos: char* = string.strrchr(func_name, 46) # 46 = ord('.')
if dot_pos is not None:
# the part after '.' is the bare function name
bare_name = dot_pos + 1
@@ -396,7 +396,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
# Default i32 placeholder: fatal_if_unknown=1 fails fast; =0 (Phase A mid-pass) stays silent.
if fatal_if_unknown != 0: self.Gen.TypeGenErr.fatal_error(node, f"external function return type is unregistered and cannot be inferred conclusively (defaulting to i32 would truncate pointers/i64; provide a stub declaration or add it to the whitelist): {func_name}", "TypeError")
return mlirlite.Int32(pool)
def _get_external_func_param_ty(self, pool: memhub.MemBuddy*, func_name: str, param_idx: int) -> mlirlite.LLVMType*:
def _get_external_func_param_ty(self, pool: memhub.MemBuddy*, func_name: char*, param_idx: int) -> mlirlite.LLVMType*:
"""Return the expected type of an external function's param_idx-th parameter, or None (fallback only)"""
if pool is None or func_name is None: return None
@@ -409,7 +409,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
# list iteration is unsupported; use range + get instead
api_count: sizet = len(win32_apis)
for ai in range(int(api_count)):
api_name: str = win32_apis.get(ai)
api_name: char* = win32_apis.get(ai)
if string.strcmp(api_name, func_name) == 0: return i64_ty
# Allocators: size params are declared i64 (size_t); without promotion on bare-name fallback call/define widths mismatch; calloc/realloc size is idx 1
@@ -421,7 +421,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
if param_idx == 1: return i64_ty
return None
def _is_sha1_mangled_call(self, call_name: str) -> int:
def _is_sha1_mangled_call(self, call_name: char*) -> int:
"""Return whether call_name is a cross-module Viper function ("{16 hex}.func")"""
if call_name is None: return 0
if GenApi.sha1_prefix_len(call_name) != 0: return 1
@@ -498,7 +498,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
# _translate_call_with_kwargs: keyword-arg call; builds args in found_func.Params order (positional first, else match by param name)
# ============================================================
def _translate_call_with_kwargs(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, func_name: str, found_func: mlirlite.Function*, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_call_with_kwargs(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, func_name: char*, found_func: mlirlite.Function*, cargs: list[ast.AST*]*, can: sizet, keywords: list[ast.AST*]*, kw_count: sizet, trans: HT.Translator*) -> mlirlite.Value*:
"""Translate a call with keyword arguments, building args in parameter order"""
head: mlirlite.Value* = None
tail: mlirlite.Value* = None
@@ -510,11 +510,11 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
# Default-arg lookup for omitted params (same source as CallGenMixin non-kwargs path):
# module SHA1 is the "{sha1}.func" prefix or current module; the name+SHA1-keyed snapshot survives resets.
kw_callee_sha: str = None
kw_found_name: str = mlirlite.function_get_name(found_func)
kw_callee_sha: char* = None
kw_found_name: char* = mlirlite.function_get_name(found_func)
if kw_found_name is not None:
if GenApi.sha1_prefix_len(kw_found_name) != 0:
kw_sha_buf: str = pool.alloc(17)
kw_sha_buf: char* = pool.alloc(17)
if kw_sha_buf is not None:
string.strncpy(kw_sha_buf, kw_found_name, 16)
kw_sha_buf[16] = '\0'
@@ -544,7 +544,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans, param_ty)
else:
# Find the matching keyword argument
param_name: str = mlirlite.param_get_name(cur_param)
param_name: char* = mlirlite.param_get_name(cur_param)
if param_name is not None:
for ki in range(int(kw_count)):
kw: ast.Keyword* = ast.Keyword*(keywords.get(ki))
@@ -582,8 +582,8 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
cur_param = mlirlite.param_get_next(cur_param)
# declare and define both use found_func.Name: normal funcs carry the SHA1 prefix, t.ExternT/t.State/t.ExportT use bare names (bare names desync call/define symbols)
call_name: str = func_name
found_name: str = mlirlite.function_get_name(found_func)
call_name: char* = func_name
found_name: char* = mlirlite.function_get_name(found_func)
if found_name is not None: call_name = found_name
# Return type (via accessor)
+115 -115
View File
@@ -44,14 +44,14 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if self.Gen.CallGenBase.StaticCount <= 0: return 0
sc_at: ast.Attribute* = ast.Attribute*(cl.func)
if sc_at.value is None or sc_at.attr is None: return 0
sc_mod: str = None
sc_mod: char* = None
if sc_at.value.kind() == ast.ASTKind.Name:
sc_nm: ast.Name* = ast.Name*(sc_at.value)
sc_mod = sc_nm.id
elif sc_at.value.kind() == ast.ASTKind.Attribute: sc_mod = self.Gen.ExprGenAttr._attr_module_path_str(pool, sc_at.value)
if sc_mod is None: return 0
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, sc_mod) == 0: return 0
sc_sha1: str = self.Gen.StructGen._resolve_module_sha1(sc_mod, trans._from_imports)
sc_sha1: char* = self.Gen.StructGen._resolve_module_sha1(sc_mod, trans._from_imports)
if sc_sha1 is None: return 0
if string.strncmp(sc_sha1, trans.ModuleSha1, 16) == 0: return 0
if self.Gen.CallGenBase.IsStaticSymbol(sc_sha1, sc_at.attr) == 0: return 0
@@ -130,7 +130,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if param_idx < can:
if cargs is not None: arg_node_kw = cargs.get(param_idx)
else:
pname_kw: str = mlirlite.param_get_name(cur_param)
pname_kw: char* = mlirlite.param_get_name(cur_param)
if pname_kw is not None and keywords is not None:
ki_kw: sizet
for ki_kw in range(int(kw_count)):
@@ -149,7 +149,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# _defaults_entry_from_vpi - 全局表里没有该函数的缺省值时,直接按定义模块 .vpi 签名临时造一条
# 快照条目(kinds/ints/strs 由文本解析得到,字符串已深拷贝进 PermPool),仅本次调用使用。
def _defaults_entry_from_vpi(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> GenTypes.FuncDefaultsEntry*:
def _defaults_entry_from_vpi(self, pool: memhub.MemBuddy*, sha1: char*, name: char*) -> GenTypes.FuncDefaultsEntry*:
"""按 .vpi 签名临时构造缺省值条目;无缺省值或不可重建返回 None"""
cap: int = 128
# 快照数组放 PermPool:条目可能在后续 pool reset 之后才被消费,放 pool 会悬空。
@@ -179,15 +179,15 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# 早期补参用的是 dfx_callee_sha,链式接收者(`self.Gen.X.m(...)`)解析不出发起模块时它会
# 退化成调用方 SHA1,表查不到;此时 call_name 已带定义模块 SHA1 前缀,是权威键,再补一次。
# 返回补齐后的实参个数。
def _fill_defaults_late(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, builder: mlirlite.IRBuilder*, trans: HT.Translator*, cl: ast.Call*, call_name: str, head: mlirlite.Value*, can: sizet) -> sizet:
def _fill_defaults_late(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, builder: mlirlite.IRBuilder*, trans: HT.Translator*, cl: ast.Call*, call_name: char*, head: mlirlite.Value*, can: sizet) -> sizet:
"""按 call_name 里的定义模块 SHA1 前缀补缺省参数;返回新的实参个数"""
if head is None or can == 0 or call_name is None: return can
if self.Gen.CallGenLib._is_sha1_mangled_call(call_name) == 0: return can
sh_buf: str = pool.alloc(17)
sh_buf: char* = pool.alloc(17)
if sh_buf is None: return can
string.strncpy(sh_buf, call_name, 16)
sh_buf[16] = '\0'
cname: str = call_name + 17
cname: char* = call_name + 17
if cname[0] == '\0': return can
# 只做 SHA1 隔离的查找:表按 (名字, 定义模块 SHA1) 定位;表里没有就直接读该模块的 .vpi。
# 绝不按裸名跨模块搜(`run` 这类名字在多个模块里都有,会拿错签名去补参/校验)。
@@ -263,7 +263,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
self._CheckStaticCallCrossModule(pool, trans, cl)
if cl is None: return None
func_name: str = self.Gen.ExprGenBase.get_func_name(cl.func)
func_name: char* = self.Gen.ExprGenBase.get_func_name(cl.func)
cargs: list[ast.AST*]* = cl.args
can: sizet = 0
@@ -291,19 +291,19 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# 走下面的跨模块回退时置 0 —— 跨模块的掩码是合并值,不能再按"字段位"裁剪(会变成漏报)。
loc_mask: int = 1
# 跨模块回退解析出的被调者模块 sha1(最终裁决要用它去查对方 .vpi 的指令契约)
mod_sha_ec: str = None
mod_sha_ec: char* = None
if esc_mask == 0:
loc_mask = 0
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_ec: ast.Attribute* = ast.Attribute*(cl.func)
mod_p_ec: str = None
mod_p_ec: char* = None
if at_ec.value is not None and at_ec.value.kind() == ast.ASTKind.Name:
mn_ec: ast.Name* = ast.Name*(at_ec.value)
if mn_ec.id is not None: mod_p_ec = mn_ec.id
elif at_ec.value is not None and at_ec.value.kind() == ast.ASTKind.Attribute:
mod_p_ec = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_ec.value)
if mod_p_ec is not None:
fi_ec: str = None
fi_ec: char* = None
if trans is not None: fi_ec = trans._from_imports
mod_sha_ec = self.Gen.StructGen._resolve_module_sha1(mod_p_ec, fi_ec)
if mod_sha_ec is None and trans is not None and trans._from_imports is not None:
@@ -353,17 +353,17 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if cargs is not None and func_name is not None:
wf_mask = GenApi.get_param_willfree(func_name)
if wf_mask == 0:
mod_sha_wf: str = None
mod_sha_wf: char* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_wf: ast.Attribute* = ast.Attribute*(cl.func)
mod_p_wf: str = None
mod_p_wf: char* = None
if at_wf.value is not None and at_wf.value.kind() == ast.ASTKind.Name:
mn_wf: ast.Name* = ast.Name*(at_wf.value)
if mn_wf.id is not None: mod_p_wf = mn_wf.id
elif at_wf.value is not None and at_wf.value.kind() == ast.ASTKind.Attribute:
mod_p_wf = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_wf.value)
if mod_p_wf is not None:
fi_wf: str = None
fi_wf: char* = None
if trans is not None: fi_wf = trans._from_imports
mod_sha_wf = self.Gen.StructGen._resolve_module_sha1(mod_p_wf, fi_wf)
if mod_sha_wf is None and trans is not None and trans._from_imports is not None:
@@ -402,7 +402,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
sub_node: ast.Subscript* = ast.Subscript*(cl.func)
if sub_node.value is not None and sub_node.value.kind() == ast.ASTKind.Name:
gen_nm_node: ast.Name* = ast.Name*(sub_node.value)
gen_class_name: str = gen_nm_node.id
gen_class_name: char* = gen_nm_node.id
# Check whether this is a generic class template.
template_cd: ast.ClassDef* = self.Gen.ClassGenGeneric._find_generic_template(gen_class_name)
if template_cd is not None:
@@ -410,7 +410,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
gen_type_args: list[str]* = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_node.slice, trans._from_imports)
if gen_type_args is not None and len(gen_type_args) > 0:
# Specialize the generic class and return the specialized name.
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_class_name, gen_type_args)
spec_name: char* = self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_class_name, gen_type_args)
if spec_name is not None:
# Generic ctor call: list[str](pool, 8) -> __new__ + __before_init__ + __init__.
# Do not use spec_name as func_name (star-import fallback would pick a wrong SHA1).
@@ -429,7 +429,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if sub_cast_arg is not None:
return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, sub_cast_arg, sub_cast_ty, cl.args.get(0), trans)
# Generic class template not found: the defining module may not be imported.
gt_err: str = pool.alloc(256)
gt_err: char* = pool.alloc(256)
if gt_err is not None:
viperlib.snprintf(gt_err, 256, "Generic class '%s': template not found; the defining module may not be imported", gen_class_name)
self.Gen.TypeGenErr.fatal_error(gen_nm_node, gt_err)
@@ -488,14 +488,14 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
mod_qual_done: int = 0
if trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_qc0: ast.Attribute* = ast.Attribute*(cl.func)
mod_path0: str = None
mod_path0: char* = None
if at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Name:
mod_nm0: ast.Name* = ast.Name*(at_qc0.value)
if mod_nm0.id is not None: mod_path0 = mod_nm0.id
elif at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Attribute: mod_path0 = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc0.value)
if mod_path0 is not None:
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_path0) != 0:
mod_sha1_0: str = self._lookup_module_sha1(mod_path0)
mod_sha1_0: char* = self._lookup_module_sha1(mod_path0)
if mod_sha1_0 is not None:
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, mod_sha1_0)
mod_qual_done = 1
@@ -515,7 +515,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if imp_ok != 0:
# Module-qualified construction: resolve by project module SHA1, not trans.ModuleSha1
# (the current file); otherwise the bare-name fallback picks the wrong same-name class.
mod_sha1_qc2: str = None
mod_sha1_qc2: char* = None
if mod_nm_qc.id is not None: mod_sha1_qc2 = self._lookup_module_sha1(mod_nm_qc.id)
if mod_sha1_qc2 is not None: gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, mod_sha1_qc2)
if gen_ctor_entry is None: gen_ctor_entry = self.Gen.StructGen.find_struct(func_name)
@@ -523,11 +523,11 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
elif at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Attribute:
# Multi-level module path construction: win32.fileio.File(...); build the path via
# attr_module_path_str, check the full import, then find the struct by that module's SHA1.
mod_path_qc: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc.value)
mod_path_qc: char* = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc.value)
if mod_path_qc is not None:
imp_ok_qc: int = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_path_qc)
if imp_ok_qc != 0:
mod_sha1_qc: str = self._lookup_module_sha1(mod_path_qc)
mod_sha1_qc: char* = self._lookup_module_sha1(mod_path_qc)
if mod_sha1_qc is not None: gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(func_name, mod_sha1_qc)
if gen_ctor_entry is None: gen_ctor_entry = self.Gen.StructGen.find_struct(func_name)
if gen_ctor_entry is not None: struct_ty = gen_ctor_entry.Ty
@@ -535,14 +535,14 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# original name, so resolve alias -> original before find_struct to avoid a link-time undefined reference.
if gen_ctor_entry is None and trans is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
if trans._from_imports is not None:
ctor_sha1: str = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0)
ctor_sha1: char* = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0)
if ctor_sha1 is not None:
ctor_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, func_name)
ctor_orig: char* = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, func_name)
if ctor_orig is not None:
# Copy into a separate NUL-terminated buffer (the from_imports buffer is not terminated).
co_len: sizet = 0
while ctor_orig[co_len] != ' ' and ctor_orig[co_len] != '\0': co_len += 1
co_buf: str = pool.alloc(co_len + 1)
co_buf: char* = pool.alloc(co_len + 1)
if co_buf is not None:
for co_i in range(int(co_len)): co_buf[co_i] = ctor_orig[co_i]
co_buf[co_len] = '\0'
@@ -614,7 +614,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
ais_bare: sizet = sizet(aip_bare)
# [D25 fail-fast] Never default to "int": with no hint and too few args it would
# wrongly specialize to list[int]/Box[int] (4-byte elements, pointer truncation/crash).
ta_bare: str = None
ta_bare: char* = None
if ais_bare < can: ta_bare = self.Gen.CallGenCtor._infer_generic_ctor_type_arg(pool, cargs.get(ais_bare), func_name, trans)
if ta_bare is None:
# [C2] Keyword-only arg (Box(v=42)): match the keyword by __init__ param name
@@ -629,7 +629,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if ta_bare is None:
self.Gen.TypeGenErr.fatal_error(cl, f"Cannot infer type argument {tpi_bare + 1} of generic class '{func_name}' from the arguments, " f"silent default to int is forbidden (it produces wrong specializations such as list[int] -> elements treated as 4 bytes/i32 -> pointer truncation crash); " f"annotate the argument type or annotate explicitly, e.g. {func_name}[Type](...)", "TypeError")
inferred_args_bare.append(ta_bare)
spec_name_bare: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, func_name, inferred_args_bare)
spec_name_bare: char* = self.Gen.ClassGenGeneric._specialize_generic_class(trans, func_name, inferred_args_bare)
if spec_name_bare is not None: return self.Gen.CallGenCtor._translate_generic_ctor_call(pool, builder, mod, spec_name_bare, cl, trans, func_name)
# REnum variant construction: ClassName.VariantName(args...), e.g. LLVMType.Int(1). Must run
@@ -639,7 +639,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Name:
rc_cls_node: ast.Name* = ast.Name*(rc_at.value)
if rc_cls_node.id is not None:
rc_cls_name: str = rc_cls_node.id
rc_cls_name: char* = rc_cls_node.id
# Check whether ClassName is a registered REnum; also use _is_renum_by_entry (a __tag
# makes it an REnum) so a missing variant table does not silently fall through to a misleading error.
if self.Gen.ClassGenEnum._is_renum_class_name(rc_cls_name) != 0 or self.Gen.ClassGenEnum._is_renum_by_entry(rc_cls_name) != 0:
@@ -652,21 +652,21 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
elif rc_at.value is not None and rc_at.value.kind() == ast.ASTKind.Attribute:
# Module-qualified REnum variant: mod.Class.Variant(args...), e.g. mlirlite.LLVMType.Struct(...).
# Only require the leftmost module imported and the class name in the global REnum table.
rc_mq_path: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, rc_at.value)
rc_mq_path: char* = self.Gen.ExprGenAttr._attr_module_path_str(pool, rc_at.value)
if rc_mq_path is not None:
rc_mq_dot: str = string.strrchr(rc_mq_path, 46) # 46 = ord('.')
rc_mq_dot: char* = string.strrchr(rc_mq_path, 46) # 46 = ord('.')
if rc_mq_dot is not None and rc_mq_dot[1] != '\0':
# Module path = everything before the last segment (multi-level supported); use
# strlen integer differences, not pointer subtraction (which emits illegal IR).
rc_mq_len: sizet = string.strlen(rc_mq_path) - string.strlen(rc_mq_dot + 1) - 1
rc_mq_mod: str = pool.alloc(rc_mq_len + 1)
rc_mq_mod: char* = pool.alloc(rc_mq_len + 1)
if rc_mq_mod is not None:
string.memcpy(rc_mq_mod, rc_mq_path, rc_mq_len)
rc_mq_mod[rc_mq_len] = '\0'
rc_mq_imp: int = 1
if trans is not None and trans._imported_modules is not None: rc_mq_imp = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, rc_mq_mod)
if rc_mq_imp != 0:
rc_mq_cls: str = rc_mq_dot + 1
rc_mq_cls: char* = rc_mq_dot + 1
# Check whether the class name is a registered REnum class.
if self.Gen.ClassGenEnum._is_renum_class_name(rc_mq_cls) != 0:
rc_mq_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class(rc_mq_cls, func_name)
@@ -677,9 +677,9 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
if func_name is not None and trans is not None:
if self.Gen.AsyncGen.is_async_func(trans, func_name) != 0:
can_buf: str = pool.alloc(64)
can_buf: char* = pool.alloc(64)
if can_buf is not None: viperlib.snprintf(can_buf, 64, "create_%s", func_name)
create_an: str = self.Gen.FuncGenBase._mangle_func_name(trans, can_buf, 0)
create_an: char* = self.Gen.FuncGenBase._mangle_func_name(trans, can_buf, 0)
create_fn_an: mlirlite.Function* = self.find_func_in_module(mod, create_an)
if create_fn_an is not None:
frame_ty_an: mlirlite.LLVMType* = self.Gen.AsyncGen._lookup_coro_frame(trans, func_name)
@@ -719,7 +719,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
# Bare-name call: allow exact + suffix matching. Custom decorator wrapper rewrite: original
# name -> __decor_wrap_{name} (wrapper is generated at function-translation end and is reachable).
decor_wrap_nm: str = self.Gen.ClassGenBase.lookup_decor_wrapper(func_name)
decor_wrap_nm: char* = self.Gen.ClassGenBase.lookup_decor_wrapper(func_name)
if decor_wrap_nm is not None: found_func = self.find_func_in_module(mod, decor_wrap_nm)
else:
# Bare-name call to a function defined in this module: hit the module function table first (authoritative for module-level names), then fall back to the module list scan.
@@ -727,7 +727,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if actual_funcs is not None:
# Prefer same-module functions: the _funcs table is polluted by dependency stubs,
# so pass trans.ModuleSha1 to match the "{ModuleSha1}.{name}" entry first (AstTest root cause).
tbl_owner_sha1: str = None
tbl_owner_sha1: char* = None
if trans is not None: tbl_owner_sha1 = trans.ModuleSha1
mod_fn_tb = self.Gen.CallGenLeafKit.find_func_in_table(actual_funcs, actual_func_count, func_name, tbl_owner_sha1)
if mod_fn_tb is not None:
@@ -735,7 +735,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# 重载:本模块同名 >=2 时按实参静态类型决议(返回值不参与)。
# 单候选维持原行为零开销;候选 >=2 且有实参无法定型 -> resolve 内 fail-fast。
if actual_funcs is not None:
ovl_owner: str = None
ovl_owner: char* = None
if trans is not None: ovl_owner = trans.ModuleSha1
ovl_cnt: int = self.Gen.CallGenLeafKit.count_owned_overloads(actual_funcs, actual_func_count, func_name, ovl_owner)
if ovl_cnt >= 2:
@@ -745,13 +745,13 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
else:
# No table entry: fall back to scanning FuncHead, also passing owner_sha1 so an
# inactive same-name forward declaration does not get mis-bound to an earlier stub.
ff_owner_sha1: str = None
ff_owner_sha1: char* = None
if trans is not None: ff_owner_sha1 = trans.ModuleSha1
found_func = self.find_func_in_module(mod, func_name, ff_owner_sha1)
# Reject suffix matches that hit a class method (SHA1.Class.method): a bare-name call sharing a
# name with a module function would bind to the method and recurse infinitely (stack overflow).
if found_func is not None:
bn_name: str = mlirlite.function_get_name(found_func)
bn_name: char* = mlirlite.function_get_name(found_func)
if bn_name is not None:
bn_dots: int = 0
bni2: sizet = 0
@@ -760,7 +760,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
bni2 += 1
if bn_dots >= 2:
# Extract segment 2 (SHA1.Class.method -> Class; SHA1.submod.func -> submod).
bn_seg2: str = pool.alloc(64)
bn_seg2: char* = pool.alloc(64)
if bn_seg2 is not None:
bn_seg2[0] = '\0'
bsi2: sizet = 0
@@ -782,10 +782,10 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
else:
# 跨模块重载:先从接收者路径解析目标模块 SHA1,再在候选集里决议。
# vpi 注入的重载符号是 {sha1}.name_v{hash}、函数表项名带后缀,所以用 prefix 模式。
xm_sha1_ovl: str = None
xm_sha1_ovl: char* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute and trans is not None and trans._imported_modules is not None:
xm_at_ovl: ast.Attribute* = ast.Attribute*(cl.func)
xm_path_ovl: str = None
xm_path_ovl: char* = None
if xm_at_ovl is not None and xm_at_ovl.value is not None:
if xm_at_ovl.value.kind() == ast.ASTKind.Name:
xm_nm_ovl: ast.Name* = ast.Name*(xm_at_ovl.value)
@@ -815,20 +815,20 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if xm_at is not None and xm_at.value is not None and trans is not None and trans._imported_modules is not None:
# Module path: one-level Name (syslibtest_impl.run) or nested Attribute (os._win32.exists).
# The nested case must be validated too, else it suffix-matches this module's own define (recursion).
xm_mod_path: str = None
xm_mod_path: char* = None
if xm_at.value.kind() == ast.ASTKind.Name:
xm_nm: ast.Name* = ast.Name*(xm_at.value)
if xm_nm.id is not None: xm_mod_path = xm_nm.id
elif xm_at.value.kind() == ast.ASTKind.Attribute: xm_mod_path = self.Gen.ExprGenAttr._attr_module_path_str(pool, xm_at.value)
if xm_mod_path is not None:
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, xm_mod_path) != 0:
mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path)
mod_sha1_chk: char* = self._lookup_module_sha1(xm_mod_path)
if mod_sha1_chk is None and trans._from_imports is not None:
# import X as Y (e.g. import fat32_diskio as diskio): resolve the alias before
# lookup, else this check is skipped and the suffix match hits a same-name local function.
mod_sha1_chk = self.Gen.StructGen._resolve_module_sha1(xm_mod_path, trans._from_imports)
if mod_sha1_chk is not None:
ff_nm2: str = mlirlite.function_get_name(found_func)
ff_nm2: char* = mlirlite.function_get_name(found_func)
if ff_nm2 is not None:
if string.strncmp(ff_nm2, mod_sha1_chk, 16) != 0:
# ExportT / t.State 型跨模块函数**定义与声明都用裸名**(stdlib.free、
@@ -848,7 +848,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if cx_ok == 0:
if self.Gen.CallGenBase._is_reexport_owner(pool, mod_sha1_chk, func_name, ff_nm2) != 0: cx_ok = 1
if cx_ok == 0: found_func = None
ff_name: str = mlirlite.function_get_name(found_func)
ff_name: char* = mlirlite.function_get_name(found_func)
if ff_name is not None:
ff_dots: int = 0
ffi2: sizet = 0
@@ -857,7 +857,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
ffi2 += 1
if ff_dots >= 2:
# Extract segment 2 (SHA1.Class.method -> Class; SHA1.submod.func -> submod).
seg2: str = pool.alloc(64)
seg2: char* = pool.alloc(64)
if seg2 is not None:
seg2[0] = '\0'
si: sizet = 0
@@ -891,9 +891,9 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if found_func is None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name and string.strcmp(func_name, "abs") == 0 and can == 1:
abs_arg: ast.AST* = cargs.get(0)
if abs_arg is not None:
abs_cls: str = self.Gen.CallGenProp._resolve_obj_class_name(pool, trans, abs_arg)
abs_cls: char* = self.Gen.CallGenProp._resolve_obj_class_name(pool, trans, abs_arg)
if abs_cls is not None:
abs_sc: str = self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, abs_cls, "__abs__", trans)
abs_sc: char* = self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, abs_cls, "__abs__", trans)
if abs_sc is not None:
abs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, abs_arg, None, 0, trans)
if abs_val is not None: return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, abs_sc, "__abs__", abs_val, None, 0, trans)
@@ -903,7 +903,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# builtins auto-link: when no C library matches, link ord/chr etc. from the builtins module by
# creating a {builtins_sha1}.{name} declare with the agreed signature (ord(i8)->i32, chr(i32)->i8).
if found_func is None:
bi_sha1: str = self._lookup_module_sha1("builtins")
bi_sha1: char* = self._lookup_module_sha1("builtins")
if bi_sha1 is not None:
bi_ret: mlirlite.LLVMType* = None
bi_p1: mlirlite.LLVMType* = None
@@ -925,10 +925,10 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
bi_ret = mlirlite.Int32(pool)
bi_p1 = mlirlite.Int32(pool)
if bi_ret is not None:
bi_sym: str = func_name
bi_sym: char* = func_name
if self.Gen.CallGenBase.is_cexport_func(bi_sha1, func_name) == 0:
bi_sym_len: sizet = string.strlen(bi_sha1) + string.strlen(func_name) + 2
bi_sym_buf: str = pool.alloc(bi_sym_len)
bi_sym_buf: char* = pool.alloc(bi_sym_len)
if bi_sym_buf is not None:
viperlib.snprintf(bi_sym_buf, bi_sym_len, "%s.%s", bi_sha1, func_name)
bi_sym = bi_sym_buf
@@ -950,9 +950,9 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
vpi_ret_ty: mlirlite.LLVMType* = None
# Source module SHA1 for cross-module return-type resolution (assigned only in the Attribute
# branch). Must be initialized to None, else a bare call reads stack garbage and crashes.
mod_ret_sha1: str = None
mod_ret_sha1: char* = None
# Module path (assigned only for Attribute); declared outside the if so the bare-name fallback can read it safely.
mod_path_ret: str = None
mod_path_ret: char* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_ret: ast.Attribute* = ast.Attribute*(cl.func)
# Resolve the callee module path: both one-level (stdlib.free) and multi-level
@@ -964,17 +964,17 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if mod_path_ret is not None:
# import X as Y (e.g. import lib.core.Gen.GenApi as GenApi): an exact module-name lookup
# cannot resolve the alias and would wrongly fail-fast; use _resolve_module_sha1 (alias restore + exact/suffix).
fi_from_rt: str = None
fi_from_rt: char* = None
if trans is not None: fi_from_rt = trans._from_imports
mod_ret_sha1 = self.Gen.StructGen._resolve_module_sha1(mod_path_ret, fi_from_rt)
if mod_ret_sha1 is not None:
# Lazy in-function import: VpiReader imports HandlesExprCall, so a top-level import would cycle.
VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir)
ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot(mod_ret_sha1, None, func_name)
ret_annot_fc: char* = VpiReader._lookup_vpi_ret_annot(mod_ret_sha1, None, func_name)
if ret_annot_fc is None:
# Package vpi miss (the function is defined in a submodule, e.g. fltk.Fl_Input_value in
# fltk._cfl_input): use re-export to locate the defining module, then query its vpi.
rr_src: str = self.Gen.ImportsGen.lookup_reexport_call(mod_ret_sha1, func_name)
rr_src: char* = self.Gen.ImportsGen.lookup_reexport_call(mod_ret_sha1, func_name)
if rr_src is None: rr_src = self._lookup_reexport_from_vpi(pool, mod_ret_sha1, func_name)
if rr_src is not None:
rr_total: sizet = string.strlen(rr_src)
@@ -985,11 +985,11 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
rr_start: sizet = rr_i
while rr_i < rr_total and rr_src[rr_i] != ' ': rr_i += 1
rr_len: sizet = rr_i - rr_start
rr_buf: str = pool.alloc(rr_len + 1)
rr_buf: char* = pool.alloc(rr_len + 1)
if rr_buf is not None:
string.strncpy(rr_buf, rr_src + rr_start, rr_len)
rr_buf[rr_len] = '\0'
rr_sha1: str = self._lookup_module_sha1(rr_buf)
rr_sha1: char* = self._lookup_module_sha1(rr_buf)
# Re-export sources may be registered under a qualified
# module name; fall back to suffix lookup on exact miss.
if rr_sha1 is None: rr_sha1 = self._lookup_module_sha1_suffix(rr_buf)
@@ -1013,21 +1013,21 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# 结果被丢弃(独立调用语句)时不 fail-fast:占位 i32 无害,没必要中断编译。
if trans is not None and trans._declare_only == 0 and trans._discard_result == 0:
vpi_rc_d: int = 0
vpi_annot_d: str = None
vpi_annot_d: char* = None
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
vpi_rc_d = VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir)
vpi_annot_d = VpiReader._lookup_vpi_ret_annot(mod_ret_sha1, None, func_name)
annot_s_d: str = "-"
annot_s_d: char* = "-"
if vpi_annot_d is not None: annot_s_d = vpi_annot_d
# sha1 来源拆解:exact / suffix 各自返回什么,用来区分"存表里就是错的"
# 与"后缀匹配撞到了别的模块"(两者修法完全不同)。
ex_s_d: str = "-"
ex_s_d: char* = "-"
if mod_path_ret is not None:
_ex_d: str = self.Gen.CallGenBase._lookup_module_sha1(mod_path_ret)
_ex_d: char* = self.Gen.CallGenBase._lookup_module_sha1(mod_path_ret)
if _ex_d is not None: ex_s_d = _ex_d
sf_s_d: str = "-"
sf_s_d: char* = "-"
if mod_path_ret is not None:
_sf_d: str = self.Gen.CallGenBase._lookup_module_sha1_suffix(mod_path_ret)
_sf_d: char* = self.Gen.CallGenBase._lookup_module_sha1_suffix(mod_path_ret)
if _sf_d is not None: sf_s_d = _sf_d
# 走 VPC Diag(源码行 + 调用栈),而不是单行 VLogger.error:
# 单行输出丢掉了位置与调用链,无法定位到底是谁在什么上下文里调用了它。
@@ -1073,9 +1073,9 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
c_struct: mlirlite.LLVMType* = None
if c_obj is not None: c_struct = self.Gen.StructGen.get_struct_type_from_value(c_obj)
if c_struct is not None:
c_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, c_struct)
c_cls: char* = self.Gen.StructGen.get_class_name_by_type(pool, c_struct)
if c_cls is not None:
c_call_cls: str = self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, c_cls, "__call__", trans)
c_call_cls: char* = self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, c_cls, "__call__", trans)
if c_call_cls is not None: return self.Gen.CallGenCall._translate_method_call(pool, builder, mod, c_call_cls, "__call__", c_obj, cl, trans)
# Closure variable (nested function hoisted and registered): extract fn_ptr/env_ptr and call indirectly.
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0:
@@ -1086,7 +1086,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# holds {fn_ptr, env_ptr} and must pass env, else a direct call jumps into closure data and crashes.
ve_fp: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, func_name)
is_cptr_annot_fp: int = 0
fp_annot: str = self.Gen.VarGen.var_annot_class_name(ve_fp)
fp_annot: char* = self.Gen.VarGen.var_annot_class_name(ve_fp)
if fp_annot is not None:
# [S4] PtrT marker = annotation base name exactly "PtrT": t.Callable[[A],R] also resolves to
# Ptr(i8) (same Spec), so only the annotation name can carry the marker; substring matching would misfire.
@@ -1152,7 +1152,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
fpi += 1
if fp is not None: fpty = mlirlite.param_get_ty(fp)
# Value-reference params (&T / &temp T): auto-address the argument (same-module callee).
ao_name: str = None
ao_name: char* = None
if found_func is not None: ao_name = mlirlite.function_get_name(found_func)
arg_val: mlirlite.Value* = self.Gen.CallGenBase.translate_call_arg(builder, pool, mod, trans, ao_name, int(ai), arg, fpty, "param")
if arg_val is not None:
@@ -1178,15 +1178,15 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
ext_param_ty: mlirlite.LLVMType* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_pt: ast.Attribute* = ast.Attribute*(cl.func)
mpath_pt: str = None
mpath_pt: char* = None
if at_pt.value is not None and at_pt.value.kind() == ast.ASTKind.Name:
nm_pt: ast.Name* = ast.Name*(at_pt.value)
mpath_pt = nm_pt.id
elif at_pt.value is not None and at_pt.value.kind() == ast.ASTKind.Attribute: mpath_pt = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_pt.value)
if mpath_pt is not None:
fi_pt: str = None
fi_pt: char* = None
if trans is not None: fi_pt = trans._from_imports
sha_pt: str = self.Gen.StructGen._resolve_module_sha1(mpath_pt, fi_pt)
sha_pt: char* = self.Gen.StructGen._resolve_module_sha1(mpath_pt, fi_pt)
if sha_pt is not None:
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha_pt, self.Gen.CallGenBase.TempDir)
ext_param_ty = VpiReader._resolve_vpi_param_annot_seg(pool, sha_pt, None, func_name, ai, mlirlite.Int32(pool), mlirlite.Ptr(pool, mlirlite.Int8(pool)))
@@ -1266,10 +1266,10 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
df_param_count: int = 0
# Compute the callee module SHA1 as a qualifier key (module.func -> that module; bare -> current),
# so usb.uhci.init(1 arg) does not wrongly match mm.init (5 params) and add extra arguments.
dfx_callee_sha: str = None
dfx_callee_sha: char* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
dfx_at: ast.Attribute* = ast.Attribute*(cl.func)
dfx_mod_path: str = None
dfx_mod_path: char* = None
if dfx_at.value is not None and dfx_at.value.kind() == ast.ASTKind.Name:
dfx_mn: ast.Name* = ast.Name*(dfx_at.value)
if dfx_mn.id is not None: dfx_mod_path = dfx_mn.id
@@ -1281,7 +1281,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# and returns None for `import X.Y as Z` (Z is not a module name). Without the alias restore the
# qualifier degrades to the current module SHA1, so find_func_defaults misses the defining module
# and the defaults are never filled (rhttps.get(url, verify) emits 2 operands for a 3-param callee).
dfx_fi: str = None
dfx_fi: char* = None
if trans is not None: dfx_fi = trans._from_imports
dfx_callee_sha = self.Gen.StructGen._resolve_module_sha1(dfx_mod_path, dfx_fi)
if dfx_callee_sha is None and self.Gen.TypeGenBase is not None: dfx_callee_sha = self.Gen.TypeGenBase.CurrentModuleSha1
@@ -1299,10 +1299,10 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# Package re-export fallback (e.g. json.write defined in includes/json/__writer.vp): the defaults
# table is keyed by the defining module SHA1, so re-resolve the re-export and query again (else defaults are skipped).
if gd_entry is None and dfx_callee_sha is not None:
rr_src_dx: str = self.Gen.ImportsGen.lookup_reexport_call(dfx_callee_sha, func_name)
rr_src_dx: char* = self.Gen.ImportsGen.lookup_reexport_call(dfx_callee_sha, func_name)
if rr_src_dx is None: rr_src_dx = self._lookup_reexport_from_vpi(pool, dfx_callee_sha, func_name)
if rr_src_dx is not None:
rr_sha_dx: str = self._lookup_module_sha1(rr_src_dx)
rr_sha_dx: char* = self._lookup_module_sha1(rr_src_dx)
if rr_sha_dx is not None: gd_entry = self.Gen.CallGenLeafKit.find_func_defaults(func_name, rr_sha_dx)
# 键失配兜底:链式接收者(`self.Gen.ExprGenMixin.translate_value(...)`)解析不出发起模块,
# dfx_callee_sha 退化成调用方自己的 SHA1,限定查表落空 —— 由后面的 _fill_defaults_late
@@ -1319,7 +1319,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
dfx_cnt: int = 0
dfx_p: mlirlite.Param* = mlirlite.function_get_param_head(found_func)
while dfx_p is not None:
dfx_nm: str = mlirlite.param_get_name(dfx_p)
dfx_nm: char* = mlirlite.param_get_name(dfx_p)
if dfx_nm is None or (string.strcmp(dfx_nm, "%__eh_msg_out__") != 0 and string.strcmp(dfx_nm, "%__eh_code_out__") != 0):
dfx_cnt += 1
dfx_p = mlirlite.param_get_next(dfx_p)
@@ -1359,7 +1359,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
fc_cnt: int = 0
fc_p: mlirlite.Param* = mlirlite.function_get_param_head(found_func)
while fc_p is not None:
fp_nm_z: str = mlirlite.param_get_name(fc_p)
fp_nm_z: char* = mlirlite.param_get_name(fc_p)
if fp_nm_z is None or (string.strcmp(fp_nm_z, "%__eh_msg_out__") != 0 and string.strcmp(fp_nm_z, "%__eh_code_out__") != 0):
fc_cnt += 1
fc_p = mlirlite.param_get_next(fc_p)
@@ -1384,12 +1384,12 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# Use the actual function name from the module (may carry a SHA1 prefix); ExternT/State/ExportT
# functions use the bare name. Both declare and define must use found_func.Name (mismatched names cause undefined references).
call_name: str = func_name
call_name: char* = func_name
# Original function name (recorded for aliased imports, e.g. from X import parse as json_parse -> "parse"),
# used to re-infer the return type after alias handling so _infer_external_func_ret_ty can match.
orig_func_name: str = None
orig_func_name: char* = None
if found_func is not None:
found_name: str = mlirlite.function_get_name(found_func)
found_name: char* = mlirlite.function_get_name(found_func)
if found_name is not None:
call_name = found_name
# ExportT bare-name fallback: a cross-module stub declare may carry a SHA1 prefix while the
@@ -1397,7 +1397,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if found_func.IsDeclared != 0:
# Use the single source of truth GenApi.sha1_prefix_len (converged in C10).
if GenApi.sha1_prefix_len(found_name) != 0:
cx_sha1_buf: str = pool.alloc(17)
cx_sha1_buf: char* = pool.alloc(17)
if cx_sha1_buf is not None:
string.memcpy(cx_sha1_buf, found_name, 16)
cx_sha1_buf[16] = '\0'
@@ -1409,13 +1409,13 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if xmod_at.value is not None and (xmod_at.value.kind() == ast.ASTKind.Name or xmod_at.value.kind() == ast.ASTKind.Attribute):
# Module path: one-level Name (string.strchr) or nested Attribute (os._win32.exists). The nested
# case must be resolved too, else it suffix-matches this module's own define (infinite recursion).
xmod_path: str = None
xmod_path: char* = None
if xmod_at.value.kind() == ast.ASTKind.Name:
xmod_nm: ast.Name* = ast.Name*(xmod_at.value)
if xmod_nm.id is not None: xmod_path = xmod_nm.id
else: xmod_path = self.Gen.ExprGenAttr._attr_module_path_str(pool, xmod_at.value)
if xmod_path is not None:
mod_sha1: str = self._lookup_module_sha1(xmod_path)
mod_sha1: char* = self._lookup_module_sha1(xmod_path)
if mod_sha1 is not None:
# fast-fail: a module-qualified call requires the module to be imported by this file;
# the global SHA1 store contains all scanned includes, so an unimported module would silently resolve.
@@ -1423,13 +1423,13 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if trans is not None and trans._imported_modules is not None:
is_xmod_imported = self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, xmod_path)
if is_xmod_imported == 0:
err_ni: str = pool.alloc(256)
err_ni: char* = pool.alloc(256)
if err_ni is not None:
viperlib.snprintf(err_ni, 256, "Cross-module call '%s.%s': module '%s' not imported (import %s first)", xmod_path, func_name, xmod_path, xmod_path)
self.Gen.TypeGenErr.fatal_error(xmod_at, err_ni)
self.Gen.TypeGenErr.fatal_error(xmod_at, "Cross-module call: module not imported")
# Check whether it is a re-export function (e.g. ast._lexer_init actually defined in ast.lexer).
reexport_src: str = self.Gen.ImportsGen.lookup_reexport_call(mod_sha1, func_name)
reexport_src: char* = self.Gen.ImportsGen.lookup_reexport_call(mod_sha1, func_name)
if reexport_src is None:
# Re-export table miss (includes modules with existing stub/text are not fully translated,
# so the table may be empty): resolve the module's .vpi from-import to locate the defining module.
@@ -1446,11 +1446,11 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
cs_start: sizet = cs_i
while cs_i < cs_total and reexport_src[cs_i] != ' ': cs_i += 1
cs_seg: sizet = cs_i - cs_start
cs_buf: str = pool.alloc(cs_seg + 1)
cs_buf: char* = pool.alloc(cs_seg + 1)
if cs_buf is not None:
string.strncpy(cs_buf, reexport_src + cs_start, cs_seg)
cs_buf[cs_seg] = '\0'
cand_sha1: str = self._lookup_module_sha1(cs_buf)
cand_sha1: char* = self._lookup_module_sha1(cs_buf)
if cand_sha1 is None: cand_sha1 = self._lookup_module_sha1_suffix(cs_buf)
if cand_sha1 is not None:
if self.Gen.CallGenBase.is_cexport_func(cand_sha1, func_name) != 0 or self._is_state_func_from_vpi(pool, cand_sha1, func_name) != 0:
@@ -1473,7 +1473,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if VpiReader._lookup_vpi_ret_annot(mod_sha1, None, func_name) is not None: orig_has_def = 1
if orig_has_def == 0:
# Original module does not define it: fall back to the first candidate (e.g. ast._lexer_init in ast.lexer).
reexport_sha1: str = self._lookup_module_sha1(reexport_src)
reexport_sha1: char* = self._lookup_module_sha1(reexport_src)
if reexport_sha1 is None: reexport_sha1 = self._lookup_module_sha1_suffix(reexport_src)
if reexport_sha1 is not None: mod_sha1 = reexport_sha1
# ExportT functions define with the bare name (@strlen), so the cross-module call must use the bare name too.
@@ -1486,7 +1486,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
call_name = func_name
else:
mangled_len: sizet = string.strlen(mod_sha1) + string.strlen(func_name) + 2
mangled: str = pool.alloc(mangled_len)
mangled: char* = pool.alloc(mangled_len)
if mangled is not None:
viperlib.snprintf(mangled, mangled_len, "%s.%s", mod_sha1, func_name)
call_name = mangled
@@ -1501,7 +1501,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if is_imported_mod != 0:
# Aliased import (import X as Y): Y is in _imported_modules but the exact SHA1 lookup fails
# (mod_arr holds file names); try from_imports (alias -> full name -> suffix SHA1) with allow_star_fallback=0.
alias_sha1: str = None
alias_sha1: char* = None
if trans is not None and trans._from_imports is not None: alias_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0)
if alias_sha1 is not None:
if self.Gen.CallGenBase.is_cexport_func(alias_sha1, func_name) != 0: call_name = func_name
@@ -1510,13 +1510,13 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
call_name = func_name
else:
AliasMangledLen: sizet = string.strlen(alias_sha1) + string.strlen(func_name) + 2
alias_mangled: str = pool.alloc(AliasMangledLen)
alias_mangled: char* = pool.alloc(AliasMangledLen)
if alias_mangled is not None:
viperlib.snprintf(alias_mangled, AliasMangledLen, "%s.%s", alias_sha1, func_name)
call_name = alias_mangled
else:
# Imported module but SHA1 unregistered: the module was not compiled, so error out immediately.
err_msg_imp: str = pool.alloc(256)
err_msg_imp: char* = pool.alloc(256)
if err_msg_imp is not None:
viperlib.snprintf(err_msg_imp, 256, "Module '%s' is imported but its SHA1 is not registered (the module was not compiled)", xmod_path)
self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg_imp)
@@ -1524,14 +1524,14 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
else:
# Check whether it is a from-import name (e.g. from .__types import LLVMType): LLVMType is a
# class, not a module, but Ptr is a module-level function of __types; disable star-import fallback.
from_sha1: str = None
from_sha1: char* = None
if trans is not None and trans._from_imports is not None: from_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0)
if from_sha1 is not None:
# ExportT functions define with the bare name, so the cross-module call must use the bare name.
if self.Gen.CallGenBase.is_cexport_func(from_sha1, func_name) != 0: call_name = func_name
else:
fmangled_len: sizet = string.strlen(from_sha1) + string.strlen(func_name) + 2
fmangled: str = pool.alloc(fmangled_len)
fmangled: char* = pool.alloc(fmangled_len)
if fmangled is not None:
viperlib.snprintf(fmangled, fmangled_len, "%s.%s", from_sha1, func_name)
call_name = fmangled
@@ -1547,12 +1547,12 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if base_defined == 0 and GenApi.is_nonlocal_name(trans, xmod_path) != 0: base_defined = 1
if base_defined == 0 and self.Gen.StructGen.find_struct(xmod_path) is not None: base_defined = 1
if base_defined == 0:
err_msg_ne: str = pool.alloc(256)
err_msg_ne: char* = pool.alloc(256)
if err_msg_ne is not None:
viperlib.snprintf(err_msg_ne, 256, "name '%s' is not defined (used as a module in '%s.%s'; missing an import?)", xmod_path, xmod_path, func_name)
self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg_ne, "NameError")
self.Gen.TypeGenErr.fatal_error(xmod_at, f"name '{xmod_path}' is not defined", "NameError")
err_msg2: str = pool.alloc(256)
err_msg2: char* = pool.alloc(256)
if err_msg2 is not None:
viperlib.snprintf(err_msg2, 256, "Cross-module call '%s.%s': module SHA1 not found", xmod_path, func_name)
self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg2)
@@ -1561,18 +1561,18 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# Bare-name call (not Attribute): check from_imports. Star-import fallback is disabled for calls
# (star import is for types/constants); a missed function then fails fast at compile time instead of linking wrong.
if trans is not None and trans._from_imports is not None:
bare_sha1: str = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0)
bare_sha1: char* = self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0)
if bare_sha1 is not None:
# Alias handling: from X import Y as Z -> calling Z must mangle with Y (e.g. from json.__parser
# import parse as json_parse -> SHA1.parse, not SHA1.json_parse).
bare_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, func_name)
bare_func_name: str = func_name
bare_orig: char* = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, func_name)
bare_func_name: char* = func_name
if bare_orig is not None:
# bare_orig points into the from_imports buffer and is not NUL-terminated, so copy it
# into a separate buffer (truncating at space or NUL).
bo_len: sizet = 0
while bare_orig[bo_len] != ' ' and bare_orig[bo_len] != '\0': bo_len += 1
bo_buf: str = pool.alloc(bo_len + 1)
bo_buf: char* = pool.alloc(bo_len + 1)
if bo_buf is not None:
for bo_i in range(int(bo_len)): bo_buf[bo_i] = bare_orig[bo_i]
bo_buf[bo_len] = '\0'
@@ -1582,28 +1582,28 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
orig_func_name = bare_func_name
# Re-export resolution: a from-imported bare function may actually be defined in a submodule
# (e.g. dump defined in ast.base, ast/__init__ only re-exports), so redirect to the defining module SHA1.
reexport_src2: str = self.Gen.ImportsGen.lookup_reexport_call(bare_sha1, bare_func_name)
reexport_src2: char* = self.Gen.ImportsGen.lookup_reexport_call(bare_sha1, bare_func_name)
if reexport_src2 is None:
# Re-export table miss: resolve the module's .vpi from-import on demand.
reexport_src2 = self._lookup_reexport_from_vpi(pool, bare_sha1, bare_func_name)
if reexport_src2 is not None:
reexport_sha1_2: str = self._lookup_module_sha1(reexport_src2)
reexport_sha1_2: char* = self._lookup_module_sha1(reexport_src2)
if reexport_sha1_2 is not None: bare_sha1 = reexport_sha1_2
if self.Gen.CallGenBase.is_cexport_func(bare_sha1, bare_func_name) != 0: call_name = bare_func_name
else:
bare_mangled_len: sizet = string.strlen(bare_sha1) + string.strlen(bare_func_name) + 2
bare_mangled: str = pool.alloc(bare_mangled_len)
bare_mangled: char* = pool.alloc(bare_mangled_len)
if bare_mangled is not None:
viperlib.snprintf(bare_mangled, bare_mangled_len, "%s.%s", bare_sha1, bare_func_name)
call_name = bare_mangled
else:
err_msg3: str = pool.alloc(256)
err_msg3: char* = pool.alloc(256)
if err_msg3 is not None:
viperlib.snprintf(err_msg3, 256, "Bare-name call '%s' is undefined and not imported", func_name)
self.Gen.TypeGenErr.fatal_error(node, err_msg3)
self.Gen.TypeGenErr.fatal_error(node, "Bare-name call undefined and not imported")
else:
err_msg4: str = pool.alloc(256)
err_msg4: char* = pool.alloc(256)
if err_msg4 is not None:
viperlib.snprintf(err_msg4, 256, "Bare-name call '%s' is undefined and not imported", func_name)
self.Gen.TypeGenErr.fatal_error(node, err_msg4)
@@ -1622,12 +1622,12 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# returning functions and the default i32 truncates them; the authority is text.ll define, then the vpi annotation.
if self.Gen.CallGenLib._is_sha1_mangled_call(call_name) != 0:
if trans is None or trans._declare_only == 0:
cf_dot: str = string.strchr(call_name, '.')
cf_last: str = string.strrchr(call_name, '.')
cf_dot: char* = string.strchr(call_name, '.')
cf_last: char* = string.strrchr(call_name, '.')
if cf_dot is not None and cf_last is not None:
cf_sha1: str = stdlib.malloc(17)
cf_sha1: char* = stdlib.malloc(17)
cf_bare_len: sizet = string.strlen(call_name) - (sizet(uint64(cf_last) - uint64(call_name))) - 1
cf_bare: str = stdlib.malloc(cf_bare_len + 1)
cf_bare: char* = stdlib.malloc(cf_bare_len + 1)
if cf_sha1 is not None and cf_bare is not None:
string.strncpy(cf_sha1, call_name, 16)
cf_sha1[16] = '\0'
@@ -1645,8 +1645,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# text.ll both key methods by class, so the class segment must be passed
# through. Only when it names a registered class: `{sha}.submod.func` has
# the same shape but its symbol is the bare module function.
cf_cls: str = None
cf_mid: str = cf_dot + 1
cf_cls: char* = None
cf_mid: char* = cf_dot + 1
if uint64(cf_last) > uint64(cf_mid):
cf_mid_len: sizet = sizet(uint64(cf_last) - uint64(cf_mid))
cf_cls_buf: bytes = stdlib.malloc(cf_mid_len + 1)
@@ -1686,11 +1686,11 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# 而 _ensure_method_declare 又会按 can 建短 declare —— 旧逻辑会把这个错误一路藏到
# mlir-opt 的 "incorrect number of operands"。含关键字实参或可变参数时跳过。
if self.Gen.CallGenLib._is_sha1_mangled_call(call_name) != 0 and (cl.keywords is None or len(cl.keywords) == 0):
ar_sha: str = pool.alloc(17)
ar_sha: char* = pool.alloc(17)
if ar_sha is not None:
string.strncpy(ar_sha, call_name, 16)
ar_sha[16] = '\0'
ar_name: str = call_name + 17
ar_name: char* = call_name + 17
ar_n: int = VpiReader.LookupParamCountByName(pool, ar_sha, ar_name)
if ar_n >= 0 and int(can) != ar_n:
# ar_n comes from a scan of the .vpi *text*. What this call actually binds to