fix(gen): define 位宽/整数提升/移位/AugAssign 规则收敛,修 va_list 与 traceback 卡死

- define 位宽:cdefine_decl_bits 单一裁决(注解 → 右侧强转 → 值最小可存宽度),定义方与 VpiReader 两处跨模块注册共用;lookup_cdefine_bits 取最后一条非零记录;存储即截断;使用侧改按声明位宽;通用求值器加编译期除零/移位越界 fail-fast
- 消除对无符号强转加宽方向的依赖:define_min_width 改 (value>>32)==0,截断改左移+算术右移,修 TPC/TPV 判定不一致导致 0x80000000 误判 64 位
- 整数提升:算术/移位、比较、一元 +/-/~ 提升到 i32;位运算保持同宽不提升
- AugAssign:y 有效宽度须 <= x、禁 y 显式有符号而 x 无符号;移位计数静态范围检查
- 函数入口复位 va_list 状态与 willFree 失效槽表(FuncGenDef/FuncGenMixin/VarGenMixin 等)
- BuildPipeline:traceback 逐行扫描改 span 判定,去掉对全文 strlen 的 O(n^2)
This commit is contained in:
TermiNexus committed 2026-10-12 12:37:48 +08:00
1 parent be4db7b5d1
commit 4e46a25ab1
18 files changed
+1247 -843

No files matched your search

+53 -27
View File
@@ -291,7 +291,7 @@ def ensure_dir(path: str) -> int:
# Temporarily truncate at each separator and create every directory level.
# CreateDirectoryA returns 0 when the dir exists (failure); ignore it.
plen: int = int(path_len)
for i in range(plen):
for i in range(int(plen)):
ch: int = buf[i]
if ch == 47 or ch == 92: # '/' = 47, '\\' = 92
saved: char = char(ch)
@@ -639,7 +639,7 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
if tasks is None or task_count <= 0: return 0
# --phase2command mode: do not spawn threads; write all opt (ASan) + llc commands to the bat
if _phase2_cmd_path is not None:
for pi2 in range(task_count):
for pi2 in range(int(task_count)):
tsk_p: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(pi2) * LlcTask.__sizeof__())
if tsk_p is None: continue
if tsk_p.MlirOptCmd is not None: _phase2_write_cmd(tsk_p.MlirOptCmd)
@@ -658,24 +658,24 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
slot_size: sizet = LlcTask.__sizeof__()
ret: int = 0
# Batched execution: batch_start steps by max_threads (each batch spawn+join+close).
for batch_start in range(0, task_count, max_threads):
for batch_start in range(0, int(task_count), int(max_threads)):
batch_n: int = max_threads
if batch_start + batch_n > task_count: batch_n = task_count - batch_start
# spawn this batch
for j in range(batch_n):
for j in range(int(batch_n)):
tsk: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(batch_start + j) * slot_size)
th: vthreading.Thread* = vthreading.Thread(_llc_task_worker, t.CPtr(tsk))
th.start()
# Read the handle right after start() (the Thread object's stack slot may be
# reused; reading self.handle at join could see a cleared/overwritten value).
handles.append(t.CPtr(th.handle))
for j in range(batch_n):
for j in range(int(batch_n)):
h_j: t.CPtr = t.CPtr(handles.get(batch_start + j))
wr: uint32 = win32sync.WaitForSingleObject(h_j, 600000)
if wr != 0: VLogger.error("llc thread join timeout/failure (not finished within 600 seconds)", "PhaseB")
win32.win32base.CloseHandle(h_j)
# check this batch's results
for j in range(batch_n):
for j in range(int(batch_n)):
tsk2: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(batch_start + j) * slot_size)
if tsk2 is not None and tsk2.Result != 0:
# Failure: use FailStage to pinpoint the step and print that step's command.
@@ -942,7 +942,7 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: sizet, linker_cmd: str, link
dt2len: sizet = dj - ds2
if dt2len == dtlen:
deq: int = 1
for dk in range(dtlen):
for dk in range(int(dtlen)):
if dedup[ds2 + dk] != obj_paths[dstart + dk]:
deq = 0
break
@@ -1147,6 +1147,20 @@ def _tb_contains(s: str, sub: str) -> int:
return 1 if string.strstr(s, sub) is not None else 0
def _tb_contains_span(s: str, ls: sizet, le: sizet, sub: str) -> int:
"""Span-aware substring test: only s[ls, le) is searched.
A tail-wide strstr would both scan the whole remaining objdump text (hundreds of MB) and
match a substring belonging to a LATER line (e.g. ".text" found past a ".rdata" header)."""
if s is None or sub is None: return 0
sl: sizet = string.strlen(sub)
if sl == 0 or le < ls or le - ls < sl: return 0
i: sizet = ls
while i + sl <= le:
if string.strncmp(s + i, sub, sl) == 0: return 1
i += 1
return 0
def _tb_startswith(s: str, pfx: str) -> int:
if s is None or pfx is None: return 0
pl: sizet = string.strlen(pfx)
@@ -1154,6 +1168,18 @@ def _tb_startswith(s: str, pfx: str) -> int:
return 1 if string.strncmp(s, pfx, pl) == 0 else 0
def _tb_startswith_span(s: str, ls: sizet, le: sizet, pfx: str) -> int:
"""Span-aware prefix test: only s[ls, le) is considered, never the tail.
_tb_startswith on `s + ls` would run strlen over the WHOLE remaining text; llvm-objdump -d
output is hundreds of MB for a large exe, so a per-line strlen makes the whole scan O(n^2)
and the compilation appears to hang (it spins inside strlen for hours)."""
if s is None or pfx is None: return 0
if le < ls: return 0
pl: sizet = string.strlen(pfx)
if le - ls < pl: return 0
return 1 if string.strncmp(s + ls, pfx, pl) == 0 else 0
def _tb_ltrim(s: str) -> str:
if s is None: return None
i: sizet = 0
@@ -1165,7 +1191,7 @@ def _tb_len_trimmed(s: str) -> sizet:
if s is None: return 0
n: sizet = string.strlen(s)
while n > 0:
c: int = _tb_cc(s, n - 1)
c: int = int(_tb_cc(s, n - 1))
if c == 32 or c == 9 or c == 13: n -= 1
else: break
return n
@@ -1206,7 +1232,7 @@ def _tb_join(mb: memhub.MemBuddy*, base: str, name: str) -> str:
nl: sizet = string.strlen(name)
buf: str = mb.alloc(bl + nl + 2)
if buf is None: return name
last: int = _tb_cc(base, bl - 1)
last: int = int(_tb_cc(base, bl - 1))
if last == 47 or last == 92: viperlib.snprintf(buf, bl + nl + 2, "%s%s", base, name)
else: viperlib.snprintf(buf, bl + nl + 2, "%s/%s", base, name)
return buf
@@ -1304,13 +1330,13 @@ def _tb_quoted_after(mb: memhub.MemBuddy*, s: str, key: str) -> str:
def _tb_dir_lookup(dirs: bytes, count: int, idx: int) -> str:
for i in range(count):
for i in range(int(count)):
if int(_tb_rd_u32(dirs, sizet(i) * TB_DIR_SIZE)) == idx: return rawptr(_tb_rd_u64(dirs, sizet(i) * TB_DIR_SIZE + 8))
return None
def _tb_file_rec(files: bytes, count: int, idx: int) -> int:
for i in range(count):
for i in range(int(count)):
if int(_tb_rd_u32(files, sizet(i) * TB_FILE_SIZE)) == idx: return i
return -1
@@ -1403,7 +1429,7 @@ def _tb_func_name_for(fbuf: bytes, fcount: int, rva: uint64) -> str:
def _tb_intern(p: TbPool*, s: str) -> uint64:
"""Intern s into the pool (linear scan; keeps offset 0 as the empty sentinel)."""
if p is None or s is None: return 0
for k in range(p.OffCount):
for k in range(int(p.OffCount)):
o: uint64 = _tb_rd_u64(p.Offs, sizet(k) * 8)
if string.strcmp(p.Pool + sizet(o), s) == 0: return o
off: uint64 = uint64(p.PoolLen)
@@ -1450,7 +1476,7 @@ def _tb_read_pe_layout(mb: memhub.MemBuddy*, exe_path: str) -> TbPeLayout*:
min_rva: uint64 = 0xFFFFFFFF
found_text: int = 0
have_sec: int = 0
for si in range(nsec):
for si in range(int(nsec)):
base: sizet = sec_off + sizet(si) * 40
if uint64(base) + 40 > uint64(got): break
va: uint64 = uint64(_tb_rd_u32(head, base + 12))
@@ -1744,7 +1770,7 @@ def _tb_grp_get(grp: bytes, cap: int, caller: str, callee: str, do_insert: int)
if grp is None or cap <= 0 or caller is None or callee is None: return -1
mask: uint64 = uint64(cap - 1)
hv: uint64 = (_tb_strhash(caller) * 0x100000001B3 + _tb_strhash(callee)) & mask
for i in range(cap):
for i in range(int(cap)):
slot: int = int((hv + uint64(i)) & mask)
off: sizet = sizet(slot) * TB_GRP_SIZE
if uint64(_tb_rd_u32(grp, off + TB_GRP_OFF_USED)) == 0:
@@ -1823,7 +1849,7 @@ def _tb_read_whole(mb: memhub.MemBuddy*, path: str) -> str:
if buf is None:
f.close()
return None
# 容量全程用 sizet(与 sz 同宽):旧写法 ULONG(sz) + ULONG(2) 会在 sz >= 4GiB 时静默回绕
# 容量全程用 sizet(与 sz 同宽):旧写法 ULONG(sz) + ULONG(2) 会在 sz >= 4GiB 时静默回绕
got: LONG = f.read_all(buf, sizet(sz) + 2)
f.close()
if got < 0: return None
@@ -1844,7 +1870,7 @@ def _tb_parse_f_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
f3: sizet = p
while f3 < b and _tb_cc(s, f3) != 9: f3 += 1
if f3 >= b: return 0
defline: int = _tb_parse_dec(s + p)
defline: int = int(_tb_parse_dec(s + p))
p = f3 + 1
file: str = _tb_dup_span(mb, s + p, b - p)
if sym is None or file is None: return 0
@@ -1870,7 +1896,7 @@ def _tb_parse_c_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
e4: sizet = p
while e4 < b and _tb_cc(s, e4) != 9: e4 += 1
if e4 >= b: return 0
line: int = _tb_parse_dec(s + p)
line: int = int(_tb_parse_dec(s + p))
p = e4 + 1
file: str = _tb_dup_span(mb, s + p, b - p)
if caller is None or callee is None or file is None: return 0
@@ -1901,7 +1927,7 @@ def _tb_parse_vpcall_file(mb: memhub.MemBuddy*, path: str, cp: TbCallParse*) ->
def _tb_load_vpcalls(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
if _tb_vpcall_buf is None or _tb_vpcall_n <= 0: return 0
for k in range(_tb_vpcall_n):
for k in range(int(_tb_vpcall_n)):
slot: str* = (str*)(uint64(_tb_vpcall_buf) + sizet(k) * 8)
path: str = slot[0]
if path is not None:
@@ -1918,7 +1944,7 @@ def _tb_build_groups(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
VLogger.error("--traceback: failed to allocate call-site group table, aborting compilation", "TRACEBACK")
return 1
string.memset(cp.Grp, 0, sizet(cp.GrpCap) * TB_GRP_SIZE)
for i in range(cp.CrN):
for i in range(int(cp.CrN)):
off: sizet = sizet(i) * TB_CR_SIZE
caller: str = rawptr(_tb_rd_u64(cp.Cr, off + TB_CR_OFF_CALLER))
callee: str = rawptr(_tb_rd_u64(cp.Cr, off + TB_CR_OFF_CALLEE))
@@ -1959,7 +1985,7 @@ def _tb_emit_call_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo
def _tb_emit_entry_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo: uint64, addr: uint64, sym: str) -> int:
"""Emit a fallback function-entry row for an objdump label sym (file/line from the F record)."""
if sym is None or cp.Fr is None or cp.FrN <= 0 or addr < img_lo: return 0
for i in range(cp.FrN):
for i in range(int(cp.FrN)):
off: sizet = sizet(i) * TB_FR_SIZE
s: str = rawptr(_tb_rd_u64(cp.Fr, off + TB_FR_OFF_SYM))
if s is not None and string.strcmp(s, sym) == 0:
@@ -2016,10 +2042,10 @@ def _tb_scan_objdump(mb: memhub.MemBuddy*, dump: str, img_lo: uint64, d: TbDump*
if pos < dlen: pos += 1
if le > ls and _tb_cc(dump, le - 1) == 13: le -= 1
if le <= ls: continue
if _tb_startswith(dump + ls, "Disassembly of section") != 0:
if _tb_startswith_span(dump, ls, le, "Disassembly of section") != 0:
# section change: a pending callq at the previous section's end has no valid return address
prev_is_call = 0
if _tb_contains(dump + ls, ".text") != 0: in_text = 1
if _tb_contains_span(dump, ls, le, ".text") != 0: in_text = 1
else: in_text = 0
continue
if in_text == 0: continue
@@ -2188,7 +2214,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
bl: uint64 = uint64(_tb_rd_u32(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_LINE))
bp: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE)
bf: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FUNC)
j: int = i + 1
j: int = int(i + 1)
while j < kept and _tb_rd_u64(d.RowBuf, sizet(j) * TB_ROW_SIZE) == rv:
lj: uint64 = uint64(_tb_rd_u32(d.RowBuf, sizet(j) * TB_ROW_SIZE + TB_ROW_OFF_LINE))
if lj > bl:
@@ -2209,7 +2235,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
return 1
# ---- 3) subprograms: normalise + sort by low_rva ----
fcount: int = d.FuncCount
fcount: int = int(d.FuncCount)
fkept: int = 0
dropped_funcs: int = 0
fi: int = 0
@@ -2283,7 +2309,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
if ro is None:
VLogger.error("--traceback: failed to allocate record offset buffer, aborting compilation", "TRACEBACK")
return 1
for i in range(uniq):
for i in range(int(uniq)):
pth2: str = rawptr(_tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE))
foff: uint64 = _tb_intern(p, pth2)
noff: uint64 = 0
@@ -2318,7 +2344,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
return 1
# file_off must be >= 1: offset 0 is the empty-string sentinel and the runtime
# rejects a whole map whose record file_off is 0.
for ci in range(uniq):
for ci in range(int(uniq)):
fo: uint64 = uint64(_tb_rd_u32(ro, sizet(ci) * 8))
if fo == 0:
VLogger.error("--traceback self-check failed: a record has file_off==0 (empty path; runtime would reject that sidecar file), aborting compilation", "TRACEBACK")
@@ -2338,7 +2364,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
_tb_wr_u32(out, 8, uint64(uniq))
_tb_wr_u32(out, 12, uint64(p.OffCount))
_tb_wr_u32(out, 16, uint64(p.PoolLen))
for ri2 in range(uniq):
for ri2 in range(int(uniq)):
roff2: sizet = 20 + sizet(ri2) * 20
rv3: uint64 = _tb_rd_u64(d.RowBuf, sizet(ri2) * TB_ROW_SIZE)
ln3: uint64 = uint64(_tb_rd_u32(d.RowBuf, sizet(ri2) * TB_ROW_SIZE + TB_ROW_OFF_LINE))
File diff suppressed because it is too large. Load diff
+426 -202
View File
@@ -7,7 +7,7 @@ import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import win32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
@@ -40,8 +40,25 @@ novirtual class AssignGenMixin:
return 0
# extract_cdefine_int_value - extract an integer constant from AnnAssign.value
# ---- 统一编译期常量表达式求值(值 + 位宽) ----
# 所有 define/常量折叠入口都走这里,避免各写一套(此前 extract_cdefine_int_value /
# _eval_const_int_expr / _cif_eval_node 三套互相独立,导致位宽信息在 define 上丢失)。
def extract_cdefine_int_value(self, val_node: ast.AST*) -> int64:
"""Extract an integer constant from an AnnAssign value node."""
"""仅取值(丢弃位宽)的薄封装;需要位宽请用 extract_cdefine_int_value_bits。"""
b_legacy: int = 0
return self.extract_cdefine_int_value_bits(val_node, None, &b_legacy)
def extract_cdefine_int_value_bits(self, val_node: ast.AST*, pool: memhub.MemBuddy*, bits_out: int*) -> int64:
"""编译期常量表达式求值:返回值,并把**位宽**写入 bits_out[0](0=未知)。
位宽规则(供 define 物化用):
- 显式强转(Call 的 callee 是整型/typedef,如 WORD / int / t.CUInt16T)→ 该位宽;
- Name 引用其它 define → 沿用其位宽;
- BinOp:两侧位宽**相等** → 保持该位宽(**不提升**);不等 → 取较宽;任一未知 → 未知;
- 字面量/一元 → 未知(物化时按值推断)。
pool 为 None 时无法解析 typedef(仅旧调用方走此路),强转按"取实参值、位宽未知"处理。
"""
if bits_out is not None: bits_out[0] = 0
if val_node is None: return 0
k: int = val_node.kind()
@@ -58,19 +75,33 @@ novirtual class AssignGenMixin:
# Case 2: BinOp - e.g. FOREGROUND_RED | FOREGROUND_GREEN, (1 << N)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(val_node)
left_val: int64 = self.extract_cdefine_int_value(bop.left)
right_val: int64 = self.extract_cdefine_int_value(bop.right)
lb: int = 0
rb: int = 0
left_val: int64 = self.extract_cdefine_int_value_bits(bop.left, pool, &lb)
right_val: int64 = self.extract_cdefine_int_value_bits(bop.right, pool, &rb)
if bits_out is not None:
if lb != 0 and rb != 0: bits_out[0] = lb if lb > rb else rb
elif lb != 0: bits_out[0] = lb
elif rb != 0: bits_out[0] = rb
if bop.op == ast.OpKind.BitOr: return left_val | right_val
if bop.op == ast.OpKind.BitAnd: return left_val & right_val
if bop.op == ast.OpKind.BitXor: return left_val ^ right_val
if bop.op == ast.OpKind.LShift: return left_val << right_val
if bop.op == ast.OpKind.RShift: return left_val >> right_val
# 移位:求值器内部按 64 位算,计数为负或 >= 64 时结果无意义(宿主侧是 poison/未定义),
# 与 require_shift_count_in_range 同一口径直接 fail-fast,不产出垃圾常量。
if bop.op == ast.OpKind.LShift or bop.op == ast.OpKind.RShift:
if right_val < 0 or right_val >= 64:
self.Gen.TypeGenErr.fatal_error(val_node, f"constant expression shift count out of range: the shift count evaluates to {right_val} at compile time, " "but the evaluator works on 64-bit integers (valid range 0..63); the result would be undefined. " "Fix: use a count in 0..63, or restructure the expression.", "ValueError")
if bop.op == ast.OpKind.LShift: return left_val << right_val
return left_val >> right_val
if bop.op == ast.OpKind.Add: return left_val + right_val
if bop.op == ast.OpKind.Sub: return left_val - right_val
if bop.op == ast.OpKind.Mult: return left_val * right_val
if bop.op == ast.OpKind.Div: return left_val // right_val
if bop.op == ast.OpKind.FloorDiv: return left_val // right_val
if bop.op == ast.OpKind.Mod: return left_val % right_val
# 编译期除零:绝不把除法交给宿主(会直接把编译器打挂),fail-fast 给出可读诊断。
if bop.op == ast.OpKind.Div or bop.op == ast.OpKind.FloorDiv or bop.op == ast.OpKind.Mod:
if right_val == 0:
self.Gen.TypeGenErr.fatal_error(val_node, "constant expression division by zero: the right operand of '/', '//' or '%' evaluates to 0 at compile time.", "ValueError")
if bop.op == ast.OpKind.Mod: return left_val % right_val
return left_val // right_val
# Unknown BinOp operator: fail fast
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
@@ -82,18 +113,29 @@ novirtual class AssignGenMixin:
if string.strcmp(nm.id, "True") == 0: return 1
if string.strcmp(nm.id, "False") == 0: return 0
looked_up: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return looked_up
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
# 沿用被引用 define 的位宽(宽度随宏传播,不再丢)
if bits_out is not None: bits_out[0] = self.Gen.TypeGenCDef.lookup_cdefine_bits(nm.id)
return looked_up
# Unregistered reference: fail fast
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
# Case 4: Call - e.g. t.CUnsignedLong(-11)
# Case 4: Call - 整型/typedef 强转(如 WORD(0x0080) / t.CUnsignedLong(-11)):
# 取实参值,并按**目标类型的位宽**规范化(这赋予常量"已知宽度")。
if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(val_node)
t_bits: int = 0
if pool is not None: t_bits = self._annot_int_bits(pool, cl.func)
# Bind the property to a list[...]* local before calling len() (else GEP base breaks)
cl_args_list: list[ast.AST*]* = cl.args
if cl_args_list is not None and len(cl_args_list) > 0:
first_arg: ast.AST* = cl_args_list.get(0)
arg_val: int64 = self.extract_cdefine_int_value(first_arg)
ib: int = 0
arg_val: int64 = self.extract_cdefine_int_value_bits(first_arg, pool, &ib)
if t_bits != 0:
if bits_out is not None: bits_out[0] = t_bits
return self._normalize_cdefine_bits(arg_val, t_bits)
if bits_out is not None: bits_out[0] = ib
return arg_val
# Call with no args: fail fast
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
@@ -101,7 +143,9 @@ novirtual class AssignGenMixin:
# Case 5: UnaryOp - e.g. -11
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp* = ast.UnaryOp*(val_node)
operand_val: int64 = self.extract_cdefine_int_value(uop.operand)
ob: int = 0
operand_val: int64 = self.extract_cdefine_int_value_bits(uop.operand, pool, &ob)
if bits_out is not None: bits_out[0] = ob
if uop.op == ast.OpKind.USub: return -operand_val
if uop.op == ast.OpKind.UAdd: return operand_val
if uop.op == ast.OpKind.Invert: return ~operand_val
@@ -113,6 +157,37 @@ novirtual class AssignGenMixin:
# After fatal_error add an explicit return so the block is properly terminated
return 0
def _normalize_cdefine_bits(self, value: int64, bits: int) -> int64:
"""把值按位宽**截断**后再重解释为该位宽的有符号表示(与 ExprGenCdef._cdefine_bits_value 同口径)。
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(如 WORD(0x80000000) → 0),
不是直接把原值搬过来、也不是只对"刚好越界一点"的值做重解释。
实现用左移后算术右移,而不是 `value & 0xFFFFFFFF` 这类大掩码字面量:
掩码字面量(如 4294967295)超出 int32 范围,其加宽方向在 TPC/TPV 上可能判成 sext,
会得到 0xFFFFFFFFFFFFFFFF 而不是 0xFFFFFFFF,导致截断失效。
"""
if bits == 8: return (value << 56) >> 56
if bits == 16: return (value << 48) >> 48
if bits == 32: return (value << 32) >> 32
return value
def cdefine_decl_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.AST*) -> int:
"""define 声明的位宽裁决(定义方与跨模块使用方**共用同一套**,避免两边位宽不一致)。
① 有声明注解 T(define X: T = ...)→ 按注解 T 的位宽;
② 无注解但右侧表达式自身已定宽(显式强转,如 WORD(0x0080))→ 用该位宽;
③ 都没有 → 按**值的最小可存宽度**(0x80000000 属 2^31..2^32-1 取 32;小值取 8/16,
窄宽度参与算术时由"算术整数提升"先升到 i32,不会溢出)。
返回 0 表示该 define 不是整型(调用方不应据此注册整型常量)。
"""
annot_bits: int = self._define_annot_bits(pool, annot)
if annot_bits != 0: return annot_bits
if value_node is None: return 0
rhs_bits: int = 0
val: int64 = self.extract_cdefine_int_value_bits(value_node, pool, &rhs_bits)
if rhs_bits != 0: return rhs_bits
return self.define_min_width(val)
# define / t.CDefine integer constant storable and width checks
# Auto-derived => 64-bit pattern; explicit type T => T's width range.
def define_min_width(self, value: int64) -> int:
@@ -126,10 +201,89 @@ novirtual class AssignGenMixin:
if value <= 255: return 8
if value <= 65535: return 16
if value <= 2147483647: return 32
# 2^31..2^32-1: fits an unsigned 32-bit bit pattern
if uint64(uint32(value)) == value: return 32
# 2^31..2^32-1:无符号 32 位位型即可容纳 → 32 位。
# 用 (value >> 32) == 0 判断,**不要**写成 uint64(uint32(value)) == value:uint32(...) 之后的
# 加宽方向(zext/sext)由 should_zext_widen 按节点无符号性决定,TPC 与 TPV 自举的判定可能不一致;
# 一旦判成 sext,uint64(0x80000000) 会变成 0xFFFFFFFF80000000 ≠ value → 误判 64 位 →
# `GENERIC_READ | GENERIC_WRITE` 被放大到 i64 → 赋给 32 位目标报隐式窄化。
if (value >> 32) == 0: return 32
return 64
# _is_static_int_expr - 该节点是否属于通用求值器一定能折叠的整型表达式(只判定,不求值)
def _binop_is_foldable(self, op: int) -> int:
"""1=通用求值器支持的整型二元运算(与 extract_cdefine_int_value_bits 的 BinOp 分支一致)"""
if op == ast.OpKind.BitOr or op == ast.OpKind.BitAnd or op == ast.OpKind.BitXor: return 1
if op == ast.OpKind.LShift or op == ast.OpKind.RShift: return 1
if op == ast.OpKind.Add or op == ast.OpKind.Sub or op == ast.OpKind.Mult: return 1
if op == ast.OpKind.Div or op == ast.OpKind.FloorDiv or op == ast.OpKind.Mod: return 1
return 0
def _is_static_int_expr(self, node: ast.AST*) -> int:
"""1=通用求值器(extract_cdefine_int_value_bits)一定能求出整数;0=不确定,**不得调用求值器**。
判定范围与求值器严格对齐(否则求值器会 fail-fast,变成误报):
整型/布尔字面量、True/False、**已注册的** CDefine 名、其 +/-/~、求值器支持的整型二元运算、
以及带实参的强转调用。变量、普通函数调用、求值器不支持的算子一律判 0 —— 即"求不出来就不检查"。
"""
if node is None: return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_BOOL: return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is None: return 0
if string.strcmp(nm.id, "True") == 0 or string.strcmp(nm.id, "False") == 0: return 1
self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0: return 0
return 1
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if self._binop_is_foldable(bop.op) == 0: return 0
if self._is_static_int_expr(bop.left) == 0: return 0
return self._is_static_int_expr(bop.right)
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp* = ast.UnaryOp*(node)
if uop.op != ast.OpKind.USub and uop.op != ast.OpKind.UAdd and uop.op != ast.OpKind.Invert: return 0
return self._is_static_int_expr(uop.operand)
if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(node)
cl_args_st: list[ast.AST*]* = cl.args
if cl_args_st is None or len(cl_args_st) == 0: return 0
return self._is_static_int_expr(cl_args_st.get(0))
return 0
# _static_int_of - 静态求值整数节点:复用 define 的**通用求值器**(单一实现,避免两套规则)
def _static_int_of(self, node: ast.AST*, out: int64*) -> int:
"""1=静态可确定(值写入 out[0]),0=无法确定(调用方不做检查,也不报错)。
先由 _is_static_int_expr 判定"求值器一定能求出来",再走同一个求值器
(extract_cdefine_int_value_bits,位宽丢弃),因此支持任意编译期可折叠表达式
(`(SHIFT / 2) + 1`、`WORD(3)`、`A | B` 等),且求不出来时不会触发求值器的 fail-fast。
"""
if node is None or out is None: return 0
if self._is_static_int_expr(node) == 0: return 0
b_na: int = 0
out[0] = self.extract_cdefine_int_value_bits(node, None, &b_na)
return 1
# require_shift_count_in_range - 移位计数的静态范围检查(x << y / x >> y 与 <<= / >>= 共用)
def require_shift_count_in_range(self, pool: memhub.MemBuddy*, trans: HT.Translator*, node: ast.AST*, count_node: ast.AST*, lhs_bits: int) -> None:
"""fail-fast:移位计数可静态确定且为负、或 >= 左操作数位宽时直接报错。
移位计数**豁免**"宽度 <= 目标宽度"与符号一致性规则(计数不是写回目标的值),
改为在编译期把能确定的无意义移位拦下来:x << y / x >> y 与 x <<= y / x >>= y 同一套。
计数无法静态确定时不检查(留给运行期)。lhs_bits 应传**提升后的有效宽度**(C 语义下
u8 << 8 合法,因为 u8 先提升到 i32)。"""
if count_node is None or lhs_bits <= 0: return
v: int64 = 0
if self._static_int_of(count_node, &v) == 0: return
if v < 0:
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] shift count is negative ({v}): shifting by a negative amount is undefined. " "Fix: use a non-negative count.", "ValueError")
if uint64(v) >= uint64(lhs_bits):
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] shift count out of range: the count {v} is >= the left operand width {lhs_bits} bits; " f"the result would be 0 or undefined (not a value the compiler may guess). " f"Fix: use a count < {lhs_bits}, or widen the left operand.", "ValueError")
def _is_cdefine_marker_node(self, node: ast.AST*) -> int:
"""Return 1 if the node is a t.CDefine marker node."""
if node is None: return 0
@@ -164,23 +318,49 @@ novirtual class AssignGenMixin:
n2: ast.Name* = ast.Name*(annot)
nm = n2.id
if nm is None: return 0
return self._int_bits_of_type_name(pool, nm, 0)
def _int_bits_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
"""类型名 → 整数位宽(0=未知/非整数)。depth 防 typedef 环。
覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_t_type)、
以及**用户 typedef**(如 stdint 的 `WORD: t.CTypedef = uint16` —— 经 lookup_ctypedef
取其底层类型文本再递归定宽)。此前用户 typedef 一律返回 0,导致
`define X: WORD = ...` 的声明宽度不参与校验/物化。
"""
if nm is None or depth > 10: return 0
# 去掉模块/类限定前缀:`t.CUInt16T` → `CUInt16T`;`stdint.uint16` → `uint16`
dot: str = string.strrchr(nm, 46)
if dot is not None and dot[1] != '\0':
return self._int_bits_of_type_name(pool, dot + 1, depth + 1)
if string.strcmp(nm, "int") == 0: return 32
if string.strcmp(nm, "bool") == 0: return 8
# Fixed-width lowercase aliases from stdint/builtins (not t module names)
if string.strcmp(nm, "int8") == 0 or string.strcmp(nm, "uint8") == 0: return 8
if string.strcmp(nm, "int16") == 0 or string.strcmp(nm, "uint16") == 0: return 16
if string.strcmp(nm, "int32") == 0 or string.strcmp(nm, "uint32") == 0: return 32
if string.strcmp(nm, "int64") == 0 or string.strcmp(nm, "uint64") == 0: return 64
if string.strcmp(nm, "sizet") == 0 or string.strcmp(nm, "size_t") == 0: return 64
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm)
if ty is None: return 0
match ty:
case mlirlite.LLVMType.Int(bits):
if bits == 1: return 8
if bits == 8 or bits == 16 or bits == 32 or bits == 64: return bits
return 0
case _:
return 0
if ty is not None:
match ty:
case mlirlite.LLVMType.Int(bits):
if bits == 1: return 8
if bits == 8 or bits == 16 or bits == 32 or bits == 64: return bits
return 0
case _:
return 0
# 用户/include typedef(如 stdint.vp 的 `WORD: t.CTypedef = uint16`):
# 表内值可能是模块限定 ref(stdint.uint16),先用 _expand_typedef_ref 展开;
# 本地表未命中再走 builtins 源扫描兜底。
underlying: str = self.Gen.TypeGenCType.lookup_ctypedef(nm)
if underlying is None:
underlying = self.Gen.TypeGenResolve._lookup_builtins_typedef(pool, nm)
if underlying is not None:
expanded: str = self.Gen.TypeGenResolve._expand_typedef_ref(pool, underlying)
if expanded is not None: underlying = expanded
if underlying is None: return 0
if string.strcmp(underlying, nm) == 0: return 0
return self._int_bits_of_type_name(pool, underlying, depth + 1)
def _define_annot_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
"""Return the bit width of a CDefine declaration's explicit type T."""
@@ -216,7 +396,7 @@ novirtual class AssignGenMixin:
if line_len < name_len + 1: continue
ok: int = 1
ki: sizet
for ki in range(name_len):
for ki in range(int(name_len)):
if buf[line_start + ki] != name[ki]:
ok = 0
break
@@ -236,7 +416,7 @@ novirtual class AssignGenMixin:
txt: str = stdlib.malloc(eq - vstart + 1)
if txt is None: continue
vi: sizet
for vi in range(eq - vstart): txt[vi] = buf[vstart + vi]
for vi in range(int(eq - vstart)): txt[vi] = buf[vstart + vi]
txt[eq - vstart] = 0
result = txt
break
@@ -303,8 +483,13 @@ novirtual class AssignGenMixin:
d += 1
return 0
def require_cdefine_storable(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, annot: ast.AST*) -> None:
"""Fail fast if a CDefine value is not storable in its declared type."""
def require_cdefine_storable(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, annot: ast.AST*, rhs_bits: int = 0) -> None:
"""Fail fast if a CDefine value is not storable in its declared type.
rhs_bits: 右侧表达式自身的位宽(0=未定宽)。右侧带显式强转(如 WORD(...))且不宽于声明注解时,
截断是**显式**的(左侧只看到截断后的数据),此时不再按"存不下"报错——这正是
"注解存不下且没有强转截断的需要报错"里被豁免的那一半。
"""
text: str = self._cdefine_int_literal_text(name)
bits: int = self._define_annot_bits(pool, annot)
wmin: int = self.define_min_width(value)
@@ -312,6 +497,9 @@ novirtual class AssignGenMixin:
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] define constant value not storable: the initial value {text} of constant '{name}' exceeds 64 bits " f"(the widest integer type in this language is 64 bits; the storable range under both signed and unsigned interpretations is " f"-9223372036854775808..18446744073709551615; no wider integer type exists => widening is required). " f"Silent truncation/downgrade is forbidden; compilation aborted. Available integer types: int8/uint8, " f"int16/uint16, int32/uint32, int64/uint64 " f"(or int8/uint8/.../uint64, char, sizet). " f"Fix: move the value into the 64-bit range, or introduce a larger integer type for this number (widen).", "TypeError")
return
if bits == 0: return
# Explicit truncation exemption: the RHS narrowed itself to rhs_bits (a cast such as WORD(...)),
# so the value the declaration stores is already the truncated one.
if rhs_bits != 0 and rhs_bits <= bits: return
# Build the 32-bit bounds from overflow-safe literals (see define_min_width)
m31: int64 = 2147483647
lo: int64 = -m31 - 1
@@ -327,7 +515,11 @@ novirtual class AssignGenMixin:
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] define constant value not storable: the initial value {value} of constant '{name}' is not storable. " f"Minimal storable width W_min: {wmin} bits; declared type width {bits} bits, " f"storable range of this declaration: {lo}..{hi}. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the explicit type to a wider type (such as int32 / int64 / uint64), " f"or correct the literal.", "TypeError")
def require_cdefine_use_width(self, pool: memhub.MemBuddy*, trans: HT.Translator*, target_annot: ast.AST*, value_node: ast.AST*) -> None:
"""Fail fast when a define constant is wider than its use-site type."""
"""Fail fast when a define constant is wider than its use-site type.
比较基准是 define 的**声明位宽**(注解 → 右侧强转 → 值的最小宽度,见 cdefine_decl_bits),
不是值本身的最小宽度:define 声明成 64 位,写进 uint8 位置就是隐式窄化,必须报错。
"""
if value_node is None or target_annot is None: return
if value_node.kind() != ast.ASTKind.Name: return
rn: ast.Name* = ast.Name*(value_node)
@@ -338,9 +530,11 @@ novirtual class AssignGenMixin:
if self.Gen.TypeGenCDef.is_cdefine_found() == 0: return
bits: int = self._annot_int_bits(pool, target_annot)
if bits == 0: return
wmin: int = self.define_min_width(cdef_val)
if bits >= wmin: return
self.Gen.TypeGenErr.fatal_error(value_node, f"[TPV] define constant use-site width insufficient: the actual value {cdef_val} of constant '{rn.id}' " f"needs at least {wmin} bits (minimal storable width W_min), " f"but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {wmin} bits (such as int{wmin}T / CUInt{wmin}T), " f"or use a smaller constant value.", "TypeError")
# 声明位宽优先;未登记位宽(0)时才回退到推断位宽(值的最小可存宽度)
dw: int = self.Gen.TypeGenCDef.lookup_cdefine_bits(rn.id)
if dw == 0: dw = self.define_min_width(cdef_val)
if bits >= dw: return
self.Gen.TypeGenErr.fatal_error(value_node, f"[TPV] define constant use-site width insufficient: constant '{rn.id}' is {dw} bits wide " f"(declared width; actual value {cdef_val}), but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {dw} bits (such as int{dw}T / CUInt{dw}T), " f"or narrow the constant at its define with an explicit cast.", "TypeError")
# _register_cdefine_from_value - register a CDefine constant (float or int)
def _register_cdefine_from_value(self, pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None) -> None:
@@ -381,10 +575,15 @@ novirtual class AssignGenMixin:
# CDefine declaration without a value node: fail fast
self.Gen.TypeGenErr.fatal_error(None, f"CDefine value extraction failed: '{name}' is missing an initial value", "TypeError")
return
val_fp: int64 = self.extract_cdefine_int_value(val_node)
# 位宽裁决:与跨模块使用方共用 cdefine_decl_bits(注解 → 右侧显式强转 → 值的最小可存宽度)。
# 有注解而存不下时:右侧带显式强转截断则不报错(左侧只看到截断值),否则由
# require_cdefine_storable 报"存不下"。
rhs_bits: int = 0
val_fp: int64 = self.extract_cdefine_int_value_bits(val_node, pool, &rhs_bits)
vb: int = self.cdefine_decl_bits(pool, annot, val_node)
# Check storability before registering (source text >64-bit or explicit T too narrow => fail)
self.require_cdefine_storable(pool, val_node, name, val_fp, annot)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, val_fp)
self.require_cdefine_storable(pool, val_node, name, val_fp, annot, rhs_bits)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, val_fp, vb)
# Recover a CDefine float value by re-reading its source line with atof
@@ -435,7 +634,7 @@ novirtual class AssignGenMixin:
if ns + name_len + 1 > line_start + line_len: continue
match_ok: int = 1
ki: sizet
for ki in range(name_len):
for ki in range(int(name_len)):
if buf[ns + ki] != name[ki]:
match_ok = 0
break
@@ -482,7 +681,7 @@ novirtual class AssignGenMixin:
stdlib.free(buf)
return 0
vi: sizet
for vi in range(eq - val_start): val_txt[vi] = buf[val_start + vi]
for vi in range(int(eq - val_start)): val_txt[vi] = buf[val_start + vi]
val_txt[eq - val_start] = 0
stdlib.free(buf)
# Use atof_out (an out param) to avoid the cross-module double return bug
@@ -589,105 +788,105 @@ novirtual class AssignGenMixin:
# ============================================================
# ── A2(§14)`@life(ret(...))`:调用点解析被调者的返回值生命期契约 ──
# _RecvClassName - 从方法的接收者表达式取它静态类型指向的类名(去 sha1 前缀);判不定返回 None。
# 只认「名字变量」接收者:变量表里查不到(如 `m.f()` 里的模块名 `m`)返回 None —— 调用方据此
# 把"模块限定函数调用"与"方法调用"分开(二者在 AST 里都是 Attribute)。
def _RecvClassName(self, trans: HT.Translator*, recv: ast.AST*) -> str:
"""接收者静态类型 → 类名(无 sha1 前缀);判不定返回 None"""
if trans is None or recv is None: return None
if recv.kind() != ast.ASTKind.Name: return None
rn: ast.Name* = ast.Name*(recv)
if rn.id is None: return None
v: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, rn.id)
if v is None: return None
ty: mlirlite.LLVMType* = v.Ty # 变量槽类型 = <变量类型>*
if ty is None or self.Gen.ExprGenBase.is_ptr_type(ty) == 0: return None
p1: mlirlite.LLVMType* = ty.Pointee # 变量类型本身
if p1 is None: return None
# 类变量通常是指针(`S*`);值语义的类变量(`s: S`,罕见)直接取本类型。
elem: mlirlite.LLVMType* = p1.Pointee
if self.Gen.ExprGenBase.is_ptr_type(p1) == 0: elem = p1
sname: str = mlirlite.get_struct_name(elem)
if sname is None: return None
pl: int = GenApi.sha1_prefix_len(sname)
if pl > 0: sname = sname + pl
return sname
# _CallArgNodeForParam - `@life(ret(borrows=<形参 pi>))` 在调用点对应的实参节点。
# **方法**调用的形参表含 self(pi=0 即接收者),故 is_method=1 时偏移一格;
# **模块限定函数**(`m.f(...)`:callee 也是 Attribute,但不是方法)形参 0 就是第一个实参,
# 不能偏移 —— 否则 pi=0 会被解析成"模块名"这个非实参节点(现场:本函数把
# `retdep2.IdentBorrow(l)` 的形参 0 取成了 `retdep2` → life=STATIC → 跨模块 ret 契约静默失效)。
def _CallArgNodeForParam(self, cl: ast.Call*, pi: int, is_method: int) -> ast.AST*:
"""第 pi 号形参对应的实参表达式;越界/判不定返回 None"""
if cl is None or pi < 0: return None
cargs: list[ast.AST*]* = cl.args
if is_method != 0:
if pi == 0:
at: ast.Attribute* = ast.Attribute*(cl.func)
return at.value
if cargs is None or sizet(pi - 1) >= len(cargs): return None
return cargs.get(sizet(pi - 1))
if cargs is None or sizet(pi) >= len(cargs): return None
return cargs.get(sizet(pi))
# _CallIsMethod - 该调用是否"接收者是对象变量"的方法调用(决定形参表是否含 self)。
def _CallIsMethod(self, trans: HT.Translator*, cl: ast.Call*) -> int:
"""1 = 方法调用(形参表含 self);0 = 模块限定函数 / 裸函数"""
if cl is None or cl.func is None: return 0
if cl.func.kind() != ast.ASTKind.Attribute: return 0
at: ast.Attribute* = ast.Attribute*(cl.func)
if self._RecvClassName(trans, at.value) is None: return 0
return 1
# _CallRetKind - 解析被调者的 `@life(ret(...))` 契约(本文件表 → 跨模块 .vpi);-1 = 无声明/判不定。
def _CallRetKind(self, trans: HT.Translator*, cl: ast.Call*) -> int:
"""被调者 ret_kind 编码(GenApi.RET_KIND_*);-1 = 无声明/判不定"""
if cl is None or cl.func is None: return -1
fnm: str = self.Gen.ExprGenBase.get_func_name(cl.func)
if fnm is None: return -1
pool: memhub.MemBuddy* = None
if trans is not None: pool = trans.Pool
if pool is None: return -1
# 方法调用:接收者是本帧变量 → 取它静态类型指向的类名;接收者是模块名 → 不当方法。
if cl.func.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(cl.func)
cls: str = self._RecvClassName(trans, at.value)
if cls is not None:
rk_m: int = GenApi.get_ret_kind_method(pool, cls, fnm)
if rk_m != 0: return rk_m
sha_m: str = self.Gen.StructGen.get_struct_sha1(cls)
if sha_m is not None:
rk_m = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(sha_m, cls, fnm)
if rk_m != 0: return rk_m
return -1
# 模块级函数(裸名,或模块限定 `m.f()`):本文件表优先,再跨模块 .vpi 回退。
rk: int = GenApi.get_ret_kind(fnm)
if rk != 0: return rk
mod_sha: str = None
if cl.func.kind() == ast.ASTKind.Attribute:
at2: ast.Attribute* = ast.Attribute*(cl.func)
# 只认裸模块名接收者(`m.f()`);`self.h.f()` 之类链式接收者判不定 → 不猜。
if at2.value is None or at2.value.kind() != ast.ASTKind.Name: return -1
mn2: ast.Name* = ast.Name*(at2.value)
mod_p: str = None
if mn2.id is not None: mod_p = mn2.id
if mod_p is not None:
fi2: str = None
if trans is not None: fi2 = trans._from_imports
mod_sha = self.Gen.StructGen._resolve_module_sha1(mod_p, fi2)
# 接收者既不是模块(解析不出 sha1)也不是变量(否则上面早按方法走了)→ 判不定。
if mod_sha is None: return -1
if mod_sha is None and trans is not None and trans._from_imports is not None:
mod_sha = self.Gen.CallGenBase._lookup_mod_sha1(pool, trans._from_imports, fnm, 0)
if mod_sha is not None:
rk = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(mod_sha, None, fnm)
if rk != 0: return rk
return -1
# life_of_expr - 求表达式当前值的生命期标签。
# target_ty 参与判定:裸本帧数组名在**指针**目标位置 = 数组退让(交出本帧栈地址),
# 在同型聚合目标位置 = 按值拷贝(LIFE_VALUE)。
# _RecvClassName - 从方法的接收者表达式取它静态类型指向的类名(去 sha1 前缀);判不定返回 None。
# 只认「名字变量」接收者:变量表里查不到(如 `m.f()` 里的模块名 `m`)返回 None —— 调用方据此
# 把"模块限定函数调用"与"方法调用"分开(二者在 AST 里都是 Attribute)。
def _RecvClassName(self, trans: HT.Translator*, recv: ast.AST*) -> str:
"""接收者静态类型 → 类名(无 sha1 前缀);判不定返回 None"""
if trans is None or recv is None: return None
if recv.kind() != ast.ASTKind.Name: return None
rn: ast.Name* = ast.Name*(recv)
if rn.id is None: return None
v: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, rn.id)
if v is None: return None
ty: mlirlite.LLVMType* = v.Ty # 变量槽类型 = <变量类型>*
if ty is None or self.Gen.ExprGenBase.is_ptr_type(ty) == 0: return None
p1: mlirlite.LLVMType* = ty.Pointee # 变量类型本身
if p1 is None: return None
# 类变量通常是指针(`S*`);值语义的类变量(`s: S`,罕见)直接取本类型。
elem: mlirlite.LLVMType* = p1.Pointee
if self.Gen.ExprGenBase.is_ptr_type(p1) == 0: elem = p1
sname: str = mlirlite.get_struct_name(elem)
if sname is None: return None
pl: int = GenApi.sha1_prefix_len(sname)
if pl > 0: sname = sname + pl
return sname
# _CallArgNodeForParam - `@life(ret(borrows=<形参 pi>))` 在调用点对应的实参节点。
# **方法**调用的形参表含 self(pi=0 即接收者),故 is_method=1 时偏移一格;
# **模块限定函数**(`m.f(...)`:callee 也是 Attribute,但不是方法)形参 0 就是第一个实参,
# 不能偏移 —— 否则 pi=0 会被解析成"模块名"这个非实参节点(现场:本函数把
# `retdep2.IdentBorrow(l)` 的形参 0 取成了 `retdep2` → life=STATIC → 跨模块 ret 契约静默失效)。
def _CallArgNodeForParam(self, cl: ast.Call*, pi: int, is_method: int) -> ast.AST*:
"""第 pi 号形参对应的实参表达式;越界/判不定返回 None"""
if cl is None or pi < 0: return None
cargs: list[ast.AST*]* = cl.args
if is_method != 0:
if pi == 0:
at: ast.Attribute* = ast.Attribute*(cl.func)
return at.value
if cargs is None or sizet(pi - 1) >= len(cargs): return None
return cargs.get(sizet(pi - 1))
if cargs is None or sizet(pi) >= len(cargs): return None
return cargs.get(sizet(pi))
# _CallIsMethod - 该调用是否"接收者是对象变量"的方法调用(决定形参表是否含 self)。
def _CallIsMethod(self, trans: HT.Translator*, cl: ast.Call*) -> int:
"""1 = 方法调用(形参表含 self);0 = 模块限定函数 / 裸函数"""
if cl is None or cl.func is None: return 0
if cl.func.kind() != ast.ASTKind.Attribute: return 0
at: ast.Attribute* = ast.Attribute*(cl.func)
if self._RecvClassName(trans, at.value) is None: return 0
return 1
# _CallRetKind - 解析被调者的 `@life(ret(...))` 契约(本文件表 → 跨模块 .vpi);-1 = 无声明/判不定。
def _CallRetKind(self, trans: HT.Translator*, cl: ast.Call*) -> int:
"""被调者 ret_kind 编码(GenApi.RET_KIND_*);-1 = 无声明/判不定"""
if cl is None or cl.func is None: return -1
fnm: str = self.Gen.ExprGenBase.get_func_name(cl.func)
if fnm is None: return -1
pool: memhub.MemBuddy* = None
if trans is not None: pool = trans.Pool
if pool is None: return -1
# 方法调用:接收者是本帧变量 → 取它静态类型指向的类名;接收者是模块名 → 不当方法。
if cl.func.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(cl.func)
cls: str = self._RecvClassName(trans, at.value)
if cls is not None:
rk_m: int = GenApi.get_ret_kind_method(pool, cls, fnm)
if rk_m != 0: return rk_m
sha_m: str = self.Gen.StructGen.get_struct_sha1(cls)
if sha_m is not None:
rk_m = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(sha_m, cls, fnm)
if rk_m != 0: return rk_m
return -1
# 模块级函数(裸名,或模块限定 `m.f()`):本文件表优先,再跨模块 .vpi 回退。
rk: int = GenApi.get_ret_kind(fnm)
if rk != 0: return rk
mod_sha: str = None
if cl.func.kind() == ast.ASTKind.Attribute:
at2: ast.Attribute* = ast.Attribute*(cl.func)
# 只认裸模块名接收者(`m.f()`);`self.h.f()` 之类链式接收者判不定 → 不猜。
if at2.value is None or at2.value.kind() != ast.ASTKind.Name: return -1
mn2: ast.Name* = ast.Name*(at2.value)
mod_p: str = None
if mn2.id is not None: mod_p = mn2.id
if mod_p is not None:
fi2: str = None
if trans is not None: fi2 = trans._from_imports
mod_sha = self.Gen.StructGen._resolve_module_sha1(mod_p, fi2)
# 接收者既不是模块(解析不出 sha1)也不是变量(否则上面早按方法走了)→ 判不定。
if mod_sha is None: return -1
if mod_sha is None and trans is not None and trans._from_imports is not None:
mod_sha = self.Gen.CallGenBase._lookup_mod_sha1(pool, trans._from_imports, fnm, 0)
if mod_sha is not None:
rk = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(mod_sha, None, fnm)
if rk != 0: return rk
return -1
# life_of_expr - 求表达式当前值的生命期标签。
# target_ty 参与判定:裸本帧数组名在**指针**目标位置 = 数组退让(交出本帧栈地址),
# 在同型聚合目标位置 = 按值拷贝(LIFE_VALUE)。
def life_of_expr(self, trans: HT.Translator*, node: ast.AST*, target_ty: mlirlite.LLVMType*) -> int:
"""表达式的 LifeTag;无法判定时 LIFE_UNKNOWN(保守放行)"""
if node is None or trans is None: return GenTypes.LIFE_VALUE
@@ -862,64 +1061,64 @@ novirtual class AssignGenMixin:
if n3.id is not None:
nm_txt = n3.id
nm_pre = "&"
# 泄漏点(可选,只有"确实报错"才去算):被调者那边**造成逃逸的写入点**在同一源文件里,
# 追加一帧 "File lifetime leak ..."。"先过 life 判定再采集"是刻意的:escape_leak_line
# 会重扫被调者函数体并动 EscConstEnv/EscIndCount,不能对"其实不报错"的实参白白跑一遍。
leak_f: str = None
leak_l: int = 0
if leak_func is not None and leak_param >= 0:
leak_l = self.Gen.StructGen.escape_leak_line(leak_func, leak_param)
if leak_l > 0: leak_f = self.Gen.TypeGenResolve.get_current_file()
self.Gen.TypeGenErr.fatal_error(node, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local; using it at {where} hands out a dangling pointer once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).", "LifetimeError", 0, 0, leak_f, leak_l)
return 0
# check_return_frame_decl - **定义端** `return <本帧存储>` 的**声明感知**检查(A2 / §14.3 第 6 条)。
# A2 移除了"一律拦"的旧判据:定义端分不清"交出本帧"与"借出调用方的存储"(后者是
# `return <借用形参>`,一律拦会误报)。但也不能一律放 —— 无声明的函数返回本帧存储会让调用点
# 永远只拿到"未知"(悬垂指针一路静默)。判据收敛到被调者自己的 `@life(ret(...))`:
# · `ret(frame)` → 放行(去向由调用点负责);
# · `ret(borrows=<该形参>)` → 放行(借出的正是那个形参);
# · 无声明 / `ret(heap|static|borrows=none)` → `LifetimeError`(提示补声明 / 改声明)。
# 拿不到当前函数定义(非三条函数体翻译路径)时**不判**:宁可少报,也不误报。
def check_return_frame_decl(self, trans: HT.Translator*, value: ast.AST*) -> int:
"""返回值是本帧存储且当前函数的 ret 声明不允许 → LifetimeError;0=放行"""
if trans is None or value is None: return 0
fd: ast.AST* = self.Gen.StructGen.NllCurFunc
if fd is None: return 0
rv_ty: mlirlite.LLVMType* = None
if trans._cur_func is not None: rv_ty = mlirlite.function_get_ret_ty(trans._cur_func)
# target_ty 必须传返回类型:裸本帧数组名在**指针**返回类型下是数组退让(交出栈地址),
# 在同型聚合返回类型下是按值拷贝(LIFE_VALUE,不在此列)。
if self.life_of_expr(trans, value, rv_ty) != GenTypes.LIFE_FRAME: return 0
rk: int = GenApi.get_ret_kind_by_func(fd)
if rk == GenApi.RET_KIND_FRAME: return 0
is_borrow_kind: int = 0
pi: int = -1
if rk >= GenApi.RET_KIND_BORROW_BASE:
is_borrow_kind = 1
pi = rk - GenApi.RET_KIND_BORROW_BASE
# 只有"返回的正是被声明的那个形参"才算借出;返回别的本帧存储仍是悬垂
if self.Gen.StructGen._ParamIndex(ast.FunctionDef*(fd), value) == pi: return 0
# 报文里给出名字(&x / x),便于定位
nm_txt: str = "expression"
nm_pre: str = ""
if value.kind() == ast.ASTKind.Name:
n2: ast.Name* = ast.Name*(value)
if n2.id is not None: nm_txt = n2.id
elif value.kind() == ast.ASTKind.UnaryOp:
u2: ast.UnaryOp* = ast.UnaryOp*(value)
if u2.operand is not None and u2.operand.kind() == ast.ASTKind.Name:
n3: ast.Name* = ast.Name*(u2.operand)
if n3.id is not None:
nm_txt = n3.id
nm_pre = "&"
if rk == 0:
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local and this function has no `@life(ret(...))` declaration, so callers cannot know the result's lifetime. Declare `@life(ret(frame))` (the result may point into this frame; callers must keep it in-frame) or `@life(ret(borrows=<param>))` (the result borrows a parameter), or return a value copy / a heap allocation (`new heap hlife ...`).", "LifetimeError")
return 0
if is_borrow_kind != 0:
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function declares `@life(ret(borrows=<param>))` (parameter #{pi}), which only permits returning that parameter. Return that parameter, or declare `@life(ret(frame))`.", "LifetimeError")
return 0
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function is declared as non-frame (`@life(ret(heap))` / `ret(static)` / `ret(borrows=none)`); the declaration contradicts the returned value. Fix the declaration, or return a non-frame value.", "LifetimeError")
# 泄漏点(可选,只有"确实报错"才去算):被调者那边**造成逃逸的写入点**在同一源文件里,
# 追加一帧 "File lifetime leak ..."。"先过 life 判定再采集"是刻意的:escape_leak_line
# 会重扫被调者函数体并动 EscConstEnv/EscIndCount,不能对"其实不报错"的实参白白跑一遍。
leak_f: str = None
leak_l: int = 0
if leak_func is not None and leak_param >= 0:
leak_l = self.Gen.StructGen.escape_leak_line(leak_func, leak_param)
if leak_l > 0: leak_f = self.Gen.TypeGenResolve.get_current_file()
self.Gen.TypeGenErr.fatal_error(node, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local; using it at {where} hands out a dangling pointer once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).", "LifetimeError", 0, 0, leak_f, leak_l)
return 0
# check_return_frame_decl - **定义端** `return <本帧存储>` 的**声明感知**检查(A2 / §14.3 第 6 条)。
# A2 移除了"一律拦"的旧判据:定义端分不清"交出本帧"与"借出调用方的存储"(后者是
# `return <借用形参>`,一律拦会误报)。但也不能一律放 —— 无声明的函数返回本帧存储会让调用点
# 永远只拿到"未知"(悬垂指针一路静默)。判据收敛到被调者自己的 `@life(ret(...))`:
# · `ret(frame)` → 放行(去向由调用点负责);
# · `ret(borrows=<该形参>)` → 放行(借出的正是那个形参);
# · 无声明 / `ret(heap|static|borrows=none)` → `LifetimeError`(提示补声明 / 改声明)。
# 拿不到当前函数定义(非三条函数体翻译路径)时**不判**:宁可少报,也不误报。
def check_return_frame_decl(self, trans: HT.Translator*, value: ast.AST*) -> int:
"""返回值是本帧存储且当前函数的 ret 声明不允许 → LifetimeError;0=放行"""
if trans is None or value is None: return 0
fd: ast.AST* = self.Gen.StructGen.NllCurFunc
if fd is None: return 0
rv_ty: mlirlite.LLVMType* = None
if trans._cur_func is not None: rv_ty = mlirlite.function_get_ret_ty(trans._cur_func)
# target_ty 必须传返回类型:裸本帧数组名在**指针**返回类型下是数组退让(交出栈地址),
# 在同型聚合返回类型下是按值拷贝(LIFE_VALUE,不在此列)。
if self.life_of_expr(trans, value, rv_ty) != GenTypes.LIFE_FRAME: return 0
rk: int = GenApi.get_ret_kind_by_func(fd)
if rk == GenApi.RET_KIND_FRAME: return 0
is_borrow_kind: int = 0
pi: int = -1
if rk >= GenApi.RET_KIND_BORROW_BASE:
is_borrow_kind = 1
pi = rk - GenApi.RET_KIND_BORROW_BASE
# 只有"返回的正是被声明的那个形参"才算借出;返回别的本帧存储仍是悬垂
if self.Gen.StructGen._ParamIndex(ast.FunctionDef*(fd), value) == pi: return 0
# 报文里给出名字(&x / x),便于定位
nm_txt: str = "expression"
nm_pre: str = ""
if value.kind() == ast.ASTKind.Name:
n2: ast.Name* = ast.Name*(value)
if n2.id is not None: nm_txt = n2.id
elif value.kind() == ast.ASTKind.UnaryOp:
u2: ast.UnaryOp* = ast.UnaryOp*(value)
if u2.operand is not None and u2.operand.kind() == ast.ASTKind.Name:
n3: ast.Name* = ast.Name*(u2.operand)
if n3.id is not None:
nm_txt = n3.id
nm_pre = "&"
if rk == 0:
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local and this function has no `@life(ret(...))` declaration, so callers cannot know the result's lifetime. Declare `@life(ret(frame))` (the result may point into this frame; callers must keep it in-frame) or `@life(ret(borrows=<param>))` (the result borrows a parameter), or return a value copy / a heap allocation (`new heap hlife ...`).", "LifetimeError")
return 0
if is_borrow_kind != 0:
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function declares `@life(ret(borrows=<param>))` (parameter #{pi}), which only permits returning that parameter. Return that parameter, or declare `@life(ret(frame))`.", "LifetimeError")
return 0
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function is declared as non-frame (`@life(ret(heap))` / `ret(static)` / `ret(borrows=none)`); the declaration contradicts the returned value. Fix the declaration, or return a non-frame value.", "LifetimeError")
return 0
# _SlifeDropOwned - 该槽已被登记为 slife 绑定时交还所有权:释放它当前拥有的对象、影子清 0。
@@ -1148,7 +1347,7 @@ novirtual class AssignGenMixin:
n_s: sizet = string.strlen(sv_s)
if sizet(arr_count) < n_s:
self.Gen.TypeGenErr.fatal_error(list_node, f"string literal length {n_s} exceeds the declared char array length {arr_count}; out-of-bounds writes are forbidden", "OverflowError")
for bi_s in range(n_s):
for bi_s in range(int(n_s)):
bidx_s: mlirlite.Value* = mlirlite.const_int32(pool, bi_s)
bp_s: mlirlite.Value* = mlirlite.build_gep_array(builder, arr_ty, elem_ty, array_alloca, bidx_s)
if bp_s is None: break
@@ -1162,7 +1361,7 @@ novirtual class AssignGenMixin:
self.Gen.TypeGenErr.fatal_error(list_node, f"array initializer element count {elts_count} exceeds the declared array length {arr_count}; out-of-bounds writes are forbidden", "OverflowError")
# Emit GEP + store for each element
for i in range(elts_count):
for i in range(int(elts_count)):
elem_node: ast.AST* = elts.get(i)
# Explicit null check (repo convention: is None); an empty slot means an element count mismatch.
# When the slot is null, fail fast with class/field/index context.
@@ -1247,10 +1446,35 @@ novirtual class AssignGenMixin:
# Double-pointer arithmetic: delegate the check to ExprGenOps.fail_if_double_ptr_binop
self.Gen.ExprGenOps.fail_if_double_ptr_binop(pool, op, lhs, rhs, node, 1)
# x op= y 的操作数约束(fail-fast,先于运算与提升):
# ① y 的有效宽度必须 <= x 的宽度。变量按其 LLVM 类型宽度;常量按其值的**最小可存宽度**
# (所以 `u8 += 1` 合法、`u8 += 300` 报错)。y 更宽 = 隐式收窄,必须先显式收窄再写。
# ② 不允许 y 显式有符号而 x 无符号:负值经符号扩展会变成巨大的无符号值,属静默语义改变。
# 移位(<<= / >>=)**豁免** ①②:计数不是写回目标的值,改用静态范围检查(见下方)。
y_node_aa: ast.AST* = None
if node is not None and node.kind() == ast.ASTKind.AugAssign: y_node_aa = ast.AugAssign*(node).value
if lhs_bits != 0 and rhs_bits != 0 and op != ast.OpKind.LShift and op != ast.OpKind.RShift:
y_bits_eff: int = rhs_bits
if y_node_aa is not None and y_node_aa.kind() == ast.ASTKind.Constant:
cn_aa: ast.Constant* = ast.Constant*(y_node_aa)
if cn_aa.const_kind == ast.CONST_INT: y_bits_eff = self.define_min_width(cn_aa.int_val)
if y_bits_eff > lhs_bits:
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] augmented assignment operand too wide: the right-hand side needs {y_bits_eff} bits, " f"but the target provides only {lhs_bits} bits; an implicit narrowing is forbidden. " f"Fix: narrow the right-hand side explicitly (e.g. uint{lhs_bits}(...) / int{lhs_bits}(...)) before the operation.", "CastError")
x_uns_aa: int = 0
if lhs_node is not None: x_uns_aa = self.Gen.ExprGenOps._node_type_unsigned(pool, lhs_node, trans)
y_sgn_aa: int = 0
if y_node_aa is not None: y_sgn_aa = self.Gen.ExprGenOps._node_type_signed(pool, y_node_aa, trans)
if x_uns_aa != 0 and y_sgn_aa != 0:
self.Gen.TypeGenErr.fatal_error(node, "[TPV] augmented assignment operand signedness conflict: the right-hand side is explicitly signed " "while the target is unsigned; sign extension would turn negative values into huge unsigned values. " "Fix: use an unsigned right-hand side (e.g. uint32(...)), or make the target signed.", "CastError")
# Integer op: type promotion
if lhs_bits > rhs_bits and rhs_bits > 0: rhs = self.Gen.ExprGenBase.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
elif rhs_bits > lhs_bits and lhs_bits > 0: lhs = self.Gen.ExprGenBase.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
# 移位:计数本身豁免宽度/符号规则,改按**提升后**的左操作数位宽做静态范围检查
if op == ast.OpKind.LShift or op == ast.OpKind.RShift:
self.require_shift_count_in_range(pool, trans, node, y_node_aa, self.Gen.ExprGenBase.get_llvm_type_bits(lhs.Ty))
# Unsigned operands use udiv/urem; signed use sdiv/srem
aug_unsigned: int = 0
if lhs_node is not None:
@@ -1612,7 +1836,7 @@ novirtual class AssignGenMixin:
elem_ty_arr: mlirlite.LLVMType* = resolved_ty.Pointee
lst_arr: ast.List* = ast.List*(value)
cnt_arr: int = 0
if lst_arr.elts is not None: cnt_arr = len(lst_arr.elts)
if lst_arr.elts is not None: cnt_arr = int(len(lst_arr.elts))
# Special case: a single string literal means length strlen+1 (include the NUL)
if elem_ty_arr is not None and cnt_arr == 1 and lst_arr.elts is not None:
e0_arr: ast.AST* = lst_arr.elts.get(0)
@@ -2296,7 +2520,7 @@ novirtual class AssignGenMixin:
mod: mlirlite.LLVMModule* = trans.Module
# const target: fail fast before assignment (Name/Attribute/Subscript)
for ti_c in range(len(targets)):
for ti_c in range(int(len(targets))):
tgt_c: ast.AST* = targets.get(ti_c)
if tgt_c is None: continue
if tgt_c.kind() == ast.ASTKind.Name:
@@ -2326,7 +2550,7 @@ novirtual class AssignGenMixin:
self.const_assign_fail(tgt_c, "element assignment", at_c2.attr, self.Gen.ExprGenAttr.attr_field_decl_src(pool, trans, sub_c.value))
# f-string escape: results are function-local temporaries; writing them out is dangling
for ti_f in range(len(targets)):
for ti_f in range(int(len(targets))):
tgt_f: ast.AST* = targets.get(ti_f)
if tgt_f is None: continue
tk_f: int = tgt_f.kind()
@@ -2415,7 +2639,7 @@ novirtual class AssignGenMixin:
new_vars: int = 0
tn: sizet = len(targets)
for ti in range(tn):
for ti in range(int(tn)):
target: ast.AST* = targets.get(ti)
if target is None: continue
@@ -2436,7 +2660,7 @@ novirtual class AssignGenMixin:
fchain: mlirlite.ParamNode* = mlirlite.get_struct_fields(rhs_val.Ty)
fcur: mlirlite.ParamNode* = fchain
fti: sizet
for fti in range(tgt_n):
for fti in range(int(tgt_n)):
tgt_elt: ast.AST* = tgt_elts.get(fti)
if tgt_elt is None or fcur is None: break
fty: mlirlite.LLVMType* = mlirlite.paramnode_get_ty(fcur)
@@ -2875,7 +3099,7 @@ novirtual class AssignGenMixin:
if seg is None: return None
start: sizet = 0
i: sizet
for i in range(alen):
for i in range(int(alen)):
if annot[i] == '|':
seglen: sizet = i - start
# Segment [start, i): trim spaces/quotes then look up the registry
+51 -49
View File
@@ -87,7 +87,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
cnt: int = 0
if ags.args is None: return 0
bn: sizet = len(ags.args)
for i in range(bn):
for i in range(int(bn)):
a0: ast.AST* = ags.args.get(i)
if a0 is None: continue
aname: str = None
@@ -130,7 +130,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if stmts is None or trans._coro_uses_sleep != 0: return
bn: sizet = len(stmts)
i: sizet = 0
for i in range(bn): self._scan_sleep_stmt(trans, stmts.get(i))
for i in range(int(bn)): self._scan_sleep_stmt(trans, stmts.get(i))
def _scan_sleep_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
if stmt is None or trans._coro_uses_sleep != 0: return
@@ -159,7 +159,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if trn.handlers is not None:
nh: sizet = len(trn.handlers)
hh: sizet = 0
for hh in range(nh):
for hh in range(int(nh)):
h0: ast.AST* = trn.handlers.get(hh)
if h0 is not None: self._scan_sleep_stmts(trans, h0.children)
self._scan_sleep_stmts(trans, trn.orelse)
@@ -202,7 +202,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cl.args is not None:
nb: sizet = len(cl.args)
j: sizet = 0
for j in range(nb): self._scan_sleep_expr(trans, cl.args.get(j))
for j in range(int(nb)): self._scan_sleep_expr(trans, cl.args.get(j))
return
if k == ast.ASTKind.BinOp:
b: ast.BinOp* = ast.BinOp*(node)
@@ -219,14 +219,14 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cm.comparators is not None:
nb2: sizet = len(cm.comparators)
j2: sizet = 0
for j2 in range(nb2): self._scan_sleep_expr(trans, cm.comparators.get(j2))
for j2 in range(int(nb2)): self._scan_sleep_expr(trans, cm.comparators.get(j2))
return
if k == ast.ASTKind.BoolOp:
bo: ast.BoolOp* = ast.BoolOp*(node)
if bo.values is not None:
nb3: sizet = len(bo.values)
j3: sizet = 0
for j3 in range(nb3): self._scan_sleep_expr(trans, bo.values.get(j3))
for j3 in range(int(nb3)): self._scan_sleep_expr(trans, bo.values.get(j3))
return
if k == ast.ASTKind.IfExp:
ie: ast.IfExp* = ast.IfExp*(node)
@@ -253,7 +253,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if stmts is None: return
bn: sizet = len(stmts)
i: sizet = 0
for i in range(bn): self._scan_stmt(trans, stmts.get(i))
for i in range(int(bn)): self._scan_stmt(trans, stmts.get(i))
def _scan_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
if stmt is None: return
@@ -300,7 +300,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if tr2.handlers is not None:
nh2: sizet = len(tr2.handlers)
hh2: sizet = 0
for hh2 in range(nh2):
for hh2 in range(int(nh2)):
h2: ast.AST* = tr2.handlers.get(hh2)
if h2 is not None: self._scan_stmts(trans, h2.children)
self._scan_stmts(trans, tr2.orelse)
@@ -368,7 +368,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cl.args is not None:
nb: sizet = len(cl.args)
j: sizet = 0
for j in range(nb): self._scan_expr(trans, cl.args.get(j))
for j in range(int(nb)): self._scan_expr(trans, cl.args.get(j))
return
if k == ast.ASTKind.UnaryOp:
u: ast.UnaryOp* = ast.UnaryOp*(node)
@@ -380,14 +380,14 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cm.comparators is not None:
nb2: sizet = len(cm.comparators)
j2: sizet = 0
for j2 in range(nb2): self._scan_expr(trans, cm.comparators.get(j2))
for j2 in range(int(nb2)): self._scan_expr(trans, cm.comparators.get(j2))
return
if k == ast.ASTKind.BoolOp:
bo: ast.BoolOp* = ast.BoolOp*(node)
if bo.values is not None:
nb3: sizet = len(bo.values)
j3: sizet = 0
for j3 in range(nb3): self._scan_expr(trans, bo.values.get(j3))
for j3 in range(int(nb3)): self._scan_expr(trans, bo.values.get(j3))
return
if k == ast.ASTKind.IfExp:
ie: ast.IfExp* = ast.IfExp*(node)
@@ -415,12 +415,12 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if stmt.children is not None:
bn4: sizet = len(stmt.children)
i4: sizet = 0
for i4 in range(bn4):
for i4 in range(int(bn4)):
if self._stmt_deep_await(stmt.children.get(i4)) != 0: return 1
if fn3.orelse is not None:
bn5: sizet = len(fn3.orelse)
i5: sizet = 0
for i5 in range(bn5):
for i5 in range(int(bn5)):
if self._stmt_deep_await(fn3.orelse.get(i5)) != 0: return 1
return 0
@@ -445,7 +445,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if tr3.handlers is not None:
nh3: sizet = len(tr3.handlers)
hh3: sizet = 0
for hh3 in range(nh3):
for hh3 in range(int(nh3)):
h3: ast.AST* = tr3.handlers.get(hh3)
if h3 is not None and self._stmts_deep_await(h3.children) != 0: return 1
if self._stmts_deep_await(tr3.orelse) != 0: return 1
@@ -456,7 +456,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if stmts is None: return 0
bn6: sizet = len(stmts)
i6: sizet = 0
for i6 in range(bn6):
for i6 in range(int(bn6)):
if self._stmt_deep_await(stmts.get(i6)) != 0: return 1
return 0
@@ -474,7 +474,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
nb7: sizet = len(cl4.args)
if nb7 != 1 and nb7 != 2: return 0
j7: sizet = 0
for j7 in range(nb7):
for j7 in range(int(nb7)):
if self._find_expr_await(cl4.args.get(j7)) is not None: return 0
return 1
@@ -492,7 +492,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if stmts is None: return
bn: sizet = len(stmts)
i: sizet = 0
for i in range(bn): self._collect_locals_stmt(trans, stmts.get(i))
for i in range(int(bn)): self._collect_locals_stmt(trans, stmts.get(i))
def _collect_locals_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
if stmt is None: return
@@ -536,7 +536,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if tr.handlers is not None:
nh: sizet = len(tr.handlers)
hh: sizet = 0
for hh in range(nh):
for hh in range(int(nh)):
h0: ast.AST* = tr.handlers.get(hh)
if h0 is None: continue
hd: ast.ExceptHandler* = ast.ExceptHandler*(h0)
@@ -569,7 +569,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
head: mlirlite.ParamNode* = None
prev: mlirlite.ParamNode* = None
fi: int = 0
for fi in range(total):
for fi in range(int(total)):
fty: mlirlite.LLVMType* = i32_ty
if fi == 4: fty = i8_ptr_ty
elif fi >= trans._coro_extra_base:
@@ -613,7 +613,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
# so names are built with snprintf ("%%arg" -> "%arg", matching resume's "%frame").
pi: int = 0
argn_buf: str = pool.alloc(16)
for pi in range(nparams):
for pi in range(int(nparams)):
if argn_buf is not None: viperlib.snprintf(argn_buf, 16, "%%arg%d", pi)
mlirlite.add_param(pool, func, i32_ty, argn_buf)
# register in the function table (bare name create_<name>)
@@ -686,7 +686,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if slot is not None: mlirlite.build_store(builder, zero, slot)
# store params into frame slots (index 5+i)
pj: int = 0
for pj in range(nparams):
for pj in range(int(nparams)):
pslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_ref, 5 + pj)
if pslot is not None:
arnb: str = pool.alloc(16)
@@ -724,7 +724,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
"""Return the wake-slot index for an async name, or -1 if none"""
if trans is None or name is None or trans._coro_wake_names is None: return -1
i: int = 0
for i in range(trans._coro_wake_count):
for i in range(int(trans._coro_wake_count)):
addr: uint64 = uint64(trans._coro_wake_names) + i * 8
slot: str* = (str*)(addr)
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return trans._coro_wake_idxs[i]
@@ -769,7 +769,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
# GEP param slots -> register in the symbol table (index 5+i)
pj: int = 0
for pj in range(nparams):
for pj in range(int(nparams)):
pname: str = self._read_param_name(trans, pj)
if pname is None: continue
pslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, 5 + pj)
@@ -777,7 +777,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
# GEP local slots -> register in the symbol table
li: int = 0
for li in range(nlocals):
for li in range(int(nlocals)):
lname: str = self._read_local_name(trans, li)
if lname is None: continue
slot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, trans._coro_local_base + li)
@@ -786,7 +786,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
# M3-M7: register extra slots (wake i64 / __f_idx/__f_len/__await i32).
# Name reverse lookup: wake=uses_sleep && ei==0; for=_coro_for_slots; else __await_M.
ei: int = 0
for ei in range(nextra):
for ei in range(int(nextra)):
ety: int = 1
if trans._coro_extra_types is not None: ety = trans._coro_extra_types[ei]
ety_llvm: mlirlite.LLVMType* = i32_ty
@@ -802,7 +802,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
is_for: int = 0
is_len: int = 0
kf: int = 0
for kf in range(trans._coro_scan_for_count):
for kf in range(int(trans._coro_scan_for_count)):
if trans._coro_for_slots[kf] == ei:
is_for = 1
break
@@ -818,7 +818,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
# __await_M: reverse lookup the position array
found: int = 0
km: int = 0
for km in range(nextra):
for km in range(int(nextra)):
if trans._coro_await_slots[km] == ei:
found = 1
break
@@ -861,7 +861,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
sw_count: int = 0
bb_name: str = pool.alloc(32)
si: int = 0
for si in range(nseg):
for si in range(int(nseg)):
if bb_name is not None: viperlib.snprintf(bb_name, 32, "state%d", si)
sbb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_name)
if sbb is None: continue
@@ -881,6 +881,8 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
# Set trans._cur_builder/_cur_func here; later position_at_end reuse the same builder object.
trans._cur_func = func
trans._cur_builder = builder
# P2 willFree 失效表:协程体是独立帧 → 入口同样复位
self.Gen.VarGen.freed_clear()
seg_bb: mlirlite.BasicBlock* = mlirlite.BasicBlock*(state_bbs[0])
mlirlite.position_at_end(builder, seg_bb)
# M3 硬约束锚点:登记"正在翻译其 body 的协程函数",CallGenCtor._translate_new_expr 据此对
@@ -911,7 +913,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if stmts is None: return
bn: sizet = len(stmts)
i: sizet = 0
for i in range(bn):
for i in range(int(bn)):
stmt: ast.AST* = stmts.get(i)
if stmt is None: continue
# current block is terminated (return/suspend) -> stop this segment
@@ -1123,7 +1125,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cl.args is not None:
nb: sizet = len(cl.args)
j: sizet = 0
for j in range(nb):
for j in range(int(nb)):
r3: ast.Await* = self._find_expr_await(cl.args.get(j))
if r3 is not None: return r3
return None
@@ -1137,7 +1139,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cm.comparators is not None:
nb2: sizet = len(cm.comparators)
j2: sizet = 0
for j2 in range(nb2):
for j2 in range(int(nb2)):
r5: ast.Await* = self._find_expr_await(cm.comparators.get(j2))
if r5 is not None: return r5
return None
@@ -1146,7 +1148,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if bo.values is not None:
nb3: sizet = len(bo.values)
j3: sizet = 0
for j3 in range(nb3):
for j3 in range(int(nb3)):
r6: ast.Await* = self._find_expr_await(bo.values.get(j3))
if r6 is not None: return r6
return None
@@ -1218,7 +1220,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cl.args is not None:
nb: sizet = len(cl.args)
j: sizet = 0
for j in range(nb):
for j in range(int(nb)):
r4: int = self._replace_expr_await(pool, cl.args.get(j), temp_name)
if r4 == 1: return 1
if r4 == 2:
@@ -1243,7 +1245,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cm.comparators is not None:
nb2: sizet = len(cm.comparators)
j2: sizet = 0
for j2 in range(nb2):
for j2 in range(int(nb2)):
r7: int = self._replace_expr_await(pool, cm.comparators.get(j2), temp_name)
if r7 == 1: return 1
if r7 == 2:
@@ -1255,7 +1257,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if bo.values is not None:
nb3: sizet = len(bo.values)
j3: sizet = 0
for j3 in range(nb3):
for j3 in range(int(nb3)):
r8: int = self._replace_expr_await(pool, bo.values.get(j3), temp_name)
if r8 == 1: return 1
if r8 == 2:
@@ -1430,7 +1432,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
tr: ast.Try* = ast.Try*(stmt)
if tr is None: return
nhandlers: int = 0
nhandlers: sizet = 0
if tr.handlers is not None: nhandlers = len(tr.handlers)
if nhandlers == 0:
err_t: str = pool.alloc(256)
@@ -1459,7 +1461,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if check_bbs is None or handler_bbs is None: return
check_bbs[0] = sizet(except_bb)
hi: int = 0
for hi in range(nhandlers):
for hi in range(int(nhandlers)):
# [FIX 2026-08-31] build block names with snprintf: "%" is not string formatting here,
# so the old "%d" form produced i32 garbage and triggered a false int->ptr cast error.
chk_name: str = pool.alloc(64)
@@ -1472,7 +1474,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
unmatched_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.try_reraise")
codes_buf: int* = pool.alloc(256)
# per handler: the check chain
for hi in range(nhandlers):
for hi in range(int(nhandlers)):
h_node: ast.AST* = tr.handlers.get(hi)
check_bb: mlirlite.BasicBlock* = (mlirlite.BasicBlock*)(check_bbs[hi])
mlirlite.position_at_end(builder, check_bb)
@@ -1493,7 +1495,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
else:
match_cond: mlirlite.Value* = None
ci: int = 0
for ci in range(ncodes):
for ci in range(int(ncodes)):
eq_v: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, exc_val, mlirlite.const_int32(pool, codes_buf[ci]))
if match_cond is None: match_cond = eq_v
else: match_cond = mlirlite.build_or(builder, match_cond, eq_v)
@@ -1505,7 +1507,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
mlirlite.build_store(builder, done_u, trans._coro_done_ptr)
mlirlite.build_br(builder, trans._coro_exit_bb)
# ---- each except handler ----
for hi in range(nhandlers):
for hi in range(int(nhandlers)):
h_node2: ast.AST* = tr.handlers.get(hi)
handler_bb2: mlirlite.BasicBlock* = (mlirlite.BasicBlock*)(handler_bbs[hi])
mlirlite.position_at_end(builder, handler_bb2)
@@ -1595,7 +1597,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
cl: ast.Call* = ast.Call*(fn.iter)
start_val: mlirlite.Value* = None
end_val: mlirlite.Value* = None
nargs: int = len(cl.args)
nargs: int = int(len(cl.args))
if nargs == 1:
start_val = mlirlite.const_int32(pool, 0)
end_val = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
@@ -1710,7 +1712,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cl is None or trans is None or cl.args is None: return mlirlite.const_int32(pool, 0)
nb: sizet = len(cl.args)
j: sizet = 0
for j in range(nb):
for j in range(int(nb)):
arg0: ast.AST* = cl.args.get(j)
async_name: str = self._var_to_async(trans, arg0)
if async_name is None: continue
@@ -1778,7 +1780,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if cl_a.args is not None:
nb: sizet = len(cl_a.args)
j: sizet = 0
for j in range(nb):
for j in range(int(nb)):
av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl_a.args.get(j), None, 0, trans)
if av is None: av = mlirlite.const_int32(pool, 0)
av.Next = None
@@ -1910,7 +1912,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if trans is None or name is None or trans._async_funcs is None: return 0
cnt: int = trans._async_func_count
i: int = 0
for i in range(cnt):
for i in range(int(cnt)):
addr: uint64 = uint64(trans._async_funcs) + i * 8
slot: str* = (str*)(addr)
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return 1
@@ -1922,7 +1924,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if trans._coro_var_async is None: return 1
cnt: int = trans._coro_var_async_count
i: int = 0
for i in range(cnt):
for i in range(int(cnt)):
addr: uint64 = uint64(trans._coro_var_async) + i * 16
pair: str* = (str*)(addr)
if pair[0] is not None and string.strcmp(pair[0], var_name) == 0: return 0
@@ -1939,7 +1941,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if trans is None or name is None or trans._async_funcs is None: return 1
cnt: int = trans._async_func_count
i: int = 0
for i in range(cnt):
for i in range(int(cnt)):
addr: uint64 = uint64(trans._async_funcs) + i * 8
slot: str* = (str*)(addr)
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return 0
@@ -1956,7 +1958,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if trans._coro_frames is None: return 1
cnt: int = trans._coro_frame_count
i: int = 0
for i in range(cnt):
for i in range(int(cnt)):
addr: uint64 = uint64(trans._coro_frames) + i * 16
pair: str* = (str*)(addr)
if pair[0] is not None and string.strcmp(pair[0], name) == 0:
@@ -1975,7 +1977,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if trans is None or name is None or trans._coro_frames is None: return None
cnt: int = trans._coro_frame_count
i: int = 0
for i in range(cnt):
for i in range(int(cnt)):
addr: uint64 = uint64(trans._coro_frames) + i * 16
pair: str* = (str*)(addr)
if pair[0] is not None and string.strcmp(pair[0], name) == 0:
@@ -1990,7 +1992,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
if trans is None or var_name is None or trans._coro_var_async is None: return None
cnt: int = trans._coro_var_async_count
i: int = 0
for i in range(cnt):
for i in range(int(cnt)):
addr: uint64 = uint64(trans._coro_var_async) + i * 16
pair: str* = (str*)(addr)
if pair[0] is not None and string.strcmp(pair[0], var_name) == 0: return pair[1]
+20 -18
View File
@@ -80,7 +80,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
# Multiple inheritance: walk parents in declaration order, dedupe by short name (first wins).
parent_count: int = self.Gen.StructGen.get_parent_count(class_name)
if parent_count > 0:
for pi in range(parent_count):
for pi in range(int(parent_count)):
pname: str = self.Gen.StructGen.get_parent_name_at(class_name, pi)
if pname is None: continue
# Look up the parent entry (prefer same-module SHA1 match).
@@ -91,7 +91,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
parent_vt_count: int = 0
if vt_parent_entry is not None: parent_vt_count = vt_parent_entry.VTableMethodCount
if parent_vt_count > 0:
for pvi in range(parent_vt_count):
for pvi in range(int(parent_vt_count)):
if method_count >= VTBL_METHOD_MAX: break
pmethod_name: str = None
if vt_parent_entry is not None: pmethod_name = self.Gen.StructGen.get_vtable_method_name(pname, pvi)
@@ -101,7 +101,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
# Check whether the subclass overrides the method and whether it is @t.NoVTable.
child_overrides: int = 0
child_nvt: int = 0
for ci2 in range(cn):
for ci2 in range(int(cn)):
stmt2: ast.AST* = children.get(ci2)
if stmt2 is None: continue
if stmt2.kind() != ast.ASTKind.FunctionDef: continue
@@ -154,7 +154,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
method_count += 1
# 2. Collect this class's virtual methods (skip ones already inherited).
for ci in range(cn):
for ci in range(int(cn)):
stmt: ast.AST* = children.get(ci)
if stmt is None: continue
if stmt.kind() != ast.ASTKind.FunctionDef: continue
@@ -171,7 +171,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
if self.Gen.ClassGenBase._should_method_be_virtual(cd, fd, trans) == 0: continue
# Skip if already inherited (avoid duplicates).
already_in_vt: int = 0
for mi in range(method_count):
for mi in range(int(method_count)):
exist_addr: sizet = method_names_buf[mi]
if exist_addr != 0:
exist_name: str = rawptr(exist_addr)
@@ -233,7 +233,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
first_node: mlirlite.ParamNode* = None
tail_node: mlirlite.ParamNode* = None
for i in range(method_count):
for i in range(int(method_count)):
pnode: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
if pnode is None: continue
tail_node = mlirlite.param_list_append(first_node, tail_node, pnode)
@@ -248,7 +248,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
init_buf[0] = '\0'
self._vtable_strcat(init_buf, 16384, "{ ")
for i in range(method_count):
for i in range(int(method_count)):
if i > 0: self._vtable_strcat(init_buf, 16384, ", ")
full_name_addr: sizet = full_names_buf[i]
@@ -335,7 +335,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
oop_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
# Pass 1: detect methods, mark OOP, and register decorator metadata.
for ci in range(cn):
for ci in range(int(cn)):
stmt: ast.AST* = children.get(ci)
if stmt is None: continue
if stmt.kind() == ast.ASTKind.FunctionDef:
@@ -390,14 +390,14 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
# Declare-before-define: forward-declare every method so lookups no longer depend on
# source order (e.g. FileW.__init__ calling self.seek defined later in the class).
for ci_pd in range(cn):
for ci_pd in range(int(cn)):
stmt_pd: ast.AST* = children.get(ci_pd)
if stmt_pd is None: continue
if stmt_pd.kind() == ast.ASTKind.FunctionDef:
fd_pd: ast.FunctionDef* = ast.FunctionDef*(stmt_pd)
if fd_pd is not None and fd_pd.name is not None: self._translate_method(trans, fd_pd, struct_ty, class_name, 1)
for ci in range(cn):
for ci in range(int(cn)):
stmt: ast.AST* = children.get(ci)
if stmt is None: continue
if stmt.kind() == ast.ASTKind.FunctionDef:
@@ -568,7 +568,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
an: sizet = len(alist)
start_idx: int = 0
if is_static_m == 0: start_idx = 1
for ai in range(start_idx, an):
for ai in range(int(start_idx), int(an)):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None:
param_ty: mlirlite.LLVMType* = i32_ty
@@ -603,7 +603,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
if is_static_m == 0:
mode_buf[mode_cnt] = char(48)
mode_cnt += 1
for ai_m in range(start_idx, an):
for ai_m in range(int(start_idx), int(an)):
arg_m: ast.Arg* = ast.Arg*(alist.get(ai_m))
mm: int = 0
if arg_m is not None and arg_m.annotation is not None:
@@ -722,7 +722,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
an2: sizet = len(alist2)
start_idx2: int = 0
if is_static_m == 0: start_idx2 = 1
for ai2 in range(start_idx2, an2):
for ai2 in range(int(start_idx2), int(an2)):
arg2: ast.Arg* = ast.Arg*(alist2.get(ai2))
if arg2 is not None and arg2.arg is not None:
param_ty2: mlirlite.LLVMType* = i32_ty
@@ -781,6 +781,8 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
trans._cur_func = func
trans._cur_builder = func_builder
GenApi.clear_scope_names(trans)
# P2 willFree 失效表:方法体也走独立路径 → 同样做函数作用域复位(栈槽地址会被后续函数复用)
self.Gen.VarGen.freed_clear()
# M3 owned slife:方法体也走**独立**的转译路径(与 FuncGenMixin 的嵌套函数同款),
# 必须自己抬升/弹回登记区间,否则方法内登记会落进外层的区间 → 内层出口 free 外层对象,
@@ -792,17 +794,17 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
body: list[ast.AST*]* = fd.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
# Translate the method body.
if body is not None:
bn2: sizet = len(body)
for bi2 in range(bn2):
for bi2 in range(int(bn2)):
stmt2: ast.AST* = body.get(bi2)
if stmt2 is not None: self.Gen.StmtGen.translate_stmt(trans, stmt2)
@@ -973,7 +975,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
vt_first_node: mlirlite.ParamNode* = None
vt_prev_node: mlirlite.ParamNode* = None
for vti in range(vt_method_count):
for vti in range(int(vt_method_count)):
vtnode: mlirlite.ParamNode* = mlirlite.new_param_node(pool, vt_i8_ptr_ty)
if vtnode is None: continue
if vt_first_node is None: vt_first_node = vtnode
@@ -1012,7 +1014,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
# 2. Store per-field defaults (via bi_entry, avoiding cross-module same-name lookups).
if bi_entry is not None:
for fi in range(bi_entry.FieldCount):
for fi in range(int(bi_entry.FieldCount)):
fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(bi_entry, fi)
if fe is None or fe.DefaultVal is None: continue
# Static-array field with a list-literal default: zero it then GEP+store each element, matching
+15 -15
View File
@@ -42,7 +42,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
suffix_start: sizet = cur_len - name_len
if cur.Name[suffix_start - 1] == '.':
Matched: int = 1
for i in range(name_len):
for i in range(int(name_len)):
if cur.Name[suffix_start + i] != name[i]:
Matched = 0
break
@@ -412,7 +412,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
cd_local: int = self.Gen.TypeGenCDef.is_cdefine_found()
if cd_local == 0:
# determine the real module name: prefer resolving the alias from _from_imports,
# whose entries look like "local_name:real_module" (e.g. win32base:w32.win32base)
# whose entries look like "local_name:real_module" (e.g. win32base:win32.win32base)
real_mod_name: str = nm_ma.id
if trans._from_imports is not None:
resolved_mod: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_ma.id, 0)
@@ -422,7 +422,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':': rm_len += 1
rm_buf: str = pool.alloc(rm_len + 1)
if rm_buf is not None:
for rm_i in range(rm_len): rm_buf[rm_i] = resolved_mod[rm_i]
for rm_i in range(int(rm_len)): rm_buf[rm_i] = resolved_mod[rm_i]
rm_buf[rm_len] = '\0'
real_mod_name = rm_buf
# even when _from_imports is None (e.g. a plain `import ast`), still attempt the
@@ -442,8 +442,8 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
# cross-module float CDefine (e.g. vipermath.U_M_PI) → float constant
dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_ma_val)
# check whether nm_ma.id.at.attr is an imported submodule (e.g. w32.win32file);
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val)
# check whether nm_ma.id.at.attr is an imported submodule (e.g. win32.win32file);
# for a submodule do not create a global, let the nested attribute path handle it
is_submod: int = 0
if trans._imported_modules is not None:
@@ -452,7 +452,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
if submod_buf is not None:
viperlib.snprintf(submod_buf, submod_len, "%s.%s", nm_ma.id, at.attr)
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, submod_buf) == 1: is_submod = 1
# also check from_imports: the at.attr alias maps to the real module w32.win32file
# also check from_imports: the at.attr alias maps to the real module win32.win32file
if is_submod == 0 and trans._from_imports is not None:
fi_mod_raw: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, at.attr, 0)
if fi_mod_raw is not None:
@@ -461,14 +461,14 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
while fi_mod_raw[fm_len] != ' ' and fi_mod_raw[fm_len] != '\0' and fi_mod_raw[fm_len] != ':': fm_len += 1
fi_mod: str = pool.alloc(fm_len + 1)
if fi_mod is not None:
for fm_i in range(fm_len): fi_mod[fm_i] = fi_mod_raw[fm_i]
for fm_i in range(int(fm_len)): fi_mod[fm_i] = fi_mod_raw[fm_i]
fi_mod[fm_len] = '\0'
# check whether fi_mod starts with "nm_ma.id." (e.g. w32.win32file with w32.)
# check whether fi_mod starts with "nm_ma.id." (e.g. win32.win32file with win32.)
prefix_len: sizet = string.strlen(nm_ma.id)
fi_mod_len: sizet = string.strlen(fi_mod)
if fi_mod_len > prefix_len + 1:
match_prefix: int = 1
for pi in range(prefix_len):
for pi in range(int(prefix_len)):
if fi_mod[pi] != nm_ma.id[pi]:
match_prefix = 0
break
@@ -504,7 +504,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
pass
return mlirlite.build_load(builder, load_ty_mod, mod_gv_ref)
# nested module attribute access (w32.win32file.GENERIC_READ → global @GENERIC_READ): when
# nested module attribute access (win32.win32file.GENERIC_READ → global @GENERIC_READ): when
# at.value is an Attribute resolving to an imported submodule, look up at.attr as a global constant
if at.value.kind() == ast.ASTKind.Attribute and trans is not None:
inner_at: ast.Attribute* = ast.Attribute*(at.value)
@@ -512,12 +512,12 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
inner_nm: ast.Name* = ast.Name*(inner_at.value)
if inner_nm.id is not None and inner_at.attr is not None:
if self.Gen.VarGen.lookup_var(trans.SymTab, inner_nm.id) is None:
# build the qualified module name: "w32.win32file"
# build the qualified module name: "win32.win32file"
qual_len: sizet = string.strlen(inner_nm.id) + string.strlen(inner_at.attr) + 2
qual_buf: str = pool.alloc(qual_len)
if qual_buf is not None:
viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr)
# module knownness: an explicit import (w32.win32file) or the global module
# module knownness: an explicit import (win32.win32file) or the global module
# registry (pkg.X registered via `from . import X` in __init__.vp, e.g. intr.idt)
mod_known_n: int = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, qual_buf)
if mod_known_n == 0 and self._lookup_module_sha1(qual_buf) is not None:
@@ -547,7 +547,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
pass
return mlirlite.build_load(builder, load_ty_nested, gv_ref_nested)
# global not found: try a CDefine constant lookup; for nested module attributes
# like w32.win32base.INVALID_HANDLE_VALUE, resolve the CDefine from the .vpi
# like win32.win32base.INVALID_HANDLE_VALUE, resolve the CDefine from the .vpi
cdef_val_nested: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# build a temporary from_imports "attr:module" for the cross-module lookup
@@ -560,7 +560,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
# cross-module float CDefine → float constant
dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val_nested)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested)
# CDefine not found: fail-fast and stop compiling (no more undefined global
# references, which would make llc report "use of undefined value").
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(at.attr) != 0:
@@ -915,7 +915,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
if bare is None: return None
st: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(bare)
if st is None: return None
for fi in range(st.FieldCount):
for fi in range(int(st.FieldCount)):
fe_u: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(st, fi)
if fe_u is not None and fe_u.Name is not None and string.strcmp(fe_u.Name, at.attr) == 0: return fe_u
return None
+46 -31
View File
@@ -7,7 +7,7 @@ import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import win32.fileio as fileio
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
@@ -36,7 +36,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if sha1_arr_ptr is None: return None
# iterate to find the matching SHA1
idx_s: int = -1
for i in range(count):
for i in range(int(count)):
sidx: sizet = sizet(i) * 17
cur_sha1: str = sha1_arr_ptr + sidx
if cur_sha1[0] == '\0': continue
@@ -129,7 +129,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
mb_buf: str = pool.alloc(mbl + 1)
if mb_buf is not None:
mi2: sizet
for mi2 in range(mbl): mb_buf[mi2] = mod_name_raw[mi2]
for mi2 in range(int(mbl)): mb_buf[mi2] = mod_name_raw[mi2]
mb_buf[mbl] = '\0'
base_mod = mb_buf
while base_mod is not None and base_mod[0] == '.': base_mod = base_mod + 1
@@ -175,14 +175,14 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if im_nlen > 0 and im_nlen < 256:
im_buf: str = pool.alloc(im_nlen + 1)
if im_buf is not None:
for mk in range(im_nlen):
for mk in range(int(im_nlen)):
im_buf[mk] = imported_modules[im_start + mk]
im_buf[im_nlen] = '\0'
sub_fi: str = pool.alloc(im_nlen + 3)
if sub_fi is not None:
sub_fi[0] = '*'
sub_fi[1] = ':'
for mk2 in range(im_nlen):
for mk2 in range(int(im_nlen)):
sub_fi[2 + mk2] = im_buf[mk2]
sub_fi[2 + im_nlen] = '\0'
sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
@@ -245,7 +245,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n': cs_line_start -= 1
if cs_i - cs_line_start >= cs_name_len + 1:
nm_ok: int = 1
for ki2 in range(cs_name_len):
for ki2 in range(int(cs_name_len)):
if pyi_buf[cs_line_start + ki2] != name[ki2]:
nm_ok = 0
break
@@ -299,7 +299,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# check whether the line starts with "name:"
if line_len < name_len + 1: continue
match_ok: int = 1
for ki in range(name_len):
for ki in range(int(name_len)):
if pyi_buf[line_start + ki] != name[ki]:
match_ok = 0
break
@@ -407,7 +407,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# copy the left token into a buffer
val_buf: str = pool.alloc(val_str_len + 1)
if val_buf is None: continue
for vi in range(val_str_len): val_buf[vi] = pyi_buf[val_str_start + vi]
for vi in range(int(val_str_len)): val_buf[vi] = pyi_buf[val_str_start + vi]
val_buf[val_str_len] = '\0'
# evaluate the left token
@@ -436,7 +436,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# 0xFFFFFFFF=4294967295 keeps full precision (constant side truncates to i32 -1).
hex_result: int64 = 0
hex_buf: str = val_buf + 2 # skip "0x"
for hi in range(val_str_len - 2):
for hi in range(int(val_str_len - 2)):
hc: char = hex_buf[hi]
hd: int = 0
if hc >= '0' and hc <= '9': hd = hc - '0'
@@ -530,7 +530,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
continue
r_buf: str = pool.alloc(r_len + 1)
if r_buf is None: continue
for r_i in range(r_len): r_buf[r_i] = pyi_buf[r_start + r_i]
for r_i in range(int(r_len)): r_buf[r_i] = pyi_buf[r_start + r_i]
r_buf[r_len] = '\0'
rval: int64 = 0
if r_name != 0:
@@ -545,7 +545,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
elif r_hex != 0:
rhex_v: int64 = 0
rh_buf: str = r_buf + 2
for rh_i in range(r_len - 2):
for rh_i in range(int(r_len - 2)):
rc: char = rh_buf[rh_i]
rd: int = 0
if rc >= '0' and rc <= '9': rd = rc - '0'
@@ -666,7 +666,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if rc_cpos + name_len_rc <= rc_scan_end:
rc_match: int = 1
rc_ki: sizet
for rc_ki in range(name_len_rc):
for rc_ki in range(int(name_len_rc)):
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
rc_match = 0
break
@@ -693,7 +693,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
if rc_sub_buf is None: continue
rc_si: sizet
for rc_si in range(rc_sub_len): rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
for rc_si in range(int(rc_sub_len)): rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
rc_sub_buf[rc_sub_len] = '\0'
# build the full module name: relative import -> package + "." + token (the base
@@ -726,7 +726,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
# recursive call to search the submodule
rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
rc_sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
result_val = rc_sub_val
result_found = 1
@@ -734,7 +734,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
stdlib.free(pyi_buf)
# 9.6 star-fallback module had no match: fall back to walking imported_modules,
# e.g. `from stdint import *` names stdint but SHARE_READ lives in `import w32.fileio`.
# e.g. `from stdint import *` names stdint but SHARE_READ lives in `import win32.fileio`.
if result_found == 0 and imported_modules is not None:
im2_len: sizet = string.strlen(imported_modules)
im2_ci: sizet = 0
@@ -748,14 +748,14 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if im2_nlen > 0 and im2_nlen < 256:
im2_buf: str = pool.alloc(im2_nlen + 1)
if im2_buf is not None:
for mk3 in range(im2_nlen):
for mk3 in range(int(im2_nlen)):
im2_buf[mk3] = imported_modules[im2_start + mk3]
im2_buf[im2_nlen] = '\0'
sub_fi2: str = pool.alloc(im2_nlen + 3)
if sub_fi2 is not None:
sub_fi2[0] = '*'
sub_fi2[1] = ':'
for mk4 in range(im2_nlen):
for mk4 in range(int(im2_nlen)):
sub_fi2[2 + mk4] = im2_buf[mk4]
sub_fi2[2 + im2_nlen] = '\0'
sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
@@ -820,24 +820,39 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
return has_upper
# ============================================================
# Translate a variable reference (Name node) -> load
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, value: int64) -> mlirlite.Value*:
"""Materialize a CDefine integer constant as an LLVM constant (TPC TypeConvert aligned)"""
if (value >= 0 and uint64(uint32(value)) != value) or value < -2147483648: return mlirlite.const_int64(pool, value)
return mlirlite.const_int32(pool, int(value))
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64) -> mlirlite.Value*:
"""Materialize a CDefine integer constant as an LLVM constant (TPC TypeConvert aligned)
位宽优先用 define 的**已知位宽**(右侧显式强转或声明注解,见 register_cdefine_constant 的 bits);
没有位宽记录时按**推断位宽**物化(值的最小可存宽度;0x80000000 → 32)。
这样 `define X: WORD = WORD(...)` 的 X 保持 u16、`X | Y` 里两个同宽 u16 不会被提升成
i32;无注解的 `define A = 0x80000000` 也不会退化成 i64 把 `A | B` 放大到 64 位,
而小值 define 说成 8/16 也在算术提升下不会溢出。
"""
dbits: int = 0
if name is not None: dbits = self.Gen.TypeGenCDef.lookup_cdefine_bits(name)
if dbits == 8 or dbits == 16 or dbits == 32 or dbits == 64:
return mlirlite.const_int(pool, dbits, self._cdefine_bits_value(value, dbits))
# 无位宽记录(例如只从 .vpi 跨模块引入、未登记 bits)→ 按**推断位宽**物化:
# 值的最小可存宽度(2^31..2^32-1 取 32 位无符号位型,不退化成 i64;
# 小值取 8/16,参与算术时由整数提升先升到 i32,不会溢出)。
wmin: int = self.Gen.AssignGen.define_min_width(value)
return mlirlite.const_int(pool, wmin, self._cdefine_bits_value(value, wmin))
# ============================================================
# cdefine_use_width_value - materialize CDefine constants at the use-site width (TPC-aligned)
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
"""Normalize a CDefine value to the signed representation of the given bit width"""
if bits == 8:
if value > 127: return value - 256
return value
if bits == 16:
if value > 32767: return value - 65536
return value
if bits == 32:
if value > 2147483647: return value - 4294967296
return value
"""把值按位宽**截断**后再重解释为该位宽的有符号表示(与 AssignGenMixin._normalize_cdefine_bits 同口径)。
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(WORD(0x80000000) → 0),
而不是只对"刚好越界一点"的值做重解释。
实现用左移后算术右移,而不是 `value & 0xFFFFFFFF` 这类大掩码字面量:
掩码字面量(如 4294967295)超出 int32 范围,其加宽方向在 TPC/TPV 上可能判成 sext,
会得到 0xFFFFFFFFFFFFFFFF 而不是 0xFFFFFFFF,导致截断失效。
"""
if bits == 8: return (value << 56) >> 56
if bits == 16: return (value << 48) >> 48
if bits == 32: return (value << 32) >> 32
return value
def cdefine_use_width_value(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, target_ty: mlirlite.LLVMType*, use_ctx: str = None) -> mlirlite.Value*:
+2 -2
View File
@@ -63,7 +63,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
# wider -> widen in place; narrower -> fail-fast; unknown -> the existing 32/64 default.
uw_val: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val_c, target_ty, use_ctx)
if uw_val is not None: return uw_val
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val_c)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val_c)
# Not found locally: try cross-module .vpi lookup, only for ALL_CAPS names so ordinary
# variable names never trigger the costly cross-module source read.
@@ -85,7 +85,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
# Same use-site width rule (cross-module CDefine)
uw_x: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx)
if uw_x is not None: return uw_x
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val)
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val)
# CDefine lookup failed: from-import module-level globals (e.g. `from Phase2 import
# PA_DEPS_MAX_STR`, an i64 global) are located by suffix after confirming the source module.
+57 -3
View File
@@ -219,7 +219,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if bare_u is not None:
st_u: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(bare_u)
if st_u is not None:
for fi_u in range(st_u.FieldCount):
for fi_u in range(int(st_u.FieldCount)):
fe_u: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(st_u, fi_u)
if fe_u is not None and fe_u.Name is not None and string.strcmp(fe_u.Name, at_u.attr) == 0:
# [S4 read] An explicit unsigned field Spec wins
@@ -483,6 +483,18 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
# Type alignment: icmp requires matching types on both sides. Only numeric types are aligned (same-level pointers icmp directly); i1 uses zext (1 stays 1).
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
# C 语义整数提升(与算术同一口径):两侧都窄于 32 位时先提升到 i32 再比较,避免 8/16 位
# 直接比较在符号/边界组合下语义模糊(如 signed i8 与 unsigned u8 直接比)。
# 提升后把 bits 记为 32,下面的等宽化不再重复处理。
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits < 32 and rhs_bits < 32:
cmp_i32_pr: mlirlite.LLVMType* = mlirlite.Int32(pool)
cmp_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
if self.should_zext_widen(pool, lhs, cmp_probe, cmp.left, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, cmp_i32_pr)
else: lhs = mlirlite.build_sext(builder, lhs, cmp_i32_pr)
if self.should_zext_widen(pool, rhs, cmp_probe, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, cmp_i32_pr)
else: rhs = mlirlite.build_sext(builder, rhs, cmp_i32_pr)
lhs_bits = 32
rhs_bits = 32
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
if lhs_bits < rhs_bits:
if self.should_zext_widen(pool, lhs, rhs, cmp.left, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, rhs.Ty)
@@ -542,6 +554,17 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
ovl_u: mlirlite.Value* = self.try_unary_operator_overload(pool, builder, mod, operand, uo.op, trans)
if ovl_u is not None: return ovl_u
# C 语义整数提升(与算术/比较同一口径):一元 +/-/~ 先把窄于 32 位的整数操作数提升到 i32,
# 这样 `0 - x` / `x ^ -1` 的常量与操作数同宽(否则是 i32 常量对 i8/i16 操作数)。
# `not x` 不需要:结果为 i1,操作数宽度不影响语义。
if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert:
u_bits: int = self.get_llvm_type_bits(operand.Ty)
if u_bits != 0 and u_bits < 32:
u_i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
u_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
if self.should_zext_widen(pool, operand, u_probe, uo.operand, trans) != 0: operand = mlirlite.build_zext(builder, operand, u_i32_ty)
else: operand = mlirlite.build_sext(builder, operand, u_i32_ty)
if uo.op == ast.OpKind.USub:
# -x
# Float type uses fneg (emit fneg directly to avoid 0.0 - x type mismatch)
@@ -700,10 +723,10 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
# phi incoming list head/tail
phi_head: mlirlite.PhiIncoming* = None
phi_tail: mlirlite.PhiIncoming* = None
phi_count: int = count - 1 # loop iteration count
phi_count: int = int(count - 1) # loop iteration count
# Evaluate and short-circuit each value except the last
for i in range(count - 1):
for i in range(int(count - 1)):
val_node: ast.AST* = values.get(i)
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, val_node, funcs_ptr, func_count, trans)
if val is None: return None
@@ -1075,6 +1098,15 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if node is not None: col_s = node.col_offset + 1
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] illegal double-pointer arithmetic type: op = '{op_lbl}'{aug_note}, " f"left operand type = {lty_lbl}, right operand type = {rty_lbl}. " f"in this language str is i8*, with no `+` concatenation semantics (`str + str` and pointer +/- pointer " f"both degrade to raw pointer add/sub => illegal IR: opt reports " f"invalid operand type for instruction). " f"silent degradation is forbidden, compilation aborted. " f"fix: build strings with f-string interpolation, string.strcat, or viperlib.snprintf.", "TypeError", col_s, col_s)
return 1
# binop_needs_int_promotion - whether this op takes C-style integer promotion (to >= 32 bits)
def binop_needs_int_promotion(self, op: int) -> int:
"""C 语义整数提升的适用范围:**算术与移位**为 1(窄于 32 位先提升再算,避免 8/16 位回绕),
**位运算(& | ^)为 0**——按既有约定"两侧同宽就不提升"(WORD | WORD 保持 u16)。"""
if op == ast.OpKind.Add or op == ast.OpKind.Sub or op == ast.OpKind.Mult: return 1
if op == ast.OpKind.Div or op == ast.OpKind.FloorDiv or op == ast.OpKind.Mod: return 1
if op == ast.OpKind.LShift or op == ast.OpKind.RShift: return 1
return 0
# translate_binop - binary operations with automatic type promotion
# ============================================================
def translate_binop(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
@@ -1170,6 +1202,23 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if binop_unsigned == 0 and rhs_node is not None:
if self._node_type_unsigned(pool, rhs_node, trans) != 0: binop_unsigned = 1
# C 语义整数提升(仅算术/移位;位运算 & | ^ 保持"同宽不提升"):
# 两侧都窄于 32 位时先提升到 32 位再算,避免 8/16 位回绕(如两个 16 位 define 的
# `MAX_INCLUDES * MAX_REL_PATH_LEN` 会算成 0)。提升后把 lhs_bits/rhs_bits 记为 32,
# 让下面的等宽化逻辑不再重复处理(否则会对同宽 i32 再做一次无意义转换)。
if self.binop_needs_int_promotion(op) != 0:
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits < 32 and rhs_bits < 32:
i32_ty_pr: mlirlite.LLVMType* = mlirlite.Int32(pool)
# 加宽方向与下面的等宽化同一判据(i1/i8 默认 zext、带符号注解 sext、无符号 typedef zext);
# probe 只是给 should_zext_widen 提供"宽侧类型",其常量值不参与判据。
i32_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
if self.should_zext_widen(pool, lhs, i32_probe, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, i32_ty_pr)
else: lhs = self.coerce_to_type(builder, lhs, i32_ty_pr, node, trans)
if self.should_zext_widen(pool, rhs, i32_probe, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, i32_ty_pr)
else: rhs = self.coerce_to_type(builder, rhs, i32_ty_pr, node, trans)
lhs_bits = 32
rhs_bits = 32
if lhs_bits > rhs_bits:
if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, lhs.Ty)
else: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
@@ -1184,6 +1233,11 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
# fail-fast: illegal bitwise operand types; abort compilation (expose the upstream root cause)
self.Gen.TypeGenErr.fatal_error(node, "illegal bitwise operand types: both lhs/rhs are non-integer types (pointer or struct), cannot perform bitwise operations", "TypeError")
# 移位:计数静态可确定时做范围检查(与 AugAssign 的 <<= / >>= 共用);
# 计数宽度本身豁免,按提升/等宽化后的左操作数有效位宽比较(C 语义下 u8 << 8 合法)。
if op == ast.OpKind.LShift or op == ast.OpKind.RShift:
self.Gen.AssignGen.require_shift_count_in_range(pool, trans, node, rhs_node, self.get_llvm_type_bits(lhs.Ty))
if op == ast.OpKind.Add: return mlirlite.build_add(builder, lhs, rhs)
elif op == ast.OpKind.Sub: return mlirlite.build_sub(builder, lhs, rhs)
elif op == ast.OpKind.Mult: return mlirlite.build_mul(builder, lhs, rhs)
+33 -18
View File
@@ -35,7 +35,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
cnt: int = 0
has_ref: int = 0
imp: str = trans._imported_modules
for i in range(an):
for i in range(int(an)):
arg: ast.Arg* = ast.Arg*(alist.get(i))
if arg is None or arg.arg is None: continue
# CVoid params are not emitted into the signature, so they must not occupy a mode slot.
@@ -81,7 +81,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
# Pre-register this module's ClassDefs in declare-only mode (struct+fields+method declares, no bodies):
# stage 1a cannot resolve dependent CTypedef (vpi not yet injected); re-registering here keeps annotations resolvable.
pre_i: sizet
for pre_i in range(cn_count):
for pre_i in range(int(cn_count)):
pre_child: ast.AST* = ch.get(pre_i)
if pre_child is None: continue
if pre_child.kind() != ast.ASTKind.ClassDef: continue
@@ -91,7 +91,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if pre_ex is not None and pre_ex.FieldCount > 0: continue
self.Gen.ClassGenDef.translate_class_def(trans, pre_child)
for ci in range(cn_count):
for ci in range(int(cn_count)):
child: ast.AST* = ch.get(ci)
if child is None: continue
if child.kind() != ast.ASTKind.FunctionDef: continue
@@ -202,7 +202,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
for ai in range(an):
for ai in range(int(an)):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is None or arg.arg is None: continue
# CVoid means empty param: skip
@@ -343,7 +343,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
decor_arg0: int64 = 0
if fd.decorator_list is not None:
ddn: sizet = len(fd.decorator_list)
for ddi in range(ddn):
for ddi in range(int(ddn)):
deco: ast.AST* = fd.decorator_list.get(ddi)
if deco is None: continue
dk: int = deco.kind()
@@ -409,7 +409,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if ags_e.args is not None:
alist_e: list[ast.AST*]* = ags_e.args
an_e: sizet = len(alist_e)
for ai_e in range(an_e):
for ai_e in range(int(an_e)):
arg_e: ast.Arg* = ast.Arg*(alist_e.get(ai_e))
if arg_e is not None and arg_e.arg is not None:
# CVoid means empty param: skip, do not add to the function signature
@@ -507,7 +507,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
for ai in range(an):
for ai in range(int(an)):
arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None:
# CVoid means empty param: skip, do not add to the function signature
@@ -624,13 +624,26 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
# Generic function templates already returned early (see the top of this function); here fd.type_params is always
# None (specialization clones clear it). The generic context is set by _specialize_generic_function before the call.
# arg()/va_list scope reset: save the ENCLOSING function's va_list state and start this body with
# none of it. A non-variadic function must not inherit the previous function's buffer: that Value
# belongs to another function while the printer emits it by NAME, so the name re-binds to
# whatever local happens to carry it here (scene: viperlib vsnprintf emitted `va_arg ptr %9`
# where %9 is `count_mode`'s i32 alloca -> va_arg loaded a garbage va_list pointer ->
# access-violation inside vsnprintf). The *args setup below re-sets both for a variadic callee,
# and old_va_list_ptr/old_vararg_name are restored after the body so that nested functions still
# see the enclosing variadic function's buffer.
old_va_list_ptr: mlirlite.Value* = trans._va_list_ptr
old_vararg_name: str = trans._vararg_name
trans._va_list_ptr = None
trans._vararg_name = None
# Create allocas for the parameters
if args_node is not None:
ags2: ast.Arguments* = ast.Arguments*(args_node)
if ags2.args is not None:
alist2: list[ast.AST*]* = ags2.args
an2: sizet = len(alist2)
for ai2 in range(an2):
for ai2 in range(int(an2)):
arg2: ast.Arg* = ast.Arg*(alist2.get(ai2))
if arg2 is not None and arg2.arg is not None:
# CVoid means empty param: skip, do not create an alloca
@@ -736,8 +749,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
old_global_count: int = trans._global_name_count
old_nonlocal_count: int = trans._nonlocal_name_count
old_env_count: int = trans._closure_env_count
old_va_list_ptr: mlirlite.Value* = trans._va_list_ptr
old_vararg_name: str = trans._vararg_name
# old_va_list_ptr / old_vararg_name were saved above (before the *args setup) so that this body
# could start from a clean va_list state; the restore below hands the enclosing value back.
# Return-annotation context ([C①], one source of type-argument fallback for bare generic construction):
# record this function's return annotation before entering the body and restore the outer value on exit.
old_ret_annot: ast.AST* = trans.CurFuncRetAnnot
@@ -750,6 +763,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
trans._cur_func = func
trans._cur_builder = func_builder
trans.CurFuncRetAnnot = fd.returns
# P2 willFree 失效表:**函数作用域复位**(表按栈槽地址记录,槽地址会被后续函数复用)
self.Gen.VarGen.freed_clear()
trans._break_bb = None
trans._continue_bb = None
trans._in_switch_case = 0
@@ -764,10 +779,10 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
body: list[ast.AST*]* = fd.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
@@ -780,7 +795,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
# Translate the function body
if body is not None:
bn2: sizet = len(body)
for bi2 in range(bn2):
for bi2 in range(int(bn2)):
stmt2: ast.AST* = body.get(bi2)
if stmt2 is not None:
# Before a nested function definition: pre-scan its in-chain nonlocals and build the heap
@@ -937,7 +952,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
wrapper: mlirlite.Function* = mlirlite.create_function(pool, mod, wrapper_name, ret_ty)
if wrapper is None: return None
pi: int = 0
for pi in range(n_params):
for pi in range(int(n_params)):
pname_w: str = pool.alloc(16)
if pname_w is not None:
viperlib.snprintf(pname_w, 16, "%%a%d", pi)
@@ -987,7 +1002,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
mlirlite.position_at_end(wb, rec_bb)
rec_args: mlirlite.Value* = None
ri: int = 0
for ri in range(n_params):
for ri in range(int(n_params)):
an_r: str = pool.alloc(16)
if an_r is None: continue
viperlib.snprintf(an_r, 16, "%%a%d", ri)
@@ -1025,7 +1040,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if n_params > 0:
fields_head: mlirlite.ParamNode* = None
fk: int = 0
for fk in range(n_params - 1, -1, -1):
for fk in range(int(n_params - 1), -1, -1):
pn_n: mlirlite.ParamNode* = mlirlite.new_param_node(pool, (mlirlite.LLVMType*)(param_tys[fk]))
if pn_n is not None:
pn_n.Next = fields_head
@@ -1034,7 +1049,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if args_ty is not None:
args_alloca = mlirlite.build_alloca(wb, args_ty)
fi2: int = 0
for fi2 in range(n_params):
for fi2 in range(int(n_params)):
fld_ty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(param_tys[fi2])
fld_ptr: mlirlite.Value* = mlirlite.build_gep_struct(wb, args_ty, fld_ty, args_alloca, fi2)
if fld_ptr is not None:
@@ -1105,7 +1120,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
call_args: mlirlite.Value* = None
if n_params > 0:
k: int = 0
for k in range(n_params):
for k in range(int(n_params)):
fld_ty_k: mlirlite.LLVMType* = (mlirlite.LLVMType*)(param_tys[k])
fld_pk: mlirlite.Value* = mlirlite.build_gep_struct(wb, args_ty, fld_ty_k, args_alloca, k)
if fld_pk is None: continue
+56 -52
View File
@@ -91,7 +91,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if ags_n.args is not None:
alist_n: list[ast.AST*]* = ags_n.args
an_n: sizet = len(alist_n)
for ai_n in range(an_n):
for ai_n in range(int(an_n)):
arg_n: ast.Arg* = ast.Arg*(alist_n.get(ai_n))
if arg_n is None or arg_n.arg is None: continue
if arg_n.annotation is not None:
@@ -144,7 +144,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
alist_p: list[ast.AST*]* = ags_p.args
idx_p: int = 0
apn: sizet = len(alist_p)
for api in range(apn):
for api in range(int(apn)):
arg_p: ast.Arg* = ast.Arg*(alist_p.get(api))
if arg_p is None or arg_p.arg is None: continue
if arg_p.annotation is not None:
@@ -199,6 +199,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
trans._cur_func = func
trans._cur_builder = func_builder
trans._cur_func_name = func_name
# P2 willFree 失效表:函数作用域复位(栈槽地址会被后续函数复用,跨函数保留会误报)
self.Gen.VarGen.freed_clear()
GenApi.clear_scope_names(trans)
trans._break_bb = None
trans._continue_bb = None
@@ -214,10 +216,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
body: list[ast.AST*]* = fd.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
for bi in range(bn):
for bi in range(int(bn)):
stmt: ast.AST* = body.get(bi)
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
@@ -225,7 +227,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
# (Python semantics). Branch AnnAssign locals are already pre-registered and excluded.
if body is not None:
bnc: sizet = len(body)
for bic in range(bnc):
for bic in range(int(bnc)):
stmtc: ast.AST* = body.get(bic)
if stmtc is not None: self._closure_scan_captures(trans, stmtc)
@@ -238,7 +240,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
# 9. Translate the body (Nonlocal statements fill _nonlocal_names)
if body is not None:
bn2: sizet = len(body)
for bi2 in range(bn2):
for bi2 in range(int(bn2)):
stmt2: ast.AST* = body.get(bi2)
if stmt2 is not None: self.Gen.StmtGen.translate_stmt(trans, stmt2)
@@ -302,7 +304,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
# compile-time map): parent local -> new heap + redirect; parent nonlocal -> reuse heap.
if env_ptr is not None:
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
for ni in range(nested_nonlocal_count):
for ni in range(int(nested_nonlocal_count)):
# Read the name from the _nonlocal_names buffer
name_addr: uint64 = uint64(trans._nonlocal_names) + ni * 8
nl_slot: str* = (str*)(name_addr)
@@ -467,10 +469,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if ifn.test is not None: self._closure_scan_captures(trans, ifn.test)
if ifn.children is not None:
bn_if: sizet = len(ifn.children)
for bi_if in range(bn_if): self._closure_scan_captures(trans, ifn.children.get(bi_if))
for bi_if in range(int(bn_if)): self._closure_scan_captures(trans, ifn.children.get(bi_if))
if ifn.orelse is not None:
bn_el: sizet = len(ifn.orelse)
for bi_el in range(bn_el): self._closure_scan_captures(trans, ifn.orelse.get(bi_el))
for bi_el in range(int(bn_el)): self._closure_scan_captures(trans, ifn.orelse.get(bi_el))
return 1
if k == ast.ASTKind.For:
for_n: ast.For* = ast.For*(node)
@@ -478,20 +480,20 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if for_n.iter is not None: self._closure_scan_captures(trans, for_n.iter)
if for_n.children is not None:
bn_f: sizet = len(for_n.children)
for bi_f in range(bn_f): self._closure_scan_captures(trans, for_n.children.get(bi_f))
for bi_f in range(int(bn_f)): self._closure_scan_captures(trans, for_n.children.get(bi_f))
if for_n.orelse is not None:
bn_fe: sizet = len(for_n.orelse)
for bi_fe in range(bn_fe): self._closure_scan_captures(trans, for_n.orelse.get(bi_fe))
for bi_fe in range(int(bn_fe)): self._closure_scan_captures(trans, for_n.orelse.get(bi_fe))
return 1
if k == ast.ASTKind.While:
wh_n: ast.While* = ast.While*(node)
if wh_n.test is not None: self._closure_scan_captures(trans, wh_n.test)
if wh_n.children is not None:
bn_w: sizet = len(wh_n.children)
for bi_w in range(bn_w): self._closure_scan_captures(trans, wh_n.children.get(bi_w))
for bi_w in range(int(bn_w)): self._closure_scan_captures(trans, wh_n.children.get(bi_w))
if wh_n.orelse is not None:
bn_we: sizet = len(wh_n.orelse)
for bi_we in range(bn_we): self._closure_scan_captures(trans, wh_n.orelse.get(bi_we))
for bi_we in range(int(bn_we)): self._closure_scan_captures(trans, wh_n.orelse.get(bi_we))
return 1
if k == ast.ASTKind.Return:
rt_n: ast.Return* = ast.Return*(node)
@@ -505,14 +507,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
wth: ast.With* = ast.With*(node)
if wth.items is not None:
bn_wi: sizet = len(wth.items)
for bi_wi in range(bn_wi):
for bi_wi in range(int(bn_wi)):
wi_n: ast.AST* = wth.items.get(bi_wi)
if wi_n is not None and wi_n.kind() == ast.ASTKind.WithItem:
wi_c: ast.WithItem* = ast.WithItem*(wi_n)
if wi_c.context_expr is not None: self._closure_scan_captures(trans, wi_c.context_expr)
if wth.children is not None:
bn_wb: sizet = len(wth.children)
for bi_wb in range(bn_wb): self._closure_scan_captures(trans, wth.children.get(bi_wb))
for bi_wb in range(int(bn_wb)): self._closure_scan_captures(trans, wth.children.get(bi_wb))
return 1
if k == ast.ASTKind.Raise:
rs_n: ast.Raise* = ast.Raise*(node)
@@ -528,36 +530,36 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
tr_n: ast.Try* = ast.Try*(node)
if tr_n.children is not None:
bn_t: sizet = len(tr_n.children)
for bi_t in range(bn_t): self._closure_scan_captures(trans, tr_n.children.get(bi_t))
for bi_t in range(int(bn_t)): self._closure_scan_captures(trans, tr_n.children.get(bi_t))
if tr_n.handlers is not None:
bn_th: sizet = len(tr_n.handlers)
for bi_th in range(bn_th):
for bi_th in range(int(bn_th)):
eh_n: ast.AST* = tr_n.handlers.get(bi_th)
if eh_n is not None and eh_n.kind() == ast.ASTKind.ExceptHandler:
eh_c: ast.ExceptHandler* = ast.ExceptHandler*(eh_n)
if eh_c.children is not None:
bn_ehb: sizet = len(eh_c.children)
for bi_ehb in range(bn_ehb): self._closure_scan_captures(trans, eh_c.children.get(bi_ehb))
for bi_ehb in range(int(bn_ehb)): self._closure_scan_captures(trans, eh_c.children.get(bi_ehb))
if tr_n.orelse is not None:
bn_to: sizet = len(tr_n.orelse)
for bi_to in range(bn_to): self._closure_scan_captures(trans, tr_n.orelse.get(bi_to))
for bi_to in range(int(bn_to)): self._closure_scan_captures(trans, tr_n.orelse.get(bi_to))
if tr_n.finalbody is not None:
bn_tf: sizet = len(tr_n.finalbody)
for bi_tf in range(bn_tf): self._closure_scan_captures(trans, tr_n.finalbody.get(bi_tf))
for bi_tf in range(int(bn_tf)): self._closure_scan_captures(trans, tr_n.finalbody.get(bi_tf))
return 1
if k == ast.ASTKind.Match:
mt_n: ast.Match* = ast.Match*(node)
if mt_n.subject is not None: self._closure_scan_captures(trans, mt_n.subject)
if mt_n.cases is not None:
bn_mc: sizet = len(mt_n.cases)
for bi_mc in range(bn_mc):
for bi_mc in range(int(bn_mc)):
mc_n: ast.AST* = mt_n.cases.get(bi_mc)
if mc_n is not None and mc_n.kind() == ast.ASTKind.MatchCase:
mc_c: ast.MatchCase* = ast.MatchCase*(mc_n)
if mc_c.guard is not None: self._closure_scan_captures(trans, mc_c.guard)
if mc_c.children is not None:
bn_mcb: sizet = len(mc_c.children)
for bi_mcb in range(bn_mcb): self._closure_scan_captures(trans, mc_c.children.get(bi_mcb))
for bi_mcb in range(int(bn_mcb)): self._closure_scan_captures(trans, mc_c.children.get(bi_mcb))
return 1
# ---- Expressions ----
if k == ast.ASTKind.BinOp:
@@ -573,24 +575,24 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
bop2: ast.BoolOp* = ast.BoolOp*(node)
if bop2.values is not None:
bn_bo: sizet = len(bop2.values)
for bi_bo in range(bn_bo): self._closure_scan_captures(trans, bop2.values.get(bi_bo))
for bi_bo in range(int(bn_bo)): self._closure_scan_captures(trans, bop2.values.get(bi_bo))
return 1
if k == ast.ASTKind.Compare:
cmp: ast.Compare* = ast.Compare*(node)
if cmp.left is not None: self._closure_scan_captures(trans, cmp.left)
if cmp.comparators is not None:
bn_cmp: sizet = len(cmp.comparators)
for bi_cmp in range(bn_cmp): self._closure_scan_captures(trans, cmp.comparators.get(bi_cmp))
for bi_cmp in range(int(bn_cmp)): self._closure_scan_captures(trans, cmp.comparators.get(bi_cmp))
return 1
if k == ast.ASTKind.Call:
cal: ast.Call* = ast.Call*(node)
if cal.func is not None: self._closure_scan_captures(trans, cal.func)
if cal.args is not None:
bn_ca: sizet = len(cal.args)
for bi_ca in range(bn_ca): self._closure_scan_captures(trans, cal.args.get(bi_ca))
for bi_ca in range(int(bn_ca)): self._closure_scan_captures(trans, cal.args.get(bi_ca))
if cal.keywords is not None:
bn_ck: sizet = len(cal.keywords)
for bi_ck in range(bn_ck):
for bi_ck in range(int(bn_ck)):
kw_n: ast.AST* = cal.keywords.get(bi_ck)
if kw_n is not None and kw_n.kind() == ast.ASTKind.Keyword:
kw_c: ast.Keyword* = ast.Keyword*(kw_n)
@@ -615,28 +617,28 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
dc: ast.Dict* = ast.Dict*(node)
if dc.keys is not None:
bn_dk: sizet = len(dc.keys)
for bi_dk in range(bn_dk): self._closure_scan_captures(trans, dc.keys.get(bi_dk))
for bi_dk in range(int(bn_dk)): self._closure_scan_captures(trans, dc.keys.get(bi_dk))
if dc.values is not None:
bn_dv: sizet = len(dc.values)
for bi_dv in range(bn_dv): self._closure_scan_captures(trans, dc.values.get(bi_dv))
for bi_dv in range(int(bn_dv)): self._closure_scan_captures(trans, dc.values.get(bi_dv))
return 1
if k == ast.ASTKind.Set:
st_n: ast.Set* = ast.Set*(node)
if st_n.elts is not None:
bn_set: sizet = len(st_n.elts)
for bi_set in range(bn_set): self._closure_scan_captures(trans, st_n.elts.get(bi_set))
for bi_set in range(int(bn_set)): self._closure_scan_captures(trans, st_n.elts.get(bi_set))
return 1
if k == ast.ASTKind.List:
ls_n: ast.List* = ast.List*(node)
if ls_n.elts is not None:
bn_ls: sizet = len(ls_n.elts)
for bi_ls in range(bn_ls): self._closure_scan_captures(trans, ls_n.elts.get(bi_ls))
for bi_ls in range(int(bn_ls)): self._closure_scan_captures(trans, ls_n.elts.get(bi_ls))
return 1
if k == ast.ASTKind.Tuple:
tp_n: ast.Tuple* = ast.Tuple*(node)
if tp_n.elts is not None:
bn_tp: sizet = len(tp_n.elts)
for bi_tp in range(bn_tp): self._closure_scan_captures(trans, tp_n.elts.get(bi_tp))
for bi_tp in range(int(bn_tp)): self._closure_scan_captures(trans, tp_n.elts.get(bi_tp))
return 1
if k == ast.ASTKind.Starred:
sr_n: ast.Starred* = ast.Starred*(node)
@@ -656,7 +658,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
js_n: ast.JoinedStr* = ast.JoinedStr*(node)
if js_n.values is not None:
bn_js: sizet = len(js_n.values)
for bi_js in range(bn_js): self._closure_scan_captures(trans, js_n.values.get(bi_js))
for bi_js in range(int(bn_js)): self._closure_scan_captures(trans, js_n.values.get(bi_js))
return 1
if k == ast.ASTKind.FormattedValue:
fv_n: ast.FormattedValue* = ast.FormattedValue*(node)
@@ -684,14 +686,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if lc_n.elt is not None: self._closure_scan_captures(trans, lc_n.elt)
if lc_n.generators is not None:
bn_gen: sizet = len(lc_n.generators)
for bi_gen in range(bn_gen):
for bi_gen in range(int(bn_gen)):
cp_n: ast.AST* = lc_n.generators.get(bi_gen)
if cp_n is not None and cp_n.kind() == ast.ASTKind.Comprehension:
cp_c: ast.Comprehension* = ast.Comprehension*(cp_n)
if cp_c.iter is not None: self._closure_scan_captures(trans, cp_c.iter)
if cp_c.ifs is not None:
bn_cpi: sizet = len(cp_c.ifs)
for bi_cpi in range(bn_cpi): self._closure_scan_captures(trans, cp_c.ifs.get(bi_cpi))
for bi_cpi in range(int(bn_cpi)): self._closure_scan_captures(trans, cp_c.ifs.get(bi_cpi))
return 1
if k == ast.ASTKind.DictComp:
dcn: ast.DictComp* = ast.DictComp*(node)
@@ -699,14 +701,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if dcn.value is not None: self._closure_scan_captures(trans, dcn.value)
if dcn.generators is not None:
bn_dgen: sizet = len(dcn.generators)
for bi_dgen in range(bn_dgen):
for bi_dgen in range(int(bn_dgen)):
cp_dn: ast.AST* = dcn.generators.get(bi_dgen)
if cp_dn is not None and cp_dn.kind() == ast.ASTKind.Comprehension:
cp_dc: ast.Comprehension* = ast.Comprehension*(cp_dn)
if cp_dc.iter is not None: self._closure_scan_captures(trans, cp_dc.iter)
if cp_dc.ifs is not None:
bn_dcpi: sizet = len(cp_dc.ifs)
for bi_dcpi in range(bn_dcpi): self._closure_scan_captures(trans, cp_dc.ifs.get(bi_dcpi))
for bi_dcpi in range(int(bn_dcpi)): self._closure_scan_captures(trans, cp_dc.ifs.get(bi_dcpi))
return 1
return 1
@@ -780,7 +782,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if fd is None or fd.children is None: return count
ch: list[ast.AST*]* = fd.children
cn: sizet = len(ch)
for ci in range(cn):
for ci in range(int(cn)):
st: ast.AST* = ch.get(ci)
if st is None: continue
k: int = st.kind()
@@ -790,13 +792,13 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if nl_node is not None and nl_node.names is not None:
nl_list: list[ast.AST*]* = nl_node.names
nn: sizet = len(nl_list)
for ni in range(nn):
for ni in range(int(nn)):
nm_node: ast.AST* = nl_list.get(ni)
if nm_node is None: continue
nm_x: ast.Name* = ast.Name*(nm_node)
if nm_x is None or nm_x.id is None: continue
dup: int = 0
for di in range(count):
for di in range(int(count)):
if string.strcmp((str)(uint64(names) + di * 8), nm_x.id) == 0:
dup = 1
break
@@ -820,7 +822,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if trans._nl_chain_names is None: return 0
trans._nl_chain_count = self._collect_chain_nonlocals(trans, node, trans._nl_chain_names, 0)
trans._nl_chain_done = 1
for ci in range(trans._nl_chain_count):
for ci in range(int(trans._nl_chain_count)):
nm_slot: str* = str*(uint64(trans._nl_chain_names) + ci * 8)
nl_name: str = nm_slot[0]
if nl_name is None: continue
@@ -902,7 +904,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
pn: sizet = len(alist)
for pi in range(pn):
for pi in range(int(pn)):
if param_count >= 64: break
arg_n: ast.Arg* = ast.Arg*(alist.get(pi))
if arg_n is None or arg_n.arg is None: continue
@@ -942,7 +944,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled_promoted, i32_ty)
if func is None: return None
mlirlite.add_param(pool, func, i8_ptr_ty, "%env")
for pi2 in range(param_count):
for pi2 in range(int(param_count)):
ty_addr2: uint64 = uint64(param_tys) + pi2 * 8
ty_slot2: str* = str*(ty_addr2)
p_ty2: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_slot2[0])
@@ -987,13 +989,15 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
trans._cur_func = func
trans._cur_builder = func_builder
trans._cur_func_name = mangled_promoted
# P2 willFree 失效表:函数作用域复位(栈槽地址会被后续函数复用,跨函数保留会误报)
self.Gen.VarGen.freed_clear()
GenApi.clear_scope_names(trans)
trans._break_bb = None
trans._continue_bb = None
trans._in_switch_case = 0
# 9. Captured variables: load values from env heap slots into local allocas
for ci in range(cap_count):
for ci in range(int(cap_count)):
# Take the capture name via str pointer offset (like param_names + pi*32). Do not use
# (str)(cap_addr): TPV compiles that to itoa, corrupting define_var keys.
cap_slot: str = cap_names + ci * 64
@@ -1017,7 +1021,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
self.Gen.VarGen.define_var(trans.SymTab, cap_slot, var_alloca)
# 10. Parameter allocas
for pi3 in range(param_count):
for pi3 in range(int(param_count)):
ty_addr4: uint64 = uint64(param_tys) + pi3 * 8
ty_slot4: str* = (str*)(ty_addr4)
p_ty3: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_slot4[0])
@@ -1076,7 +1080,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if env_size_val is not None: env_size_val.Next = None
env_ptr = mlirlite.build_call(builder, "malloc", env_size_val, 1, i8_ptr_ty, 0)
if env_ptr is not None:
for ci3 in range(cap_count):
for ci3 in range(int(cap_count)):
# Same as step 9: null-check via str pointer offset, avoiding itoa semantics
cap_slot3: str = cap_names + ci3 * 64
if cap_slot3[0] == '\0': continue
@@ -1149,10 +1153,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
nm: ast.Name* = ast.Name*(node)
if nm.id is None: return cap_count
# Skip lambda parameters
for pi in range(param_count):
for pi in range(int(param_count)):
if string.strcmp(param_names + pi * 32, nm.id) == 0: return cap_count
# Skip already-captured names
for ci in range(cap_count):
for ci in range(int(cap_count)):
if string.strcmp(cap_names + ci * 64, nm.id) == 0: return cap_count
# Look up the current scope chain
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
@@ -1184,7 +1188,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if bop2.values is not None:
vs: list[ast.AST*]* = bop2.values
vn: sizet = len(vs)
for vi in range(vn):
for vi in range(int(vn)):
cc2 = self._lambda_scan_body(trans, vs.get(vi), param_names, param_count, cap_names, cap_allocas, cap_tys, cc2)
return cc2
if k == ast.ASTKind.Compare:
@@ -1193,7 +1197,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if cmp.comparators is not None:
cl: list[ast.AST*]* = cmp.comparators
cn: sizet = len(cl)
for cvi in range(cn):
for cvi in range(int(cn)):
cc3 = self._lambda_scan_body(trans, cl.get(cvi), param_names, param_count, cap_names, cap_allocas, cap_tys, cc3)
return cc3
if k == ast.ASTKind.Call:
@@ -1202,12 +1206,12 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if cal.args is not None:
als: list[ast.AST*]* = cal.args
an: sizet = len(als)
for ai in range(an):
for ai in range(int(an)):
cc4 = self._lambda_scan_body(trans, als.get(ai), param_names, param_count, cap_names, cap_allocas, cap_tys, cc4)
if cal.keywords is not None:
kws: list[ast.AST*]* = cal.keywords
kn: sizet = len(kws)
for ki in range(kn):
for ki in range(int(kn)):
kw: ast.Keyword* = ast.Keyword*(kws.get(ki))
if kw is not None and kw.value is not None:
cc4 = self._lambda_scan_body(trans, kw.value, param_names, param_count, cap_names, cap_allocas, cap_tys, cc4)
@@ -1232,7 +1236,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if ls.elts is not None:
es: list[ast.AST*]* = ls.elts
en: sizet = len(es)
for ei in range(en):
for ei in range(int(en)):
cc7 = self._lambda_scan_body(trans, es.get(ei), param_names, param_count, cap_names, cap_allocas, cap_tys, cc7)
return cc7
if k == ast.ASTKind.Lambda:
+15 -7
View File
@@ -164,9 +164,17 @@ def is_cdefine_found() -> int:
# ---------------- register_cdefine_constant (delegates to TypeGenCDef.register_cdefine_constant) ----------------
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64):
"""Delegate to gen.register_cdefine_constant"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value)
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0):
"""Delegate to gen.register_cdefine_constant (bits: 0=未知;8/16/32/64=已知位宽)"""
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value, bits)
# ---------------- cdefine_decl_bits (delegates to AssignGenMixin.cdefine_decl_bits) ----------------
def cdefine_decl_bits(pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.AST*) -> int:
"""define 声明的位宽裁决(注解 → 右侧显式强转 → 值的最小可存宽度);0=非整型/无法裁决。
跨模块(.vpi 重解析 define 的 AST)注册时必须用它,否则使用方位宽与定义方不一致。"""
if _g_gen is not None: return _g_gen.AssignGen.cdefine_decl_bits(pool, annot, value_node)
return 0
# ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ----------------
@@ -341,7 +349,7 @@ def compute_willfree_mask(fd: ast.AST*) -> int:
dn: sizet = len(dl)
if dn == 0: return 0
mask: int = 0
for i in range(dn):
for i in range(int(dn)):
deco: ast.AST* = dl.get(i)
if deco is None or deco.kind() != ast.ASTKind.Call: continue
dc: ast.Call* = ast.Call*(deco)
@@ -352,7 +360,7 @@ def compute_willfree_mask(fd: ast.AST*) -> int:
# @life(<tag>, <tag>, ...):逐个标签并入
if dc.args is not None:
an: sizet = len(dc.args)
for j in range(an):
for j in range(int(an)):
mask = _WillFreeMergeTag(fn, dc.args.get(j), mask)
elif string.strcmp(fname.id, "willFree") == 0:
# 兼容形式:@willFree(0) 直接叠加(与 @life 等价、可混用)
@@ -636,7 +644,7 @@ def _WillFreeArgIndex(fn: ast.FunctionDef*, arg: ast.AST*) -> int:
if arg.kind() == ast.ASTKind.Name:
nma: ast.Name* = ast.Name*(arg)
if nma.id is None: return -1
for k in range(pn):
for k in range(int(pn)):
pa: ast.AST* = pl.get(k)
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
aa: ast.Arg* = ast.Arg*(pa)
@@ -901,7 +909,7 @@ def fail_if_fstring_escape(node: ast.AST*, where: str) -> int:
tp: ast.Tuple* = ast.Tuple*(node)
if tp.elts is not None:
en: sizet = len(tp.elts)
for i in range(en):
for i in range(int(en)):
e: ast.AST* = tp.elts.get(i)
if e is not None:
ek: int = e.kind()
+14 -12
View File
@@ -49,7 +49,7 @@ novirtual class TranslatorGenMixin:
if ch is None: return 0
cn_count: sizet = len(ch)
added_total: int = 0
for ci in range(cn_count):
for ci in range(int(cn_count)):
child: ast.AST* = ch.get(ci)
if child is None: continue
kd: int = child.kind()
@@ -123,7 +123,7 @@ novirtual class TranslatorGenMixin:
al: list[ast.AST*]* = ags.args
an: sizet = len(al)
ai: sizet
for ai in range(an):
for ai in range(int(an)):
ag: ast.Arg* = ast.Arg*(al.get(ai))
if ag is not None and ag.annotation is not None: self.Gen.TypeGenResolve.resolve_annotation_type(pool, ag.annotation, imp, fi, trans, 0)
@@ -194,7 +194,7 @@ novirtual class TranslatorGenMixin:
elem_ty_str_n: str = pool.alloc_tagged(4096, memhub.DETSITE_TRANSLATORGEN_185)
if elem_ty_str_n is None: return None
mlirlite.TypePrint(elem_ty_str_n, 4096, elem_ty, pool)
for i_n in range(cnt_total_n):
for i_n in range(int(cnt_total_n)):
sub_n: str = None
if i_n < elts_count:
en_n: ast.AST* = elts.get(i_n)
@@ -216,7 +216,7 @@ novirtual class TranslatorGenMixin:
pos_n += sizet(written_n)
viperlib.snprintf(buf_n + pos_n, buf_size_n - pos_n, "]")
return buf_n
for zi in range(elts_count):
for zi in range(int(elts_count)):
ze: ast.AST* = elts.get(zi)
if ze is None: continue
zk: int = ze.kind()
@@ -245,7 +245,7 @@ novirtual class TranslatorGenMixin:
# Write elements up to the declared length arr_count, zero-padding short lists so the
# initializer element count always matches the type llc expects.
for i in range(cnt_total):
for i in range(int(cnt_total)):
elem_val: int64 = 0
if i < elts_count:
elem_node: ast.AST* = elts.get(i)
@@ -291,7 +291,7 @@ novirtual class TranslatorGenMixin:
if buf is None: return None
written: int = viperlib.snprintf(buf, buf_size, "[")
pos: sizet = sizet(written)
for i in range(sizet(arr_count)):
for i in range(int(sizet(arr_count))):
if i > 0:
written = viperlib.snprintf(buf + pos, buf_size - pos, ", ")
pos += sizet(written)
@@ -841,7 +841,7 @@ novirtual class TranslatorGenMixin:
pos_v += sizet(written_v)
else:
fc_v: int = resolved_stg_entry.FieldCount
for fi_v in range(1, fc_v):
for fi_v in range(1, int(fc_v)):
fe_v: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(resolved_stg_entry, fi_v)
if fe_v is None or fe_v.Ty is None: continue
ty_buf_v: str = pool.alloc_tagged(4096, memhub.DETSITE_TRANSLATORGEN_832)
@@ -951,13 +951,15 @@ novirtual class TranslatorGenMixin:
# Set the current translation context (Handles access it via self.Trans._cur_builder)
trans._cur_func = main_func
trans._cur_builder = builder
# P2 willFree 失效表:进入 main 体前复位(避免上一翻译单元残留的栈槽失效态泄漏进来)
self.Gen.VarGen.freed_clear()
# Pre-process module-level variables (create LLVM globals) before pre_scan_allocas, or
# PreScan would create local allocas that other functions reference by SSA number.
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn_count: sizet = len(ch)
for ci in range(cn_count):
for ci in range(int(cn_count)):
child: ast.AST* = ch.get(ci)
if child is not None:
kd: int = child.kind()
@@ -966,7 +968,7 @@ novirtual class TranslatorGenMixin:
# Prescan top-level statements to pre-create allocas for in-function AnnAssign; module-level
# variables are already registered so PreScan's lookup_current skips them.
if ch is not None:
for ci in range(cn_count):
for ci in range(int(cn_count)):
child: ast.AST* = ch.get(ci)
if child is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, child)
@@ -1003,7 +1005,7 @@ novirtual class TranslatorGenMixin:
def is_global_name(self, trans: HT.Translator*, name: str) -> int:
"""Return 1 when name is in the current function's global set."""
if trans is None or name is None or trans._global_names is None: return 0
for i in range(trans._global_name_count):
for i in range(int(trans._global_name_count)):
entry_addr: uint64 = uint64(trans._global_names) + i * 8
slot_ptr: str* = (str*)(entry_addr)
entry: str = slot_ptr[0]
@@ -1062,7 +1064,7 @@ novirtual class TranslatorGenMixin:
def is_nonlocal_name(self, trans: HT.Translator*, name: str) -> int:
"""Return 1 when name is in the current function's nonlocal set."""
if trans is None or name is None or trans._nonlocal_names is None: return 0
for i in range(trans._nonlocal_name_count):
for i in range(int(trans._nonlocal_name_count)):
entry_addr: uint64 = uint64(trans._nonlocal_names) + i * 8
slot_ptr: str* = (str*)(entry_addr)
entry: str = slot_ptr[0]
@@ -1073,7 +1075,7 @@ novirtual class TranslatorGenMixin:
def get_nonlocal_index(self, trans: HT.Translator*, name: str) -> int:
"""Return the 0-based env index of a nonlocal variable; -1 when not found."""
if trans is None or name is None or trans._nonlocal_names is None: return -1
for i in range(trans._nonlocal_name_count):
for i in range(int(trans._nonlocal_name_count)):
entry_addr: uint64 = uint64(trans._nonlocal_names) + i * 8
slot_ptr: str* = (str*)(entry_addr)
entry: str = slot_ptr[0]
+8 -4
View File
@@ -41,7 +41,10 @@ novirtual class TypeGenBase:
CDefineNames: list[str]*
CDefineValues: list[str]*
CDefineDoubleFlags: list[int]*
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
# 值表达式的**位宽**(bit);0=未知(按值推断)。来源:右侧显式强转(如 WORD(0x0080))优先,
# 否则声明注解(define X: WORD = ... 的 T)。物化时据此定宽,避免小位宽常量被提升为 i32。
CDefineBits: list[int]*
CDefineIsDouble: int
CDefineIsStr: int
CDefineFound: int
@@ -111,7 +114,8 @@ novirtual class TypeGenBase:
self.CDefineNames = None
self.CDefineValues = None
self.CDefineDoubleFlags = None
self.CDefineStrFlags = None
self.CDefineStrFlags = None
self.CDefineBits = None
self.CDefineIsDouble = 0
self.CDefineIsStr = 0
self.CDefineFound = 0
@@ -197,7 +201,7 @@ novirtual class TypeGenBase:
frame.col_end = 0
def clear_trace(self) -> None:
"""Clear the trace stack (called before translating each file)."""
for i in range(self.TraceCount):
for i in range(int(self.TraceCount)):
frame: GenTypes.TraceFrame* = self._get_trace_frame(i)
if frame is not None:
frame.file_name = None
@@ -249,7 +253,7 @@ novirtual class TypeGenBase:
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
rg_n: sizet = len(self.GenericTpNames)
rg_i: sizet
for rg_i in range(rg_n):
for rg_i in range(int(rg_n)):
rg_tpn: str = self.GenericTpNames.get(rg_i)
if rg_tpn is not None and string.strcmp(name, rg_tpn) == 0:
rg_ta: str = self.GenericTypeArgs.get(rg_i)
+49 -16
View File
@@ -64,7 +64,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
h: uint64 = self._cdef_idx_hash(name)
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
si: sizet = sizet(h) & mask
for i in range(self.CDefineNameIdxCap):
for i in range(int(self.CDefineNameIdxCap)):
slot: sizet* = self._cdef_idx_slot(si)
sv: sizet = slot[0]
if sv == 0: return -1
@@ -80,7 +80,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
h: uint64 = self._cdef_idx_hash(name)
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
si: sizet = sizet(h) & mask
for i in range(self.CDefineNameIdxCap):
for i in range(int(self.CDefineNameIdxCap)):
slot: sizet* = self._cdef_idx_slot(si)
if slot[0] == 0:
slot[0] = sizet(slot_index + 1)
@@ -101,8 +101,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool
return pool
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64) -> None:
"""Register an integer CDefine constant; store the value as a decimal string."""
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0) -> None:
"""Register an integer CDefine constant; store the value as a decimal string.
bits: 值表达式的位宽(0=未知,按值推断;8/16/32/64 = 显式强转或声明注解给出的位宽)。
"""
if name is None: return
if self.CDefineNames is None:
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
@@ -110,19 +113,21 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
tv: list[str]* = list[str](tp, 64)
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
tbits: list[int]* = list[int](tp, 64)
# Table build failed (pool unusable/exhausted): report and return, never let the
# append below hit None or an uninitialized address (would write NULL/0xCD).
if tn is None or tv is None or td is None or tsf is None:
if tn is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# Table-ready guard: missing/half-initialized body -> fail-fast, no garbage writes
# (assert non-null before append).
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
name_copy: str = pool.alloc(string.strlen(name) + 1)
@@ -142,6 +147,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if val_buf is None: self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' value buffer allocation failed", "MemoryError")
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
if self.CDefineBits is not None: self.CDefineBits.append(bits)
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double) -> None:
"""Register a float CDefine constant; store the value as %f text and flag it."""
if name is None: return
@@ -151,16 +157,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
tv: list[str]* = list[str](tp, 64)
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
tbits: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
@@ -179,6 +187,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
else: self.CDefineValues.append("0")
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(1)
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
if self.CDefineBits is not None: self.CDefineBits.append(0)
# fail-fast: a failed CDefine value buffer must abort compilation
if val_buf is None:
VLogger.error("CDefine float value buffer allocation failed", "CDEF-DBG")
@@ -195,16 +204,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
tv: list[str]* = list[str](tp, 64)
td: list[int]* = list[int](tp, 64)
tsf: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
tbits: list[int]* = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None or tbits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self.CDefineBits = tbits
self._cdef_idx_init(tp)
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
return
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
@@ -223,6 +234,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
self.CDefineValues.append(vcopy)
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(1)
if self.CDefineBits is not None: self.CDefineBits.append(0)
# Also set the "last result is a string" flag (read by is_cdefine_str()).
# Cross-module string CDefine reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash.
self.CDefineIsStr = 1
@@ -235,7 +247,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if self.XmodCdefNames is not None:
x_n: sizet = len(self.XmodCdefNames)
x_i: sizet
for x_i in range(x_n):
for x_i in range(int(x_n)):
x_nm: str = self.XmodCdefNames.get(x_i)
if x_nm is not None and string.strcmp(x_nm, name) == 0: return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
@@ -266,7 +278,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if name is None or self.XmodCdefNames is None: return None
n: sizet = len(self.XmodCdefNames)
i: sizet
for i in range(n):
for i in range(int(n)):
nm: str = self.XmodCdefNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.XmodCdefValues.get(i)
@@ -293,7 +305,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if cd_hit < 0: return 0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
for i in range(int(cd_start), int(cd_end)):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
@@ -309,6 +321,26 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
return string.atoll(val_str)
return string.atoll(val_str)
return 0
def lookup_cdefine_bits(self, name: str) -> int:
"""Look up a CDefine constant's value-expression bit width; 0 = unknown (width inferred by value).
同一名字可能被注册**多次**:本模块注册带位宽(如 WORD(...) 或声明注解),而 stub/.vpi
等路径以 bits=0 注册。若用「命中即返回」,哈希索引先命中的可能是那条 0 位宽记录,
于是已知位宽被丢掉、常量被物化成 i32。故这里**全表扫描并优先返回非 0 位宽**。
"""
if self.CDefineNames is None or self.CDefineBits is None or name is None: return 0
n: sizet = len(self.CDefineNames)
# 同一名字可能被注册多次:stub/.vpi 等早期路径先注册(位宽可能未知 → 兜底值),
# 本模块自己的 define 翻译在后(注解/强转齐全,最权威)。故取**最后**一条非零记录,
# 而不是"命中即返回"或"第一条非零"——否则早退的 stub 记录会盖掉真实位宽。
found: int = 0
for i in range(int(n)):
nm: str = self.CDefineNames.get(i)
if nm is None or string.strcmp(nm, name) != 0: continue
if sizet(i) >= len(self.CDefineBits): continue
b: int = self.CDefineBits.get(i)
if b != 0: found = b
return found
def lookup_cdefine_double(self, name: str) -> double:
"""Look up a CDefine constant and return its double value (float or int)."""
self.CDefineFound = 0
@@ -326,7 +358,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if cd_hit < 0: return 0.0
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
for i in range(int(cd_start), int(cd_end)):
nm: str = self.CDefineNames.get(i)
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs.
if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0:
@@ -360,7 +392,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if cd_hit < 0: return None
cd_start = sizet(cd_hit)
cd_end = sizet(cd_hit + 1)
for i in range(cd_start, cd_end):
for i in range(int(cd_start), int(cd_end)):
nm: str = self.CDefineNames.get(i)
if nm is not None and string.strcmp(nm, name) == 0:
val_str: str = self.CDefineValues.get(i)
@@ -388,7 +420,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
if self.CDefineNames is not None: self.CDefineNames.clear()
if self.CDefineValues is not None: self.CDefineValues.clear()
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.clear()
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
if self.CDefineBits is not None: self.CDefineBits.clear()
# Clear index slots (store index+1; 0=empty) and reset Built to 0 (empty table and index
# in sync, matching a freshly built table before its first register).
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
+21 -9
View File
@@ -82,7 +82,7 @@ novirtual class VarGenMixin:
"""Look up by name in the variable table"""
if name is None or vars_ptr is None: return None
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
for i in range(int(var_count)):
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry* = GenTypes.VarEntry*(entry_addr)
if entry.Name is not None and entry.Used != 0:
@@ -96,7 +96,7 @@ novirtual class VarGenMixin:
"""Look up by name in the variable table, return the VarEntry or None"""
if name is None or vars_ptr is None: return None
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
for i in range(int(var_count)):
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry* = GenTypes.VarEntry*(entry_addr)
if entry.Name is not None and entry.Used != 0:
@@ -403,6 +403,18 @@ novirtual class VarGenMixin:
_g_freed_count = int(n)
return 0
def freed_clear(self) -> None:
"""进入新函数体:清空失效槽表(函数作用域复位)。
表按 **alloca 槽地址** 记录(_FreedCheck 比较槽指针),而栈槽地址会被后续函数复用
(前帧已销毁)⇒ 若跨函数保留,上一函数的失效态会误加到新函数的同名/同址变量上
(现场:ExprGenCls._resolve_doc_symbol_attr 首行读形参 out_sym 即报 "already released",
而该函数体内根本没有 willFree 调用——是上一个函数的栈槽地址被复用)。
故每个函数体入口清零;嵌套函数体结束后不恢复外层失效态(仅为少报,绝非误报)。
"""
global _g_freed_count
_g_freed_count = 0
def freed_join(self, buf: sizet*) -> int:
"""合流:当前失效槽表与快照**求交**(只有两路都失效才算失效);0=完成 / 1=无快照"""
global _g_freed_slots, _g_freed_count
@@ -753,7 +765,7 @@ novirtual class VarGenMixin:
def _SlifeSlotFirstSeen(self, idx: int, slot: uint64) -> int:
"""槽 slot 在 (idx, _g_slife_top) 里是否已经出现过;1=本次首次出现(该释放)/ 0=重复(跳过)"""
j: int = idx + 1
j: int = int(idx + 1)
while j < _g_slife_top:
if uint64(_g_slife_owned[j * 2]) == slot: return 0
j += 1
@@ -769,7 +781,7 @@ novirtual class VarGenMixin:
while i >= _g_slife_count:
slot: uint64 = uint64(_g_slife_owned[i * 2])
sh: mlirlite.Value* = mlirlite.Value*(_g_slife_owned[i * 2 + 1])
if sh is not None and self._SlifeSlotFirstSeen(i, slot) != 0:
if sh is not None and self._SlifeSlotFirstSeen(int(i), slot) != 0:
self._SlifeEmitGuardedFree(pool, builder, mod, trans, mlirlite.Value*(_g_slife_owned[i * 2]), sh)
i -= 1
return 0
@@ -830,7 +842,7 @@ novirtual class VarGenMixin:
if ctx.Marker != GenTypes.REPL_CTX_MARKER: return None
if ctx.Table is None or ctx.Count <= 0: return None
entry_size: sizet = GenTypes.ReplVarEntry.__sizeof__()
for i in range(ctx.Count):
for i in range(int(ctx.Count)):
entry_addr: uint64 = uint64(ctx.Table) + i * entry_size
entry: GenTypes.ReplVarEntry* = GenTypes.ReplVarEntry*(entry_addr)
if entry.Name is not None and string.strcmp(entry.Name, name) == 0: return entry
@@ -879,7 +891,7 @@ novirtual class VarGenMixin:
if ctx.Marker != GenTypes.REPL_CTX_MARKER: return None
if ctx.FuncTable is None or ctx.FuncCount <= 0: return None
entry_size: sizet = GenTypes.ReplFuncEntry.__sizeof__()
for i in range(ctx.FuncCount):
for i in range(int(ctx.FuncCount)):
entry_addr: uint64 = uint64(ctx.FuncTable) + i * entry_size
entry: GenTypes.ReplFuncEntry* = GenTypes.ReplFuncEntry*(entry_addr)
if entry.Name is not None and string.strcmp(entry.Name, name) == 0: return entry
@@ -938,7 +950,7 @@ novirtual class VarGenMixin:
if ptypes is None: return None
string.memset(ptypes, 0, 8 * GenTypes.REPL_FUNC_PARAM_MAX)
ai_chk: int
for ai_chk in range(entry.ParamCount):
for ai_chk in range(int(entry.ParamCount)):
pty_chk: mlirlite.LLVMType* = self.repl_func_ty(pool, int(entry.ParamTags[ai_chk]), int(entry.ParamBits[ai_chk]))
if pty_chk is None:
self.Gen.TypeGenErr.fatal_error(node, f"REPL: type token of parameter {ai_chk + 1} of session function '{func_name}' is not supported (M3 supports iN/float/double/i8*)", "TypeError")
@@ -951,7 +963,7 @@ novirtual class VarGenMixin:
tail: mlirlite.Value* = head
arg_count: int = 1
ai: int
for ai in range(entry.ParamCount):
for ai in range(int(entry.ParamCount)):
arg: ast.AST* = cargs.get(ai)
if arg is None:
self.Gen.TypeGenErr.fatal_error(node, f"REPL: argument {ai + 1} of session function '{func_name}' is missing", "SyntaxError")
@@ -983,7 +995,7 @@ novirtual class VarGenMixin:
fp_first: mlirlite.ParamNode* = None
fp_prev: mlirlite.ParamNode* = None
pi: int
for pi in range(arg_count):
for pi in range(int(arg_count)):
cur_ty: mlirlite.LLVMType* = i8_ptr_ty
if pi > 0:
pty_slot3: str* = (str*)(uint64(ptypes) + (pi - 1) * 8)
+82 -83
View File
@@ -1,4 +1,3 @@
import t
from stdint import *
import stdio
import stdlib
@@ -7,10 +6,10 @@ import viperlib
import mlirlite
import memhub
import ast
import w32.fileio as fileio
import w32.win32file as w32file
import w32.win32base as win32base
import w32.win32process as win32process
import win32.fileio as fileio
import win32.win32file as win32file
import win32.win32base as win32base
import win32.win32process as win32process
import lib.core.VLogger as VLogger
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.GenTypes as GenTypes
@@ -159,7 +158,7 @@ def ReplFindProject(Out: bytes, Cap: int, Diag: bytes, DiagCap: int) -> int:
c1[0] = 0
c2[0] = 0
# ① cwd/repl.vpj
n_cwd: ULONG = w32file.GetCurrentDirectoryA(ULONG(Cap), c1)
n_cwd: ULONG = win32file.GetCurrentDirectoryA(ULONG(Cap), c1)
if n_cwd > 0:
cl: int = int(n_cwd)
# Defensive: GetCurrentDirectoryA writes are undefined on overflow, so force a NUL.
@@ -173,7 +172,7 @@ def ReplFindProject(Out: bytes, Cap: int, Diag: bytes, DiagCap: int) -> int:
return 1
else: string.strcpy(c1, "(GetCurrentDirectoryA failed)")
# 2) exe-dir/repl.vpj (GetModuleFileNameA(NULL) returns our own full path).
n_exe: ULONG = w32file.GetModuleFileNameA(None, c2, ULONG(Cap))
n_exe: ULONG = win32file.GetModuleFileNameA(None, c2, ULONG(Cap))
if n_exe > 0:
el: int = int(n_exe)
# Defensive: on truncation the return equals Cap and the string may lack a NUL.
@@ -321,10 +320,10 @@ ReplIncClsMissing: int = 0
def _is_file(path: str) -> int:
"""1=exists and is not a directory; 0=missing, a directory, or bad args."""
if path is None: return 0
attr: ULONG = w32file.GetFileAttributesA(path)
attr: ULONG = win32file.GetFileAttributesA(path)
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
return 0
if (attr & w32file.FILE_ATTRIBUTE_DIRECTORY) != 0: return 0
if (attr & win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: return 0
return 1
@@ -340,7 +339,7 @@ def ReplFindHostImplib() -> int:
if exe_buf is None or cand is None: return 0
exe_buf[0] = 0
cand[0] = 0
n_exe: ULONG = w32file.GetModuleFileNameA(None, exe_buf, ULONG(_REPL_PATH_CAP))
n_exe: ULONG = win32file.GetModuleFileNameA(None, exe_buf, ULONG(_REPL_PATH_CAP))
if n_exe <= 0: return 0
el: int = int(n_exe)
# Defensive: on truncation the write may lack a NUL (same as ReplFindProject).
@@ -379,7 +378,7 @@ def _read_line(h: win32base.HANDLE, buf: bytes, cap: int) -> int:
eof: int = 0
while total < cap - 1:
bytes_read = 0
ok = w32file.ReadFile(h, buf + total, 1, &bytes_read, None)
ok = win32file.ReadFile(h, buf + total, 1, &bytes_read, None)
if ok == 0 or bytes_read == 0:
eof = 1
break
@@ -415,7 +414,7 @@ def _line_starts_block(line: str, n: int) -> int:
while s < n and (line[s] == ' ' or line[s] == '\t'): s += 1
if s >= n: return 0
if line[s] == '@': return 1
i: int = n
i: int = int(n)
while i > 0:
ch: char = line[i - 1]
if ch != ' ' and ch != '\t': break
@@ -521,7 +520,7 @@ def _repl_base_cdefine_find(name: str) -> int:
"""Find a baseline CDefine entry index by name; -1 when not found."""
if name is None or ReplBaseCDefineNames is None: return -1
i: int
for i in range(ReplBaseCDefineCount):
for i in range(int(ReplBaseCDefineCount)):
nm: str = rawptr(ReplBaseCDefineNames[i])
if nm is not None and string.strcmp(nm, name) == 0: return i
return -1
@@ -532,7 +531,7 @@ def ReplBaselineRestore(pool: memhub.MemBuddy*) -> int:
if pool is None or ReplBaseCDefineNames is None: return 1
if GenApi._g_gen is None: return 1
i: int
for i in range(ReplBaseCDefineCount):
for i in range(int(ReplBaseCDefineCount)):
nm: str = rawptr(ReplBaseCDefineNames[i])
val: str = rawptr(ReplBaseCDefineValues[i])
kind: int = int(ReplBaseCDefineKinds[i])
@@ -543,7 +542,7 @@ def ReplBaselineRestore(pool: memhub.MemBuddy*) -> int:
# String CDefine: GenApi has no wrapper, so call gen directly (same shape as
# the clear_cdefine_constants wrapper).
GenApi._g_gen.TypeGenCDef.register_cdefine_string(pool, nm, val)
for i in range(ReplBaseCTypedefCount):
for i in range(int(ReplBaseCTypedefCount)):
tn: str = rawptr(ReplBaseCTypedefNames[i])
tv: str = rawptr(ReplBaseCTypedefTypeStrs[i])
if tn is None or tv is None: continue
@@ -562,7 +561,7 @@ def ReplBaselineCapture() -> int:
cdef_str: list[int]* = GenApi._g_gen.TypeGenCDef.CDefineStrFlags
n: sizet = len(cdef_names)
i: sizet
for i in range(n):
for i in range(int(n)):
nm: str = cdef_names.get(i)
if nm is None: continue
if _repl_base_cdefine_find(nm) >= 0: continue
@@ -586,13 +585,13 @@ def ReplBaselineCapture() -> int:
ct_vals: list[str]* = GenApi._g_gen.TypeGenCType.CTypedefTypeStrs
cn2: sizet = len(ct_names)
i2: sizet
for i2 in range(cn2):
for i2 in range(int(cn2)):
tn: str = ct_names.get(i2)
if tn is None: continue
if ReplBaseCTypedefCount >= _REPL_BASE_CTYPEDEF_MAX: break
dup_hit: int = 0
k: int
for k in range(ReplBaseCTypedefCount):
for k in range(int(ReplBaseCTypedefCount)):
ex: str = rawptr(ReplBaseCTypedefNames[k])
if ex is not None and string.strcmp(ex, tn) == 0:
dup_hit = 1
@@ -637,12 +636,12 @@ def ReplFragImplibRecord(implib_path: str) -> int:
def _repl_cmd_append_implibs(cmd: bytes, cap: int, pos: int) -> int:
if cmd is None or ReplFragImplibs is None: return pos
i: int
for i in range(ReplFragImplibCount):
for i in range(int(ReplFragImplibCount)):
p: str = rawptr(ReplFragImplibs[i])
if p is None: continue
pos = _repl_cat_str(cmd, cap, pos, " ")
pos = _repl_cat_str(cmd, cap, int(pos), " ")
if pos < 0: return -1
pos = _repl_cat_str(cmd, cap, pos, p)
pos = _repl_cat_str(cmd, cap, int(pos), p)
if pos < 0: return -1
return pos
@@ -688,10 +687,10 @@ def ReplInitIncludesClasses(mb: memhub.MemBuddy*, sr: IncludesScanner.ScanResult
budget_t0: uint64 = win32base.GetTickCount64()
budget_hit: int = 0
p_i: int
for p_i in range(_REPL_INC_CLS_PASSES):
for p_i in range(int(_REPL_INC_CLS_PASSES)):
pass_ok: int = 0
i: int
for i in range(n_mod):
for i in range(int(n_mod)):
if int(st[i]) != 0: continue
# Startup budget (one time check per module; GetTickCount64 is cheap): on overrun
# stop, leaving the remaining modules to the on-demand import path.
@@ -758,7 +757,7 @@ def ReplInitIncludesClasses(mb: memhub.MemBuddy*, sr: IncludesScanner.ScanResult
# Summary (aggregated warnings): per-module warnings were capped, so print full counts.
skip_cnt: int = 0
si: int
for si in range(n_mod):
for si in range(int(n_mod)):
if int(st[si]) == 0: skip_cnt += 1
stdio.printf("includes classes: registered vpi classes for %d modules (failed %d, missing stub %d, " "not attempted/to retry %d, warnings dropped %d)\n", ReplIncClsMods, ReplIncClsFail, ReplIncClsMissing, skip_cnt, warn_dropped)
if budget_hit != 0:
@@ -786,7 +785,7 @@ def ReplInitSession() -> int:
# 1) %TEMP% prefix (GetTempPathA returns a trailing backslash; 0 means failure).
tmp_buf: bytes = _perm_pool.alloc(_REPL_TMP_CAP)
if tmp_buf is None: return 1
tmp_len: ULONG = w32file.GetTempPathA(_REPL_TMP_CAP, tmp_buf)
tmp_len: ULONG = win32file.GetTempPathA(_REPL_TMP_CAP, tmp_buf)
if tmp_len == 0: return 1
if int(tmp_len) >= _REPL_TMP_CAP: return 1
# 2) Session dir <TEMP>TransPyV_repl/<pid> (pid isolates sessions; paths use '/').
@@ -919,7 +918,7 @@ def _repl_gen_includes_vpi(mb: memhub.MemBuddy*, sr: IncludesScanner.ScanResult*
es: sizet = IncludesScanner.FileEntry.__sizeof__()
dl: sizet = string.strlen(vpi_dir)
n: int
for n in range(sr.Count):
for n in range(int(sr.Count)):
ent: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(uint64(sr.Entries) + uint64(n) * es)
if ent is None or ent.Sha1 is None or ent.Path is None:
ReplIncVpiFail += 1
@@ -1061,7 +1060,7 @@ def ReplInitIncludes(mb: memhub.MemBuddy*) -> int:
def _repl_show_line(p: bytes, n: int) -> str:
"""Copy a line fragment into a session buffer as a NUL-terminated string (truncated to 118 bytes)."""
if ReplLineShow is None or p is None: return "(?)"
m: int = n
m: int = int(n)
if m > 118: m = 118
if m < 0: m = 0
string.memcpy(ReplLineShow, p, sizet(m))
@@ -1106,7 +1105,7 @@ def _repl_cat(dst: bytes, cap: int, pos: int, src: bytes, n: int) -> int:
# Append a NUL-terminated string to dst; returns new length, -1 = out of capacity.
def _repl_cat_str(dst: bytes, cap: int, pos: int, s: str) -> int:
if s is None: return pos
return _repl_cat(dst, cap, pos, s, int(string.strlen(s)))
return _repl_cat(dst, cap, int(pos), s, int(string.strlen(s)))
# Record a lifted function name (reserved-prefix check + same-fragment dedup + limit); 0=ok, 1=reject.
@@ -1121,7 +1120,7 @@ def _repl_lift_add_name(name: str) -> int:
stdio.printf("M3: session function name cannot start with the reserved prefix '%s': %s\n", pfx, name)
return 1
i: int
for i in range(ReplLiftNameCount):
for i in range(int(ReplLiftNameCount)):
if string.strcmp(ReplLiftNames + i * _REPL_FN_NAME_MAX, name) == 0:
stdio.printf("M3: session function '%s' defined twice in the same fragment (define it only once)\n", name)
return 1
@@ -1149,10 +1148,10 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
p += 1
nlen: int = p - ns
if nlen <= 0:
stdio.printf("M3: def header is missing the function name: %s\n", _repl_show_line(line, n))
stdio.printf("M3: def header is missing the function name: %s\n", _repl_show_line(line, int(n)))
return 1
if nlen >= name_cap:
stdio.printf("M3: session function name too long (limit %d bytes): %s\n", name_cap - 1, _repl_show_line(line, n))
stdio.printf("M3: session function name too long (limit %d bytes): %s\n", name_cap - 1, _repl_show_line(line, int(n)))
return 1
string.memcpy(name_out, line + ns, sizet(nlen))
name_out[nlen] = 0
@@ -1168,10 +1167,10 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
break
p += 1
if d != 0:
stdio.printf("M3: unclosed generic-parameter bracket in def header: %s\n", _repl_show_line(line, n))
stdio.printf("M3: unclosed generic-parameter bracket in def header: %s\n", _repl_show_line(line, int(n)))
return 1
if p >= n or line[p] != '(':
stdio.printf("M3: def header is missing '(' after the function name: %s\n", _repl_show_line(line, n))
stdio.printf("M3: def header is missing '(' after the function name: %s\n", _repl_show_line(line, int(n)))
return 1
lp: int = p
# Matching ')'.
@@ -1188,7 +1187,7 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
break
q += 1
if rp < 0:
stdio.printf("M3: def header is missing ')' (the signature must be on one line): %s\n", _repl_show_line(line, n))
stdio.printf("M3: def header is missing ')' (the signature must be on one line): %s\n", _repl_show_line(line, int(n)))
return 1
# ':' must be on the same line (as must the return annotation).
has_colon: int = 0
@@ -1199,7 +1198,7 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
break
q2 += 1
if has_colon == 0:
stdio.printf("M3: def header ':' is not on the same line (the signature must be on one line): %s\n", _repl_show_line(line, n))
stdio.printf("M3: def header ':' is not on the same line (the signature must be on one line): %s\n", _repl_show_line(line, int(n)))
return 1
# Whether the parameter list is empty.
empty: int = 1
@@ -1219,12 +1218,12 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
seg += 1
# Emit: "export " + original header [0,lp] + injection + original header [lp+1,n) + '\n'.
pos: int = int(string.strlen(ReplLiftBuf))
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "export ")
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, pos, line, lp + 1)
if empty != 0: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "__repl_ctx__: bytes")
else: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "__repl_ctx__: bytes, ")
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, pos, line + lp + 1, n - lp - 1)
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "\n")
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "export ")
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), line, lp + 1)
if empty != 0: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "__repl_ctx__: bytes")
else: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "__repl_ctx__: bytes, ")
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), line + lp + 1, n - lp - 1)
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "\n")
if pos < 0:
stdio.printf("M3: lift source buffer too small (%d bytes)\n", _REPL_LIFT_CAP)
return 2
@@ -1244,28 +1243,28 @@ def _repl_split_body(body: bytes) -> int:
in_body: int = 0 # past the def header (indented lines are the body; check raise)
name_buf: bytes = ReplMsgName
while i < total:
n: int = _repl_line_len(body + i)
ind: int = _repl_indent_len(body + i, n)
n: int = int(_repl_line_len(body + i))
ind: int = int(_repl_indent_len(body + i, int(n)))
blank: int = 0
if ind >= n: blank = 1
if in_lift != 0:
if blank != 0:
# Blank line: stays in the lift block (blank body lines are indentation-neutral).
lp0: int = int(string.strlen(ReplLiftBuf))
lp0 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp0, body + i, n)
lp0 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp0, body + i, int(n))
lp0 = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, lp0, "\n")
if lp0 < 0: return 2
i += n + 1
continue
if ind > 0:
if need_header != 0:
stdio.printf("M3: a def header must immediately follow the decorator: %s\n", _repl_show_line(body + i, n))
stdio.printf("M3: a def header must immediately follow the decorator: %s\n", _repl_show_line(body + i, int(n)))
return 1
if in_body != 0 and _repl_line_has_kw(body + i, n, ind, "raise") != 0:
stdio.printf("M3: session function '%s' has raise in its body: %s\n" " M3 does not support cross-fragment exception propagation (raise appends the " "__eh_msg_out__/__eh_code_out__ out params to the function, " "but indirect calls through the function pointer do not pass them) -- the whole fragment is skipped\n", name_buf, _repl_show_line(body + i, n))
if in_body != 0 and _repl_line_has_kw(body + i, int(n), ind, "raise") != 0:
stdio.printf("M3: session function '%s' has raise in its body: %s\n" " M3 does not support cross-fragment exception propagation (raise appends the " "__eh_msg_out__/__eh_code_out__ out params to the function, " "but indirect calls through the function pointer do not pass them) -- the whole fragment is skipped\n", name_buf, _repl_show_line(body + i, int(n)))
return 1
lp1: int = int(string.strlen(ReplLiftBuf))
lp1 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp1, body + i, n)
lp1 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp1, body + i, int(n))
lp1 = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, lp1, "\n")
if lp1 < 0: return 2
i += n + 1
@@ -1277,9 +1276,9 @@ def _repl_split_body(body: bytes) -> int:
continue
if blank != 0 or ind > 0:
# Into the fragment body (4 spaces per line, same as the block path).
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, " ")
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, body + i, n)
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, "\n")
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), " ")
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), body + i, int(n))
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), "\n")
if pos < 0: return 2
i += n + 1
continue
@@ -1289,13 +1288,13 @@ def _repl_split_body(body: bytes) -> int:
need_header = 1
in_body = 0
lp2: int = int(string.strlen(ReplLiftBuf))
lp2 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp2, body + i, n)
lp2 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp2, body + i, int(n))
lp2 = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, lp2, "\n")
if lp2 < 0: return 2
i += n + 1
continue
if _repl_line_has_kw(body + i, n, 0, "def") != 0:
rc_h: int = _repl_lift_def_header(body + i, n, name_buf, _REPL_FN_NAME_MAX)
if _repl_line_has_kw(body + i, int(n), 0, "def") != 0:
rc_h: int = int(_repl_lift_def_header(body + i, int(n), name_buf, _REPL_FN_NAME_MAX))
if rc_h != 0: return rc_h
if _repl_lift_add_name(name_buf) != 0: return 1
in_lift = 1
@@ -1303,12 +1302,12 @@ def _repl_split_body(body: bytes) -> int:
in_body = 1
i += n + 1
continue
if _repl_line_has_kw(body + i, n, 0, "async") != 0 and _repl_line_has_kw(body + i, n, 6, "def") != 0:
stdio.printf("M3: async def session functions are not supported (coroutine ABI is incompatible with " "the M3 signature token; rewrite as a plain def): %s\n", _repl_show_line(body + i, n))
if _repl_line_has_kw(body + i, int(n), 0, "async") != 0 and _repl_line_has_kw(body + i, int(n), 6, "def") != 0:
stdio.printf("M3: async def session functions are not supported (coroutine ABI is incompatible with " "the M3 signature token; rewrite as a plain def): %s\n", _repl_show_line(body + i, int(n)))
return 1
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, " ")
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, body + i, n)
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, "\n")
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), " ")
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), body + i, int(n))
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), "\n")
if pos < 0: return 2
i += n + 1
if need_header != 0:
@@ -1330,12 +1329,12 @@ def _repl_build_frag_src_multi(name: str, body: bytes) -> int:
rc: int = _repl_split_body(body)
if rc != 0: return rc
pos: int = ReplWrapPos
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, " return 0\n")
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), " return 0\n")
if pos < 0: return 2
# Append lifted module-level defs after the fragment entry function (module scope, order-free).
lift_len: int = int(string.strlen(ReplLiftBuf))
if lift_len > 0:
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, ReplLiftBuf, lift_len)
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), ReplLiftBuf, lift_len)
if pos < 0: return 2
ReplWrapPos = pos
return 0
@@ -1350,7 +1349,7 @@ def _repl_func_entry_find(name: str) -> GenTypes.ReplFuncEntry*:
if ReplCtxObj is None or ReplCtxObj.FuncTable is None or name is None: return None
es: sizet = GenTypes.ReplFuncEntry.__sizeof__()
i: int
for i in range(ReplCtxObj.FuncCount):
for i in range(int(ReplCtxObj.FuncCount)):
e: GenTypes.ReplFuncEntry* = GenTypes.ReplFuncEntry*(uint64(ReplCtxObj.FuncTable) + uint64(i) * uint64(es))
if e.Name is not None and string.strcmp(e.Name, name) == 0: return e
return None
@@ -1377,11 +1376,11 @@ def _repl_sig_to_text(tag: int, bits: int, pc: int) -> str:
"""Render ReplSigResult + ReplSigTags/Bits as "(P1, P2) -> R" (PermPool buffer)."""
if ReplSigText is None: return "(?)"
pos: int = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, 0, "(")
for i in range(pc):
if i > 0: pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, ", ")
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(int(ReplSigTags[i]), int(ReplSigBits[i])))
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, ") -> ")
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(tag, bits))
for i in range(int(pc)):
if i > 0: pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), ", ")
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(int(ReplSigTags[i]), int(ReplSigBits[i])))
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), ") -> ")
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(tag, bits))
if pos < 0: return "(?)"
return ReplSigText
@@ -1395,7 +1394,7 @@ def _repl_precheck_session_funcs(mod: mlirlite.LLVMModule*) -> int:
return 1
new_cnt: int = 0
idx: int
for idx in range(ReplLiftNameCount):
for idx in range(int(ReplLiftNameCount)):
nm: str = _repl_lift_name(idx)
if nm is None: return 1
rc: int = _repl_model_func_sig(mod, nm, ReplSigResult)
@@ -1426,7 +1425,7 @@ def _repl_precheck_session_funcs(mod: mlirlite.LLVMModule*) -> int:
sig_same: int = 1
if old.RetTag != ReplSigResult.RetTag or old.RetBits != ReplSigResult.RetBits or old.ParamCount != ReplSigResult.ParamCount: sig_same = 0
if sig_same != 0:
for pi in range(old.ParamCount):
for pi in range(int(old.ParamCount)):
if int(old.ParamTags[pi]) != int(ReplSigTags[pi]) or int(old.ParamBits[pi]) != int(ReplSigBits[pi]):
sig_same = 0
break
@@ -1443,11 +1442,11 @@ def _repl_precheck_session_funcs(mod: mlirlite.LLVMModule*) -> int:
def _repl_entry_sig_text(e: GenTypes.ReplFuncEntry*) -> str:
if e is None or ReplSigText2 is None: return "(?)"
pos: int = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, 0, "(")
for i in range(e.ParamCount):
if i > 0: pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, ", ")
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(int(e.ParamTags[i]), int(e.ParamBits[i])))
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, ") -> ")
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(e.RetTag, e.RetBits))
for i in range(int(e.ParamCount)):
if i > 0: pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), ", ")
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(int(e.ParamTags[i]), int(e.ParamBits[i])))
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), ") -> ")
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(e.RetTag, e.RetBits))
if pos < 0: return "(?)"
return ReplSigText2
@@ -1460,7 +1459,7 @@ def _repl_register_session_funcs(h_lib: win32base.HANDLE, frag_no: int, mod: mli
stdio.printf("M3: session function table/ctx not initialized\n")
return 1
idx: int
for idx in range(ReplLiftNameCount):
for idx in range(int(ReplLiftNameCount)):
nm: str = _repl_lift_name(idx)
if nm is None: return 1
# Exported symbol is the bare name: `export def` sets has_export without a SHA1 prefix,
@@ -1476,7 +1475,7 @@ def _repl_register_session_funcs(h_lib: win32base.HANDLE, frag_no: int, mod: mli
if rc != 0:
stdio.printf("M3: failed to re-parse session function '%s' signature (rc=%d)\n", nm, rc)
return 1
j: int = ReplCtxObj.FuncCount
j: int = int(ReplCtxObj.FuncCount)
es: sizet = GenTypes.ReplFuncEntry.__sizeof__()
ent = GenTypes.ReplFuncEntry*(uint64(ReplCtxObj.FuncTable) + uint64(j) * uint64(es))
nm_c: str = _repl_perm_dup(nm)
@@ -1625,8 +1624,8 @@ def _repl_model_func_sig(mod: mlirlite.LLVMModule*, name: str, out: ReplSigOut*)
out.Bad = 1
out.BadType = _repl_ty_name(p_ty)
return 2
ReplSigTags[out.ParamCount] = tk // 1000
ReplSigBits[out.ParamCount] = tk % 1000
ReplSigTags[out.ParamCount] = char(tk // 1000)
ReplSigBits[out.ParamCount] = char(tk % 1000)
out.ParamCount = out.ParamCount + 1
pi += 1
p_cur = mlirlite.param_get_next(p_cur)
@@ -1900,7 +1899,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
# 12') Session function registration (M3), before calling the entry so a failure skips this
# fragment's statements; in-fragment calls to the same name still use direct calls.
if _repl_register_session_funcs(h_lib, n, tr.Module) != 0:
if _repl_register_session_funcs(h_lib, int(n), tr.Module) != 0:
if ReplCtxObj is not None: ReplCtxObj.FuncCount = ReplSavedFuncCount
return 1
@@ -1971,7 +1970,7 @@ def run(mb: memhub.MemBuddy*) -> int:
if lg is not None: lg.set_jit_flag(False)
return 1
h_in: win32base.HANDLE = w32file.GetStdHandle(w32file.STD_INPUT_HANDLE)
h_in: win32base.HANDLE = win32file.GetStdHandle(win32file.STD_INPUT_HANDLE)
line: bytes = stdlib.malloc(_REPL_LINE_CAP)
if line is None:
stdio.printf("REPL: line buffer allocation failed\n")
@@ -2079,7 +2078,7 @@ def run(mb: memhub.MemBuddy*) -> int:
mb.reset_to(mark)
continue
# In continuation: every line is buffered; no state flips on ':' and no indent analysis.
if _block_append(line, n) != 0:
if _block_append(line, int(n)) != 0:
stdio.printf("REPL: block input exceeds the block buffer cap of %d bytes: this block was discarded, " "returning to >>> (reduce the block size or run it in chunks)\n", _REPL_BLOCK_CAP)
ReplInBlock = 0
ReplBlockLen = 0
@@ -2089,10 +2088,10 @@ def run(mb: memhub.MemBuddy*) -> int:
if n == 0: # blank line: skip
mb.reset_to(mark)
continue
if _line_starts_block(line, n) != 0:
if _line_starts_block(line, int(n)) != 0:
# Trailing ':' enters continuation: buffer this line without executing it. The first line
# cannot overflow (line cap < block cap), so append failure falls through below.
if _block_append(line, n) == 0:
if _block_append(line, int(n)) == 0:
ReplInBlock = 1
mb.reset_to(mark)
continue