Files
ViperC/App/lib/core/Gen/Expr/ExprGenCdef.vp
T

1193 lines
62 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.Stub.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
# ============================================================
# ExprGenCdef - ExprGenMixin 拆分(阶段 D):跨模块 CDefine 查找
# ============================================================
novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
"""跨模块 CDefine 查找"""
# _build_source_path_from_sha1:通过 SHA1 从 StubMerger 全局存储器构建源 .vp/.py 文件路径(.vpi 未生成时回退读源 .py);rel_path 以 "lib/" 开头 → {Config.SourceDir}/{rel_path},其他 → {Config.IncludesDir}/{rel_path}。返回 stdlib.malloc 分配的路径字符串(调用者负责 free),失败返回 None
def _build_source_path_from_sha1(self, sha1: str) -> str:
"""通过 SHA1 从全局存储器构建源 .py 文件路径"""
if sha1 is None: return None
if Config.IncludesDir is None: return None
# 从 StubMerger 全局存储器获取相对路径数组
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
count: int = StubMergerStore.GetSha1StoreCount()
if rel_arr_ptr is None or count <= 0: return None
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
if sha1_arr_ptr is None: return None
# 遍历查找匹配的 SHA1
idx_s: int = -1
for i in range(count):
sidx: sizet = sizet(i) * 17
cur_sha1: str = sha1_arr_ptr + sidx
if cur_sha1[0] == '\0': continue
if string.strcmp(cur_sha1, sha1) == 0:
idx_s = i
break
if idx_s < 0:
return None
# 获取相对路径(MAX_REL_PATH_LEN=256,避免跨模块 CDefine 解析问题)
MAX_RPL: sizet = 256
ridx: sizet = sizet(idx_s) * MAX_RPL
rel_path: str = rel_arr_ptr + ridx
if rel_path[0] == '\0':
return None
# 选择基础目录(优先级):1) 条目存储的 includes 根目录 IncDir(绝对路径,支持多 includes 目录,避免"首 includes 目录"启发式对非首目录文件失效);2) rel_path 前缀启发式——以 "lib/" 开头 → App 源文件(SourceDir);"includes/" 开头 → includes 文件(去掉前缀);其他无前缀路径先试 SourceDir,不存在再试 IncludesDir
base_dir: str = None
rel_used: str = rel_path
inc_arr_ptr: bytes = StubMergerStore.GetSha1StoreIncdirArrPtr()
if inc_arr_ptr is not None:
iidx: sizet = sizet(idx_s) * StubMerger.MAX_INC_DIR_LEN
inc_dir: str = inc_arr_ptr + iidx
if inc_dir[0] != '\0':
base_dir = inc_dir
if base_dir is None and string.strlen(rel_path) >= 4:
if (rel_path[0] == 'l' and rel_path[1] == 'i' and
rel_path[2] == 'b' and rel_path[3] == '/'):
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
if base_dir is None and string.strlen(rel_path) >= 9:
if (rel_path[0] == 'i' and rel_path[1] == 'n' and
rel_path[2] == 'c' and rel_path[3] == 'l' and
rel_path[4] == 'u' and rel_path[5] == 'd' and
rel_path[6] == 'e' and rel_path[7] == 's' and
rel_path[8] == '/'):
rel_used = rel_path + 9
base_dir = Config.IncludesDir
if base_dir is None:
base_dir = Config.SourceDir if Config.SourceDir is not None else Config.IncludesDir
# 构建完整路径: {base_dir}/{rel_used}
base_len: sizet = string.strlen(base_dir)
rel_len: sizet = string.strlen(rel_used)
path_len: sizet = base_len + rel_len + 2
buf: str = stdlib.malloc(path_len)
if buf is None:
return None
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_used)
# 存在性检查:SourceDir 下不存在且 IncludesDir 不同 → 重试 IncludesDir
pf_chk: rawptr[fileio.File] = fileio.File(buf, fileio.MODE.R)
if pf_chk.closed:
pf_chk.close()
if Config.IncludesDir is not None and base_dir != Config.IncludesDir:
stdlib.free(buf)
base_len2: sizet = string.strlen(Config.IncludesDir)
path_len2: sizet = base_len2 + rel_len + 2
buf = stdlib.malloc(path_len2)
if buf is None:
return None
viperlib.snprintf(buf, path_len2, "%s/%s", Config.IncludesDir, rel_used)
pf_chk2: rawptr[fileio.File] = fileio.File(buf, fileio.MODE.R)
if pf_chk2.closed:
pf_chk2.close()
stdlib.free(buf)
return None
pf_chk2.close()
else:
stdlib.free(buf)
return None
else:
pf_chk.close()
return buf
# ============================================================
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
#
# 当当前模块的 CDefine 表中找不到某个 Name 时,从 from_imports
# 解析出源模块,再从该模块的 pyi 文件中解析 CDefine 常量值。
# 找到后缓存到当前模块的 CDefine 表中,避免重复查找。
#
# pyi 文件格式: "NAME: t.CDefine = value"
# ============================================================
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*,
name: str,
from_imports: str,
imported_modules: str = None,
depth: int = 0,
node: ast.AST* = None) -> int64:
"""跨模块查找 CDefine 常量,返回值(64 位)或 -1(未找到)
查找顺序:
1. from_imports 精确匹配(from X import NAME)
2. from_imports star 回退(from X import *)
3. imported_modules 遍历(import X):当通过 import X 导入模块时,
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
depth: 递归深度(子模块递归/imported_modules 遍历的内部调用递增)。
node: 触发查找的 AST 节点(递归内传 None,line 0;顶层调用点传实际节点)。
"""
# 递归深度守卫:子模块递归(from .X import NAME re-export 链)与
# imported_modules 遍历组合可能形成循环(模块间互相 from import),
# 无上限时无限递归 → 每次递归 pool.alloc 缓冲(rc_sub_buf/rc_full_mod/
# rc_fi/im_buf/sub_fi)→ O(n²) 累积 → 主池 1.5GB OOM
# (NewAstTest CONST_INT 单次查找 14593 次调用,fi 恒为 CONST_INT:ast)。
# 正常跨模块 CDefine 查找深度 < 8(如 ast → ast.base),32 上限 fail-fast
# 暴露循环 import 链(而非静默 OOM)。报错带 node 行号 + 当前递归模块链。
if depth > 32:
err_buf: str = pool.alloc(256)
if err_buf is not None:
viperlib.snprintf(err_buf, 256,
"跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export): %s (depth=%d fi=%s)",
name, depth,
from_imports if from_imports is not None else "(nil)")
self.Gen.TypeGenErr.fatal_error(node, err_buf, "InternalCompilerError")
self.Gen.TypeGenErr.fatal_error(node,
"跨模块 CDefine 查找递归过深(疑似模块循环 import/re-export)", "InternalCompilerError")
# 入口处清零标志,确保所有早退路径(return -1)都返回"未找到"状态
# 避免递归调用时前一次成功的标志残留导致误判
self.Gen.TypeGenCDef.set_cdefine_found(0)
if name is None:
return -1
if from_imports is None:
from_imports = ""
# 1. 从 from_imports 查找名称对应的模块名
# 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 0)
if mod_name_raw is None:
# 精确匹配失败:尝试 star import 回退
mod_name_raw = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 1)
# 截断模块名到 NUL 结尾:lookup_from_import 返回指向 from_imports 内部的
# 指针(非 NUL 结尾,后随空格分隔的其他条目,如 "ast CONST_STR:ast ...")。
# 不截断会污染 base_mod:strlen 读到整个 from_imports 末尾 → rc_full_mod
# 每层累积整个列表 + ".base"(dump:ast.base.base.base 无限递归根因),
# 且缓存 key 错乱("CONST_INT@ast CONST_STR:ast ...")无法命中。
# 与 _lookup_sha1_from_modname_raw 的截断逻辑一致(空格/冒号/NUL 分隔)。
base_mod: str = None
if mod_name_raw is not None:
mbl: sizet = 0
while mod_name_raw[mbl] != ' ' and mod_name_raw[mbl] != '\0' \
and mod_name_raw[mbl] != ':':
mbl += 1
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]
mb_buf[mbl] = '\0'
base_mod = mb_buf
while base_mod is not None and base_mod[0] == '.':
base_mod = base_mod + 1
# 跨模块 CDefine 查找缓存(perm_pool 永久,跨文件复用):
# 首次查找成功后结果缓存,后续文件(含 reset_to(mark2)/模块切换)直接命中,
# 避免 CONST_INT 跨模块递归查找反复触发(NewAstTest OOM 根因——
# 14593 次调用全部重复递归 ast → ast.base → ...)。
# key = "name@源模块"(源模块维度,用去点后的 base_mod 保证一致),
# 保持模块隔离:不同源模块的同名 CDefine 值不同时不串值。
# 仅 ALL_CAPS 全局唯一宏名才进入本函数(_is_cdefine_name_pattern 已过滤)。
xkey_buf: str = None
if base_mod is not None:
xk_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
xkey_buf = pool.alloc(xk_len)
if xkey_buf is not None:
viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod)
xval: str = self.Gen.TypeGenCDef.lookup_xmod_cdefine(xkey_buf)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval)
elif self.Gen.TypeGenCDef.is_cdefine_double() != 0:
xd_v: double = string.atof(xval)
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v)
else:
xv_i: int64 = string.atoll(xval)
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i)
return string.atoll(xval)
# 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6)
# 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中,
# 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
if mod_name_raw is None:
# from_imports 完全没有匹配(精确和 star 都失败)
# 直接进入 imported_modules 遍历(步骤 9.6)
if imported_modules is None:
return -1
# 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
# 设置 result_found=0 跳过步骤 3-9,直接到 9.6
self.Gen.TypeGenCDef.set_cdefine_found(0)
# 直接遍历 imported_modules
im_len: sizet = string.strlen(imported_modules)
im_ci: sizet = 0
while im_ci < im_len:
while im_ci < im_len and imported_modules[im_ci] == ' ':
im_ci += 1
if im_ci >= im_len:
break
im_start: sizet = im_ci
while im_ci < im_len and imported_modules[im_ci] != ' ':
im_ci += 1
im_end: sizet = im_ci
im_nlen: sizet = im_end - im_start
if im_nlen > 0 and im_nlen < 256:
im_buf: str = pool.alloc(im_nlen + 1)
if im_buf is not None:
mk: sizet = 0
while mk < im_nlen:
im_buf[mk] = imported_modules[im_start + mk]
mk += 1
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] = ':'
mk2: sizet = 0
while mk2 < im_nlen:
sub_fi[2 + mk2] = im_buf[mk2]
mk2 += 1
sub_fi[2 + im_nlen] = '\0'
sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
return sub_val
return -1
# 2. 截断模块名 → 去前导点 → 精确+后缀查找 SHA1(复用 HandlesExprCall 公共逻辑)
sha1: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw)
if sha1 is None:
return -1
# 5. 获取 temp_dir
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
if temp_dir is None:
return -1
# 6. 构建 vpi 文件路径
td_len: sizet = string.strlen(temp_dir)
pyi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "vpi")
if pyi_path is None:
return -1
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .vp/.py 文件
PYI_READ_BUF_SIZE: sizet = 65536
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
if pyi_buf is None:
stdlib.free(pyi_path)
return -1
pf: rawptr[fileio.File] = fileio.File(pyi_path, fileio.MODE.R)
need_py_fallback: int = 0
bytes_read: LONG = 0
if pf.closed:
# .vpi 文件不存在:回退读取源 .vp/.py 文件
need_py_fallback = 1
else:
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
pf.close()
# vpi 内容过短(< 10 字节)视为空 stub,回退 .py
if bytes_read < 10:
need_py_fallback = 1
else:
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
# vpi(存根签名文件)中的 CDefine 声明可能只带名字不带值
# (Converter 旧版只对 CONST_INT/BOOL 等输出 "= value",CONST_FLOAT
# 等不输出 → numpy 的 pi/e 在 stub 中无值),真正的值在源
# .vp/.py 文件中。旧启发式"vpi 中任一 CDefine 带值 → 不读源"是错的:
# 模块同时含 int CDefine(带值,如 numpy.MAX_NDIM=4)与 float
# CDefine(无值,如 pi)时,int 行让 cd_sig=1 而漏回退源 → pi
# 解析失败 → 退化 @pi 全局指针 → float<->ptr 报错(NumpyTest 根因)。
# 改为按目标名判定:仅当 vpi 中 name 的 CDefine 行带值时才信任
# vpi,否则(name 行无值 / name 不在 vpi / 整个 vpi 无带值 CDefine)
# 一律回退读源文件。
cd_sig: int = 0
name_has_val: int = 0
cs_name_len: sizet = string.strlen(name)
cs_total: sizet = string.strlen(pyi_buf)
cs_i: sizet = 0
while cs_i + 9 < cs_total:
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and
pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and
pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and
pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and
pyi_buf[cs_i + 8] == 'e'):
# 检查该行剩余部分是否有 '='(带值声明才在 vpi 中匹配)
cd_eq: int = 0
cs_j: sizet = cs_i + 9
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
if pyi_buf[cs_j] == '=':
cd_eq = 1
break
cs_j += 1
if cd_eq != 0:
cd_sig = 1
# 定位该行行首,检查是否以 "name:" 开头(目标名带值)
cs_line_start: sizet = cs_i
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):
if pyi_buf[cs_line_start + ki2] != name[ki2]:
nm_ok = 0
break
if nm_ok == 1 and pyi_buf[cs_line_start + cs_name_len] == ':':
name_has_val = 1
cs_i += 1
if cd_sig == 0 or name_has_val == 0:
need_py_fallback = 1
stdlib.free(pyi_path)
if need_py_fallback != 0:
# 从 SHA1 存储器查找相对路径,构建 {base_dir}/{rel_path}
src_path: str = self._build_source_path_from_sha1(sha1)
if src_path is None:
# 存储器中找不到 SHA1 = 编译器 bug(Phase1/Phase2 未填充存储器)
# 报错终止,不静默返回 -1(避免后续生成错误的 @UNDEFINED 全局变量引用)
err_buf: str = pool.alloc(512)
if err_buf is not None:
viperlib.snprintf(err_buf, 512,
"跨模块 CDefine 查找失败:SHA1 %s 不在存储器中(base_mod=%s name=%s)。"
"Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore",
sha1, base_mod, name)
VLogger.error(err_buf, "XMOD-CD")
stdlib.free(pyi_buf)
return -1
pf = fileio.File(src_path, fileio.MODE.R)
stdlib.free(src_path)
if pf.closed:
stdlib.free(pyi_buf)
return -1
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
pf.close()
if bytes_read <= 0:
stdlib.free(pyi_buf)
return -1
if bytes_read < PYI_READ_BUF_SIZE:
pyi_buf[bytes_read] = 0
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
# 格式: NAME: t.CDefine = value
name_len: sizet = string.strlen(name)
total_len: sizet = string.strlen(pyi_buf)
pos: sizet = 0
result_val: int64 = 0
result_found: int = 0
res_is_double: int = 0
res_is_str: int = 0
while pos < total_len:
# 找到行首
line_start: sizet = pos
# 找到行尾
while pos < total_len and pyi_buf[pos] != '\n':
pos += 1
line_len: sizet = pos - line_start
if pos < total_len:
pos += 1 # 跳过 '\n'
# 检查行是否以 "name:" 开头
if line_len < name_len + 1:
continue
match_ok: int = 1
for ki in range(name_len):
if pyi_buf[line_start + ki] != name[ki]:
match_ok = 0
break
if match_ok == 0:
continue
if pyi_buf[line_start + name_len] != ':':
continue
# 检查行是否包含 "t.CDefine"
# 在行中查找 "t.CDefine"
has_cdefine: int = 0
ci: sizet = line_start + name_len + 1
while ci + 8 <= line_start + line_len:
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and
pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and
pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and
pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and
pyi_buf[ci + 8] == 'e'):
has_cdefine = 1
break
ci += 1
if has_cdefine == 0:
continue
# 找到 "=" 后面的值
eq_pos: sizet = line_start + name_len + 1
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=':
eq_pos += 1
if eq_pos >= line_start + line_len:
continue
eq_pos += 1 # 跳过 '='
# 跳过空格
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / int(...) 等类型构造函数
# 格式: t.CUnsignedLong(-11) / t.CUnsignedLong(0xFFFFFFFF)
# 如果是,跳过 "t.CXxx(" 前缀,解析括号内的值,忽略结尾 ')'
is_type_ctor: int = 0
if eq_pos + 2 < line_start + line_len:
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
# 找到 '(' 的位置
paren_pos: sizet = eq_pos + 2
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(':
paren_pos += 1
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
is_type_ctor = 1
eq_pos = paren_pos + 1 # 跳过 '('
# 跳过括号内可能的前导空格
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 括号包裹标记:(1 << 23) / (512) / (A | B)
is_paren_expr: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '(':
is_paren_expr = 1
eq_pos += 1
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# === 解析左操作数 token(数字 / hex / 负号 / 名称) ===
# 负号标记
is_neg: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
is_neg = 1
eq_pos += 1
val_str_start: sizet = eq_pos
val_str_len: sizet = 0
is_hex: int = 0
is_float_val: int = 0
is_name_val: int = 0
is_str_val: int = 0
# 字符串值(如 ZLIB_VERSION: t.CDefine = "1.3.2"):读引号内内容。
# 跨模块读回此前不支持引号 → val_str_len=0 被跳过 → "跨模块 CDefine
# 未找到"(test_pyzlib.vp pyzlib.ZLIB_VERSION 根因)
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
is_str_val = 1
eq_pos += 1 # 跳过开引号
val_str_start = eq_pos
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '"':
val_str_len += 1
eq_pos += 1
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
eq_pos += 1 # 跳过闭引号
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
is_hex = 1
val_str_len = 2
eq_pos += 2
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \
(pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \
pyi_buf[eq_pos] == '_':
is_name_val = 1
while eq_pos < line_start + line_len:
c_tk: char = pyi_buf[eq_pos]
if (c_tk >= 'A' and c_tk <= 'Z') or (c_tk >= 'a' and c_tk <= 'z') or \
(c_tk >= '0' and c_tk <= '9') or c_tk == '_':
val_str_len += 1
eq_pos += 1
else:
break
# 数字/hex 字符续读(字符串值已整体读取,跳过)
while eq_pos < line_start + line_len and is_name_val == 0 and is_str_val == 0:
ch: char = pyi_buf[eq_pos]
if is_hex == 1:
if ((ch >= '0' and ch <= '9') or
(ch >= 'a' and ch <= 'f') or
(ch >= 'A' and ch <= 'F')):
val_str_len += 1
eq_pos += 1
else:
break
else:
if ch >= '0' and ch <= '9':
val_str_len += 1
eq_pos += 1
elif ch == '.' and is_float_val == 0:
# 浮点值:'3.14159'('.' 只允许一次)
is_float_val = 1
val_str_len += 1
eq_pos += 1
else:
break
if val_str_len == 0 and is_str_val == 0:
continue
# 复制左 token 到缓冲区
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]
val_buf[val_str_len] = '\0'
# 左 token 求值
lval: int64 = 0
l_is_double: int = 0
ldbl: double = 0.0
if is_str_val != 0:
# 字符串 CDefine(如 ZLIB_VERSION = "1.3.2"):注册字符串值
# (含空串 ""),result_found 标记 + res_is_str 供尾部跳过整数
# 注册/缓存(否则 register_cdefine_constant 把字符串当 0 整数)
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf)
result_val = 0
result_found = 1
res_is_str = 1
elif is_name_val != 0:
# True/False 布尔字面量 → 1/0(对齐 TPC:bool 在 CDefine 语境按整数处理)
if string.strcmp(val_buf, "True") == 0:
lval = 1
elif string.strcmp(val_buf, "False") == 0:
lval = 0
else:
# 名称引用:查当前 CDefine 表(如 FOREGROUND_COMBO = FOREGROUND_RED)
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# 名称未注册:该行无法求值,跳过
continue
elif is_hex == 1:
# 手动解析十六进制(跳过 0x 前缀)——64 位累加,
# 0xFFFFFFFF=4294967295 保持全精度(常量生成端 i32 截断回 -1)
hex_result: int64 = 0
hex_buf: str = val_buf + 2 # 跳过 "0x"
for hi in range(val_str_len - 2):
hc: char = hex_buf[hi]
hd: int = 0
if hc >= '0' and hc <= '9':
hd = hc - '0'
elif hc >= 'a' and hc <= 'f':
hd = hc - 'a' + 10
elif hc >= 'A' and hc <= 'F':
hd = hc - 'A' + 10
else:
break
hex_result = hex_result * 16 + hd
lval = hex_result
elif is_float_val != 0:
l_is_double = 1
ldbl = string.atof(val_buf)
lval = string.atoll(val_buf)
else:
lval = string.atoll(val_buf)
if is_neg != 0:
lval = -lval
if l_is_double != 0:
ldbl = -ldbl
# 跳过空格,检查是否有二元运算符
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
# 解析运算符(<< >> | & ^ + - * /)
cd_op: int = 0
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '<' and pyi_buf[eq_pos + 1] == '<':
cd_op = 1 # LShift
eq_pos += 2
elif pyi_buf[eq_pos] == '>' and pyi_buf[eq_pos + 1] == '>':
cd_op = 2 # RShift
eq_pos += 2
if cd_op == 0 and eq_pos < line_start + line_len:
if pyi_buf[eq_pos] == '|':
cd_op = 3 # BitOr
eq_pos += 1
elif pyi_buf[eq_pos] == '&':
cd_op = 4 # BitAnd
eq_pos += 1
elif pyi_buf[eq_pos] == '^':
cd_op = 5 # BitXor
eq_pos += 1
elif pyi_buf[eq_pos] == '+':
cd_op = 6 # Add
eq_pos += 1
elif pyi_buf[eq_pos] == '-':
cd_op = 7 # Sub
eq_pos += 1
elif pyi_buf[eq_pos] == '*':
cd_op = 8 # Mult
eq_pos += 1
elif pyi_buf[eq_pos] == '/':
cd_op = 9 # Div
eq_pos += 1
if cd_op != 0:
# === 解析右操作数 token ===
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
r_neg: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
r_neg = 1
eq_pos += 1
r_start: sizet = eq_pos
r_len: sizet = 0
r_hex: int = 0
r_name: int = 0
if eq_pos + 1 < line_start + line_len:
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
r_hex = 1
r_len = 2
eq_pos += 2
elif (pyi_buf[eq_pos] >= 'A' and pyi_buf[eq_pos] <= 'Z') or \
(pyi_buf[eq_pos] >= 'a' and pyi_buf[eq_pos] <= 'z') or \
pyi_buf[eq_pos] == '_':
r_name = 1
while eq_pos < line_start + line_len:
c_r: char = pyi_buf[eq_pos]
if (c_r >= 'A' and c_r <= 'Z') or (c_r >= 'a' and c_r <= 'z') or \
(c_r >= '0' and c_r <= '9') or c_r == '_':
r_len += 1
eq_pos += 1
else:
break
while eq_pos < line_start + line_len and r_name == 0:
c_r2: char = pyi_buf[eq_pos]
if r_hex == 1:
if ((c_r2 >= '0' and c_r2 <= '9') or
(c_r2 >= 'a' and c_r2 <= 'f') or
(c_r2 >= 'A' and c_r2 <= 'F')):
r_len += 1
eq_pos += 1
else:
break
else:
if c_r2 >= '0' and c_r2 <= '9':
r_len += 1
eq_pos += 1
else:
break
if r_len == 0:
# 运算符后无右操作数:无法求值,跳过该行
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]
r_buf[r_len] = '\0'
rval: int64 = 0
if r_name != 0:
# True/False 布尔字面量 → 1/0
if string.strcmp(r_buf, "True") == 0:
rval = 1
elif string.strcmp(r_buf, "False") == 0:
rval = 0
else:
rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
# 名称未注册:无法求值,跳过该行
continue
elif r_hex != 0:
rhex_v: int64 = 0
rh_buf: str = r_buf + 2
for rh_i in range(r_len - 2):
rc: char = rh_buf[rh_i]
rd: int = 0
if rc >= '0' and rc <= '9':
rd = rc - '0'
elif rc >= 'a' and rc <= 'f':
rd = rc - 'a' + 10
elif rc >= 'A' and rc <= 'F':
rd = rc - 'A' + 10
else:
break
rhex_v = rhex_v * 16 + rd
rval = rhex_v
else:
rval = string.atoll(r_buf)
if r_neg != 0:
rval = -rval
# 计算二元表达式结果(对齐 TPC ConstEvaluator)
if cd_op == 1:
result_val = lval << rval
elif cd_op == 2:
result_val = lval >> rval
elif cd_op == 3:
result_val = lval | rval
elif cd_op == 4:
result_val = lval & rval
elif cd_op == 5:
result_val = lval ^ rval
elif cd_op == 6:
result_val = lval + rval
elif cd_op == 7:
result_val = lval - rval
elif cd_op == 8:
result_val = lval * rval
else:
if rval == 0:
continue
result_val = lval // rval
result_found = 1
break
else:
# 单值(无运算符):括号内单值如 (512) / (-11) 也走这里
if is_paren_expr != 0:
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
eq_pos += 1
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == ')':
eq_pos += 1
if is_float_val != 0:
# 浮点 CDefine(如 U_M_PI = 3.14159...):
# 值缓存为 double(register_cdefine_double),供调用处生成
# double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl)
result_val = lval
result_found = 1
res_is_double = 1
else:
result_val = lval
result_found = 1
break
# 9.5 如果当前模块文件中没找到,递归查找子模块
# 处理 from .X import NAME 和 from .X import * 的情况
# 例如 ast.__init__.vp 中 from .base import CONST_INT, ...
# 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
if result_found == 0:
# base_mod 已在函数开头计算(mod_name_raw 去前导点),
# 供子模块递归构建 "base_mod.X" 全名。
name_len_rc: sizet = string.strlen(name)
rc_pos: sizet = 0
while rc_pos < total_len and result_found == 0:
# 找到行首
rc_line_start: sizet = rc_pos
# 找到行尾
while rc_pos < total_len and pyi_buf[rc_pos] != '\n':
rc_pos += 1
rc_line_len: sizet = rc_pos - rc_line_start
if rc_pos < total_len:
rc_pos += 1
# 检查行是否以 "from " 开头(模块导入)
if rc_line_len < 12:
continue
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \
pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \
pyi_buf[rc_line_start+4] != ' ':
continue
# 提取模块路径 token(从 rc_line_start+5 到空格/tab 为止,跳过前导点)
# 两种写法都要支持:
# 相对导入 `from .base import X` → 拼 base_mod.base
# 绝对导入 `from vqt6._types import *` → 直接用 token 自身
# 此前只认相对导入(硬要求 rc_line_start+5 是 '.'),绝对写法被
# 直接跳过 → vqt6 包(__init__.vp 用 `from vqt6._types import *`
# 重导出 QT_ALIGN_* 等)里的 CDefine 全部查不到 →
# "跨模块 CDefine 未找到: 'vqt6.QT_ALIGN_CENTER'"。
rc_is_rel: int = 0
rc_sub_start: sizet = rc_line_start + 5
while rc_sub_start < rc_line_start + rc_line_len and \
pyi_buf[rc_sub_start] == '.':
rc_is_rel = 1
rc_sub_start += 1
rc_sub_end: sizet = rc_sub_start
while rc_sub_end < rc_line_start + rc_line_len and \
pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t':
rc_sub_end += 1
rc_sub_len: sizet = rc_sub_end - rc_sub_start
if rc_sub_len == 0:
continue
# 检查行是否包含 "import"
rc_has_import: int = 0
rc_ipos: sizet = rc_sub_end
while rc_ipos + 6 <= rc_line_start + rc_line_len:
if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \
pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \
pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't':
rc_has_import = 1
break
rc_ipos += 1
if rc_has_import == 0:
continue
# 检查 NAME 是否在导入列表中,或导入是 *
# 处理多行导入:from .X import (... NAME, ...)
# 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止
rc_imp_start: sizet = rc_ipos + 6
rc_star: int = 0
rc_name_match: int = 0
# 确定扫描范围:单行或跨行(括号内)
rc_scan_end: sizet = rc_line_start + rc_line_len
rc_has_paren: int = 0
rc_pp: sizet = rc_imp_start
while rc_pp < rc_line_start + rc_line_len:
if pyi_buf[rc_pp] == '(':
rc_has_paren = 1
break
if pyi_buf[rc_pp] == '*':
rc_star = 1
rc_pp += 1
if rc_has_paren != 0:
# 多行导入:从 "(" 之后扫描到 ")" 为止
rc_scan_end = rc_pp + 1
rc_paren_closed: int = 0
rc_sp: sizet = rc_pp + 1
while rc_sp < total_len and rc_paren_closed == 0:
if pyi_buf[rc_sp] == ')':
rc_paren_closed = 1
rc_scan_end = rc_sp
break
rc_sp += 1
if rc_paren_closed == 0:
rc_scan_end = total_len
# 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
rc_cpos: sizet = rc_imp_start
while rc_cpos < rc_scan_end:
rc_ch: char = pyi_buf[rc_cpos]
if rc_ch == '*':
rc_star = 1
# 检查 NAME 是否匹配(作为完整单词)
if rc_cpos + name_len_rc <= rc_scan_end:
rc_match: int = 1
rc_ki: sizet
for rc_ki in range(name_len_rc):
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
rc_match = 0
break
if rc_match == 1:
# 检查前字符是否是非标识符字符
rc_before_ok: int = 0
if rc_cpos == rc_imp_start:
rc_before_ok = 1
else:
rc_before_ch: char = pyi_buf[rc_cpos - 1]
if rc_before_ch == ' ' or rc_before_ch == '\t' or \
rc_before_ch == '(' or rc_before_ch == ',' or \
rc_before_ch == '\n' or rc_before_ch == '\r':
rc_before_ok = 1
# 检查后字符是否是非标识符字符
rc_after_ok: int = 0
rc_after_pos: sizet = rc_cpos + name_len_rc
if rc_after_pos >= rc_scan_end:
rc_after_ok = 1
else:
rc_after_ch: char = pyi_buf[rc_after_pos]
if rc_after_ch == ' ' or rc_after_ch == '\t' or \
rc_after_ch == ')' or rc_after_ch == ',' or \
rc_after_ch == '\r' or rc_after_ch == '\n':
rc_after_ok = 1
if rc_before_ok == 1 and rc_after_ok == 1:
rc_name_match = 1
rc_cpos += 1
if rc_star == 0 and rc_name_match == 0:
continue
# 复制子模块名到新缓冲区(pyi_buf + rc_sub_start 不是 NUL 结尾)
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]
rc_sub_buf[rc_sub_len] = '\0'
# 构建完整模块名:
# 相对导入(rc_is_rel=1)→ <当前模块所属包> + "." + token
# 基准必须是"包"(rel_path 的目录部分)而不是模块名本身:
# pkg/sub/mod.vp 里的 `from .X import Y` 指向 pkg.sub.X,
# 用 base_mod("pkg.sub.mod")会拼成 pkg.sub.mod.X(错)。
# __init__.vp 时包名 == 模块名,此前只在这种情况碰巧正确
# (CallGenBase._lookup_reexport_from_vpi 用的是包名,此处对齐)。
# 绝对导入(rc_is_rel=0)→ 直接用 token(vqt6._types)
# 自导入(目标模块 == base_mod)跳过,避免无意义递归
rc_full_mod: str = None
if rc_is_rel != 0:
rc_base_mod: str = base_mod
rc_pkg: str = self.Gen.CallGenBase._module_package_from_sha1(pool, sha1)
if rc_pkg is not None:
rc_base_mod = rc_pkg
rc_full_len: sizet = string.strlen(rc_base_mod) + 1 + rc_sub_len + 1
rc_full_buf: str = pool.alloc(rc_full_len)
if rc_full_buf is None:
continue
viperlib.snprintf(rc_full_buf, rc_full_len, "%s.%s", rc_base_mod, rc_sub_buf)
rc_full_mod = rc_full_buf
else:
if string.strcmp(rc_sub_buf, base_mod) == 0:
continue
rc_full_mod = rc_sub_buf
# 防循环:若 base_mod 已以 "."+rc_sub_buf 结尾(如 ast.base 再拼
# .base → ast.base.base),说明子模块递归在重复追加同一模块段
# (模块间循环 re-export / 后缀匹配容错命中自身),跳过该次递归
# (NewAstTest CONST_INT 查找 dump:ast.base.base.base 无限增长根因)。
rc_blen: sizet = string.strlen(base_mod)
if rc_blen > rc_sub_len + 1:
rc_tail: str = base_mod + (rc_blen - rc_sub_len)
if rc_tail[0] == '.' and \
string.strncmp(rc_tail + 1, rc_sub_buf, rc_sub_len) == 0:
continue
# 构建临时 from_imports "name:full_mod"
rc_fi_len: sizet = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
rc_fi: str = pool.alloc(rc_fi_len)
if rc_fi is None:
continue
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
# 递归调用查找子模块
rc_sub_val: int = 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
stdlib.free(pyi_buf)
# 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历
# 当 `from stdint import *` 导致 star fallback 返回 stdint,但目标常量
# (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时,
# 需要遍历 imported_modules 逐个查找
if result_found == 0 and imported_modules is not None:
im2_len: sizet = string.strlen(imported_modules)
im2_ci: sizet = 0
while im2_ci < im2_len and result_found == 0:
while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
im2_ci += 1
if im2_ci >= im2_len:
break
im2_start: sizet = im2_ci
while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
im2_ci += 1
im2_end: sizet = im2_ci
im2_nlen: sizet = im2_end - im2_start
if im2_nlen > 0 and im2_nlen < 256:
im2_buf: str = pool.alloc(im2_nlen + 1)
if im2_buf is not None:
mk3: sizet = 0
while mk3 < im2_nlen:
im2_buf[mk3] = imported_modules[im2_start + mk3]
mk3 += 1
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] = ':'
mk4: sizet = 0
while mk4 < im2_nlen:
sub_fi2[2 + mk4] = im2_buf[mk4]
mk4 += 1
sub_fi2[2 + im2_nlen] = '\0'
sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
result_val = sub_val2
result_found = 1
# 10. 缓存到当前模块的 CDefine 表中,并写入跨模块 perm_pool 缓存
# 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存
# (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。
if result_found != 0:
if res_is_str != 0:
# 字符串 CDefine:值已在匹配循环 register_cdefine_string(val_buf
# 是命中行的字符串内容,pool 分配跨函数有效),此处只写跨模块缓存
if base_mod is not None:
ck_len_s: sizet = string.strlen(name) + string.strlen(base_mod) + 2
ckey_s: str = pool.alloc(ck_len_s)
if ckey_s is not None:
viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod)
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_s, val_buf, 0, 1)
elif res_is_double == 0:
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val)
# 跨模块查找缓存(perm_pool 永久,跨文件复用):整数
# key 与入口查询一致:"name@源模块"(用截断后的 base_mod,
# 与 mod_name_raw 一致——两者都是去点后的模块名,保证 key 匹配)
if base_mod is not None:
ck_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2
ckey_buf: str = pool.alloc(ck_len)
if ckey_buf is not None:
viperlib.snprintf(ckey_buf, ck_len, "%s@%s", name, base_mod)
cbuf_i: str = pool.alloc(32)
if cbuf_i is not None:
viperlib.snprintf(cbuf_i, 32, "%lld", result_val)
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_buf, cbuf_i, 0, 0)
else:
# 浮点:从本地表取回 %.17g 文本,写入跨模块缓存(保精度)
if base_mod is not None:
ck_len2: sizet = string.strlen(name) + string.strlen(base_mod) + 2
ckey2: str = pool.alloc(ck_len2)
if ckey2 is not None:
viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod)
dval_cc: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name)
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
cbuf_d: str = pool.alloc(64)
if cbuf_d is not None:
stdio.snprintf(cbuf_d, 64, "%.17g", dval_cc)
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey2, cbuf_d, 1, 0)
self.Gen.TypeGenCDef.set_cdefine_found(1)
else:
self.Gen.TypeGenCDef.set_cdefine_found(0)
return result_val
# ============================================================
# _is_cdefine_name_pattern - 检查名字是否符合 CDefine 命名约定
#
# CDefine 常量(如 FOREGROUND_GREEN, STD_OUTPUT_HANDLE, SCOPE_MODULE)
# 都遵循 ALL_CAPS 约定:仅含大写字母 A-Z、数字 0-9、下划线 _,
# 且至少含一个大写字母。
#
# Python 关键字(match, if, for)和变量名(self, pool, builder)
# 都含小写字母,不会匹配,从而避免 fail-fast 误报。
#
# 返回: 1=符合 CDefine 命名约定, 0=不符合
# ============================================================
def _is_cdefine_name_pattern(self, name: str) -> int:
"""检查名字是否符合 CDefine 命名约定(全大写+下划线+数字)"""
if name is None:
return 0
has_upper: int = 0
i: sizet = 0
while name[i] != '\0':
c: char = name[i]
if c >= 'a' and c <= 'z':
return 0
if c >= 'A' and c <= 'Z':
has_upper = 1
i += 1
return has_upper
# ============================================================
# 翻译变量引用(Name 节点)→ load
# ============================================================
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*,
value: int64) -> llvmlite.Value*:
"""CDefine 整数常量 → LLVM 常量(对齐 TPC TypeConvert 的 64 位升级)
**使用处宽度("W_min + 使用处宽度"规则)与物化宽度的关系**(与 TPC 同口径,
对照表见 Debugger/define-constants.md):
· 本函数是"取不到使用处宽度"的物化点(调用方 ExprGenName/ExprGenAttr 只有
常量名与值,没有目标类型)⇒ 按语言默认整型宽度物化:能落进 32 位 ⇒ i32,
否则 i64。**不**降到 W_min——降到 i8/i16 会把窄立即数喂进 i32 上下文与
C 默认实参提升(printf 变参),属静默降级/ABI 破坏。
· "使用处更宽 ⇒ 以该宽度物化"由**消费端**实现:translate_binop 按较宽侧
zext/sext/coerce_to_type 对齐,赋值/形参按目标存储类型 coerce。
· "使用处更窄 ⇒ fail-fast"在**注册处与声明处**实现:
AssignGenMixin.require_cdefine_storable(显式类型 T 的位宽)与
require_cdefine_use_width(`a: T = C`);超 64 位字面量由
_int_text_exceeds_64 以**源码文本**判定(lexer 的 atoll 会在字面量层
回绕,回绕后的 int64 不可信,故本函数不承担溢出判定)。
规则(与 translate_constant 的 i32 溢出升级一致,但保留 0xFFFFFFFF 语义):
值超 32 位无符号范围(> 0xFFFFFFFF)→ i64 全精度常量
(如 sys.maxsize = 9223372036854775807);
值低于 i32 下限(< -2147483648)→ i64 全精度常量。符号位为 1 的 64 位
模式(如 MEMBUDDY_CANARY_PATTERN = 0xA5A5A5A5A5A5A5A5)以 %lld 文本
存回即负数 -6510615555426900571,仅按 u32 截断回比较会漏判;
否则 → i32 常量,内部 int 截断(0xFFFFFFFF → -1,保持既有
INVALID_HANDLE_VALUE / WAIT_FAILED = -1 语义不变,避免 i32 4294967295
生成非法 LLVM IR 文本 "i32 4294967295")。
注意:不能用 `value > 4294967295` 直接比较——TPV 编译自身源码时大数字面量
(> 0x7FFFFFFF)在字面量层回绕成 i32(sext 后等于 -1),阈值恒成立导致所有
正数 CDefine 都被升 i64(INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 从 i32 -1 变
i64 4294967295 → 与 _get_attrs 的 sext(-1) 比较失败,SysLibTest exists FAIL 根因)。
改用截断回比较:uint32(value) 回绕后还原,与 value 不等即超 u32 范围。
负数侧漏判的现场:`(0xA5A5A5A5 << 32) | 0xA5A5A5A5`(includes/memhub.vp:123)
经 CDefine 求值(ExprGenCdef 内 lval << rval 的 int64 回绕)得
-6510615555426900571,再经 TypeGenCDef.register_cdefine_constant 的
"%lld" 文本存回(TypeGenCDef.vp:156)仍为负数 → 原判据物化成 i32
-1515870811:写入路径 inttoptr i32(x86 movl 零扩展)实存
0x00000000A5A5A5A5,比较/打印路径 sext i32→i64 期望 0xFFFFFFFFA5A5A5A5
⇒ MemBuddy canary 校验 rc=6 假阳性。下限判据与既有
ExprGenConst.vp:108 / CallGenLeafKit.vp:1477 的写法一致。
"""
if (value >= 0 and uint64(uint32(value)) != value) or value < -2147483648:
return llvmlite.const_int64(pool, value)
return llvmlite.const_int32(pool, int(value))
# ============================================================
# cdefine_use_width_value - "使用处宽度"下的 CDefine 常量物化
# (与 TPC HandlesExpr._HandleNameLlvm + ConstWidthHint 同口径)
# ============================================================
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
"""把 CDefine 值归一到 bits 位的**有符号表示**(LLVM 整数字面量口径)。
前提:调用方已用 W_min 判据保证 value 落在
[-(2^(bits-1)), 2^bits-1](两解释口径)。非负但最高位为 1 的值
(如 0xFFFFFFFF)转成同一位模式的负数(i32 4294967295 → i32 -1),
与 _const_int_for_cdefine 既有的 i32 截断口径一致,避免生成非法 IR 文本。
"""
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
return value
def cdefine_use_width_value(self, pool: memhub.MemBuddy*,
node: ast.AST*, name: str,
value: int64,
target_ty: llvmlite.LLVMType*,
use_ctx: str = None) -> llvmlite.Value*:
"""CDefine 整数常量在**已知使用处类型**时的物化(与 TPC 同口径)。
返回 None ⇒ "取不到使用处宽度"(调用方沿用 _const_int_for_cdefine 的
语言默认整型宽度 32/64)。只当 target_ty 是 i8/i16/i32/i64 时启用
(i1 是比较/逻辑结果、指针不是整数使用处 ⇒ 落回默认,否则 `C == 0`
之类会拿到位宽 1 而误报"使用处宽度不足"):
使用处更宽(bits >= W_min)⇒ 按 bits 就地物化(立即数升宽无损);
使用处更窄(bits < W_min)⇒ fail-fast(替换静默截断)。
只被 CDefine 整数物化点调用 ⇒ 不改变非常量实参/返回值的既有 IR。
use_ctx:**真实站点种类**标签(如 "局部变量注解" / "模块级全局" / "形参" /
"返回注解"),由 `translate_value` 的调用方按站点下传;None ⇒ 报文不写
括注(**不得**再写死"形参 / 返回类型"——AnnAssign 的 `a: T = C` 现场也被
本函数拦到时,硬编码标签会给出错误站点)。
"""
bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(target_ty)
if bits != 8 and bits != 16 and bits != 32 and bits != 64:
return None
wmin: int = self.Gen.AssignGen.define_min_width(value)
if bits < wmin:
# "该使用处可表示的范围"与判据同一口径 = TPC `DefineWidthLoHi`
# = [-(2^(bits-1)), 2^bits - 1](有符号/无符号两种解释的并集;
# 判据本身只按**位宽**比较,故报文必须报"位宽 w 的可接受值域")。
# 字面量纪律见 AssignGenMixin.define_min_width:2147483648 /
# 4294967295 会被 TPC 压成 i32 并在提升时 sext,故用安全字面量
# 2147483647 的算术等价式构造。
m31: int64 = 2147483647
lo: int64 = -m31 - 1
hi: int64 = m31 * 2 + 1
if bits == 8:
lo = -128
hi = 255
elif bits == 16:
lo = -32768
hi = 65535
ctx: str = ""
if use_ctx is not None:
# 本语言的 str 是 i8*,**没有** `str + str` 的拼接语义(`+` 会退化
# 成裸指针 add → "invalid operand type for instruction")。
# 构建字符串一律用本仓库惯用法:f-string 插值 / string.strcat /
# viperlib.snprintf。
ctx = f"({use_ctx})"
self.Gen.TypeGenErr.fatal_error(
node,
f"[TPV] define 常量使用处宽度不足: 常量 '{name}' 的实际值 {value} "
f"需要至少 {wmin} 位(最小可容纳宽度 W_min),"
f"但使用处{ctx}只提供 {bits} 位"
f"(该位宽按有符号/无符号两种解释可接受的范围: {lo}..{hi})。"
f"禁止静默截断/降级,已终止编译。"
f"修法: 把使用处{ctx}的类型加宽到至少 {wmin} 位"
f"(如 t.CInt{wmin}T / CUInt{wmin}T 及更宽类型),或改用更小的常量值。",
"TypeError")
return None
return llvmlite.const_int(pool, bits, self._cdefine_bits_value(value, bits))