feat(types): 禁止隐式窄化(整数 高→低);_tb_read_whole 容量改 sizet
规则收紧(2026-10-10):整数位宽 高→低 隐式窄化一律 fail-fast(CastError),仅两类豁免:① 目标 i1 的真值归一化;② 源是编译期常量且值落进目标位宽。_scalar_convert_reason 的 int↔int 同步改为'目标位宽≥源位宽才 PC_OK'。BuildPipeline._tb_read_whole 去掉 ULONG(sz)+ULONG(2) 的窄化,改 sizet(sz)+2。
This commit is contained in:
1 parent
9db3748f68
commit
5bb6538703
2 files changed
+43
-35
No files matched your search
@@ -7,11 +7,11 @@ import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import vthreading
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import w32.win32process as win32process
|
||||
import w32.win32sync as win32sync
|
||||
import win32.fileio as fileio
|
||||
import win32.win32file
|
||||
import win32.win32base
|
||||
import win32.win32process as win32process
|
||||
import win32.win32sync as win32sync
|
||||
import subprocess
|
||||
import viperlib
|
||||
import ast
|
||||
@@ -280,8 +280,8 @@ def ensure_dir(path: str) -> int:
|
||||
|
||||
# Hot path (PROF: ensure_dir was 4.5%): if the final dir exists, return success early,
|
||||
# saving an alloc + N+1 CreateDirectoryA calls. Semantics unchanged (exists == success).
|
||||
attr: ULONG = w32.win32file.GetFileAttributesA(path)
|
||||
if attr != 0xFFFFFFFF and (attr & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: return 0
|
||||
attr: ULONG = win32.win32file.GetFileAttributesA(path)
|
||||
if attr != 0xFFFFFFFF and (attr & win32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: return 0
|
||||
|
||||
# copy the path into a writable buffer (for stepwise truncation)
|
||||
buf: bytes = _mbuddy.alloc(path_len + 1)
|
||||
@@ -296,11 +296,11 @@ def ensure_dir(path: str) -> int:
|
||||
if ch == 47 or ch == 92: # '/' = 47, '\\' = 92
|
||||
saved: int = ch
|
||||
buf[i] = '\0'
|
||||
w32.win32file.CreateDirectoryA(buf, None)
|
||||
win32.win32file.CreateDirectoryA(buf, None)
|
||||
buf[i] = saved
|
||||
|
||||
# create the final directory
|
||||
w32.win32file.CreateDirectoryA(buf, None)
|
||||
win32.win32file.CreateDirectoryA(buf, None)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -567,15 +567,15 @@ def _run_cmd_wait(cmd: str) -> int:
|
||||
cmd_line: bytes = stdlib.malloc(cl_len + 1)
|
||||
if cmd_line is None: return -1
|
||||
string.strcpy(cmd_line, cmd)
|
||||
sa: w32.win32base.SECURITY_ATTRIBUTES = w32.win32base.SECURITY_ATTRIBUTES()
|
||||
sa: win32.win32base.SECURITY_ATTRIBUTES = win32.win32base.SECURITY_ATTRIBUTES()
|
||||
si: win32process.STARTUPINFOA = win32process.STARTUPINFOA()
|
||||
pi: win32process.PROCESS_INFORMATION = win32process.PROCESS_INFORMATION()
|
||||
# T() only allocas, no memset: unset STARTUPINFOA fields are stack garbage, so concurrent
|
||||
# threads get random dwFlags and CreateProcessA uses junk stdio handles. Clear si/sa/pi.
|
||||
string.memset(&si, 0, win32process.STARTUPINFOA.__sizeof__())
|
||||
string.memset(&sa, 0, w32.win32base.SECURITY_ATTRIBUTES.__sizeof__())
|
||||
string.memset(&sa, 0, win32.win32base.SECURITY_ATTRIBUTES.__sizeof__())
|
||||
string.memset(&pi, 0, win32process.PROCESS_INFORMATION.__sizeof__())
|
||||
sa.nLength = w32.win32base.SECURITY_ATTRIBUTES.__sizeof__()
|
||||
sa.nLength = win32.win32base.SECURITY_ATTRIBUTES.__sizeof__()
|
||||
sa.lpSecurityDescriptor = t.CPtr(0)
|
||||
sa.bInheritHandle = 1
|
||||
si.cb = win32process.STARTUPINFOA.__sizeof__()
|
||||
@@ -585,15 +585,15 @@ def _run_cmd_wait(cmd: str) -> int:
|
||||
# CreateProcessA failure diagnostics: read GetLastError immediately (later calls overwrite
|
||||
# it). Use warning, not error (error would sys.exit in a worker thread).
|
||||
if ok == 0:
|
||||
cp_err: uint32 = w32.win32base.GetLastError()
|
||||
cp_err: uint32 = win32.win32base.GetLastError()
|
||||
VLogger.warning(f"CreateProcessA failed (GetLastError={cp_err}) external command not started: cmd={cmd}", "LLC")
|
||||
stdlib.free(cmd_line)
|
||||
if ok == 0: return -1
|
||||
win32sync.WaitForSingleObject(pi.hProcess, w32.win32base.INFINITE)
|
||||
win32sync.WaitForSingleObject(pi.hProcess, win32.win32base.INFINITE)
|
||||
exit_code: uint32 = 0
|
||||
win32process.GetExitCodeProcess(pi.hProcess, &exit_code)
|
||||
w32.win32base.CloseHandle(pi.hProcess)
|
||||
w32.win32base.CloseHandle(pi.hThread)
|
||||
win32.win32base.CloseHandle(pi.hProcess)
|
||||
win32.win32base.CloseHandle(pi.hThread)
|
||||
return int(exit_code)
|
||||
|
||||
|
||||
@@ -673,7 +673,7 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
|
||||
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")
|
||||
w32.win32base.CloseHandle(h_j)
|
||||
win32.win32base.CloseHandle(h_j)
|
||||
# check this batch's results
|
||||
for j in range(batch_n):
|
||||
tsk2: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(batch_start + j) * slot_size)
|
||||
@@ -702,11 +702,11 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
|
||||
|
||||
def _CollectObjFilesImpl(base_dir: str, bd_len: sizet, out_buf: bytes, out_pos: sizet, out_size: sizet) -> sizet:
|
||||
"""Recursively scan base_dir, collect .obj paths into out_buf, return the new out_pos."""
|
||||
find_data: w32.win32file.WIN32_FIND_DATAA* = _mbuddy.alloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
find_data: win32.win32file.WIN32_FIND_DATAA* = _mbuddy.alloc(win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
if find_data is None:
|
||||
VLogger.warning("_CollectObjFilesImpl failed to allocate find_data, fewer .obj collected", "collect")
|
||||
return out_pos
|
||||
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
string.memset(find_data, 0, win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
|
||||
pattern: bytes = _mbuddy.alloc(bd_len + 8)
|
||||
if pattern is None:
|
||||
@@ -714,9 +714,9 @@ def _CollectObjFilesImpl(base_dir: str, bd_len: sizet, out_buf: bytes, out_pos:
|
||||
return out_pos
|
||||
viperlib.snprintf(pattern, bd_len + 8, "%s/*", base_dir)
|
||||
|
||||
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
|
||||
handle: win32.win32base.HANDLE = win32.win32file.FindFirstFileA(pattern, find_data)
|
||||
_mbuddy.free(pattern)
|
||||
if handle == w32.win32base.INVALID_HANDLE_VALUE:
|
||||
if handle == win32.win32base.INVALID_HANDLE_VALUE:
|
||||
VLogger.warning("_CollectObjFilesImpl cannot open directory, fewer .obj collected", "collect")
|
||||
return out_pos
|
||||
|
||||
@@ -732,7 +732,7 @@ def _CollectObjFilesImpl(base_dir: str, bd_len: sizet, out_buf: bytes, out_pos:
|
||||
if is_dot == 0:
|
||||
attrs: ULONG = find_data.dwFileAttributes
|
||||
is_dir: int = 0
|
||||
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
|
||||
if (attrs & win32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
|
||||
if is_dir != 0:
|
||||
# recurse into the subdirectory
|
||||
sub_dir: bytes = _mbuddy.alloc(bd_len + fname_len + 2)
|
||||
@@ -752,9 +752,9 @@ def _CollectObjFilesImpl(base_dir: str, bd_len: sizet, out_buf: bytes, out_pos:
|
||||
viperlib.snprintf(out_buf + out_pos, need, "%s/%s", base_dir, fname)
|
||||
out_pos += bd_len + 1 + fname_len
|
||||
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
|
||||
w32.win32file.FindClose(handle)
|
||||
win32.win32file.FindClose(handle)
|
||||
return out_pos
|
||||
|
||||
|
||||
@@ -1823,7 +1823,8 @@ def _tb_read_whole(mb: memhub.MemBuddy*, path: str) -> str:
|
||||
if buf is None:
|
||||
f.close()
|
||||
return None
|
||||
got: LONG = f.read_all(buf, ULONG(sz) + ULONG(2))
|
||||
# 容量全程用 sizet(与 sz 同宽):旧写法 ULONG(sz) + ULONG(2) 会在 sz >= 4GiB 时静默回绕
|
||||
got: LONG = f.read_all(buf, sizet(sz) + 2)
|
||||
f.close()
|
||||
if got < 0: return None
|
||||
return buf
|
||||
|
||||
@@ -322,8 +322,10 @@ novirtual class ExprGenBase:
|
||||
d_int: int = self.get_llvm_type_bits(dst_pt)
|
||||
s_flt: int = self.get_llvm_float_bits(src_pt)
|
||||
d_flt: int = self.get_llvm_float_bits(dst_pt)
|
||||
# int <-> int: narrowing and widening are both allowed.
|
||||
if s_int != 0 and d_int != 0: return PC_OK
|
||||
# int <-> int: widening is lossless; narrowing (high->low) is lossy -> forbidden.
|
||||
if s_int != 0 and d_int != 0:
|
||||
if d_int >= s_int: return PC_OK
|
||||
return PC_LOSSY
|
||||
# float <-> float: allowed.
|
||||
if s_flt != 0 and d_flt != 0: return PC_OK
|
||||
# int -> float: lossless only when float width >= int width.
|
||||
@@ -534,14 +536,19 @@ novirtual class ExprGenBase:
|
||||
# otherwise sext. trans is required so unsigned annotations resolve.
|
||||
if self._zext_or_sext_val(builder.Pool, val, node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
|
||||
return mlirlite.build_sext(builder, val, target_ty)
|
||||
# Narrowing diagnostic (_Type.md issue 8): a known constant exceeding the
|
||||
# target unsigned range fails fast; negative constants wrap (mod 2^bits).
|
||||
if val.IsConst == 1 and val.IntVal > 0:
|
||||
target_max_u: uint64 = (uint64(1) << target_bits) - 1
|
||||
if uint64(val.IntVal) > target_max_u:
|
||||
max_print: int64 = int64(target_max_u)
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"narrowing truncation overflow: constant {val.IntVal} exceeds the {target_bits}-bit range [0, {max_print}] of the target type, the assignment would be silently truncated", "OverflowError")
|
||||
return mlirlite.build_trunc(builder, val, target_ty)
|
||||
# 隐式窄化**禁止**(规则 2026-10-10 收紧,取代"整数↔整数 所有方向都允许"):
|
||||
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。仅两类豁免:
|
||||
# ① 目标 i1:真值归一化(x != 0),语义明确,不是位截断(已在上方返回);
|
||||
# ② 源是**编译期常量且值能落进目标位宽**(如字面量 5 → i32),无信息损失。
|
||||
if val.IsConst == 1:
|
||||
iv: int64 = val.IntVal
|
||||
lo_c: int64 = -(int64(1) << (target_bits - 1))
|
||||
hi_c: int64 = (int64(1) << target_bits) - 1
|
||||
if iv < lo_c or iv > hi_c:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"narrowing truncation overflow: constant {iv} is outside the {target_bits}-bit representable range [{lo_c}, {hi_c}]; use an explicit cast or widen the target type", "OverflowError")
|
||||
return mlirlite.build_trunc(builder, val, target_ty)
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to a {target_bits}-bit type (the high bits would be lost); use an explicit cast (e.g. int32(x) / t.CInt32T(x)) or widen the target type", "CastError")
|
||||
return val
|
||||
# Float conversion.
|
||||
val_fbits: int = self.get_llvm_float_bits(val.Ty)
|
||||
target_fbits: int = self.get_llvm_float_bits(target_ty)
|
||||
|
||||
Reference in new issue
Block a user