diff --git a/App/lib/Phase/BuildPipeline.vp b/App/lib/Phase/BuildPipeline.vp index bfac086..cfc8099 100644 --- a/App/lib/Phase/BuildPipeline.vp +++ b/App/lib/Phase/BuildPipeline.vp @@ -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)) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index a534202..82c4d65 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -11,7 +11,7 @@ import sys import memhub import mlirlite 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 @@ -216,7 +216,7 @@ def _vpi_idx_find(keys: bytes, key_stride: int, idx_base: bytes, cap: int, key: h: uint64 = _vpi_idx_hash(key) mask: sizet = sizet(cap - 1) si: sizet = sizet(h) & mask - for i in range(cap): + for i in range(int(cap)): slot: sizet* = _vpi_idx_slot(idx_base, si) sv: sizet = slot[0] if sv == 0: return -1 @@ -233,7 +233,7 @@ def _vpi_idx_insert(idx_base: bytes, cap: int, slot_index: int, key: bytes) -> i h: uint64 = _vpi_idx_hash(key) mask: sizet = sizet(cap - 1) si: sizet = sizet(h) & mask - for i in range(cap): + for i in range(int(cap)): slot: sizet* = _vpi_idx_slot(idx_base, si) if slot[0] == 0: slot[0] = sizet(slot_index + 1) @@ -263,7 +263,7 @@ def _vpi_rebuild_ret_idx(new_cap: int) -> int: _g_vpi_ret_idx_cap = new_cap fail: int = 0 ri: int - for ri in range(_g_vpi_ret_count): + for ri in range(int(_g_vpi_ret_count)): if _vpi_idx_insert(new_idx, new_cap, ri, _g_vpi_ret_keys + ri * VPI_KEY_LEN) != 0: fail = 1 break @@ -295,7 +295,7 @@ def _vpi_rebuild_param_idx(new_cap: int) -> int: _g_vpi_param_idx_cap = new_cap fail: int = 0 ri: int - for ri in range(_g_vpi_param_count): + for ri in range(int(_g_vpi_param_count)): if _vpi_idx_insert(new_idx, new_cap, ri, _g_vpi_param_keys + ri * VPI_PARAM_KEY_LEN) != 0: fail = 1 break @@ -427,7 +427,7 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int: # Fast path: equal keys share the first byte, so skip # strcmp when the candidate's first byte differs. dup_first: int = key_buf[0] - for dki in range(_g_vpi_ret_count): + for dki in range(int(_g_vpi_ret_count)): ent_d: bytes = _g_vpi_ret_keys + dki * VPI_KEY_LEN if ent_d[0] == dup_first and string.strcmp(ent_d, key_buf) == 0: dup_key = 1 @@ -580,7 +580,7 @@ def _cache_vpi_param_annots_text(vpi_buf: bytes, src_sha1: str) -> int: plist[plist_pos] = '@' plist_pos += 1 ai2: sizet - for ai2 in range(a_end - a_start): + for ai2 in range(int(a_end - a_start)): plist[plist_pos] = vpi_buf[a_start + ai2] plist_pos += 1 plist[plist_pos] = '\0' @@ -612,7 +612,7 @@ def _cache_vpi_param_annots_text(vpi_buf: bytes, src_sha1: str) -> int: # Fast path: equal keys share the first byte, so skip # strcmp when the candidate's first byte differs. dup_first: int = key_buf[0] - for dki in range(_g_vpi_param_count): + for dki in range(int(_g_vpi_param_count)): ent_d: bytes = _g_vpi_param_keys + dki * VPI_PARAM_KEY_LEN if ent_d[0] == dup_first and string.strcmp(ent_d, key_buf) == 0: dup_key = 1 @@ -681,7 +681,7 @@ def _lookup_vpi_param_annots(src_sha1: str, cls_name: str, method_name: str) -> # Linear-scan fallback: equal keys share the first byte, so skip strcmp when # the candidate's first byte differs (negative check only, result unchanged). key_first: int = key_buf[0] - for ki in range(_g_vpi_param_count): + for ki in range(int(_g_vpi_param_count)): ent_p: bytes = _g_vpi_param_keys + ki * VPI_PARAM_KEY_LEN if ent_p[0] == key_first and string.strcmp(ent_p, key_buf) == 0: hit_p = ki @@ -716,7 +716,7 @@ def _resolve_vpi_param_annot_seg_template(pool: memhub.MemBuddy*, src_sha1: str, sha1_len_t: sizet = string.strlen(src_sha1) ty_t: mlirlite.LLVMType* = None ki_t: int - for ki_t in range(_g_vpi_param_count): + for ki_t in range(int(_g_vpi_param_count)): gk_t: bytes = _g_vpi_param_keys + ki_t * VPI_PARAM_KEY_LEN if string.strncmp(gk_t, src_sha1, sha1_len_t) != 0 or gk_t[sha1_len_t] != '.': continue if string.strncmp(gk_t + sha1_len_t + 1, base_t, base_len_t) != 0: continue @@ -862,7 +862,7 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: # so checking them skips strncmp for most entries. s0: int = src_sha1[0] s1: int = src_sha1[1] - for pre_i in range(_g_vpi_ret_count): + for pre_i in range(int(_g_vpi_ret_count)): pre_key: bytes = _g_vpi_ret_keys + pre_i * VPI_KEY_LEN if pre_key[0] == s0 and pre_key[1] == s1 and string.strncmp(pre_key, src_sha1, sha1_len) == 0 and pre_key[sha1_len] == '.': return 1 # build path {temp_dir}/{sha1[0:2]}/{sha1}.vpi (char by char, avoiding %c formatting) @@ -897,219 +897,219 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: return 1 -# _cache_vpi_esc_clears_text - 抽取行尾**所有** `escg=` 标记(跨模块常量特化)。 -# 与 wf 表同构,但同一行可有多条(一条一个打包值),故是"一键多行"。值 = p|k|m 打包整数 -# (见 StructGenMixin._PackEscapeClear:p=守卫形参位、k=常量、m=可清掉的位掩码)。 -def _cache_vpi_esc_clears_text(vpi_buf: bytes, src_sha1: str) -> int: - """抽取 vpi 行尾的 escg= 标记进缓存;1=处理完成 / 0=缓冲区或分配失败""" - global _g_vpi_esg_keys, _g_vpi_esg_vals, _g_vpi_esg_count - global _g_vpi_esd_keys, _g_vpi_esd_e, _g_vpi_esd_n, _g_vpi_esd_r, _g_vpi_esd_count - if vpi_buf is None or src_sha1 is None: return 0 - if _g_vpi_esg_keys is None: - _g_vpi_esg_keys = stdlib.malloc(VPI_ESG_MAX * VPI_ESG_KEY_LEN) - _g_vpi_esg_vals = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) - if _g_vpi_esg_keys is None or _g_vpi_esg_vals is None: return 0 - string.memset(_g_vpi_esg_keys, 0, VPI_ESG_MAX * VPI_ESG_KEY_LEN) - string.memset(_g_vpi_esg_vals, 0, VPI_ESG_MAX * 8) - if _g_vpi_esd_keys is None: - _g_vpi_esd_keys = stdlib.malloc(VPI_ESG_MAX * VPI_ESG_KEY_LEN) - _g_vpi_esd_e = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) - _g_vpi_esd_n = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) - _g_vpi_esd_r = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) - if _g_vpi_esd_keys is None or _g_vpi_esd_e is None or _g_vpi_esd_n is None or _g_vpi_esd_r is None: return 0 - string.memset(_g_vpi_esd_keys, 0, VPI_ESG_MAX * VPI_ESG_KEY_LEN) - string.memset(_g_vpi_esd_e, 0, VPI_ESG_MAX * 8) - string.memset(_g_vpi_esd_n, 0, VPI_ESG_MAX * 8) - string.memset(_g_vpi_esd_r, 0, VPI_ESG_MAX * 8) - sha1_len: sizet = string.strlen(src_sha1) - buf_len: sizet = string.strlen(vpi_buf) - cur_cls: bytes = None - pos: sizet = 0 - while pos < buf_len: - ls: sizet = pos - le: sizet = pos - while le < buf_len and vpi_buf[le] != '\n': le += 1 - i: sizet = ls - while i < le and vpi_buf[i] == ' ': i += 1 - if i < le: - if le - i >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0: - cstart: sizet = i + 6 - cend: sizet = cstart - while cend < le and vpi_buf[cend] != ':' and vpi_buf[cend] != ' ' and vpi_buf[cend] != '(': cend += 1 - if cend > cstart: - if cur_cls is not None: stdlib.free(cur_cls) - cur_cls = stdlib.malloc(cend - cstart + 1) - if cur_cls is not None: - string.strncpy(cur_cls, vpi_buf + cstart, cend - cstart) - cur_cls[cend - cstart] = '\0' - elif le - i >= 4 and string.strncmp(vpi_buf + i, "def ", 4) == 0: - # 模块级函数(无缩进)清掉类上下文,避免上一类泄漏 - if i == ls and cur_cls is not None: - stdlib.free(cur_cls) - cur_cls = None - # 只在注释段('#' 之后)里找 escg=:避开参数名 - hm: sizet = le - j: sizet = i - while j < le: - if vpi_buf[j] == '#': - hm = j - break - j += 1 - dstart: sizet = i + 4 - dend: sizet = dstart - while dend < le and vpi_buf[dend] != '(': dend += 1 - if hm < le and dend > dstart: - mname_len: sizet = dend - dstart - cls_len_e: sizet = 0 - if cur_cls is not None: cls_len_e = string.strlen(cur_cls) - key_len_e: sizet = sha1_len + 1 + cls_len_e + 1 + mname_len - if key_len_e < VPI_ESG_KEY_LEN: - mname_buf_e: bytes = stdlib.malloc(mname_len + 1) - key_buf_e: bytes = stdlib.malloc(key_len_e + 1) - if mname_buf_e is not None and key_buf_e is not None: - string.strncpy(mname_buf_e, vpi_buf + dstart, mname_len) - mname_buf_e[mname_len] = '\0' - if cur_cls is not None: viperlib.snprintf(key_buf_e, key_len_e + 1, "%s.%s.%s", src_sha1, cur_cls, mname_buf_e) - else: viperlib.snprintf(key_buf_e, key_len_e + 1, "%s.%s", src_sha1, mname_buf_e) - j3: sizet = hm + 1 - while j3 + 5 <= le: - if vpi_buf[j3] == 'e' and vpi_buf[j3 + 1] == 's' and vpi_buf[j3 + 2] == 'c' and vpi_buf[j3 + 3] == 'g' and vpi_buf[j3 + 4] == '=': - kq: sizet = j3 + 5 - eg_v: int = 0 - while kq < le and vpi_buf[kq] >= '0' and vpi_buf[kq] <= '9': - eg_v = eg_v * 10 + (int(vpi_buf[kq]) - 48) - kq += 1 - if eg_v > 0 and _g_vpi_esg_count < VPI_ESG_MAX: - string.strcpy(_g_vpi_esg_keys + sizet(_g_vpi_esg_count) * VPI_ESG_KEY_LEN, key_buf_e) - _g_vpi_esg_vals[_g_vpi_esg_count] = sizet(eg_v) - _g_vpi_esg_count += 1 - j3 = kq - else: - j3 += 1 - # 声明式指令契约(esce= / escn= / escr=):每函数一条,只认 '#' 注释段 - de_v: int = 0 - dn_v: int = 0 - dr_v: int = 0 - j4: sizet = hm + 1 - while j4 + 5 <= le: - if vpi_buf[j4] == 'e' and vpi_buf[j4 + 1] == 's' and vpi_buf[j4 + 2] == 'c' and vpi_buf[j4 + 3] == 'e' and vpi_buf[j4 + 4] == '=': - kq2: sizet = j4 + 5 - v2: int = 0 - while kq2 < le and vpi_buf[kq2] >= '0' and vpi_buf[kq2] <= '9': - v2 = v2 * 10 + (int(vpi_buf[kq2]) - 48) - kq2 += 1 - if v2 > 0: de_v = v2 - j4 = kq2 - elif vpi_buf[j4] == 'e' and vpi_buf[j4 + 1] == 's' and vpi_buf[j4 + 2] == 'c' and vpi_buf[j4 + 3] == 'n' and vpi_buf[j4 + 4] == '=': - kq3: sizet = j4 + 5 - v3: int = 0 - while kq3 < le and vpi_buf[kq3] >= '0' and vpi_buf[kq3] <= '9': - v3 = v3 * 10 + (int(vpi_buf[kq3]) - 48) - kq3 += 1 - if v3 > 0: dn_v = v3 - j4 = kq3 - elif vpi_buf[j4] == 'e' and vpi_buf[j4 + 1] == 's' and vpi_buf[j4 + 2] == 'c' and vpi_buf[j4 + 3] == 'r' and vpi_buf[j4 + 4] == '=': - kq5: sizet = j4 + 5 - v5: int = 0 - while kq5 < le and vpi_buf[kq5] >= '0' and vpi_buf[kq5] <= '9': - v5 = v5 * 10 + (int(vpi_buf[kq5]) - 48) - kq5 += 1 - if v5 > 0: dr_v = v5 - j4 = kq5 - else: - j4 += 1 - if (de_v != 0 or dn_v != 0 or dr_v != 0) and _g_vpi_esd_count < VPI_ESG_MAX: - string.strcpy(_g_vpi_esd_keys + sizet(_g_vpi_esd_count) * VPI_ESG_KEY_LEN, key_buf_e) - _g_vpi_esd_e[_g_vpi_esd_count] = sizet(de_v) - _g_vpi_esd_n[_g_vpi_esd_count] = sizet(dn_v) - _g_vpi_esd_r[_g_vpi_esd_count] = sizet(dr_v) - _g_vpi_esd_count += 1 - if mname_buf_e is not None: stdlib.free(mname_buf_e) - if key_buf_e is not None: stdlib.free(key_buf_e) - pos = le + 1 - if cur_cls is not None: stdlib.free(cur_cls) - return 1 - - -# _lookup_vpi_escape_clears - 跨模块常量守卫查询:按调用点常量环境算出"该清掉的位";0 = 无命中 -def _lookup_vpi_escape_clears(src_sha1: str, cls_name: str, method_name: str, env: sizet*) -> int: - """env = 常量环境(2 槽/形参:[0]=是否已知、[1]=值,下标 = 形参位);返回该清掉的位掩码 OR 值""" - if _g_vpi_esg_keys is None or _g_vpi_esg_count == 0: return 0 - if src_sha1 is None or method_name is None or env is None: return 0 - sha1_len: sizet = string.strlen(src_sha1) - cl_len: sizet = 0 - if cls_name is not None: cl_len = string.strlen(cls_name) - me_len: sizet = string.strlen(method_name) - key_len: sizet = sha1_len + 1 + cl_len + 1 + me_len - kb: bytes = stdlib.malloc(key_len + 1) - if kb is None: return 0 - if cls_name is not None: viperlib.snprintf(kb, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name) - else: viperlib.snprintf(kb, key_len + 1, "%s.%s", src_sha1, method_name) - cleared: int = 0 - idx: int = 0 - while idx < _g_vpi_esg_count: - ent: bytes = _g_vpi_esg_keys + sizet(idx) * VPI_ESG_KEY_LEN - # 键以模块 sha1 打头:先比前 2 个十六进制字符,避免逐项 strcmp - if ent[0] == kb[0] and ent[1] == kb[1] and string.strcmp(ent, kb) == 0: - v: int = int(_g_vpi_esg_vals[idx]) - if v > 0: - p: int = v & 255 - kv: int = (v >> 8) & 255 - if kv >= 128: kv -= 256 - m: int = v >> 16 - if p >= 0 and p < VPI_ESG_ENV_PARAM_MAX and env[p * 2] != 0: - if int64(env[p * 2 + 1]) == int64(kv): cleared = cleared | m - idx += 1 - stdlib.free(kb) - return cleared - - -# _lookup_vpi_escape_dir - 跨模块声明式指令契约查询(`@life(escape(...))` / `@life(noescape(...))` / -# `@life(ret(...))`)。which: 0=强制逃逸位 / 1=强制不逃逸位 / 2=ret_kind(A2)。 -# 0 = 无该标注(含该模块未加载过)。 -def _lookup_vpi_escape_dir(src_sha1: str, cls_name: str, method_name: str, which: int) -> int: - if _g_vpi_esd_keys is None or _g_vpi_esd_count == 0: return 0 - if src_sha1 is None or method_name is None: return 0 - sha1_len: sizet = string.strlen(src_sha1) - cl_len: sizet = 0 - if cls_name is not None: cl_len = string.strlen(cls_name) - me_len: sizet = string.strlen(method_name) - key_len: sizet = sha1_len + 1 + cl_len + 1 + me_len - kb: bytes = stdlib.malloc(key_len + 1) - if kb is None: return 0 - if cls_name is not None: viperlib.snprintf(kb, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name) - else: viperlib.snprintf(kb, key_len + 1, "%s.%s", src_sha1, method_name) - r: int = 0 - i: int = 0 - while i < _g_vpi_esd_count: - ent: bytes = _g_vpi_esd_keys + sizet(i) * VPI_ESG_KEY_LEN - # 键以模块 sha1 打头:先比前 2 个十六进制字符,避免逐项 strcmp - if ent[0] == kb[0] and ent[1] == kb[1] and string.strcmp(ent, kb) == 0: - if which == 2: r = int(_g_vpi_esd_r[i]) - elif which != 0: r = int(_g_vpi_esd_n[i]) - else: r = int(_g_vpi_esd_e[i]) - break - i += 1 - stdlib.free(kb) - return r - - -# _cache_vpi_annots_text - 所有"从 vpi 文本解析"的缓存**单一入口**。 -# 任何新增的文本缓存都加在这里,不要再散落到各加载点——漏喂一处就会**静默退化** -# (现场:逃逸摘要只在 _ensure_vpi_ret_annot_loaded 里喂,而 stub 注入加载点没喂, -# 前者开头又有"本模块 ret 注解已缓存即 return 1"的短路,于是跨模块逃逸查询永久查空, -# EscapeXmodTest 漏检)。 -def _cache_vpi_annots_text(vpi_buf: bytes, src_sha1: str) -> int: - """一次性把 vpi 文本派生出的所有缓存喂齐;1=完成 / 0=入参非法""" - if vpi_buf is None or src_sha1 is None: return 0 - _cache_vpi_return_annots_text(vpi_buf, src_sha1) - _cache_vpi_param_annots_text(vpi_buf, src_sha1) - _cache_vpi_escape_annots_text(vpi_buf, src_sha1) - _cache_vpi_willfree_annots_text(vpi_buf, src_sha1) - _cache_vpi_esc_clears_text(vpi_buf, src_sha1) - return 1 - - +# _cache_vpi_esc_clears_text - 抽取行尾**所有** `escg=` 标记(跨模块常量特化)。 +# 与 wf 表同构,但同一行可有多条(一条一个打包值),故是"一键多行"。值 = p|k|m 打包整数 +# (见 StructGenMixin._PackEscapeClear:p=守卫形参位、k=常量、m=可清掉的位掩码)。 +def _cache_vpi_esc_clears_text(vpi_buf: bytes, src_sha1: str) -> int: + """抽取 vpi 行尾的 escg= 标记进缓存;1=处理完成 / 0=缓冲区或分配失败""" + global _g_vpi_esg_keys, _g_vpi_esg_vals, _g_vpi_esg_count + global _g_vpi_esd_keys, _g_vpi_esd_e, _g_vpi_esd_n, _g_vpi_esd_r, _g_vpi_esd_count + if vpi_buf is None or src_sha1 is None: return 0 + if _g_vpi_esg_keys is None: + _g_vpi_esg_keys = stdlib.malloc(VPI_ESG_MAX * VPI_ESG_KEY_LEN) + _g_vpi_esg_vals = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) + if _g_vpi_esg_keys is None or _g_vpi_esg_vals is None: return 0 + string.memset(_g_vpi_esg_keys, 0, VPI_ESG_MAX * VPI_ESG_KEY_LEN) + string.memset(_g_vpi_esg_vals, 0, VPI_ESG_MAX * 8) + if _g_vpi_esd_keys is None: + _g_vpi_esd_keys = stdlib.malloc(VPI_ESG_MAX * VPI_ESG_KEY_LEN) + _g_vpi_esd_e = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) + _g_vpi_esd_n = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) + _g_vpi_esd_r = sizet*(stdlib.malloc(VPI_ESG_MAX * 8)) + if _g_vpi_esd_keys is None or _g_vpi_esd_e is None or _g_vpi_esd_n is None or _g_vpi_esd_r is None: return 0 + string.memset(_g_vpi_esd_keys, 0, VPI_ESG_MAX * VPI_ESG_KEY_LEN) + string.memset(_g_vpi_esd_e, 0, VPI_ESG_MAX * 8) + string.memset(_g_vpi_esd_n, 0, VPI_ESG_MAX * 8) + string.memset(_g_vpi_esd_r, 0, VPI_ESG_MAX * 8) + sha1_len: sizet = string.strlen(src_sha1) + buf_len: sizet = string.strlen(vpi_buf) + cur_cls: bytes = None + pos: sizet = 0 + while pos < buf_len: + ls: sizet = pos + le: sizet = pos + while le < buf_len and vpi_buf[le] != '\n': le += 1 + i: sizet = ls + while i < le and vpi_buf[i] == ' ': i += 1 + if i < le: + if le - i >= 6 and string.strncmp(vpi_buf + i, "class ", 6) == 0: + cstart: sizet = i + 6 + cend: sizet = cstart + while cend < le and vpi_buf[cend] != ':' and vpi_buf[cend] != ' ' and vpi_buf[cend] != '(': cend += 1 + if cend > cstart: + if cur_cls is not None: stdlib.free(cur_cls) + cur_cls = stdlib.malloc(cend - cstart + 1) + if cur_cls is not None: + string.strncpy(cur_cls, vpi_buf + cstart, cend - cstart) + cur_cls[cend - cstart] = '\0' + elif le - i >= 4 and string.strncmp(vpi_buf + i, "def ", 4) == 0: + # 模块级函数(无缩进)清掉类上下文,避免上一类泄漏 + if i == ls and cur_cls is not None: + stdlib.free(cur_cls) + cur_cls = None + # 只在注释段('#' 之后)里找 escg=:避开参数名 + hm: sizet = le + j: sizet = i + while j < le: + if vpi_buf[j] == '#': + hm = j + break + j += 1 + dstart: sizet = i + 4 + dend: sizet = dstart + while dend < le and vpi_buf[dend] != '(': dend += 1 + if hm < le and dend > dstart: + mname_len: sizet = dend - dstart + cls_len_e: sizet = 0 + if cur_cls is not None: cls_len_e = string.strlen(cur_cls) + key_len_e: sizet = sha1_len + 1 + cls_len_e + 1 + mname_len + if key_len_e < VPI_ESG_KEY_LEN: + mname_buf_e: bytes = stdlib.malloc(mname_len + 1) + key_buf_e: bytes = stdlib.malloc(key_len_e + 1) + if mname_buf_e is not None and key_buf_e is not None: + string.strncpy(mname_buf_e, vpi_buf + dstart, mname_len) + mname_buf_e[mname_len] = '\0' + if cur_cls is not None: viperlib.snprintf(key_buf_e, key_len_e + 1, "%s.%s.%s", src_sha1, cur_cls, mname_buf_e) + else: viperlib.snprintf(key_buf_e, key_len_e + 1, "%s.%s", src_sha1, mname_buf_e) + j3: sizet = hm + 1 + while j3 + 5 <= le: + if vpi_buf[j3] == 'e' and vpi_buf[j3 + 1] == 's' and vpi_buf[j3 + 2] == 'c' and vpi_buf[j3 + 3] == 'g' and vpi_buf[j3 + 4] == '=': + kq: sizet = j3 + 5 + eg_v: int = 0 + while kq < le and vpi_buf[kq] >= '0' and vpi_buf[kq] <= '9': + eg_v = eg_v * 10 + (int(vpi_buf[kq]) - 48) + kq += 1 + if eg_v > 0 and _g_vpi_esg_count < VPI_ESG_MAX: + string.strcpy(_g_vpi_esg_keys + sizet(_g_vpi_esg_count) * VPI_ESG_KEY_LEN, key_buf_e) + _g_vpi_esg_vals[_g_vpi_esg_count] = sizet(eg_v) + _g_vpi_esg_count += 1 + j3 = kq + else: + j3 += 1 + # 声明式指令契约(esce= / escn= / escr=):每函数一条,只认 '#' 注释段 + de_v: int = 0 + dn_v: int = 0 + dr_v: int = 0 + j4: sizet = hm + 1 + while j4 + 5 <= le: + if vpi_buf[j4] == 'e' and vpi_buf[j4 + 1] == 's' and vpi_buf[j4 + 2] == 'c' and vpi_buf[j4 + 3] == 'e' and vpi_buf[j4 + 4] == '=': + kq2: sizet = j4 + 5 + v2: int = 0 + while kq2 < le and vpi_buf[kq2] >= '0' and vpi_buf[kq2] <= '9': + v2 = v2 * 10 + (int(vpi_buf[kq2]) - 48) + kq2 += 1 + if v2 > 0: de_v = v2 + j4 = kq2 + elif vpi_buf[j4] == 'e' and vpi_buf[j4 + 1] == 's' and vpi_buf[j4 + 2] == 'c' and vpi_buf[j4 + 3] == 'n' and vpi_buf[j4 + 4] == '=': + kq3: sizet = j4 + 5 + v3: int = 0 + while kq3 < le and vpi_buf[kq3] >= '0' and vpi_buf[kq3] <= '9': + v3 = v3 * 10 + (int(vpi_buf[kq3]) - 48) + kq3 += 1 + if v3 > 0: dn_v = v3 + j4 = kq3 + elif vpi_buf[j4] == 'e' and vpi_buf[j4 + 1] == 's' and vpi_buf[j4 + 2] == 'c' and vpi_buf[j4 + 3] == 'r' and vpi_buf[j4 + 4] == '=': + kq5: sizet = j4 + 5 + v5: int = 0 + while kq5 < le and vpi_buf[kq5] >= '0' and vpi_buf[kq5] <= '9': + v5 = v5 * 10 + (int(vpi_buf[kq5]) - 48) + kq5 += 1 + if v5 > 0: dr_v = v5 + j4 = kq5 + else: + j4 += 1 + if (de_v != 0 or dn_v != 0 or dr_v != 0) and _g_vpi_esd_count < VPI_ESG_MAX: + string.strcpy(_g_vpi_esd_keys + sizet(_g_vpi_esd_count) * VPI_ESG_KEY_LEN, key_buf_e) + _g_vpi_esd_e[_g_vpi_esd_count] = sizet(de_v) + _g_vpi_esd_n[_g_vpi_esd_count] = sizet(dn_v) + _g_vpi_esd_r[_g_vpi_esd_count] = sizet(dr_v) + _g_vpi_esd_count += 1 + if mname_buf_e is not None: stdlib.free(mname_buf_e) + if key_buf_e is not None: stdlib.free(key_buf_e) + pos = le + 1 + if cur_cls is not None: stdlib.free(cur_cls) + return 1 + + +# _lookup_vpi_escape_clears - 跨模块常量守卫查询:按调用点常量环境算出"该清掉的位";0 = 无命中 +def _lookup_vpi_escape_clears(src_sha1: str, cls_name: str, method_name: str, env: sizet*) -> int: + """env = 常量环境(2 槽/形参:[0]=是否已知、[1]=值,下标 = 形参位);返回该清掉的位掩码 OR 值""" + if _g_vpi_esg_keys is None or _g_vpi_esg_count == 0: return 0 + if src_sha1 is None or method_name is None or env is None: return 0 + sha1_len: sizet = string.strlen(src_sha1) + cl_len: sizet = 0 + if cls_name is not None: cl_len = string.strlen(cls_name) + me_len: sizet = string.strlen(method_name) + key_len: sizet = sha1_len + 1 + cl_len + 1 + me_len + kb: bytes = stdlib.malloc(key_len + 1) + if kb is None: return 0 + if cls_name is not None: viperlib.snprintf(kb, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name) + else: viperlib.snprintf(kb, key_len + 1, "%s.%s", src_sha1, method_name) + cleared: int = 0 + idx: int = 0 + while idx < _g_vpi_esg_count: + ent: bytes = _g_vpi_esg_keys + sizet(idx) * VPI_ESG_KEY_LEN + # 键以模块 sha1 打头:先比前 2 个十六进制字符,避免逐项 strcmp + if ent[0] == kb[0] and ent[1] == kb[1] and string.strcmp(ent, kb) == 0: + v: int = int(_g_vpi_esg_vals[idx]) + if v > 0: + p: int = v & 255 + kv: int = (v >> 8) & 255 + if kv >= 128: kv -= 256 + m: int = v >> 16 + if p >= 0 and p < VPI_ESG_ENV_PARAM_MAX and env[p * 2] != 0: + if int64(env[p * 2 + 1]) == int64(kv): cleared = cleared | m + idx += 1 + stdlib.free(kb) + return cleared + + +# _lookup_vpi_escape_dir - 跨模块声明式指令契约查询(`@life(escape(...))` / `@life(noescape(...))` / +# `@life(ret(...))`)。which: 0=强制逃逸位 / 1=强制不逃逸位 / 2=ret_kind(A2)。 +# 0 = 无该标注(含该模块未加载过)。 +def _lookup_vpi_escape_dir(src_sha1: str, cls_name: str, method_name: str, which: int) -> int: + if _g_vpi_esd_keys is None or _g_vpi_esd_count == 0: return 0 + if src_sha1 is None or method_name is None: return 0 + sha1_len: sizet = string.strlen(src_sha1) + cl_len: sizet = 0 + if cls_name is not None: cl_len = string.strlen(cls_name) + me_len: sizet = string.strlen(method_name) + key_len: sizet = sha1_len + 1 + cl_len + 1 + me_len + kb: bytes = stdlib.malloc(key_len + 1) + if kb is None: return 0 + if cls_name is not None: viperlib.snprintf(kb, key_len + 1, "%s.%s.%s", src_sha1, cls_name, method_name) + else: viperlib.snprintf(kb, key_len + 1, "%s.%s", src_sha1, method_name) + r: int = 0 + i: int = 0 + while i < _g_vpi_esd_count: + ent: bytes = _g_vpi_esd_keys + sizet(i) * VPI_ESG_KEY_LEN + # 键以模块 sha1 打头:先比前 2 个十六进制字符,避免逐项 strcmp + if ent[0] == kb[0] and ent[1] == kb[1] and string.strcmp(ent, kb) == 0: + if which == 2: r = int(_g_vpi_esd_r[i]) + elif which != 0: r = int(_g_vpi_esd_n[i]) + else: r = int(_g_vpi_esd_e[i]) + break + i += 1 + stdlib.free(kb) + return r + + +# _cache_vpi_annots_text - 所有"从 vpi 文本解析"的缓存**单一入口**。 +# 任何新增的文本缓存都加在这里,不要再散落到各加载点——漏喂一处就会**静默退化** +# (现场:逃逸摘要只在 _ensure_vpi_ret_annot_loaded 里喂,而 stub 注入加载点没喂, +# 前者开头又有"本模块 ret 注解已缓存即 return 1"的短路,于是跨模块逃逸查询永久查空, +# EscapeXmodTest 漏检)。 +def _cache_vpi_annots_text(vpi_buf: bytes, src_sha1: str) -> int: + """一次性把 vpi 文本派生出的所有缓存喂齐;1=完成 / 0=入参非法""" + if vpi_buf is None or src_sha1 is None: return 0 + _cache_vpi_return_annots_text(vpi_buf, src_sha1) + _cache_vpi_param_annots_text(vpi_buf, src_sha1) + _cache_vpi_escape_annots_text(vpi_buf, src_sha1) + _cache_vpi_willfree_annots_text(vpi_buf, src_sha1) + _cache_vpi_esc_clears_text(vpi_buf, src_sha1) + return 1 + + # _cache_vpi_escape_annots_text - extract the trailing `# esc= ret=` markers. # v3 跨模块逃逸摘要:不依赖 AST,按行扫描(与 _cache_vpi_return_annots_text 同构)。 def _cache_vpi_escape_annots_text(vpi_buf: bytes, src_sha1: str) -> int: @@ -1349,7 +1349,7 @@ def _vpi_param_annot_seg_is(src_sha1: str, cls_name: str, method_name: str, seg_ if prm is None or prm[0] == '\0': return 0 cur: sizet = 0 # locate segment start: skip seg_idx '@'-separated segments - for seg in range(seg_idx): + for seg in range(int(seg_idx)): while prm[cur] != '\0' and prm[cur] != '@': cur += 1 if prm[cur] == '\0': return 0 cur += 1 @@ -1358,7 +1358,7 @@ def _vpi_param_annot_seg_is(src_sha1: str, cls_name: str, method_name: str, seg_ wlen: sizet = string.strlen(want) if slen != wlen: return 0 i: sizet - for i in range(slen): + for i in range(int(slen)): if prm[cur + i] != want[i]: return 0 return 1 @@ -1369,7 +1369,7 @@ def _vpi_param_annot_seg_mode(prm_annots: str, seg_idx: int) -> int: """Return the value-reference mode encoded in a vpi param-annotation segment.""" if prm_annots is None or seg_idx < 0: return 0 cur: sizet = 0 - for seg in range(seg_idx): + for seg in range(int(seg_idx)): while prm_annots[cur] != '\0' and prm_annots[cur] != '@': cur += 1 if prm_annots[cur] == '\0': return 0 cur += 1 @@ -1412,7 +1412,7 @@ def _ensure_vpi_param_annot_loaded(src_sha1: str, temp_dir: str) -> int: # so checking them skips strncmp for most entries. s0: int = src_sha1[0] s1: int = src_sha1[1] - for pre_i in range(_g_vpi_param_count): + for pre_i in range(int(_g_vpi_param_count)): pre_key: bytes = _g_vpi_param_keys + pre_i * VPI_PARAM_KEY_LEN if pre_key[0] == s0 and pre_key[1] == s1 and string.strncmp(pre_key, src_sha1, sha1_len) == 0 and pre_key[sha1_len] == '.': return 1 # build path {temp_dir}/{sha1[0:2]}/{sha1}.vpi (char by char, avoiding %c formatting) @@ -1516,7 +1516,7 @@ def _lookup_vpi_ret_annot(src_sha1: str, cls_name: str, method_name: str) -> str else: # Linear-scan fallback: equal keys share the first byte; skip candidates whose first byte differs. key_first: int = key_buf[0] - for i in range(_g_vpi_ret_count): + for i in range(int(_g_vpi_ret_count)): ent_r: bytes = _g_vpi_ret_keys + i * VPI_KEY_LEN if ent_r[0] == key_first and string.strcmp(ent_r, key_buf) == 0: hit_r = i @@ -1543,7 +1543,7 @@ def _lookup_vpi_ret_annot(src_sha1: str, cls_name: str, method_name: str) -> str param_v[param_len_v] = '\0' # scan cache for {sha1}.{base}[xxx].{method} gi: int - for gi in range(_g_vpi_ret_count): + for gi in range(int(_g_vpi_ret_count)): gk: bytes = _g_vpi_ret_keys + gi * VPI_KEY_LEN if string.strncmp(gk, src_sha1, sha1_len) == 0 and gk[sha1_len] == '.' and string.strncmp(gk + sha1_len + 1, base_v, base_len_v) == 0 and gk[sha1_len + 1 + base_len_v] == '[': gklb: str = string.strchr(gk + sha1_len + 1 + base_len_v, ']') @@ -1595,7 +1595,7 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L star_off_ps: sizet = string.strlen(star_buf_ps) string.strncpy(star_buf_ps + star_off_ps, seg, star_base_end_ps) star_buf_ps[star_off_ps + star_base_end_ps] = '\0' - for star_k_ps in range(star_cnt_ps): + for star_k_ps in range(int(star_cnt_ps)): string.strcat(star_buf_ps, "]") star_ty_ps: mlirlite.LLVMType* = _resolve_single_vpi_type_str(pool, star_buf_ps) stdlib.free(star_buf_ps) @@ -1801,8 +1801,8 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: m if lvl_cs > cptr_sub_lvl: cptr_sub_lvl = lvl_cs cptr_sub_ty = seg_ty_cs - elif string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: has_cptr = 1 - elif string.strcmp(seg_buf, "t.CRef") == 0 or string.strcmp(seg_buf, "CRef") == 0: has_cref = 1 + elif string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: has_cptr = 1 + elif string.strcmp(seg_buf, "t.CRef") == 0 or string.strcmp(seg_buf, "CRef") == 0: has_cref = 1 elif string.strcmp(seg_buf, "t.CTempRef") == 0 or string.strcmp(seg_buf, "CTempRef") == 0: has_cref = 1 elif string.strcmp(seg_buf, "void") == 0 or string.strcmp(seg_buf, "CVoid") == 0 or string.strcmp(seg_buf, "void") == 0: # Bare void is an alias of t.CVoid and must be handled the same, else the @@ -1850,7 +1850,7 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: m if cptr_sub_lvl > 0: if first_ty is None: return cptr_sub_ty res_ty_cs: mlirlite.LLVMType* = first_ty - for wrap_i_cs in range(cptr_sub_lvl): + for wrap_i_cs in range(int(cptr_sub_lvl)): res_ty_cs = mlirlite.Ptr(pool, res_ty_cs) return res_ty_cs if has_cvoid != 0: @@ -1929,7 +1929,7 @@ def _vpi_typedef_visited_check(sha1: str) -> int: if sha1 is None: return 1 if _g_vpi_typedef_visited is None: return 0 vi: int - for vi in range(_g_vpi_typedef_visited_count): + for vi in range(int(_g_vpi_typedef_visited_count)): if string.strcmp(_g_vpi_typedef_visited + vi * 17, sha1) == 0: return 1 return 0 @@ -2029,7 +2029,7 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp # length, then allocate exactly and fill (no fixed cap, so nothing is dropped). sub_need: sizet = 1 # trailing NUL ci_c: sizet - for ci_c in range(cn_r): + for ci_c in range(int(cn_r)): child_c: ast.AST* = ch_r.get(ci_c) if child_c is None: continue kk_c: int = child_c.kind() @@ -2037,7 +2037,7 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp imp_c: ast.Import* = ast.Import*(child_c) if imp_c.names is not None: in_nc: sizet = len(imp_c.names) - for ii_c in range(in_nc): + for ii_c in range(int(in_nc)): al_c: ast.Alias* = ast.Alias*(imp_c.names.get(ii_c)) if al_c is not None and al_c.name is not None: sub_need += string.strlen(al_c.name) + 1 elif kk_c == ast.ASTKind.ImportFrom: @@ -2048,7 +2048,7 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp sub_imports[0] = '\0' sub_len: sizet = 0 ci_r: sizet - for ci_r in range(cn_r): + for ci_r in range(int(cn_r)): child_r: ast.AST* = ch_r.get(ci_r) if child_r is None: continue kk_r: int = child_r.kind() @@ -2057,7 +2057,7 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp if imp_r.names is not None: in_n: sizet = len(imp_r.names) ii_r: sizet - for ii_r in range(in_n): + for ii_r in range(int(in_n)): al_r: ast.Alias* = ast.Alias*(imp_r.names.get(ii_r)) if al_r is not None and al_r.name is not None: il_r: sizet = string.strlen(al_r.name) @@ -2079,7 +2079,7 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp sub_imports[sub_len] = '\0' # second pass: register this module's AnnAssign CTypedef/CDefine ci2_r: sizet - for ci2_r in range(cn_r): + for ci2_r in range(int(cn_r)): child2_r: ast.AST* = ch_r.get(ci2_r) if child2_r is None: continue if child2_r.kind() != ast.ASTKind.AnnAssign: continue @@ -2095,7 +2095,9 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp if cn_v.const_kind == ast.CONST_FLOAT: GenApi.register_cdefine_double(pool, cdef_t.id, cn_v.float_val) elif cn_v.const_kind == ast.CONST_INT: cdef_val: int64 = GenApi.extract_cdefine_int_value(aa_r.value) - GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val) + # 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度), + # 否则使用方拿 bits=0 只能兜底,跨模块位宽会与定义方不一致 + GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa_r.annotation, aa_r.value)) # recurse into child imports (cycle guarded by the visited marks) if sub_imports[0] != '\0': _vpi_register_typedef_closure(pool, sub_imports, temp_dir, depth + 1) stdlib.free(sub_imports) @@ -2171,7 +2173,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v # Bookkeeping goes through StubMergerStore (same as ImportsGenMixin.translate_import): # `import X as Y -> from_imports "Y:X"`; `import a.b.c -> imported_modules "a"`. stub_ctx: StubMergerStore.ImportCtx* = StubMergerStore.AllocImportCtx(pool, None, None) - for si in range(n): + for si in range(int(n)): schild: ast.AST* = ch.get(si) if schild is None: continue sk: int = schild.kind() @@ -2179,7 +2181,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v simp: ast.Import* = ast.Import*(schild) if simp.names is not None: sn_n: sizet = len(simp.names) - for sni in range(sn_n): + for sni in range(int(sn_n)): sal: ast.Alias* = ast.Alias*(simp.names.get(sni)) if sal is not None and sal.name is not None: StubMergerStore.NotePlainImport(pool, stub_ctx, sal.name, sal.asname) elif sk == ast.ASTKind.ImportFrom: @@ -2263,7 +2265,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v if sifr.names is not None: sifr_nn: sizet = len(sifr.names) sifri: sizet - for sifri in range(sifr_nn): + for sifri in range(int(sifr_nn)): sial: ast.Alias* = ast.Alias*(sifr.names.get(sifri)) if sial is not None and sial.name is not None: slocal: str = sial.name @@ -2300,7 +2302,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v src_mod_buf: str = stdlib.malloc(sr_len_src + 1) if src_mod_buf is not None: sri_src: sizet - for sri_src in range(sr_len_src): + for sri_src in range(int(sr_len_src)): ch_src: char = stub_rel_src[sri_src] if ch_src == '/' or ch_src == '\\': src_mod_buf[sri_src] = '.' else: src_mod_buf[sri_src] = ch_src @@ -2328,7 +2330,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v # Pass 1A: register ALL ClassDefs (fields + structs) first, without methods, so a class # defined after a global's AnnAssign is still registered before that AnnAssign resolves. i0: sizet - for i0 in range(n): + for i0 in range(int(n)): child0: ast.AST* = ch.get(i0) if child0 is None: continue if child0.kind() != ast.ASTKind.ClassDef: continue @@ -2348,7 +2350,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v # Pass 1B: process method declares only after all classes are registered, since method # params/returns may reference classes defined later in the file (forward references). - for i1 in range(n): + for i1 in range(int(n)): child1: ast.AST* = ch.get(i1) if child1 is None: continue if child1.kind() != ast.ASTKind.ClassDef: continue @@ -2361,7 +2363,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v cls_children1: list[ast.AST*]* = class_cd1.children if cls_children1 is None: continue cn21: sizet = len(cls_children1) - for ci21 in range(cn21): + for ci21 in range(int(cn21)): m_child1: ast.AST* = cls_children1.get(ci21) if m_child1 is None: continue if m_child1.kind() != ast.ASTKind.FunctionDef: continue @@ -2372,7 +2374,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v _inject_method_declare(pool, mod, m_fd1, cls_name_v1, trans, src_sha1, imported_modules, from_imports, i32_ty, i8_ptr_ty, funcs_ptr, max_funcs) i: sizet - for i in range(n): + for i in range(int(n)): child: ast.AST* = ch.get(i) if child is None: continue kd: int = child.kind() @@ -2422,7 +2424,8 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val) elif cdef_is_str == 0: cdef_val: int64 = GenApi.extract_cdefine_int_value(aa.value) - GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val) + # 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度) + GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa.annotation, aa.value)) else: _inject_global_variable_declare(pool, mod, aa, trans, stub_imported_modules, stub_from_imports, i32_ty, i8_ptr_ty, src_sha1) continue @@ -2472,7 +2475,7 @@ def _ensure_dep_vpi_injected(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, _vpi_inj_count = 0 # already injected / currently injecting (cycle) -> treat as ready and short-circuit ii: int - for ii in range(_vpi_inj_count): + for ii in range(int(_vpi_inj_count)): if string.strcmp(_vpi_inj_sha1s + sizet(ii) * 17, dep_sha1) == 0: return 1 if _vpi_inj_count >= VPI_INJ_MAX or _vpi_inj_depth >= 32: return 0 temp_dir: str = GenApi.get_temp_dir() @@ -2553,7 +2556,7 @@ def _vpi_class_has_packed(decorator_list: list[ast.AST*]*) -> int: if decorator_list is None: return 0 dn: sizet = len(decorator_list) if dn == 0: return 0 - for di in range(dn): + for di in range(int(dn)): deco: ast.AST* = decorator_list.get(di) if deco is None: continue if deco.kind() == ast.ASTKind.Call: @@ -2563,7 +2566,7 @@ def _vpi_class_has_packed(decorator_list: list[ast.AST*]*) -> int: if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue if cl.args is None: continue an: sizet = len(cl.args) - for ai in range(an): + for ai in range(int(an)): ag: ast.AST* = cl.args.get(ai) if ag is not None and _vpi_tattr_name(ag, "packed") != 0: return 1 else: @@ -2576,7 +2579,7 @@ def _vpi_class_is_renum(class_cd: ast.ClassDef*) -> int: if class_cd is None or class_cd.bases is None: return 0 bases: list[ast.AST*]* = class_cd.bases bn: sizet = len(bases) - for bi in range(bn): + for bi in range(int(bn)): base_node: ast.AST* = bases.get(bi) if base_node is None: continue bk: int = base_node.kind() @@ -2649,7 +2652,7 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul cls_children_c: list[ast.AST*]* = class_cd.children if cls_children_c is not None: cn_c: sizet = len(cls_children_c) - for ci_c in range(cn_c): + for ci_c in range(int(cn_c)): m_child_c: ast.AST* = cls_children_c.get(ci_c) if m_child_c is None: continue if m_child_c.kind() != ast.ASTKind.AnnAssign: continue @@ -2672,7 +2675,7 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul # 4. build the ParamNode list first_node_c: mlirlite.ParamNode* = None prev_node_c: mlirlite.ParamNode* = None - for fi_c in range(field_count): + for fi_c in range(int(field_count)): fty_c: mlirlite.LLVMType* = (mlirlite.LLVMType*)(field_types_buf[fi_c]) pnode_c: mlirlite.ParamNode* = pool.alloc(mlirlite.ParamNode.__sizeof__()) if pnode_c is None: continue @@ -2728,7 +2731,7 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul # Also register fields into the StructEntry field table (name + type): a set Ty with an empty # field table makes lookup_field_by_class fail by name, which silently zeroes reads. afi_c: int = 0 - for afi_c in range(field_count): + for afi_c in range(int(field_count)): af_name_c: str = str(field_names_buf[afi_c]) af_ty_c: mlirlite.LLVMType* = (mlirlite.LLVMType*)(field_types_buf[afi_c]) GenApi.add_field(pool, entry_c, af_name_c, af_ty_c) @@ -2744,7 +2747,7 @@ def _inject_nested_class_declares(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModu children: list[ast.AST*]* = class_cd.children if children is None: return cn: sizet = len(children) - for ci in range(cn): + for ci in range(int(cn)): child_ast: ast.AST* = children.get(ci) if child_ast is None: continue if child_ast.kind() != ast.ASTKind.ClassDef: continue @@ -2949,7 +2952,7 @@ def _inject_function_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, args_node: ast.Arguments* = ast.Arguments*(fd.args) if args_node.args is not None: args_list: list[ast.AST*]* = args_node.args - param_count = len(args_list) + param_count = int(len(args_list)) # Param-count upper-bound sentinel (fail-fast): a corrupted vpi tree node makes __len__() # return a huge value, so add_param runs massively and the Params chain balloons. if param_count < 0 or param_count > 128: @@ -2957,7 +2960,7 @@ def _inject_function_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, # convergence point: longjmp back to the REPL loop in a REPL session (diagnostics printed) VLogger.repl_abort() pi: sizet - for pi in range(param_count): + for pi in range(int(param_count)): ag: ast.Arg* = ast.Arg*(args_list.get(pi)) if ag is None: continue pname: str = ag.arg @@ -2966,7 +2969,7 @@ def _inject_function_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, param_ty: mlirlite.LLVMType* = i8_ptr_ty # Prefer the vpi text annotation cache (same source as return types): corrupted tree # nodes make AST resolve return garbage types and mis-size cross-module declare params. - pseg_ty: mlirlite.LLVMType* = _resolve_vpi_param_annot_seg(pool, src_sha1, None, fd.name, pi, i32_ty, i8_ptr_ty) + pseg_ty: mlirlite.LLVMType* = _resolve_vpi_param_annot_seg(pool, src_sha1, None, fd.name, int(pi), i32_ty, i8_ptr_ty) if pseg_ty is not None: match pseg_ty: case mlirlite.LLVMType.Struct(_, _, _): @@ -3004,7 +3007,7 @@ def _inject_function_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, # cross-module calls missing args fill from the table instead of reading stack garbage. if args_node.defaults is not None: fd_dlist: list[ast.AST*]* = args_node.defaults - fd_default_count = len(fd_dlist) + fd_default_count = int(len(fd_dlist)) if fd_default_count < 0 or fd_default_count > 128: # A corrupted args.defaults node makes len return a huge value, so # _register_global_defaults would over-allocate and write out of bounds; skip it. @@ -3083,7 +3086,7 @@ def _inject_method_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, m_ args_node: ast.Arguments* = ast.Arguments*(m_fd.args) if args_node.args is not None: args_list: list[ast.AST*]* = args_node.args - param_count = len(args_list) + param_count = int(len(args_list)) # Param-count upper-bound sentinel (fail-fast): same cause as _inject_function_declare # (a corrupted vpi tree node makes __len__() return a huge value). if param_count < 0 or param_count > 128: @@ -3091,7 +3094,7 @@ def _inject_method_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, m_ # convergence point: longjmp back to the REPL loop in a REPL session (diagnostics printed) VLogger.repl_abort() pi: sizet - for pi in range(param_count): + for pi in range(int(param_count)): ag: ast.Arg* = ast.Arg*(args_list.get(pi)) if ag is None: continue pname: str = ag.arg @@ -3106,7 +3109,7 @@ def _inject_method_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, m_ param_ty: mlirlite.LLVMType* = i8_ptr_ty # Prefer the vpi text annotation cache (same source as return types): corrupted tree # nodes mis-size declare params, causing trunc i64->i32 and a caller/callee mismatch. - pseg_ty: mlirlite.LLVMType* = _resolve_vpi_param_annot_seg(pool, src_sha1, cls_name, m_fd.name, pi, i32_ty, i8_ptr_ty) + pseg_ty: mlirlite.LLVMType* = _resolve_vpi_param_annot_seg(pool, src_sha1, cls_name, m_fd.name, int(pi), i32_ty, i8_ptr_ty) if pseg_ty is not None: match pseg_ty: case mlirlite.LLVMType.Struct(_, _, _): @@ -3152,7 +3155,7 @@ def _inject_method_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, m_ md_args_node: ast.Arguments* = ast.Arguments*(m_fd.args) if md_args_node.defaults is not None: md_dlist: list[ast.AST*]* = md_args_node.defaults - md_default_count = len(md_dlist) + md_default_count = int(len(md_dlist)) if md_default_count < 0 or md_default_count > 128: # Same as _inject_function_declare: a corrupted tree node makes defaults len huge, # which would write out of bounds into perm_pool; skip registration. @@ -3373,8 +3376,8 @@ def _repl_stub_find(key: str) -> ReplStubClass*: """Look up a class-table entry by "."; None if not found.""" if key is None: return None i: int - for i in range(ReplStubCount): - e: ReplStubClass* = _repl_stub_at(i) + for i in range(int(ReplStubCount)): + e: ReplStubClass* = _repl_stub_at(int(i)) if e is None or e.Key is None: continue if string.strcmp(e.Key, key) == 0: return e return None @@ -3386,14 +3389,14 @@ def _repl_stub_find_base(e: ReplStubClass*, bi: int) -> ReplStubClass*: pname: str = rawptr(e.Bases[bi]) if pname is None: return None i: int - for i in range(ReplStubCount): - pe: ReplStubClass* = _repl_stub_at(i) + for i in range(int(ReplStubCount)): + pe: ReplStubClass* = _repl_stub_at(int(i)) if pe is None or pe.ClassName is None: continue if string.strcmp(pe.ClassName, pname) != 0: continue if e.Sha1 is not None and pe.Sha1 is not None: if string.strcmp(pe.Sha1, e.Sha1) == 0: return pe - for i in range(ReplStubCount): - pe2: ReplStubClass* = _repl_stub_at(i) + for i in range(int(ReplStubCount)): + pe2: ReplStubClass* = _repl_stub_at(int(i)) if pe2 is None or pe2.ClassName is None: continue if string.strcmp(pe2.ClassName, pname) == 0: return pe2 return None @@ -3403,7 +3406,7 @@ def _repl_stub_has_name(arr: sizet*, cnt: int, name: str) -> int: """Check whether the name is already in the list (first-declaration wins).""" if arr is None or name is None: return 0 i: int - for i in range(cnt): + for i in range(int(cnt)): addr: sizet = arr[i] if addr == 0: continue ex: str = rawptr(addr) @@ -3417,7 +3420,7 @@ def _repl_stub_deco_flags(deco_list: list[ast.AST*]*) -> int: flags: int = 0 dn: sizet = len(deco_list) di: sizet - for di in range(dn): + for di in range(int(dn)): deco: ast.AST* = deco_list.get(di) if deco is None: continue nm: str = None @@ -3504,7 +3507,7 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: return -1 # (1) base classes must be ready (not ready -> defer; refused -> this layout is untrusted) bi: int - for bi in range(e.BaseCount): + for bi in range(int(e.BaseCount)): pe: ReplStubClass* = _repl_stub_find_base(e, bi) if pe is None: return 0 if pe.State == _REPL_SS_PENDING: return 0 @@ -3530,23 +3533,23 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: else: hv = 1 if (e.Flags & _REPL_SF_ANYFUNC) != 0 else 0 # (3) field flattening (base fields first + own fields, deduped by short name, first wins) cn: int = 0 - for bi in range(e.BaseCount): + for bi in range(int(e.BaseCount)): pe: ReplStubClass* = _repl_stub_find_base(e, bi) fi: int - for fi in range(pe.Count): + for fi in range(int(pe.Count)): if cn >= _REPL_STUB_FIELD_MAX: break nm: str = rawptr(pe.Names[fi]) if nm is None: continue - if _repl_stub_has_name(e.Names, cn, nm) != 0: continue + if _repl_stub_has_name(e.Names, int(cn), nm) != 0: continue e.Names[cn] = pe.Names[fi] e.Types[cn] = pe.Types[fi] cn += 1 oi: int - for oi in range(e.OwnCount): + for oi in range(int(e.OwnCount)): if cn >= _REPL_STUB_FIELD_MAX: break nm2: str = rawptr(e.OwnNames[oi]) if nm2 is None: continue - if _repl_stub_has_name(e.Names, cn, nm2) != 0: continue + if _repl_stub_has_name(e.Names, int(cn), nm2) != 0: continue e.Names[cn] = e.OwnNames[oi] e.Types[cn] = e.OwnTypes[oi] cn += 1 @@ -3561,10 +3564,10 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: # (5) virtual method short-name order (base virtuals first + own eligible methods in class order) vn: int = 0 if hv == 1: - for bi in range(e.BaseCount): + for bi in range(int(e.BaseCount)): pe: ReplStubClass* = _repl_stub_find_base(e, bi) vi: int - for vi in range(pe.VtCount): + for vi in range(int(pe.VtCount)): if vn >= _REPL_STUB_VT_MAX: break mn: str = rawptr(pe.VtNames[vi]) if mn is None: continue @@ -3572,7 +3575,7 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: e.VtNames[vn] = pe.VtNames[vi] vn += 1 mi: int - for mi in range(e.MethodCount): + for mi in range(int(e.MethodCount)): if vn >= _REPL_STUB_VT_MAX: break mn2: str = rawptr(e.MethodNames[mi]) if mn2 is None: continue @@ -3591,7 +3594,7 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: ReplStubWarnLeft -= 1 stdio.printf("includes class layout: vtable slot count of %s.%s does not match the stub (%d vs %d) " "-- this class skips virtual dispatch (degrades to direct call, session continues)\n", e.Sha1, e.ClassName, vn, e.VtArity) # (6) build the LLVM struct (named "%.", same as the definition side) - total: int = cn + hv + total: int = int(cn + hv) i8_ptr: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool)) if i8_ptr is None: return 0 first_node: mlirlite.ParamNode* = None @@ -3604,7 +3607,7 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: first_node = nd0 prev_node = nd0 fi2: int - for fi2 in range(cn): + for fi2 in range(int(cn)): fty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(e.Types[fi2]) if fty is None: continue nd: mlirlite.ParamNode* = pool.alloc(mlirlite.ParamNode.__sizeof__()) @@ -3629,7 +3632,7 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: entry.HasVTable = 1 GenApi.add_field(pool, entry, "__vtable__", i8_ptr) fi3: int - for fi3 in range(cn): + for fi3 in range(int(cn)): fname: str = rawptr(e.Names[fi3]) fty3: mlirlite.LLVMType* = (mlirlite.LLVMType*)(e.Types[fi3]) if fname is None or fty3 is None: continue @@ -3644,7 +3647,7 @@ def _repl_stub_try(e: ReplStubClass*, mod: mlirlite.LLVMModule*) -> int: if arr is not None: string.memset(arr, 0, 8 * e.BaseCount) bi3: int - for bi3 in range(e.BaseCount): + for bi3 in range(int(e.BaseCount)): bnm: str = rawptr(e.Bases[bi3]) bdup: str = _repl_stub_dupstr(pool, bnm) if bdup is not None: arr[bi3] = sizet(bdup) @@ -3708,7 +3711,7 @@ def _repl_stub_add_class(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cd: if bcnt > 0: if bcnt > _REPL_STUB_FIELD_MAX: bcnt = _REPL_STUB_FIELD_MAX bi: int - for bi in range(bcnt): + for bi in range(int(bcnt)): bnm: str = rawptr(pb[bi]) bdup: str = _repl_stub_dupstr(pool, bnm) if bdup is not None: e.Bases[bi] = sizet(bdup) @@ -3719,7 +3722,7 @@ def _repl_stub_add_class(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, cd: if children is not None: cn: sizet = len(children) ci: sizet - for ci in range(cn): + for ci in range(int(cn)): ch2: ast.AST* = children.get(ci) if ch2 is None: continue k2: int = ch2.kind() @@ -3811,7 +3814,7 @@ def _repl_stub_collect_module(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, stdio.printf("includes class layout: module %s has no .meta stub (data missing) -- this module " "classes with parent/vtable will each be explicitly refused as data missing (layout metadata cannot be read => no " "field count to compare, not a field count mismatch). First run batch processing once in the same project to generate stubs, " "then reuse them with `--project .\\project.vpj --repl` (in the TransPyV directory)\n", src_sha1) cnt: int = 0 i: sizet - for i in range(n): + for i in range(int(n)): child: ast.AST* = ch.get(i) if child is None: continue if child.kind() != ast.ASTKind.ClassDef: continue @@ -3836,24 +3839,24 @@ def ReplStubLayoutFinish() -> int: global ReplStubRefused, ReplStubWarnLeft if ReplStubLayoutMode == 0: return 0 rounds: int - for rounds in range(_REPL_STUB_FINISH_PASSES): + for rounds in range(int(_REPL_STUB_FINISH_PASSES)): prog: int = 0 i: int - for i in range(ReplStubCount): - e: ReplStubClass* = _repl_stub_at(i) + for i in range(int(ReplStubCount)): + e: ReplStubClass* = _repl_stub_at(int(i)) if e is None or e.State != _REPL_SS_PENDING: continue if _repl_stub_try(e, None) != 0: prog += 1 if prog == 0: break # still not ready -> the base was never registered (missing stub/refused): explicitly refuse i2: int - for i2 in range(ReplStubCount): + for i2 in range(int(ReplStubCount)): e2: ReplStubClass* = _repl_stub_at(i2) if e2 is None or e2.State != _REPL_SS_PENDING: continue e2.State = _REPL_SS_REFUSED # find the still-unready base class name (to identify the upstream) pbad: str = "(not located)" bi2: int - for bi2 in range(e2.BaseCount): + for bi2 in range(int(e2.BaseCount)): peb: ReplStubClass* = _repl_stub_find_base(e2, bi2) if peb is not None and peb.State == _REPL_SS_OK: continue if peb is not None and peb.ClassName is not None: pbad = peb.ClassName diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 9e10dea..a692b02 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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=))` (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=))` (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=))` (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=))` (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 diff --git a/App/lib/core/Gen/AsyncGenMixin.vp b/App/lib/core/Gen/AsyncGenMixin.vp index 224746f..030f947 100644 --- a/App/lib/core/Gen/AsyncGenMixin.vp +++ b/App/lib/core/Gen/AsyncGenMixin.vp @@ -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_) @@ -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] diff --git a/App/lib/core/Gen/Class/ClassGenVTable.vp b/App/lib/core/Gen/Class/ClassGenVTable.vp index 0c023e5..851ca9c 100644 --- a/App/lib/core/Gen/Class/ClassGenVTable.vp +++ b/App/lib/core/Gen/Class/ClassGenVTable.vp @@ -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 diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 4de7585..1eaa88a 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -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 diff --git a/App/lib/core/Gen/Expr/ExprGenCdef.vp b/App/lib/core/Gen/Expr/ExprGenCdef.vp index 86e60f4..1436402 100644 --- a/App/lib/core/Gen/Expr/ExprGenCdef.vp +++ b/App/lib/core/Gen/Expr/ExprGenCdef.vp @@ -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*: diff --git a/App/lib/core/Gen/Expr/ExprGenName.vp b/App/lib/core/Gen/Expr/ExprGenName.vp index 4bef76b..bb21e41 100644 --- a/App/lib/core/Gen/Expr/ExprGenName.vp +++ b/App/lib/core/Gen/Expr/ExprGenName.vp @@ -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. diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index 6cf4b2a..032950c 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -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) diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index 3907f3e..113cf33 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -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 diff --git a/App/lib/core/Gen/Func/FuncGenMixin.vp b/App/lib/core/Gen/Func/FuncGenMixin.vp index 357d3e3..8e5e15f 100644 --- a/App/lib/core/Gen/Func/FuncGenMixin.vp +++ b/App/lib/core/Gen/Func/FuncGenMixin.vp @@ -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: diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index b60f47e..cf6f17d 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -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(, , ...):逐个标签并入 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() diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 47a969b..bba612f 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -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] diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index 14f16d9..951d83b 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -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) diff --git a/App/lib/core/Gen/Type/TypeGenCDef.vp b/App/lib/core/Gen/Type/TypeGenCDef.vp index 4909207..6a8f251 100644 --- a/App/lib/core/Gen/Type/TypeGenCDef.vp +++ b/App/lib/core/Gen/Type/TypeGenCDef.vp @@ -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: diff --git a/App/lib/core/Gen/VarGenMixin.vp b/App/lib/core/Gen/VarGenMixin.vp index a69f596..796c1e1 100644 --- a/App/lib/core/Gen/VarGenMixin.vp +++ b/App/lib/core/Gen/VarGenMixin.vp @@ -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) diff --git a/App/lib/core/Repl.vp b/App/lib/core/Repl.vp index d848103..092d389 100644 --- a/App/lib/core/Repl.vp +++ b/App/lib/core/Repl.vp @@ -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 TransPyV_repl/ (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 diff --git a/ORIGINALSIN b/ORIGINALSIN new file mode 100644 index 0000000..eaa3589 --- /dev/null +++ b/ORIGINALSIN @@ -0,0 +1 @@ +Viper's original sin is that the person who invented it wasn't smart enough; its abilities don't match its ideals. \ No newline at end of file