增加 JIT REPL

This commit is contained in:
TermiNexus committed 2026-09-21 16:17:19 +08:00
1 parent 9910e05760
commit a5ceb3d4eb
56 files changed
+7621 -584

No files matched your search

+52 -1
View File
@@ -115,7 +115,10 @@ def TranslateFileGetTrans(mb: rawptr[memhub.MemBuddy], file_path: str,
pyi_buf: bytes = stdlib.malloc(PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_buf[0] = 0
pyi_pos: sizet = StubConverter._GeneratePyiFromAst(mb, tree, None, pyi_buf, PYI_BUF_SIZE)
# rel_path 传 file_path(本文件绝对路径):vpi 生成期间 fatal_error
# 的报错归因需指向本源文件(Phase A 预注册/翻译时 file_path 已可用),
# 且绝对路径可被 fatal_error 的 _read_source_line 直接打开打印源码行。
pyi_pos: sizet = StubConverter._GeneratePyiFromAst(mb, tree, file_path, pyi_buf, PYI_BUF_SIZE)
if pyi_pos > 0:
# 写入 {sha1}.vpi 到 temp_dir(切片路径)
td_len_vpi: sizet = len(temp_dir)
@@ -709,6 +712,37 @@ def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: sizet)
return out_pos
def _append_host_export_implib(linker_flags: str, implib_path: str) -> str:
"""宿主导出模式:为链接参数追加 `--out-implib=<绝对路径>`(REPL 片段导入宿主用)
触发条件 = linker_flags 显式含 "--export-all-symbols"(工程级开关,宿主的
project.vpj 里声明)。此时 exe 会带导出表,REPL 片段 DLL 需要一份 import lib
才能把 IR 里的跨模块引用(形如 `<16位hex>.<函数名>`,见 .text.ll 的
`define i32 @"<sha1>.utf8_is_valid"`)解析到宿主的导出符号。
为什么路径由本层计算而不写进 project.vpj:Config 对 linker.flags 既不做
%VAR% 展开、也不按 project_dir 解析(Config.vpj:180-230 的 _join_flags_array
只做拼接;:570-632 只重写 -T 后的脚本路径;env 展开只发生在 static_libs/
dynamic_libs/includes/build_dir 等字段),而 out-implib 的值是相对
clang 进程 cwd 解析的 ⇒ 只有本层能从 output_dir + linker_output 算出绝对路径。
未开启工程的 linker_flags 不含该标志 ⇒ 原样返回,链接命令逐字节不变。
"""
if linker_flags is None or implib_path is None:
return linker_flags
if string.strstr(linker_flags, "--export-all-symbols") is None:
return linker_flags
extra: sizet = string.strlen(" -Wl,--out-implib=") + string.strlen(implib_path) + 1
buf: str = _mbuddy.alloc(string.strlen(linker_flags) + extra)
if buf is None:
VLogger.warning("宿主导出:import lib 参数缓冲分配失败(片段将无法导入宿主符号)", "link")
return linker_flags
viperlib.snprintf(buf, string.strlen(linker_flags) + extra,
"%s -Wl,--out-implib=%s", linker_flags, implib_path)
VLogger.info(f"宿主导出:产出 import lib {implib_path}", "link")
return buf
def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_flags: str, linker_output: str,
includes_binary_dir: str, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> int:
"""调用 clang++ 链接 .obj 为 .exe
@@ -734,6 +768,16 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
if obj_path is None:
return 1
# 宿主 exe 导出(工程 linker.flags 含 --export-all-symbols 时)同步产出 import lib:
# {output_dir}/{linker_output}.dll.a —— REPL 片段链接时把它加进命令行即可导入
# 宿主(同一进程/同一份 includes 代码与数据)。未开启的工程不受影响。
if linker_output is not None:
implib_len: sizet = string.strlen(output_dir) + string.strlen(linker_output) + 8
implib_path: str = _mbuddy.alloc(implib_len)
if implib_path is not None:
viperlib.snprintf(implib_path, implib_len, "%s/%s.dll.a", output_dir, linker_output)
linker_flags = _append_host_export_implib(linker_flags, implib_path)
# 收集 includes.binary 的 .obj 文件路径
EXTRA_BUF_SIZE: sizet = 32768
extra_objs: bytes = _mbuddy.alloc(EXTRA_BUF_SIZE)
@@ -988,6 +1032,13 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: sizet,
# 构造命令: clang++ obj_paths extra_objs -o linker_output linker_flags
# ASan 运行库同样不再由 TPV 注入(在 link_obj_to_exe 里已有说明):
# 由 project.vpj 声明,--asan 下由 main.vp 校验。
# 宿主导出:linker_output 是完整输出路径 ⇒ import lib 取名 <linker_output>.dll.a
if linker_output is not None:
implib_len2: sizet = string.strlen(linker_output) + 8
implib_path2: str = _mbuddy.alloc(implib_len2)
if implib_path2 is not None:
viperlib.snprintf(implib_path2, implib_len2, "%s.dll.a", linker_output)
linker_flags = _append_host_export_implib(linker_flags, implib_path2)
use_flags: str = linker_flags
static_libs2: str = linker_static_libs
if static_libs2 is None:
+1 -1
View File
@@ -1007,7 +1007,7 @@ def RunPhase1(mb: rawptr[memhub.MemBuddy], includes_dir: str, temp_dir: str,
pyi_buf: bytes = stdlib.malloc(Phase1.PYI_BUF_SIZE)
if pyi_buf is not None:
pyi_pos: sizet = StubConverter._GeneratePyiFromAst(
mb, tree, entry.RelPath, pyi_buf, Phase1.PYI_BUF_SIZE)
mb, tree, entry.Path, pyi_buf, Phase1.PYI_BUF_SIZE)
if pyi_pos > 0:
pf_1b: rawptr[fileio.File] = fileio.File(vpi_path_1b, fileio.MODE.W)
if not pf_1b.closed:
+11 -11
View File
@@ -133,7 +133,7 @@ def _ScanClassInheritance(mb: rawptr[memhub.MemBuddy],
continue
# 遍历 AST 顶层节点,收集 ClassDef
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn: sizet = len(ch)
for ci in range(cn):
@@ -305,7 +305,7 @@ def _PreRegisterAllGenericTemplates(mb: rawptr[memhub.MemBuddy],
stdlib.free(src_buf_pg)
continue
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
ch_pg: rawptr[list[rawptr[ast.AST]]] = tree_pg.children
ch_pg: list[ast.AST*]* = tree_pg.children
if ch_pg is not None:
cn_pg: sizet = len(ch_pg)
for ci_pg in range(cn_pg):
@@ -405,13 +405,13 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
elif k_s == ast.ASTKind.Call:
cl_s2: rawptr[ast.Call] = rawptr[ast.Call](node)
_ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box, depth + 1)
cl_args_s: rawptr[list[rawptr[ast.AST]]] = cl_s2.args
cl_args_s: list[ast.AST*]* = cl_s2.args
if cl_args_s is not None:
na_s: sizet = len(cl_args_s)
i_a2: sizet
for i_a2 in range(na_s):
_ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
cl_kws_s: rawptr[list[rawptr[ast.AST]]] = cl_s2.keywords
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
if cl_kws_s is not None:
nk_s: sizet = len(cl_kws_s)
i_k2: sizet
@@ -422,7 +422,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
elif k_s == ast.ASTKind.Assign:
asn_s: rawptr[ast.Assign] = rawptr[ast.Assign](node)
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
tgts_s: rawptr[list[rawptr[ast.AST]]] = asn_s.targets
tgts_s: list[ast.AST*]* = asn_s.targets
if tgts_s is not None:
nt_s: sizet = len(tgts_s)
i_t2: sizet
@@ -437,7 +437,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box, depth + 1)
fn_args_s: rawptr[ast.Arguments] = rawptr[ast.Arguments](fn_s.args)
if fn_args_s is not None:
fa_s: rawptr[list[rawptr[ast.AST]]] = fn_args_s.args
fa_s: list[ast.AST*]* = fn_args_s.args
if fa_s is not None:
nf_s: sizet = len(fa_s)
i_f2: sizet
@@ -447,7 +447,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.ClassDef:
cd_s: rawptr[ast.ClassDef] = rawptr[ast.ClassDef](node)
bases_s: rawptr[list[rawptr[ast.AST]]] = cd_s.bases
bases_s: list[ast.AST*]* = cd_s.bases
if bases_s is not None:
nb_s: sizet = len(bases_s)
i_b2: sizet
@@ -458,7 +458,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.Tuple:
tu_s: rawptr[ast.Tuple] = rawptr[ast.Tuple](node)
elts_s: rawptr[list[rawptr[ast.AST]]] = tu_s.elts
elts_s: list[ast.AST*]* = tu_s.elts
if elts_s is not None:
ne_s: sizet = len(elts_s)
i_e2: sizet
@@ -476,7 +476,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
elif k_s == ast.ASTKind.Compare:
cp_s: rawptr[ast.Compare] = rawptr[ast.Compare](node)
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
cps_s: rawptr[list[rawptr[ast.AST]]] = cp_s.comparators
cps_s: list[ast.AST*]* = cp_s.comparators
if cps_s is not None:
nc_s: sizet = len(cps_s)
i_c2: sizet
@@ -484,7 +484,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
_ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
elif k_s == ast.ASTKind.BoolOp:
bo_s: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
bvs_s: rawptr[list[rawptr[ast.AST]]] = bo_s.values
bvs_s: list[ast.AST*]* = bo_s.values
if bvs_s is not None:
nv_s: sizet = len(bvs_s)
i_v2: sizet
@@ -498,7 +498,7 @@ def _ScanTreeForContainers(node: rawptr[ast.AST],
# need_dict_box[0] = 1
# 递归语句块子节点
if node.children is not None:
ch_s: rawptr[list[rawptr[ast.AST]]] = node.children
ch_s: list[ast.AST*]* = node.children
n_s: sizet = len(ch_s)
ci_s: sizet
for ci_s in range(n_s):
+4 -3
View File
@@ -304,7 +304,7 @@ def _PreRegisterAppGenericTemplates(mb: rawptr[memhub.MemBuddy],
stdlib.free(src_buf_pa)
continue
# 遍历顶层 ClassDef,注册泛型模板(type_params 非 None)
ch_pa: rawptr[list[rawptr[ast.AST]]] = tree_pa.children
ch_pa: list[ast.AST*]* = tree_pa.children
if ch_pa is not None:
cn_pa: sizet = len(ch_pa)
for ci_pa in range(cn_pa):
@@ -765,7 +765,8 @@ def RunMultiFileProject(mb: rawptr[memhub.MemBuddy],
continue
stdlib.free(vpi_path_vp2)
# 读取源文件 → 解析 AST → 生成 vpi
# (rel_path 传 None:与 TranslateFileGetTrans 后续覆盖写的内容一致)
# (rel_path 传 ent_vp2.Path:vpi 内容不依赖它——与 TranslateFileGetTrans
# 后续覆盖写的内容一致;此处仅用于 fatal_error 的报错归因,指向本源文件)
f_vp2: rawptr[fileio.File] = fileio.File(ent_vp2.Path, fileio.MODE.R)
if f_vp2.closed:
vpi_failed_p2 += 1
@@ -812,7 +813,7 @@ def RunMultiFileProject(mb: rawptr[memhub.MemBuddy],
pyi_buf_vp2: bytes = stdlib.malloc(PYI_BUF_SIZE_P2)
if pyi_buf_vp2 is not None:
pyi_pos_vp2: sizet = StubConverter._GeneratePyiFromAst(
mb, tree_vp2, None, pyi_buf_vp2, PYI_BUF_SIZE_P2)
mb, tree_vp2, ent_vp2.Path, pyi_buf_vp2, PYI_BUF_SIZE_P2)
if pyi_pos_vp2 > 0:
pyi_path_vp2: str = StubMergerCollect._sliced_path(temp_dir, td_len_vpa, ent_vp2.Sha1, "vpi")
if pyi_path_vp2 is not None:
+11
View File
@@ -241,9 +241,20 @@ def _join_path(rel: str, project_dir: str) -> str:
拼接结果经 _normalize_path 规范化:./Test/../includes → ./includes,
消除 .. 段,保证路径显示干净且相对寻址正确。
含 %VAR% 的路径(如 repl.vpj 的 build_dir = %temp%/viperepl/.tpv_build)
先经 fileio 路径层展开为实路径:TempDir/OutputDir 等由 BuildDir 派生,
且后续消费者不止文件打开(ensure_dir 直接调 CreateDirectoryA、
collect_obj_files 调 FindFirstFileA、命令串走 CreateProcessA),
故展开必须发生在**配置路径设置层**而不是逐个调用点。
"""
if rel is None:
return None
# 环境变量展开:仅在含 '%' 时生效(fileio.ExpandEnvironmentPath 自带
# "失败/无 '%' 返回 None" 契约),展开失败时保持原串不变
ep: str = fileio.ExpandEnvironmentPath(rel)
if ep is not None:
rel = ep
# 绝对路径(以 / 或 \ 开头)保持不变
if rel[0] == '/' or rel[0] == '\\':
return rel
+148 -23
View File
@@ -202,11 +202,41 @@ def _PyiIndent(buf: bytes, buf_size: sizet, pos: sizet,
return pos
# ============================================================
# _PyiBinOpOpText - 输出二元运算符两侧的分隔文本
#
# 与 TPC StubGen/_GetTypeString 的 BinOp 运算符表逐字节一致(" | " / " + " /
# " - " / " * " / " / " / " % " / " & " / " >> ")。支持集必须两侧相同,否则
# 同一份源码写出的 .vpi 注解文本不同——旧实现恒写 " | ",使
# zhuff.vp `t.CArray[zhuff_decode_node, 2 * ZHUFF_MAX_CODES]` 写成
# `2 | ZHUFF_MAX_CODES`(读回端按位或算成 15,正确值 30 → 数组长度错)。
# 表外运算符 fail-fast:静默写错运算符会生成错误的类型/长度文本,
# 让下游按错误结果解析(与 TPC 落到未知节点 fail-fast 同语义)。
# ============================================================
def _PyiBinOpOpText(buf: bytes, buf_size: sizet, pos: sizet,
op: int, node: rawptr[ast.AST]) -> sizet:
"""按运算符种类输出 " OP " 文本,表外运算符 fail-fast"""
if op == ast.OpKind.BitOr: return _PyiEmit(buf, buf_size, pos, " | ")
if op == ast.OpKind.Add: return _PyiEmit(buf, buf_size, pos, " + ")
if op == ast.OpKind.Sub: return _PyiEmit(buf, buf_size, pos, " - ")
if op == ast.OpKind.Mult: return _PyiEmit(buf, buf_size, pos, " * ")
if op == ast.OpKind.Div: return _PyiEmit(buf, buf_size, pos, " / ")
if op == ast.OpKind.Mod: return _PyiEmit(buf, buf_size, pos, " % ")
if op == ast.OpKind.BitAnd: return _PyiEmit(buf, buf_size, pos, " & ")
if op == ast.OpKind.RShift: return _PyiEmit(buf, buf_size, pos, " >> ")
GenApi.fatal_error(node, "vpi 类型注解节点类型未知", "TypeError")
return pos # 不可达(fatal_error 已退出)
# ============================================================
# _PyiTypeStr - 递归生成类型注解字符串
#
# 将 AST 类型注解节点转换为字符串表示,写入 buf 的 pos 位置。
# 支持: Name, Attribute, Subscript, BinOp(|), Constant(前向引用), Tuple
# 支持集与 TPC StubGen/_GetTypeString 等价:
# Name, Attribute, Subscript, Tuple, BinOp, UnaryOp, Compare, BoolOp,
# Constant, List, Call
# 差异项:TPV 额外支持裸 Tuple(TPC 仅在 Subscript.slice 位置接受 Tuple);
# TPC 额外有 Index 分支(vast 遗留类,解析器不产出该节点)。
# ============================================================
def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet,
annot: rawptr[ast.AST],
@@ -248,14 +278,77 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet,
pos = _PyiEmit(buf, buf_size, pos, "]")
return pos
# BinOp: 联合类型 (如 int | str, File | t.CPtr)
# BinOp: 二元运算 (联合类型 int | str;CArray 计数表达式 MAX_ORDER + 1、
# 2 * ZHUFF_MAX_CODES;条件宏实参 MASK_BYTE & 0x0F)。
# 运算符文本按 _PyiBinOpOpText 输出,与 TPC 的运算符表逐字节一致。
if k == ast.ASTKind.BinOp:
bop: rawptr[ast.BinOp] = rawptr[ast.BinOp](annot)
pos = _PyiTypeStr(buf, buf_size, pos, bop.left, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, " | ")
pos = _PyiBinOpOpText(buf, buf_size, pos, bop.op, annot)
pos = _PyiTypeStr(buf, buf_size, pos, bop.right, depth + 1)
return pos
# UnaryOp: 一元运算 (计数表达式 -1 / ~0;条件宏实参 not ct.isptr(x))。
# 输出与 TPC 的 UnaryOp 分支逐字节一致:Not → "not "、Invert → "~"、
# USub → "-";其余一元运算符两侧都不支持 → fail-fast(不静默写错符号)。
if k == ast.ASTKind.UnaryOp:
uop_t: rawptr[ast.UnaryOp] = rawptr[ast.UnaryOp](annot)
if uop_t.op == ast.OpKind.Not:
pos = _PyiEmit(buf, buf_size, pos, "not ")
elif uop_t.op == ast.OpKind.Invert:
pos = _PyiEmit(buf, buf_size, pos, "~")
elif uop_t.op == ast.OpKind.USub:
pos = _PyiEmit(buf, buf_size, pos, "-")
else:
GenApi.fatal_error(annot, "vpi 类型注解节点类型未知", "TypeError")
return pos # 不可达(fatal_error 已退出)
return _PyiTypeStr(buf, buf_size, pos, uop_t.operand, depth + 1)
# Compare: 比较表达式 (条件宏实参 MAX_SIZE > 512 / val == 1)。
# 运算符文本与 TPC 的 Compare 分支逐字节一致:== != < <= > >=;
# 表外运算符(is/in 等)两侧一致输出 " ? "。
if k == ast.ASTKind.Compare:
cmp_t: rawptr[ast.Compare] = rawptr[ast.Compare](annot)
pos = _PyiTypeStr(buf, buf_size, pos, cmp_t.left, depth + 1)
ops_ct: list[int] | t.CPtr = (list[int] | t.CPtr)(cmp_t.ops)
cmps_ct: list[ast.AST*]* = cmp_t.comparators
if ops_ct is not None:
on_ct: sizet = len(ops_ct)
oi_ct: sizet = 0
while oi_ct < on_ct:
oid_ct: int = ops_ct.get(oi_ct)
if oid_ct == ast.OpKind.Eq: pos = _PyiEmit(buf, buf_size, pos, " == ")
elif oid_ct == ast.OpKind.Ne: pos = _PyiEmit(buf, buf_size, pos, " != ")
elif oid_ct == ast.OpKind.Lt: pos = _PyiEmit(buf, buf_size, pos, " < ")
elif oid_ct == ast.OpKind.Le: pos = _PyiEmit(buf, buf_size, pos, " <= ")
elif oid_ct == ast.OpKind.Gt: pos = _PyiEmit(buf, buf_size, pos, " > ")
elif oid_ct == ast.OpKind.Ge: pos = _PyiEmit(buf, buf_size, pos, " >= ")
else: pos = _PyiEmit(buf, buf_size, pos, " ? ")
# TPC 按 ops 的下标取 comparators[i];此处加长度守卫,
# 畸形 AST(ops/comparators 不等长)不越界。
if cmps_ct is not None and oi_ct < len(cmps_ct):
pos = _PyiTypeStr(buf, buf_size, pos, cmps_ct.get(oi_ct), depth + 1)
oi_ct += 1
return pos
# BoolOp: 布尔运算 (条件宏实参 ENABLED and MAX_SIZE > 0 / A or B)。
# 与 TPC 的 BoolOp 分支一致:and → " and "、or → " or "。
if k == ast.ASTKind.BoolOp:
bool_t: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](annot)
vals_bt: list[ast.AST*]* = bool_t.values
if vals_bt is not None:
vn_bt: sizet = len(vals_bt)
vi_bt: sizet = 0
while vi_bt < vn_bt:
if vi_bt > 0:
if bool_t.op == ast.OpKind.Or:
pos = _PyiEmit(buf, buf_size, pos, " or ")
else:
pos = _PyiEmit(buf, buf_size, pos, " and ")
pos = _PyiTypeStr(buf, buf_size, pos, vals_bt.get(vi_bt), depth + 1)
vi_bt += 1
return pos
# Constant: 字符串前向引用 (如 'MyClass')、None 或数字常量(CArray count)
if k == ast.ASTKind.Constant:
cn: rawptr[ast.Constant] = rawptr[ast.Constant](annot)
@@ -280,11 +373,31 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet,
return pos
return _PyiEmit(buf, buf_size, pos, "int")
# List: 函数指针参数表 (如 t.Callable[[t.CPtr, ULONG], BOOL] 的内层
# [t.CPtr, ULONG];t.Callable[[], int] 的空表)。TPV 的 AST 把 Callable
# 的参数表成型为 List(TypeGenResolve 的类型名提取同样按 List 递归),
# 故必须输出 "[a, b]" 形式——缺失该分支时该节点落入未知类型 fail-fast。
if k == ast.ASTKind.List:
lst: rawptr[ast.List] = rawptr[ast.List](annot)
pos = _PyiEmit(buf, buf_size, pos, "[")
if lst.elts is not None:
lst_elts: list[ast.AST*]* = lst.elts
ln: sizet = len(lst_elts)
li: sizet = 0
while li < ln:
if li > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
le: rawptr[ast.AST] = lst_elts.get(li)
pos = _PyiTypeStr(buf, buf_size, pos, le, depth + 1)
li += 1
pos = _PyiEmit(buf, buf_size, pos, "]")
return pos
# Tuple: 多参数下标 (如 Dict[str, int])
if k == ast.ASTKind.Tuple:
tup: rawptr[ast.Tuple] = rawptr[ast.Tuple](annot)
if tup.elts is not None:
tup_elts: rawptr[list[rawptr[ast.AST]]] = tup.elts
tup_elts: list[ast.AST*]* = tup.elts
tn: sizet = len(tup_elts)
ti: sizet = 0
while ti < tn:
@@ -301,7 +414,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet,
pos = _PyiTypeStr(buf, buf_size, pos, cl.func, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, "(")
if cl.args is not None:
cl_args: rawptr[list[rawptr[ast.AST]]] = cl.args
cl_args: list[ast.AST*]* = cl.args
can: sizet = len(cl_args)
cai: sizet = 0
while cai < can:
@@ -395,7 +508,7 @@ def _PyiDefaultStr(buf: bytes, buf_size: sizet, pos: sizet,
# 据此合成 `-> tuple[a, b, c]`(tuple 分支同样打包成 Struct),使 stub declare 与 define 的返回类型一致。
# 返回 None = 装饰器列表里没有 CReturn。
# ============================================================
def _PyiCreturnArgs(decorators: rawptr[list[rawptr[ast.AST]]]) -> rawptr[list[rawptr[ast.AST]]]:
def _PyiCreturnArgs(decorators: list[ast.AST*]*) -> list[ast.AST*]*:
"""在装饰器列表里找 @c.CReturn(...),返回其实参列表;未找到返回 None"""
if decorators is None:
return None
@@ -426,7 +539,7 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet,
"""生成函数签名到 buf,返回新的 pos"""
# 装饰器
if fn.decorator_list is not None:
fn_decorator_list: rawptr[list[rawptr[ast.AST]]] = fn.decorator_list
fn_decorator_list: list[ast.AST*]* = fn.decorator_list
dn: sizet = len(fn_decorator_list)
di: sizet = 0
while di < dn:
@@ -452,12 +565,12 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet,
# 位置参数
if args.args is not None:
args_args: rawptr[list[rawptr[ast.AST]]] = args.args
args_args: list[ast.AST*]* = args.args
an: sizet = len(args_args)
# 默认值右对齐到 args 尾部:defaults[i] 对应 args[an - dn + i]。
# 必须输出 '= <default>',否则 VpiReader 解析出的默认参数表无条目,跨模块缺参调用(如 depth=0)读到栈垃圾。
# arg_defaults 必须内联声明:联合接收者的 .get 会被误分派,预声明 + 重赋值会解析成 list[int].get 返回 i32 截断指针而崩溃。
arg_defaults: rawptr[list[rawptr[ast.AST]]] = args.defaults
arg_defaults: list[ast.AST*]* = args.defaults
dn: sizet = 0
if arg_defaults is not None:
dn = len(arg_defaults)
@@ -508,7 +621,7 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet,
pos = _PyiTypeStr(buf, buf_size, pos, fn.returns)
else:
# 无 `->` 注解但带 @c.CReturn(...):合成 " -> tuple[a, b, c]"(N11)
cre_args: rawptr[list[rawptr[ast.AST]]] = _PyiCreturnArgs(fn.decorator_list)
cre_args: list[ast.AST*]* = _PyiCreturnArgs(fn.decorator_list)
if cre_args is not None:
cre_n: sizet = len(cre_args)
if cre_n > 0:
@@ -538,7 +651,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet,
"""生成类定义到 buf,返回新的 pos"""
# 装饰器
if cls.decorator_list is not None:
cls_decorator_list: rawptr[list[rawptr[ast.AST]]] = cls.decorator_list
cls_decorator_list: list[ast.AST*]* = cls.decorator_list
dcn: sizet = len(cls_decorator_list)
dci: sizet = 0
while dci < dcn:
@@ -557,7 +670,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet,
# PEP 695 类型参数 (class Foo[T]:)
if cls.type_params is not None:
cls_type_params: rawptr[list[rawptr[ast.AST]]] = cls.type_params
cls_type_params: list[ast.AST*]* = cls.type_params
tpn: sizet = len(cls_type_params)
if tpn > 0:
pos = _PyiEmit(buf, buf_size, pos, "[")
@@ -573,7 +686,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet,
# 基类
if cls.bases is not None:
cls_bases: rawptr[list[rawptr[ast.AST]]] = cls.bases
cls_bases: list[ast.AST*]* = cls.bases
bn: sizet = len(cls_bases)
if bn > 0:
pos = _PyiEmit(buf, buf_size, pos, "(")
@@ -592,7 +705,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet,
has_member: int = 0
if cls.children is not None:
cls_children: rawptr[list[rawptr[ast.AST]]] = cls.children
cls_children: list[ast.AST*]* = cls.children
cn: sizet = len(cls_children)
ci: sizet = 0
while ci < cn:
@@ -606,7 +719,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet,
# __init__ 方法:先提取 self.x: Type 属性
if mfn.name is not None and string.strcmp(mfn.name, "__init__") == 0:
if mfn.children is not None:
mfn_children: rawptr[list[rawptr[ast.AST]]] = mfn.children
mfn_children: list[ast.AST*]* = mfn.children
icn: sizet = len(mfn_children)
ici: sizet = 0
while ici < icn:
@@ -663,7 +776,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet,
# ============================================================
# _GeneratePyiFromAst - 遍历 AST Module 节点,提取函数签名/类定义/变量声明/import 语句,生成 .vpi 存根文件内容到 buf
#
# Args: mb 内存池(保留以符合调用约定,实际未使用);tree AST Module 根节点;rel_path 相对路径(用于模块名,当前未使用);buf 输出缓冲区;buf_size 缓冲区大小
# Args: mb 内存池(保留以符合调用约定,实际未使用);tree AST Module 根节点;rel_path 源文件路径(仅用于报错归因的"当前文件",应传绝对路径以便 fatal_error 读取源码行);buf 输出缓冲区;buf_size 缓冲区大小
# Returns: 写入的字节数(0 表示失败)
# ============================================================
def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
@@ -673,13 +786,25 @@ def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
if tree is None or buf is None or buf_size == 0:
return 0
# 错误归因:把"当前文件"指向正在生成 stub 的这份(rel_path 非 None 时)。
# 本函数内触发的 fatal_error(如 L227/L303/L430 的注解节点类型未知)应指向
# 本源文件,而非上一次 set_current_file 的残留文件。
# 调用侧必须传**绝对路径**:fatal_error 的 _read_source_line 直接用该路径
# 打开文件读取行,传相对路径会打不开 ⇒ 误报"无法读取该行源码"。
# 放在函数入口而非各分支:全函数共用同一套遍历/报错路径,逐分支设置会漏掉
# 中途新增的分支;入口设一次覆盖全部(含后续所有 fatal_error / push_trace)。
# rel_path 为 None 时不改写 CurrentFile(保持上一次设置值):调用侧确实拿不到
# 路径时,宁可沿用"上一次设置的文件"也不要把归因抹成 None。
if rel_path is not None:
GenApi.set_current_file_type(rel_path)
pos: sizet = 0
# 预处理:按声明顺序注册本文件 CDefine 到 _g_pyi 缓存(vpi 生成早于翻译阶段注册,直接查全局表为空会使 BinOp 内 Name 引用退化为 0,跨模块读回错误常量)。
# 必须带 global 声明:TPV 语义下函数内对模块级变量赋值无 global 时会创建局部变量,_lookup_cdefine_for_pyi 读到的仍是模块级 None,缓存永不命中。
global _g_pyi_cdef_names, _g_pyi_cdef_vals, _g_pyi_cdef_count
if tree.children is not None:
pre_children: rawptr[list[rawptr[ast.AST]]] = tree.children
pre_children: list[ast.AST*]* = tree.children
pcn: sizet = len(pre_children)
# 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配)
_g_pyi_cdef_count = 0
@@ -724,7 +849,7 @@ def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
buf[pos] = 0
return pos
tree_children: rawptr[list[rawptr[ast.AST]]] = tree.children
tree_children: list[ast.AST*]* = tree.children
n: sizet = len(tree_children)
# 第一遍扫描:检测 import t/c
@@ -735,7 +860,7 @@ def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
if stmt is not None and stmt.kind() == ast.ASTKind.Import:
imp: rawptr[ast.Import] = rawptr[ast.Import](stmt)
if imp.names is not None:
imp_names: rawptr[list[rawptr[ast.AST]]] = imp.names
imp_names: list[ast.AST*]* = imp.names
an: sizet = len(imp_names)
for ai in range(an):
al: rawptr[ast.Alias] = rawptr[ast.Alias](imp_names.get(ai))
@@ -766,7 +891,7 @@ def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
imp2: rawptr[ast.Import] = rawptr[ast.Import](stmt)
pos = _PyiEmit(buf, buf_size, pos, "import ")
if imp2.names is not None:
imp2_names: rawptr[list[rawptr[ast.AST]]] = imp2.names
imp2_names: list[ast.AST*]* = imp2.names
an2: sizet = len(imp2_names)
ai2: sizet = 0
while ai2 < an2:
@@ -793,7 +918,7 @@ def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
pos = _PyiEmit(buf, buf_size, pos, ifr.module)
pos = _PyiEmit(buf, buf_size, pos, " import ")
if ifr.names is not None:
ifr_names: rawptr[list[rawptr[ast.AST]]] = ifr.names
ifr_names: list[ast.AST*]* = ifr.names
an3: sizet = len(ifr_names)
ai3: sizet = 0
while ai3 < an3:
@@ -884,7 +1009,7 @@ def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
# 提取第一个参数的整数值并输出
call_cv: rawptr[ast.Call] = rawptr[ast.Call](aa.value)
if call_cv.args is not None:
call_args_cv: rawptr[list[rawptr[ast.AST]]] = call_cv.args
call_args_cv: list[ast.AST*]* = call_cv.args
if len(call_args_cv) > 0:
first_arg: rawptr[ast.AST] = call_args_cv.get(0)
if first_arg is not None:
@@ -952,7 +1077,7 @@ def _GeneratePyiFromAst(mb: rawptr[memhub.MemBuddy], tree: rawptr[ast.AST],
elif k == ast.ASTKind.Assign:
asg: rawptr[ast.Assign] = rawptr[ast.Assign](stmt)
if asg.targets is not None and asg.value is not None:
asg_targets: rawptr[list[rawptr[ast.AST]]] = asg.targets
asg_targets: list[ast.AST*]* = asg.targets
atn: sizet = len(asg_targets)
if atn == 1:
atg: rawptr[ast.AST] = asg_targets.get(0)
File diff suppressed because it is too large. Load diff
@@ -269,6 +269,83 @@ def GetSha1StoreCount() -> int:
def LookupSha1ByModName(mod_name: str) -> str:
"""从模块名查找 SHA1(精确匹配 + 包名回退),委托通用实现 _FindSha1ByModName"""
return StubMergerIR._FindSha1ByModName(StubMerger._sha1_store_arr, StubMerger._sha1_store_mod, StubMerger._sha1_store_count, mod_name)
# ============================================================
# 泛型类归属表(泛型类名 → 定义模块 SHA1)——仅 REPL 会话登记/查询
#
# 为什么需要:REPL 的 includes 会话初始化只注入 `.vpi` 的类(泛型类被跳过),
# 不跑批处理的 _PreRegister*GenericTemplates ⇒ 会话内泛型模板表为空 ⇒ 注解里的
# `list[rawptr[AST]]` 等泛型下标解析(`TypeGenResolve._resolve_generic_subscript_ty`)
# 恒 miss。会话侧的最后兜底要按**定义侧特化命名** "%{模板模块 sha1}.{mangled}"
# 造 opaque 具名类型(实证:`%"b56d2f995104690d.list[rawptr[AST]]"`),因此需要
# "泛型类定义在哪个模块"这一事实——由注入期扫 `.vpi` 的 ClassDef 登记(见
# VpiReader._repl_stub_collect_module)。
#
# 归属模块 sha1 无法从类名反推(模块名取自文件路径,class list ← _list.vp;用户
# 泛型类更是名字与模块名无关),故必须显式登记,不做"按名字猜"。
# 批处理不登记(表恒空 ⇒ 查询恒 None ⇒ 批处理路径一字不改)。
# 容量:includes 现有 6 个泛型类定义(list/dict 系 + Vector/GSList/ndarray),取 32 槽。
# ============================================================
GENERIC_OWNER_MAX: t.CDefine = 32
_generic_owner_names: bytes = None
_generic_owner_sha1s: bytes = None
_generic_owner_count: int = 0
def ResetGenericOwners() -> None:
"""清空泛型类归属表(REPL 会话初始化开始时调用一次)"""
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
if _generic_owner_names is not None:
stdlib.free(_generic_owner_names)
_generic_owner_names = None
if _generic_owner_sha1s is not None:
stdlib.free(_generic_owner_sha1s)
_generic_owner_sha1s = None
_generic_owner_count = 0
def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
"""登记"泛型类名 → 定义模块 SHA1";1=已登记(含重复)/ 0=失败(参数/满/同名异 sha1)
同名异 sha1(跨模块同名泛型类)保留首个,与泛型模板表按类名去重的口径一致。
"""
global _generic_owner_names, _generic_owner_sha1s, _generic_owner_count
if cls_name is None or mod_sha1 is None:
return 0
if cls_name[0] == '\0' or mod_sha1[0] == '\0':
return 0
if _generic_owner_names is None:
_generic_owner_names = stdlib.malloc(GENERIC_OWNER_MAX * 64)
_generic_owner_sha1s = stdlib.malloc(GENERIC_OWNER_MAX * 17)
if _generic_owner_names is None or _generic_owner_sha1s is None:
return 0
string.memset(_generic_owner_names, 0, GENERIC_OWNER_MAX * 64)
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
_generic_owner_count = 0
i_go: int
for i_go in range(_generic_owner_count):
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0:
return 1
return 0
if _generic_owner_count >= GENERIC_OWNER_MAX:
return 0
string.strncpy(_generic_owner_names + sizet(_generic_owner_count) * 64, cls_name, 63)
string.strncpy(_generic_owner_sha1s + sizet(_generic_owner_count) * 17, mod_sha1, 16)
_generic_owner_count += 1
return 1
def LookupGenericOwnerSha1(cls_name: str) -> str:
"""按泛型类名取定义模块 SHA1(返回内部指针,无需释放;未登记 None)"""
if cls_name is None or _generic_owner_names is None:
return None
i_lg: int
for i_lg in range(_generic_owner_count):
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0:
return _generic_owner_sha1s + sizet(i_lg) * 17
return None
# ============================================================
# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1(用于 from .X import Y:impf.module 是 ".X",而映射表键是 "pkg.X")
#
+183 -51
View File
@@ -125,7 +125,7 @@ class AssignGenMixin:
cl: rawptr[ast.Call] = rawptr[ast.Call](val_node)
# 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
cl_args_list: rawptr[list[rawptr[ast.AST]]] = cl.args
cl_args_list: list[ast.AST*]* = cl.args
if cl_args_list is not None and len(cl_args_list) > 0:
first_arg: rawptr[ast.AST] = cl_args_list.get(0)
arg_val: int64 = self.extract_cdefine_int_value(first_arg)
@@ -167,7 +167,7 @@ class AssignGenMixin:
if val_node is not None and val_node.kind() == ast.ASTKind.Call:
cl_cd: rawptr[ast.Call] = rawptr[ast.Call](val_node)
if cl_cd is not None and cl_cd.args is not None:
cl_args_cd: rawptr[list[rawptr[ast.AST]]] = cl_cd.args
cl_args_cd: list[ast.AST*]* = cl_cd.args
if len(cl_args_cd) > 0:
first_arg_cd: rawptr[ast.AST] = cl_args_cd.get(0)
if first_arg_cd is not None and first_arg_cd.kind() == ast.ASTKind.Constant:
@@ -640,7 +640,7 @@ class AssignGenMixin:
lst: rawptr[ast.List] = rawptr[ast.List](list_node)
if lst is None or lst.elts is None:
return 1
elts: rawptr[list[rawptr[ast.AST]]] = lst.elts
elts: list[ast.AST*]* = lst.elts
elts_count: sizet = len(elts)
# 特例:单个字符串字面量初始化字符数组([N x i8],N = strlen+1)
@@ -839,8 +839,17 @@ class AssignGenMixin:
if target_ty is None:
# fail-fast: 字段类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "字段类型信息缺失", "TypeError")
# t.CVolatile 字段:+= 的 load 与 store 均带 volatile 限定符
vol_fa: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):经其访问的
# 字段 += 的 load/store 亦带 volatile(两来源取或)。
at_aug: rawptr[ast.Attribute] = rawptr[ast.Attribute](target)
if at_aug is not None:
if self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, at_aug.value) != 0:
vol_fa = 1
cur_val: rawptr[llvmlite.Value] = llvmlite.build_load(
builder, target_ty, field_ptr)
builder, target_ty, field_ptr, vol_fa)
if cur_val is None:
# fail-fast: 属性当前值加载失败
self.Gen.TypeGenErr.fatal_error(target, "无法加载 attribute 当前值", "TypeError")
@@ -864,7 +873,7 @@ class AssignGenMixin:
if result is None:
# fail-fast: 类型对齐失败
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
llvmlite.build_store(builder, result, field_ptr)
llvmlite.build_store(builder, result, field_ptr, vol_fa)
return 0
# Subscript 目标: self.state[i] += x / arr[i] += x
@@ -888,8 +897,15 @@ class AssignGenMixin:
if target_ty is None:
# fail-fast: 元素类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "元素类型信息缺失", "TypeError")
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p[i] += 的
# load/store 均带 volatile。仅基址可解析到 VarEntry/FieldEntry 时为 1。
vol_sa: int = 0
sub_aug: rawptr[ast.Subscript] = rawptr[ast.Subscript](target)
if sub_aug is not None:
vol_sa = self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, sub_aug.value)
cur_val: rawptr[llvmlite.Value] = llvmlite.build_load(
builder, target_ty, elem_ptr)
builder, target_ty, elem_ptr, vol_sa)
if cur_val is None:
# fail-fast: 元素当前值加载失败
self.Gen.TypeGenErr.fatal_error(target, "无法加载 subscript 元素当前值", "TypeError")
@@ -913,7 +929,7 @@ class AssignGenMixin:
if result is None:
# fail-fast: 类型对齐失败
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
llvmlite.build_store(builder, result, elem_ptr)
llvmlite.build_store(builder, result, elem_ptr, vol_sa)
return 0
if tk != ast.ASTKind.Name:
@@ -925,6 +941,17 @@ class AssignGenMixin:
if nm is None or nm.id is None:
return 0
# REPL 模式(M2/M3)会话变量增强赋值:读槽 → 二元运算 → 写槽 → 写结果区。
# 未登记的会话名返回 0(继续原路径 → 明确的"无法加载…当前值"报错)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 本分支整体不经过。
# 作用域用 repl_in_frag_entry_scope(M3):被提升的会话函数体内 `x += 1`
# 按普通局部变量路径处理(不写会话槽)。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
if self.Gen.VarGen.repl_try_augassign(
trans, nm.id, aug.op, aug.value, node) != 0:
return 0
pool: rawptr[memhub.MemBuddy] = trans.Pool
builder: rawptr[llvmlite.IRBuilder] = trans._cur_builder
mod: rawptr[llvmlite.LLVMModule] = trans.Module
@@ -936,6 +963,10 @@ class AssignGenMixin:
elif GenApi.is_nonlocal_name(trans, nm.id) != 0:
scope_type = 2
# t.CVolatile 变量:+= 的 load 与 store 均带 volatile 限定符
vol_aa: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
# 2. 加载当前值
cur_val: rawptr[llvmlite.Value] = None
target_alloca: rawptr[llvmlite.Value] = None
@@ -949,7 +980,7 @@ class AssignGenMixin:
# fail-fast: 变量类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "变量类型信息缺失", "TypeError")
target_ty = target_alloca.Ty.Pointee
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
cur_val = llvmlite.build_load(builder, target_ty, target_alloca, vol_aa)
elif scope_type == 2:
# nonlocal 变量(通过闭包 env)
cur_val = self.Gen.StmtGenDecl.load_nonlocal_var(trans, nm.id)
@@ -963,7 +994,7 @@ class AssignGenMixin:
# fail-fast: 变量类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "变量类型信息缺失", "TypeError")
target_ty = target_alloca.Ty.Pointee
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
cur_val = llvmlite.build_load(builder, target_ty, target_alloca, vol_aa)
if cur_val is None:
# fail-fast: 目标变量当前值加载失败
@@ -996,7 +1027,7 @@ class AssignGenMixin:
if scope_type == 1:
# global 变量
if target_alloca is not None:
llvmlite.build_store(builder, result, target_alloca)
llvmlite.build_store(builder, result, target_alloca, vol_aa)
elif scope_type == 2:
# nonlocal 变量
nl_ptr: rawptr[llvmlite.Value] = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
@@ -1006,7 +1037,7 @@ class AssignGenMixin:
else:
# 普通局部变量
if target_alloca is not None:
llvmlite.build_store(builder, result, target_alloca)
llvmlite.build_store(builder, result, target_alloca, vol_aa)
return 0
@@ -1176,6 +1207,15 @@ class AssignGenMixin:
if self.is_ctypedef_annotation(aa.annotation) != 0:
return 0
# REPL 模式(M2/M3):片段**入口**函数本体内的 AnnAssign 一律走会话槽(见
# translate_annassign 的会话分支),此处不得预建局部 alloca——否则局部名
# 会遮蔽同名会话变量(此后读 `x` 命中空局部槽,槽值丢失)。
# 被提升的会话函数体内不走该分支(那里按普通局部声明预建 alloca)。
# CDefine/CTypedef 两个分支在本次返回之前,其行为不变。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
return 0
# global/nonlocal 变量不需要局部 alloca
if GenApi.is_global_name(trans, nm.id) != 0:
return 0
@@ -1252,6 +1292,18 @@ class AssignGenMixin:
if self._is_ptr_element_annotation(annot) != 0:
self.Gen.VarGen.set_var_ptr_element(
trans.SymTab, nm.id)
# t.CVolatile:声明期即标记该变量的 load/store 带 volatile。
# 覆盖「无初值声明」`x: t.CVolatile | T`(有初值路径
# translate_annassign 亦会登记,幂等)。动机:volatile 禁优化器
# 把忙等循环的重复 load 提升/消除,不能依赖优化器保序。
if self.Gen.TypeGenErr.has_decorator_marker(
annot, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
# rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):解引用(p[i]/p.field/c.Deref(p))的 load/store
# 带 volatile。声明期登记,覆盖「无初值声明」。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot) != 0:
self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
return 0
return 0
@@ -1401,6 +1453,20 @@ class AssignGenMixin:
self.Gen.TypeGenCType.register_ctypedef(pool_ct, nm.id, ctd_str)
return 0
# REPL 模式(M2/M3)会话变量:片段**入口**函数本体内的带注解声明/赋值
# (`x: int = 5` 定义并定型;`x: int` 仅定型)。注解先解析并定型条目,
# 定型后的槽类型再作为 RHS 的 target_ty(与普通 AnnAssign 的 P1-A 同口径)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 本分支整体不经过。
# 被提升的会话函数体内不走该分支(`x: int = 5` 是局部声明,Python 语义)。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
rp_e: rawptr[GenTypes.ReplVarEntry] = \
self.Gen.VarGen.repl_ensure_entry(trans, nm.id, aa)
if rp_e is not None:
self.Gen.VarGen.repl_try_annassign(
trans, nm.id, aa.annotation, aa.value, aa)
return 0
pool: rawptr[memhub.MemBuddy] = trans.Pool
builder: rawptr[llvmlite.IRBuilder] = trans._cur_builder
mod: rawptr[llvmlite.LLVMModule] = trans.Module
@@ -1434,7 +1500,12 @@ class AssignGenMixin:
callable_ret: rawptr[llvmlite.LLVMType] = self.Gen.TypeGenErr.try_extract_callable_ret(
pool, annot, trans._imported_modules, trans._from_imports, trans)
if callable_ret is not None:
self.Gen.CallGenLeafKit.register_func_ptr_sig(nm.id, callable_ret)
# 返回值:0=成功/同名同类型幂等命中;非 0 已在 register_func_ptr_sig
# 内部 fatal_error(表满/同名不同类型/表分配失败)终止编译。此处分支
# 仅为防御——报错通道异常时不带着未登记的签名继续翻译。
if self.Gen.CallGenLeafKit.register_func_ptr_sig(
pool, nm.id, callable_ret, aa) != 0:
return 0
# global 变量:写入模块作用域中的全局变量
if GenApi.is_global_name(trans, nm.id) != 0:
@@ -1447,12 +1518,12 @@ class AssignGenMixin:
aa_glob_ty: rawptr[llvmlite.LLVMType] = None
if mod_alloca.Ty is not None:
aa_glob_ty = mod_alloca.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支(普通局部变量路径)同一口径——必须在翻译 RHS 之前写入
# CurAssignAnnot/CurAssignNode。四处(主分支 / 本 global 分支 /
# 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步,否则
# `x: int = c.Deref(p)` 的读取宽度与 `s: Stack[int] = Stack()`
# 的类型实参推断都会拿不到注解。
# 供裸名泛型构造从注解推断类型实参(消费者见 CallGenCtor.vp 的
# CurAssignNode 判定):与主分支(普通局部变量路径)同一口径——
# 必须在翻译 RHS 之前写入 CurAssignAnnot/CurAssignNode。四处
# (主分支 / 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal
# 分支)必须同步,否则 `s: Stack[int] = Stack()` 的类型实参推断
# 会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
@@ -1493,11 +1564,13 @@ class AssignGenMixin:
target_ty = mod_alloca.Ty.Pointee
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
llvmlite.build_store(builder, rhs_val, mod_alloca)
# t.CVolatile(global 声明)全局:初始化 store 带 volatile 限定符
vol_gaa: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
llvmlite.build_store(builder, rhs_val, mod_alloca, vol_gaa)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续 while 条件/比较等处的 c.Deref 被误判读取宽度
# (i8* 按 i32* 读 4 字节),也会污染裸名泛型构造的类型实参推断
# 否则残留注解会污染后续语句里裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
@@ -1515,12 +1588,11 @@ class AssignGenMixin:
nl_target_ty: rawptr[llvmlite.LLVMType] = None
if nl_ptr is not None and nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支、下方 mod_glob 分支同一口径——必须在
# 翻译 RHS 之前写入 CurAssignAnnot/CurAssignNode。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步,否则
# `nonlocal x: int = c.Deref(p)` 的读取宽度与
# `nonlocal s: Stack[int] = Stack()` 的类型实参推断都会拿不到注解。
# 供裸名泛型构造从注解推断类型实参:与主分支、上方 global 分支、
# 下方 mod_glob 分支同一口径——必须在翻译 RHS 之前写入
# CurAssignAnnot/CurAssignNode。四处(主分支 / global / mod_glob /
# 本 nonlocal 分支)必须同步,否则
# `nonlocal s: Stack[int] = Stack()` 的类型实参推断会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
@@ -1542,9 +1614,8 @@ class AssignGenMixin:
builder, rhs_val, nl_target_ty, node)
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步清除,否则残留注解会让
# 后续 while 条件/比较等处的 c.Deref 被误判读取宽度(i8* 按 i32* 读 4
# 字节),也会污染裸名泛型构造的类型实参推断
# global / mod_glob / 本 nonlocal 分支)必须同步清除,否则残留注解会
# 污染后续语句里裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
@@ -1568,11 +1639,10 @@ class AssignGenMixin:
mg_val_ty: rawptr[llvmlite.LLVMType] = None
if mod_glob.Ty is not None:
mg_val_ty = mod_glob.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支同一口径——翻译 RHS 前写入
# CurAssignAnnot/CurAssignNode。模块级 `s: Stack[int] = Stack()`
# 走的就是本分支,四处(主分支 / global / 本 mod_glob 分支 /
# 上方 nonlocal 分支)必须同步。
# 供裸名泛型构造从注解推断类型实参:与主分支、上方 global 分支
# 同一口径——翻译 RHS 前写入 CurAssignAnnot/CurAssignNode。
# 模块级 `s: Stack[int] = Stack()` 走的就是本分支,四处(主分支 /
# global / 本 mod_glob 分支 / 上方 nonlocal 分支)必须同步。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val_mg: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
@@ -1613,11 +1683,13 @@ class AssignGenMixin:
target_ty_mg = mod_glob.Ty.Pointee
if target_ty_mg is not None:
rhs_val_mg = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node)
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
# t.CVolatile 模块级全局:初始化 store 带 volatile 限定符
vol_mga: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
llvmlite.build_store(builder, rhs_val_mg, mod_glob, vol_mga)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 上方 global 分支 / 本 mod_glob 分支 / 上方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续语句的 c.Deref 被误判读取宽度,并污染裸名泛型
# 构造的类型实参推断
# 否则残留注解会污染后续语句里裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
@@ -1759,9 +1831,8 @@ class AssignGenMixin:
self._init_global_array_from_list(
builder, pool, mod, alloca, aa.value, trans)
else:
# 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → 读 i32
# CurAssignNode 记录 RHS 节点:translate_c_deref 据此判定 Deref
# 是否为赋值直接 RHS(对齐 TPC _infer_deref_target_type)。
# 供裸名泛型构造从注解推断类型实参(消费者见 CallGenCtor.vp 的
# CurAssignNode 判定:`s: Stack[int] = Stack()` 用注解里的 int 特化)。
# 注意:本主分支(普通局部变量)、上方 global 分支、mod_glob 分支、
# nonlocal 分支四处的 set/clear 必须同步(模块级 AnnAssign 走
# mod_glob;闭包内 AnnAssign 走 nonlocal)。
@@ -1854,7 +1925,19 @@ class AssignGenMixin:
target_ty = alloca.Ty.Pointee
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
llvmlite.build_store(builder, rhs_val, alloca)
# t.CVolatile 局部变量:标记 VarEntry 并让本次 store 带 volatile
vol_laa: int = 0
if aa.annotation is not None and nm.id is not None:
if self.Gen.TypeGenErr.has_decorator_marker(
aa.annotation, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
vol_laa = 1
# rawptr[t.CVolatile | T](volatile 指针指向物):登记
# PointeeVolatile;本 store 写的是指针变量自身,不影响。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(
aa.annotation) != 0:
self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
llvmlite.build_store(builder, rhs_val, alloca, vol_laa)
# FakeDuck: 初始化影子结构体 __data__ 字段 = 初始字符串指针
# (对齐 TPC HandlesAnnAssign.py: 仅当有初值时写入 __data__)
if fd_str_ty is not None and fd_meta_alloca is not None:
@@ -1868,8 +1951,8 @@ class AssignGenMixin:
if fd_data_ptr is not None:
llvmlite.build_store(builder, rhs_val, fd_data_ptr)
# 清除赋值上下文(防残留):CurAssignAnnot/CurAssignNode 只在该
# AnnAssign 的 RHS 翻译期间有效,否则后续 while 条件/比较等处的
# c.Deref 会被残留注解误判读取宽度(i8* 被 bitcast i32* 读 4 字节)。
# AnnAssign 的 RHS 翻译期间有效,否则后续语句里裸名泛型构造的类型
# 实参推断会拿到残留注解。
# 四处(本主分支 / global / mod_glob / nonlocal)必须同步清除。
trans.CurAssignAnnot = None
trans.CurAssignNode = None
@@ -1911,7 +1994,7 @@ class AssignGenMixin:
VLogger.error("cast failed", "ASGN")
return 0
targets: rawptr[list[rawptr[ast.AST]]] = asgn.targets
targets: list[ast.AST*]* = asgn.targets
if targets is None:
VLogger.error("targets is None", "ASGN")
return 0
@@ -1923,7 +2006,7 @@ class AssignGenMixin:
# 翻译 RHS 值
# 无注解赋值无目标类型可推断,清空 CurAssignAnnot/CurAssignNode
# (防残留误判 c.Deref 宽度)
# (防残留污染裸名泛型构造的类型实参推断)
trans.CurAssignAnnot = None
trans.CurAssignNode = None
# P1-A(上下文位宽传播):单 Name 目标且其存储类型已知时,把存储类型作为
@@ -1943,6 +2026,16 @@ class AssignGenMixin:
trans.SymTab, asg_nm0.id)
if asg_slot is not None and asg_slot.Ty is not None:
asg_target_ty = asg_slot.Ty.Pointee
elif trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
# REPL 模式(M2/M3):已定型的会话变量也用槽类型做 target_ty
# (与 P1-A 同口径;未定型则维持 None,由 RHS 类型定型)。
# 只在片段入口函数生效(被提升的会话函数体内同名赋值是局部变量)
asg_re: rawptr[GenTypes.ReplVarEntry] = \
self.Gen.VarGen.repl_find_entry(trans.ReplCtx, asg_nm0.id)
if asg_re is not None \
and asg_re.Kind != GenTypes.REPL_KIND_UNTYPED:
asg_target_ty = self.Gen.VarGen.repl_slot_ty(pool, asg_re)
rhs_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
builder, pool, mod, asgn.value, None, 0, trans, asg_target_ty)
if rhs_val is None:
@@ -2008,7 +2101,7 @@ class AssignGenMixin:
# store + GEP 逐字段 load → store 到各 Name 目标。
if tk == ast.ASTKind.Tuple:
tgt_tup: rawptr[ast.Tuple] = rawptr[ast.Tuple](target)
tgt_elts: rawptr[list[rawptr[ast.AST]]] = tgt_tup.elts
tgt_elts: list[ast.AST*]* = tgt_tup.elts
# 守卫:rhs 不是 struct(tuple 打包)时跳过本 target
if tgt_elts is None or rhs_val is None or rhs_val.Ty is None:
continue
@@ -2084,7 +2177,14 @@ class AssignGenMixin:
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, elem_ptr.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, elem_ptr)
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p[i] = v
# 的 store 带 volatile。仅基址可解析到条目时为 1(否则文本不变)。
vol_sw: int = 0
sub_wr: rawptr[ast.Subscript] = rawptr[ast.Subscript](target)
if sub_wr is not None:
vol_sw = self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, sub_wr.value)
llvmlite.build_store(builder, store_val, elem_ptr, vol_sw)
continue
# get_subscript_ptr 返回 None: 尝试 __setitem__ 运算符重载
# 适用于自定义类(如 hashtable[key]=val → hashtable.__setitem__(key, val))
@@ -2262,7 +2362,17 @@ class AssignGenMixin:
if field_ptr.Ty is not None and field_ptr.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, field_ptr.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, field_ptr)
# t.CVolatile 字段:store 带 volatile 限定符
vol_fw: int = self.Gen.ExprGenAttr.attr_field_is_volatile(
pool, trans, target)
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p.field = v
# 的 store 亦带 volatile(两来源取或)。
at_wr: rawptr[ast.Attribute] = rawptr[ast.Attribute](target)
if at_wr is not None:
if self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, at_wr.value) != 0:
vol_fw = 1
llvmlite.build_store(builder, store_val, field_ptr, vol_fw)
continue
# 守卫:field_ptr 为 None → 构造详细错误信息
attr_node: rawptr[ast.Attribute] = rawptr[ast.Attribute](target)
@@ -2333,7 +2443,12 @@ class AssignGenMixin:
target_ty = mod_alloca.Ty.Pointee
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
llvmlite.build_store(builder, rhs_val, mod_alloca)
# t.CVolatile 全局:store 带 volatile 限定符
vol_gas: int = 0
if nm.id is not None:
vol_gas = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
llvmlite.build_store(builder, rhs_val, mod_alloca, vol_gas)
continue
# nonlocal 变量:通过闭包 env 写入
@@ -2362,6 +2477,20 @@ class AssignGenMixin:
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
continue
# REPL 模式(M2/M3)会话变量:片段**入口**函数本体内的 Name 目标即
# 会话变量(首次出现=定义:申请槽位 + 按 RHS 定型 + 写槽 + 写结果区)。
# 守卫 trans.ReplCtx(非 REPL 恒 None ⇒ 本分支整体不经过);
# repl_in_frag_entry_scope 排除被提升的会话函数体与片段内嵌套函数体
# (那些仍用普通局部 alloca:Python 局部语义 + 防形参同名串味)。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
rp_entry: rawptr[GenTypes.ReplVarEntry] = \
self.Gen.VarGen.repl_ensure_entry(trans, nm.id, target)
if rp_entry is not None:
self.Gen.VarGen.repl_try_assign(
trans, nm.id, rhs_val, asgn.value, node)
continue
# 拦截:函数内对模块级全局变量赋值但没 global 声明
# 否则静默创建局部变量遮蔽全局,全局保持 null 导致运行时崩溃
# (_mbuddy = mb 缺 global 声明即此类 bug:Int32 解引用 null 崩溃)
@@ -2387,7 +2516,10 @@ class AssignGenMixin:
if alloca.Ty is not None and alloca.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, alloca.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, alloca)
# t.CVolatile 局部变量:store 带 volatile 限定符
vol_las: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
llvmlite.build_store(builder, store_val, alloca, vol_las)
# lambda 表达式赋给变量:注册为闭包变量名,
# 调用分派(is_closure_name)才走闭包间接调用路径;
# 否则 i8** alloca 被 _is_func_ptr_alloca 误判为
+8 -8
View File
@@ -198,7 +198,7 @@ class AsyncGenMixin(CallGenBase.CallGenBase):
self._scan_sleep_stmts(trans, fd.children)
def _scan_sleep_stmts(self, trans: rawptr[HT.Translator],
stmts: rawptr[list[rawptr[ast.AST]]]):
stmts: list[ast.AST*]*):
if stmts is None or trans._coro_uses_sleep != 0:
return
bn: sizet = len(stmts)
@@ -332,7 +332,7 @@ class AsyncGenMixin(CallGenBase.CallGenBase):
# ---------- 第二遍:填额外槽表 + 状态号计数 ----------
def _scan_stmts(self, trans: rawptr[HT.Translator],
stmts: rawptr[list[rawptr[ast.AST]]]):
stmts: list[ast.AST*]*):
if stmts is None:
return
bn: sizet = len(stmts)
@@ -578,7 +578,7 @@ class AsyncGenMixin(CallGenBase.CallGenBase):
return self._stmts_deep_await(tr3.finalbody)
return self._stmt_has_await(stmt)
def _stmts_deep_await(self, stmts: rawptr[list[rawptr[ast.AST]]]) -> int:
def _stmts_deep_await(self, stmts: list[ast.AST*]*) -> int:
if stmts is None:
return 0
bn6: sizet = len(stmts)
@@ -633,7 +633,7 @@ class AsyncGenMixin(CallGenBase.CallGenBase):
return trans._coro_local_count
def _collect_locals_stmts(self, trans: rawptr[HT.Translator],
stmts: rawptr[list[rawptr[ast.AST]]]):
stmts: list[ast.AST*]*):
if stmts is None:
return
bn: sizet = len(stmts)
@@ -729,8 +729,8 @@ class AsyncGenMixin(CallGenBase.CallGenBase):
trans._coro_local_base = 5 + nparams
trans._coro_extra_base = trans._coro_local_base + nlocals
total: int = 5 + nparams + nlocals + nextra
head: rawptr[llvmlite.Function] = None
prev: rawptr[llvmlite.Function] = None
head: rawptr[llvmlite.ParamNode] = None
prev: rawptr[llvmlite.ParamNode] = None
fi: int = 0
for fi in range(total):
fty: rawptr[llvmlite.LLVMType] = i32_ty
@@ -1205,7 +1205,7 @@ class AsyncGenMixin(CallGenBase.CallGenBase):
builder: rawptr[llvmlite.IRBuilder],
pool: rawptr[memhub.MemBuddy],
mod: rawptr[llvmlite.LLVMModule],
stmts: rawptr[list[rawptr[ast.AST]]]):
stmts: list[ast.AST*]*):
"""递归翻译协程体语句。builder 当前块 = 活跃执行块(state0 或某恢复块)。"""
if stmts is None:
return
@@ -2001,7 +2001,7 @@ class AsyncGenMixin(CallGenBase.CallGenBase):
for hi in range(nhandlers):
h_node2: rawptr[ast.AST] = tr.handlers.get(hi)
handler_bb2: rawptr[llvmlite.BasicBlock] = (
rawptr[llvmlite.BasicBlock])(handler_bbs[hi], t.CPtr)
rawptr[llvmlite.BasicBlock])(handler_bbs[hi])
llvmlite.position_at_end(builder, handler_bb2)
if h_node2 is not None:
hd2: rawptr[ast.ExceptHandler] = rawptr[ast.ExceptHandler](h_node2)
+18 -6
View File
@@ -29,8 +29,17 @@ import lib.Stub.StubGen.VpiReader as VpiReader
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# 常量(Gen 层各处容量上限)
#
# GLOBAL_FUNC_MAX / GLOBAL_DEFAULTS_MAX / MAX_CLOSURE_NAMES /
# MAX_FUNC_PTR_SIGS / ISINSTANCE_CLASS_MAX 五个容量上限以本文件为**全仓唯一
# 来源**,消费方 CallGenLeafKit.vp 在函数体内以 CallGenBase.<NAME> 取值:
# 跨模块整型 t.CDefine 在函数体内可读(与 GenTypes.FIELD_MAX /
# StubMerger.MAX_INCLUDES 走同一条 ExprGenAttr 跨模块 CDefine 查找路径),
# 限制仅在「模块级 t.CDefine 初始化式里引用跨模块常量」。改此一处即全仓生效。
# ============================================================
# 全局函数表 / 默认参数表容量(消费方:CallGenLeafKit._init_global_func_table /
# _register_global_func / _init_global_defaults_table / _register_global_defaults)
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
@@ -41,11 +50,12 @@ ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
# 闭包名表大小(消费方:CallGenLeafKit._ensure_closure_names/register_closure_name)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
# 函数指针签名表大小(消费方:CallGenLeafKit._ensure_funcptr_sigs/register_func_ptr_sig)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32。
# 消费方:CallGenLeafKit._collect_subclass_vtable_classes)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
@@ -603,7 +613,8 @@ class CallGenBase:
existing_steps += 1
if existing_steps > DECLARE_CHAIN_LIMIT:
stdio.printf("[CALLGEN] 链表异常: module funcs(call_name=%s,遍历超限,终止)\n", call_name)
stdlib.exit(1)
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印)
VLogger.repl_abort()
if existing.Name is not None:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
if existing.Name[0] == call_name[0] \
@@ -622,7 +633,8 @@ class CallGenBase:
cur_count += 1
if cur_count > DECLARE_CHAIN_LIMIT:
stdio.printf("[CALLGEN] 链表异常: func params(call_name=%s,参数链遍历超限,终止)\n", call_name)
stdlib.exit(1)
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印)
VLogger.repl_abort()
cur_p = cur_p.Next
# 只追加缺失的参数(cur_count < param_count 时)
if cur_count < param_count:
+55 -107
View File
@@ -17,42 +17,12 @@ from . import CallGenBase
# ============================================================
# _IsTypeExpr / _IsCastWrappingDeref - c.Deref 加载宽度推断的转换包裹判定
# (镜像 TPC HandlesSpecialCall:转换链包裹 Deref 时允许从赋值注解推断
# 加载宽度;ifexp/binop 包裹不放行,避免 i8* 错 bitcast 成其他位宽读)
# 常量(与 TPC 侧 HandlesExprCall 保持一致)
#
# GLOBAL_FUNC_MAX / GLOBAL_DEFAULTS_MAX / MAX_CLOSURE_NAMES /
# MAX_FUNC_PTR_SIGS / ISINSTANCE_CLASS_MAX:唯一来源在 CallGenBase.vp
# (本文件不使用,故不留副本;消费方 CallGenLeafKit.vp 经 CallGenBase.<NAME> 引用)。
# ============================================================
def _IsTypeExpr(node: rawptr[ast.AST]) -> int:
"""节点是否为类型表达式(Name/Attribute/Subscript/BinOp|)"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Name or k == ast.ASTKind.Attribute or k == ast.ASTKind.Subscript:
return 1
if k == ast.ASTKind.BinOp:
bop: rawptr[ast.BinOp] = rawptr[ast.BinOp](node)
if bop.op == ast.OpKind.BitOr:
return 1
return 0
def _IsCastWrappingDeref(rhs: rawptr[ast.AST], deref_node: rawptr[ast.AST]) -> int:
"""rhs 是否为"类型转换链包裹 c.Deref"(如 (T | t.CPtr)(t.CPtr(c.Deref(p))))"""
if rhs is None:
return 0
if uint64(rhs) == uint64(deref_node):
return 1
if rhs.kind() == ast.ASTKind.Call:
cl: rawptr[ast.Call] = rawptr[ast.Call](rhs)
if cl.args is not None and len(cl.args) == 1 and _IsTypeExpr(cl.func) != 0:
return _IsCastWrappingDeref(cl.args.get(0), deref_node)
return 0
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
# clobber(~{...})容量独立于操作数上限:vipersimd ×8 内核单条 c.Asm
@@ -66,12 +36,6 @@ ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
@@ -87,10 +51,16 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
mod: rawptr[llvmlite.LLVMModule],
cl: rawptr[ast.Call],
trans: rawptr[HT.Translator] = None) -> rawptr[llvmlite.Value]:
"""c.Deref(ptr) → load 按 ptr 的 pointee 类型"""
"""c.Deref(ptr) → load,宽度只由 ptr 的 pointee 类型决定
pointee 是 i8(t.CPtr / void* / str / bytes)恒读 1 字节;不再按赋值
目标注解(CurAssignAnnot)反推宽度——那会让同一处 c.Deref(i8*) 的读取
宽度取决于它是否恰好出现在带注解赋值的 RHS 里。需要按更宽类型读时,
由调用点显式给出元素类型指针后再解引用(见下方读取分支注释)。
"""
if cl is None:
return None
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return None
@@ -99,53 +69,22 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if ptr_val is None:
return None
# 按 pointee 类型读取;非指针参数默认按 i8(保持向后兼容)
# rawptr[t.CVolatile | T] 实参(volatile 指针指向物):c.Deref(p) 的
# 解引用 load 带 volatile。仅当实参可解析到 VarEntry(Name)/ FieldEntry
# (Attribute)时为 1;其余(函数返回/调用/复杂表达式)为 0(明确不支持)。
vol_deref: int = self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, cargs.get(0))
# 读取宽度只由指针类型决定:按 pointee 类型 load 一个元素。
# pointee 是 i8(t.CPtr / void* / str / bytes)即恒读 1 字节。
# 需要 4/8 字节等更宽读取时,必须在调用点把指针显式变成有元素类型的
# 指针再解引用(c.Deref((rawptr[T])(p)) / c.Deref((T | t.CPtr)(x))),
# 不再由赋值目标注解反推——否则同一处 c.Deref(i8*) 的宽度会随上下文
# (是否带注解赋值的 RHS)而变。
if self.Gen.ExprGenBase.is_ptr_type(ptr_val.Ty) != 0:
pointee: rawptr[llvmlite.LLVMType] = ptr_val.Ty.Pointee
if pointee is not None:
# i8*(void*)时按赋值目标类型推断读取宽度(对齐 TPC
# HandlesSpecialCall._infer_deref_target_type):
# v: int = c.Deref(p) → bitcast i32* 后 load 4 字节。
# 背景:DictTest d3[key5]=500 读出 -12——固定按 i8 读 1 字节 +
# sext 截断(0xF4 → 0xFFFFFFF4);Test 1 的小值(10)恰好正确
# 掩盖了缺陷。
match pointee:
case llvmlite.LLVMType.Int(pointee_width):
# 允许推断:c.Deref 是赋值语句直接 RHS(v: int = c.Deref(p))
# 或处于类型转换链包裹中(如 vt: t.CPtr =
# t\.CVoid(c.Deref(p)))。嵌套在 while 条件/比较/binop/
# 其他调用中(如 while c.Deref(p) != 0、total +=
# int(c.Deref(p)))不推断,否则 i8* 会被残留
# CurAssignAnnot 误判为 i32* 读 4 字节 → StringIterTest
# ASCII sum/manual strcmp 失败。
allow_infer: int = 0
if trans is not None and trans.CurAssignNode is not None:
if uint64(cl) == uint64(trans.CurAssignNode):
allow_infer = 1
elif _IsCastWrappingDeref(trans.CurAssignNode, cl) != 0:
allow_infer = 1
if pointee_width == 8 and allow_infer != 0 \
and trans is not None and trans.CurAssignAnnot is not None:
dref_ty: rawptr[llvmlite.LLVMType] = self.Gen.TypeGenResolve.resolve_annotation_type(
trans.Pool, trans.CurAssignAnnot,
trans._imported_modules, trans._from_imports, trans, 0)
if dref_ty is not None:
dref_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(dref_ty)
dref_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(dref_ty)
dref_is_ptr: int = self.Gen.ExprGenBase.is_ptr_type(dref_ty)
# 整数/浮点/指针目标均推断(指针 → bitcast 后
# load 8 字节;struct 值目标不推断,保持 i8 读)
dref_ptr: rawptr[llvmlite.Value] = None
if (dref_bits != 0 or dref_fbits != 0 or dref_is_ptr != 0) \
and dref_bits != 8:
dref_ptr_ty: rawptr[llvmlite.LLVMType] = llvmlite.Ptr(pool, dref_ty)
dref_ptr = llvmlite.build_bitcast(
builder, ptr_val, dref_ptr_ty)
if dref_ptr is not None:
return llvmlite.build_load(builder, dref_ty, dref_ptr)
case _:
pass
return llvmlite.build_load(builder, pointee, ptr_val)
return llvmlite.build_load(builder, pointee, ptr_val, vol_deref)
# 非指针参数即类型推断退化:fail-fast,绝不按 i8 兜底读取(会读到垃圾宽度)
self.Gen.TypeGenErr.fatal_error(cl, "c.Deref 参数不是指针类型(类型推断退化)", "TypeError")
return None
@@ -161,14 +100,15 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
"""
if cl is None:
return None
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return None
# 单参形式:deref 读(paging.vp save_pt_range L263
# saved = c.DerefAs(save_count)、restore_pt_range L277-278
# c.DerefAs(entry_ptr) 根因——此前 len<2 直接 return None →
# 跌落跨模块函数调用生成 call @DerefAs → ld.lld undefined symbol:
# DerefAs。复用 translate_c_deref 的类型推断读取逻辑。
# DerefAs。读取宽度与 c.Deref 同口径(只认 pointee 类型),
# 故直接复用 translate_c_deref。
if len(cargs) < 2:
return self.translate_c_deref(pool, builder, mod, cl, trans)
@@ -221,7 +161,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
"""c.Set(a, b) → a = b(赋值语义,a 为左值)"""
if cl is None:
return None
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 2:
return None
a_node: rawptr[ast.AST] = cargs.get(0)
@@ -236,7 +176,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if d_call.func is not None and d_call.func.kind() == ast.ASTKind.Attribute:
d_at: rawptr[ast.Attribute] = rawptr[ast.Attribute](d_call.func)
if d_at.attr is not None and string.strcmp(d_at.attr, "Deref") == 0:
d_args: rawptr[list[rawptr[ast.AST]]] = d_call.args
d_args: list[ast.AST*]* = d_call.args
if d_args is not None and len(d_args) >= 1:
ptr_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
builder, pool, mod, d_args.get(0), None, 0, trans)
@@ -345,7 +285,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
"""c.Addr(var) → 返回变量的 alloca 指针(不 load)"""
if cl is None:
return None
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return None
@@ -385,6 +325,14 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if nl_addr is not None:
return nl_addr
return_var: rawptr[llvmlite.Value] = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
# REPL 模式(M2)会话变量取址:c.Addr(x) → ctx 内该槽的地址(地址同一性)。
# 位置在局部查找之后(片段自己声明的局部名优先,与读路径同序)、
# 模块全局/函数引用之前。守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 不变。
if return_var is None and trans is not None and trans.ReplCtx is not None:
sess_addr: rawptr[llvmlite.Value] = self.Gen.VarGen.repl_try_addr(
trans, nm.id, arg0)
if sess_addr is not None:
return sess_addr
if return_var is None and mod is not None:
# 模块全局变量 c.Addr(模块全局) → 返回全局变量指针:必须经 make_global_ref 转成带引号的 Ptr(Ty) 引用(直接返回 GlobalVariable 节点时其 Ty 是 pointee、Name 无 @ 引号,参与 ptrtoint 会渲染成 "ptrtoint void to i64")
gv_ref_nm: rawptr[llvmlite.GlobalVariable] = self.Gen.ExprGenAttr.find_global_in_module(mod, nm.id)
@@ -646,7 +594,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
"""c.Load(a, b) → *a = *b(加载 b 的值,存储到 a)"""
if cl is None:
return None
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 2:
return None
@@ -1061,7 +1009,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if cl is None:
return None
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return None
@@ -1117,7 +1065,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if tk == ast.ASTKind.JoinedStr:
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](tmpl_node)
jvalues: rawptr[list[rawptr[ast.AST]]] = js.values
jvalues: list[ast.AST*]* = js.values
if jvalues is not None:
jvlen: sizet = len(jvalues)
for vi in range(jvlen):
@@ -1144,7 +1092,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
else:
continue
fargs: rawptr[list[rawptr[ast.AST]]] = fcall.args
fargs: list[ast.AST*]* = fcall.args
if fargs is None or len(fargs) < 1:
continue
@@ -1237,7 +1185,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
# ============================================================
# 2. 解析关键字参数 out=/inp=/op=/clobber=
# ============================================================
keywords: rawptr[list[rawptr[ast.AST]]] = cl.keywords
keywords: list[ast.AST*]* = cl.keywords
if keywords is not None:
kwlen: sizet = len(keywords)
for ki in range(kwlen):
@@ -1251,7 +1199,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if kwk != ast.ASTKind.List:
continue
out_lst: rawptr[ast.List] = rawptr[ast.List](kw.value)
out_elts: rawptr[list[rawptr[ast.AST]]] = out_lst.elts
out_elts: list[ast.AST*]* = out_lst.elts
if out_elts is None:
continue
oelen: sizet = len(out_elts)
@@ -1263,7 +1211,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
oattr: str = self._asm_is_c_call(ocall)
if oattr is None or string.strcmp(oattr, "AsmOut") != 0:
continue
oargs: rawptr[list[rawptr[ast.AST]]] = ocall.args
oargs: list[ast.AST*]* = ocall.args
if oargs is None or len(oargs) < 1:
continue
oval_node: rawptr[ast.AST] = oargs.get(0)
@@ -1294,7 +1242,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if kwk != ast.ASTKind.List:
continue
in_lst: rawptr[ast.List] = rawptr[ast.List](kw.value)
in_elts: rawptr[list[rawptr[ast.AST]]] = in_lst.elts
in_elts: list[ast.AST*]* = in_lst.elts
if in_elts is None:
continue
ielen: sizet = len(in_elts)
@@ -1306,7 +1254,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
iattr: str = self._asm_is_c_call(icall)
if iattr is None or string.strcmp(iattr, "AsmInp") != 0:
continue
iargs: rawptr[list[rawptr[ast.AST]]] = icall.args
iargs: list[ast.AST*]* = icall.args
if iargs is None or len(iargs) < 1:
continue
ival_node: rawptr[ast.AST] = iargs.get(0)
@@ -1325,7 +1273,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if kwk != ast.ASTKind.List:
continue
cl_lst: rawptr[ast.List] = rawptr[ast.List](kw.value)
cl_elts: rawptr[list[rawptr[ast.AST]]] = cl_lst.elts
cl_elts: list[ast.AST*]* = cl_lst.elts
if cl_elts is None:
continue
clen: sizet = len(cl_elts)
@@ -1349,7 +1297,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
pos_node: rawptr[ast.AST] = cargs.get(1)
if pos_node is not None and pos_node.kind() == ast.ASTKind.List:
pos_lst: rawptr[ast.List] = rawptr[ast.List](pos_node)
pos_elts: rawptr[list[rawptr[ast.AST]]] = pos_lst.elts
pos_elts: list[ast.AST*]* = pos_lst.elts
if pos_elts is not None:
plen: sizet = len(pos_elts)
for pi in range(plen):
@@ -1501,7 +1449,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
tk2: int = tmpl_node.kind()
if tk2 == ast.ASTKind.JoinedStr:
js2: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](tmpl_node)
jvalues2: rawptr[list[rawptr[ast.AST]]] = js2.values
jvalues2: list[ast.AST*]* = js2.values
if jvalues2 is not None:
jvlen2: sizet = len(jvalues2)
fv_fi: int = 0
@@ -1696,7 +1644,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if cl is None or builder is None or pool is None or trans is None:
return None
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
self.Gen.TypeGenErr.fatal_error(cl, "c.LLVMIR 缺少模板参数", "TypeError")
@@ -1726,7 +1674,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
if tk == ast.ASTKind.JoinedStr:
js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](first_arg)
jvalues: rawptr[list[rawptr[ast.AST]]] = js.values
jvalues: list[ast.AST*]* = js.values
if jvalues is not None:
jvlen: sizet = len(jvalues)
for vi in range(jvlen):
@@ -1751,7 +1699,7 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
fcall: rawptr[ast.Call] = rawptr[ast.Call](fexpr)
attr_nm: str = self._asm_is_c_call(fcall)
if attr_nm is not None:
fargs: rawptr[list[rawptr[ast.AST]]] = fcall.args
fargs: list[ast.AST*]* = fcall.args
if fargs is None or len(fargs) < 1:
continue
val_node: rawptr[ast.AST] = fargs.get(0)
+5 -5
View File
@@ -724,7 +724,7 @@ class CallGenCall(CallGenBase.CallGenBase):
case _:
pass
if lb_obj is not None:
lb_cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
lb_cargs: list[ast.AST*]* = cl.args
lb_can: sizet = 0
if lb_cargs is not None:
lb_can = len(lb_cargs)
@@ -1191,11 +1191,11 @@ class CallGenCall(CallGenBase.CallGenBase):
"TypeError")
# 翻译调用参数
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
can: sizet = 0
if cargs is not None:
can = len(cargs)
kw_calls: rawptr[list[rawptr[ast.AST]]] = cl.keywords
kw_calls: list[ast.AST*]* = cl.keywords
kwc: sizet = 0
if kw_calls is not None:
kwc = len(kw_calls)
@@ -1689,7 +1689,7 @@ class CallGenCall(CallGenBase.CallGenBase):
cl: rawptr[ast.Call],
lm_name: str,
lm_obj: rawptr[llvmlite.Value],
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
trans: rawptr[HT.Translator]) -> rawptr[llvmlite.Value]:
"""翻译 list 内置方法调用,返回 call 结果 Value(void 方法返回 None)"""
@@ -2033,7 +2033,7 @@ class CallGenCall(CallGenBase.CallGenBase):
if mangled_sp is not None:
call_name = mangled_sp
# 5. 翻译参数 + 按形参 coerce(self 是形参 0,第 ai 个实参对应形参 ai+1)
cargs_sp: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs_sp: list[ast.AST*]* = cl.args
can_sp: sizet = 0
if cargs_sp is not None:
can_sp = len(cargs_sp)
+17 -16
View File
@@ -111,7 +111,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
mod: rawptr[llvmlite.LLVMModule],
struct_ty: rawptr[llvmlite.LLVMType],
class_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
trans: rawptr[HT.Translator]) -> rawptr[llvmlite.Value]:
"""结构体构造函数:按字段顺序 store 参数值,返回结构体值
@@ -361,7 +361,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
class_name: str,
variant_name: str,
tag_value: int,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
trans: rawptr[HT.Translator]) -> rawptr[llvmlite.Value]:
"""构造 REnum 变体实例,返回指向 alloca 的指针
@@ -462,9 +462,9 @@ class CallGenCtor(CallGenBase.CallGenBase):
mod: rawptr[llvmlite.LLVMModule],
struct_ty: rawptr[llvmlite.LLVMType],
class_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
keywords: rawptr[list[rawptr[ast.AST]]],
keywords: list[ast.AST*]*,
kw_count: sizet,
trans: rawptr[HT.Translator]) -> rawptr[llvmlite.Value]:
"""结构体构造函数(关键字参数版本)"""
@@ -791,7 +791,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
tn: str = None
if k == ast.ASTKind.Tuple:
tp: rawptr[ast.Tuple] = rawptr[ast.Tuple](slice_node)
elts: rawptr[list[rawptr[ast.AST]]] = tp.elts
elts: list[ast.AST*]* = tp.elts
if elts is not None:
n: sizet = len(elts)
i: sizet
@@ -922,7 +922,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
return self._annotation_refers_type_param(bop_a.right, tp_name)
return 0
def _get_ctor_init_params(self, class_name: str) -> rawptr[list[rawptr[ast.AST]]]:
def _get_ctor_init_params(self, class_name: str) -> list[ast.AST*]*:
"""取泛型类模板 __init__ 的形参列表(含 self,形参 0)
模板不存在 / 无 __init__ / args 形态不可用 → None。
@@ -937,7 +937,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
template_cd: rawptr[ast.ClassDef] = self.Gen.ClassGenGeneric._find_generic_template(class_name)
if template_cd is None or template_cd.children is None:
return None
children: rawptr[list[rawptr[ast.AST]]] = template_cd.children
children: list[ast.AST*]* = template_cd.children
cn: sizet = len(children)
ci: sizet
for ci in range(cn):
@@ -967,7 +967,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
"""
if class_name is None or tp_name is None:
return -1
plist: rawptr[list[rawptr[ast.AST]]] = self._get_ctor_init_params(class_name)
plist: list[ast.AST*]* = self._get_ctor_init_params(class_name)
if plist is None:
return -1
pn: sizet = len(plist)
@@ -1011,7 +1011,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
"""
if class_name is None or kw_name is None:
return -1
plist_sb: rawptr[list[rawptr[ast.AST]]] = self._get_ctor_init_params(class_name)
plist_sb: list[ast.AST*]* = self._get_ctor_init_params(class_name)
if plist_sb is None:
return -1
pn_sb: sizet = len(plist_sb)
@@ -1029,7 +1029,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
def _find_ctor_type_param_kw_arg_index(self, class_name: str,
tp_name: str,
keywords: rawptr[list[rawptr[ast.AST]]],
keywords: list[ast.AST*]*,
kw_count: sizet,
pos_count: int) -> int:
"""仅关键字实参的泛型构造:类型形参 tp_name 对应的关键字实参下标
@@ -1051,7 +1051,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
return -1 # 未找到,或落在 self 位(0 不是实参)
if pi_kw - 1 < pos_count:
return -1 # 该形参位已被位置实参占用
plist_kw: rawptr[list[rawptr[ast.AST]]] = self._get_ctor_init_params(class_name)
plist_kw: list[ast.AST*]* = self._get_ctor_init_params(class_name)
if plist_kw is None:
return -1
pa_kw: rawptr[ast.AST] = plist_kw.get(sizet(pi_kw))
@@ -1079,10 +1079,11 @@ class CallGenCtor(CallGenBase.CallGenBase):
场景:`s: Stack[int] = Stack()`(__init__ 无参 ⇒ 实参推断不到 T)、
`def f() -> Stack[int]: return Stack()`。注解里已显式写出 T,必须按它
特化——这不是"取不到就默认 int"的兜底,而是用户显式写下的类型实参。
来源(两者都要求本次 Call 是注解语句的**直接值**,identity 判定先例:
CallGenCBuiltin 的 CurAssignNode 判定 L122-125):
来源(两者都要求本次 Call 是注解语句的**直接值**,判据即下方 ① 的
identity 比较):
① trans.CurAssignAnnot:AnnAssign 赋值目标注解(AssignGenMixin 在
翻译 RHS 前写入,L1760)+ trans.CurAssignNode 为本次 Call;
翻译 RHS 前写入,四处 set:主分支 / global / mod_glob / nonlocal)
+ trans.CurAssignNode 为本次 Call;
② trans.CurFuncRetAnnot:当前函数返回注解(FuncGenDef 进入函数体时
写入)+ trans.CurRetNode 为本次 Call。
注解 → 类型实参的解析复用 _extract_type_args_from_slice(与
@@ -1162,11 +1163,11 @@ class CallGenCtor(CallGenBase.CallGenBase):
return None
# 翻译实参为参数指针数组(extra_args[slot] = Value 指针):位置实参 → 槽 0..can-1;关键字实参 → 按 __init__ 形参名定位槽位(与 _find_ctor_type_param_kw_arg_index 共用同一套形参遍历)。位置实参优先,关键字不可按出现顺序追加——乱序会串位
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
can: sizet = 0
if cargs is not None:
can = len(cargs)
keywords: rawptr[list[rawptr[ast.AST]]] = cl.keywords
keywords: list[ast.AST*]* = cl.keywords
kw_count: sizet = 0
if keywords is not None:
kw_count = len(keywords)
+52 -8
View File
@@ -41,7 +41,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
func_count: int,
trans: rawptr[HT.Translator],
func_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
hit_box: t.CPtr = None) -> rawptr[llvmlite.Value]:
"""super()/ctraits 编译期检测(未命中返回 None)
@@ -77,7 +77,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
ct_nm: rawptr[ast.Name] = rawptr[ast.Name](ct_at.value)
if ct_nm.id is not None and string.strcmp(ct_nm.id, "ctraits") == 0 \
and string.strcmp(func_name, "isptr") == 0:
ct_args: rawptr[list[rawptr[ast.AST]]] = cl.args
ct_args: list[ast.AST*]* = cl.args
if ct_args is not None and len(ct_args) >= 1:
ct_arg0: rawptr[ast.AST] = ct_args.get(0)
if ct_arg0 is not None and ct_arg0.kind() == ast.ASTKind.Name:
@@ -97,7 +97,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
func_count: int,
trans: rawptr[HT.Translator],
func_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
hit_box: t.CPtr = None) -> rawptr[llvmlite.Value]:
"""c.Deref/Asm/LLVMIR 等特殊调用(未命中返回 None)
@@ -146,7 +146,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
elif string.strcmp(func_name, "CIf") == 0:
# 运行时 c.CIf(expr)(编译期求值失败回退):expr 与 0 比较得到 i1
# 对齐 TPC HandlesExprCall.py L2434-2444
cif_args: rawptr[list[rawptr[ast.AST]]] = cl.args
cif_args: list[ast.AST*]* = cl.args
if cif_args is not None and len(cif_args) >= 1:
cif_arg_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
builder, pool, mod, cif_args.get(0), funcs_ptr, func_count, trans)
@@ -201,7 +201,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
func_count: int,
trans: rawptr[HT.Translator],
func_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet) -> rawptr[llvmlite.Value]:
"""stdarg 糖 + str/int/bool 内置强转(未命中返回 None)"""
# stdarg 糖函数处理: va_arg / arg / va_start / va_end
@@ -290,7 +290,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
func_count: int,
trans: rawptr[HT.Translator],
func_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet) -> rawptr[llvmlite.Value]:
"""t.XXX / typedef 类型转换(未命中返回 None)"""
# 检测 t.XXX 类型转换: uint64(ptr), t.CPtr(val), int(val) 等
@@ -558,7 +558,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
func_count: int,
trans: rawptr[HT.Translator],
func_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet) -> rawptr[llvmlite.Value]:
"""__sizeof__/len/isinstance 内置(未命中返回 None)"""
# 检测 ClassName.__sizeof__() — 返回结构体大小常量(i64)
@@ -1010,7 +1010,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
func_count: int,
trans: rawptr[HT.Translator],
func_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
hit_box: t.CPtr = None) -> rawptr[llvmlite.Value]:
"""静态类/方法调用/表达式接收者/self.field/list 内置(未命中返回 None)
@@ -1151,6 +1151,50 @@ class CallGenDispatch(CallGenBase.CallGenBase):
if st_res2 is not None:
return st_res2
# [M4·REPL 专用分支] 会话变量接收者的方法调用:`obj.m(args)` 的 obj 是**会话
# 变量**时,其值在 ctx 槽里、不在 SymTab ⇒ 上面的 lookup_var 分支恒不命中
# (本次之前:整个分派都靠 SymTab,跨片段的 `p.get()` 直接落空)。此处按
# 会话表判定:命中即取接收者(PTR 条目取槽内指针值;STRUCT 条目取**槽地址**
# = 对象自身地址,正是 self 需要的形态),再用接收者类型的类名走同一条
# `_translate_method_call`(符号 <sha1>.<类>.<方法>、返回类型经 text.ll/vpi)。
# 守卫 trans.ReplCtx is not None ⇒ 非 REPL 编译路径一行不经过(逐字不变)。
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute \
and trans is not None and trans.ReplCtx is not None and pool is not None:
rp_at: rawptr[ast.Attribute] = rawptr[ast.Attribute](cl.func)
if rp_at is not None and rp_at.value is not None and rp_at.attr is not None:
if rp_at.value.kind() == ast.ASTKind.Name:
rp_nm: rawptr[ast.Name] = rawptr[ast.Name](rp_at.value)
if rp_nm.id is not None:
rp_ent: rawptr[GenTypes.ReplVarEntry] = \
self.Gen.VarGen.repl_find_entry(trans.ReplCtx, rp_nm.id)
if rp_ent is not None:
rp_val: rawptr[llvmlite.Value] = None
if rp_ent.TyTag == GenTypes.REPL_TY_STRUCT:
# 结构体值内联在槽里:槽地址即对象地址(self)
rp_val = self.Gen.VarGen.repl_try_addr(trans, rp_nm.id, cl)
else:
rp_val = self.Gen.VarGen.repl_try_read(trans, rp_nm.id, cl)
rp_cls: str = None
if rp_val is not None and rp_val.Ty is not None:
rp_sty: rawptr[llvmlite.LLVMType] = \
self.Gen.StructGen.get_struct_type_from_value(rp_val)
if rp_sty is not None:
rp_cls = self.Gen.StructGen.get_class_name_by_type(
pool, rp_sty)
if rp_cls is not None:
rp_obj: rawptr[llvmlite.Value] = rp_val
if self.Gen.ExprGenBase.is_ptr_type(rp_val.Ty) != 0 \
and rp_val.Ty.Pointee is not None \
and self.Gen.ExprGenBase.is_ptr_type(rp_val.Ty.Pointee) != 0:
rp_obj = llvmlite.build_load(builder, rp_val.Ty, rp_val)
if rp_obj is not None:
if hit_box is not None:
hit_box[0] = 1
return self.Gen.CallGenCall._translate_method_call(
pool, builder, mod, rp_cls, rp_at.attr, rp_obj, cl, trans)
# 会话变量命中但接收者不是结构体类(如 int/str/其它指针):
# 不拦截、不置 hit,交给下方既有分支(list 内置/FakeDuck 等)
# 检测方法调用: obj.method(args)
# 当 cl.func 是 Attribute 且 obj 是已注册的结构体变量时,翻译为方法调用
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
+1 -1
View File
@@ -132,7 +132,7 @@ class CallGenFd(CallGenBase.CallGenBase):
llvmlite.value_set_next(fd_self, None)
fd_head: rawptr[llvmlite.Value] = fd_self
fd_tail: rawptr[llvmlite.Value] = fd_self
fd_args: rawptr[list[rawptr[ast.AST]]] = cl.args
fd_args: list[ast.AST*]* = cl.args
fd_can: int = 0
if fd_args is not None:
fd_can = len(fd_args)
+100 -39
View File
@@ -14,6 +14,7 @@ import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Type.TypeSpec as TSpec
from . import CallGenCBuiltin
from . import CallGenBase
# ============================================================
@@ -28,10 +29,12 @@ from . import CallGenCBuiltin
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# 常量(与 TPC 侧 HandlesExprCall 保持一致)
#
# GLOBAL_FUNC_MAX / GLOBAL_DEFAULTS_MAX / MAX_CLOSURE_NAMES /
# MAX_FUNC_PTR_SIGS / ISINSTANCE_CLASS_MAX:唯一来源在 CallGenBase.vp
# (本文件是唯一消费方,经 CallGenBase.<NAME> 取值,本地不再保留副本)。
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
@@ -40,17 +43,13 @@ ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
# 全局函数表名索引(名 → 槽下标,开放寻址 | 线性探测)
#
# 槽数 = 2 * GLOBAL_FUNC_MAX(2 的幂,负载 < 0.5,线性探测必然终止)。
# 槽数 = 2 * CallGenBase.GLOBAL_FUNC_MAX(2 的幂,负载 < 0.5,线性探测必然终止)。
# 值写成字面量 16384:模块级 t.CDefine 初始化式不能引用跨模块常量
# (跨模块 t.CDefine 只能在函数体内读),故与 CallGenBase.GLOBAL_FUNC_MAX 手工保持 2 倍关系。
# 槽存槽下标 + 1(0=空槽)。GlobalFuncs 在 PermPool 定长分配、只追加不重排、
# 从不 reset ⇒ 槽下标稳定;Built == GlobalFuncCount 才可信。
# ============================================================
@@ -402,7 +401,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
arr[0] = sizet(at.attr)
elif k == ast.ASTKind.Tuple:
tp: rawptr[ast.Tuple] = rawptr[ast.Tuple](node)
elts: rawptr[list[rawptr[ast.AST]]] = tp.elts
elts: list[ast.AST*]* = tp.elts
if elts is not None:
n: sizet = len(elts)
for i in range(n):
@@ -431,14 +430,14 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
"""反向 BFS 收集目标类及全部子类(含自身,仅 HasVTable==1),返回 0 结尾数组"""
if class_name is None:
return None
names: sizet | t.CPtr = pool.alloc(8 * (ISINSTANCE_CLASS_MAX + 1))
queue: sizet | t.CPtr = pool.alloc(8 * ISINSTANCE_CLASS_MAX)
visited: sizet | t.CPtr = pool.alloc(8 * ISINSTANCE_CLASS_MAX)
names: sizet | t.CPtr = pool.alloc(8 * (CallGenBase.ISINSTANCE_CLASS_MAX + 1))
queue: sizet | t.CPtr = pool.alloc(8 * CallGenBase.ISINSTANCE_CLASS_MAX)
visited: sizet | t.CPtr = pool.alloc(8 * CallGenBase.ISINSTANCE_CLASS_MAX)
if names is None or queue is None or visited is None:
return None
string.memset(names, 0, 8 * (ISINSTANCE_CLASS_MAX + 1))
string.memset(queue, 0, 8 * ISINSTANCE_CLASS_MAX)
string.memset(visited, 0, 8 * ISINSTANCE_CLASS_MAX)
string.memset(names, 0, 8 * (CallGenBase.ISINSTANCE_CLASS_MAX + 1))
string.memset(queue, 0, 8 * CallGenBase.ISINSTANCE_CLASS_MAX)
string.memset(visited, 0, 8 * CallGenBase.ISINSTANCE_CLASS_MAX)
qcount: int = 1
outcount: int = 0
vcount: int = 0
@@ -459,12 +458,12 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
break
if dup != 0:
continue
if vcount < ISINSTANCE_CLASS_MAX:
if vcount < CallGenBase.ISINSTANCE_CLASS_MAX:
visited[vcount] = sizet(cur)
vcount += 1
centry: rawptr[GenTypes.StructEntry] = self.Gen.StructGen.find_struct(cur)
if centry is not None and centry.HasVTable != 0:
if outcount < ISINSTANCE_CLASS_MAX:
if outcount < CallGenBase.ISINSTANCE_CLASS_MAX:
names[outcount] = sizet(cur)
outcount += 1
# 子类入队:遍历全部已注册 struct,ParentNames 含 cur 者
@@ -483,7 +482,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
if pa != 0:
pn: str = (str | t.CPtr)(pa)
if pn is not None and string.strcmp(pn, cur) == 0:
if qcount < ISINSTANCE_CLASS_MAX:
if qcount < CallGenBase.ISINSTANCE_CLASS_MAX:
queue[qcount] = sizet(e2.Name)
qcount += 1
break
@@ -574,7 +573,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
builder: rawptr[llvmlite.IRBuilder],
mod: rawptr[llvmlite.LLVMModule],
closure_alloca: rawptr[llvmlite.Value],
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
trans: rawptr[HT.Translator] = None) -> rawptr[llvmlite.Value]:
"""通过闭包指针间接调用(支持带参 lambda:i32(i8*, args...))"""
@@ -663,9 +662,9 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
def _ensure_closure_names(self) -> int:
"""确保闭包名表已分配,0 失败"""
if self.ClosureNames is not None: return 1
self.ClosureNames = CompilerFactory.factory.PermPool.alloc(64 * MAX_CLOSURE_NAMES)
self.ClosureNames = CompilerFactory.factory.PermPool.alloc(64 * CallGenBase.MAX_CLOSURE_NAMES)
if self.ClosureNames is None: return 0
string.memset(self.ClosureNames, 0, 64 * MAX_CLOSURE_NAMES)
string.memset(self.ClosureNames, 0, 64 * CallGenBase.MAX_CLOSURE_NAMES)
return 1
@@ -673,7 +672,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
"""注册嵌套函数(闭包变量)名,0 成功"""
if name is None: return 1
if self._ensure_closure_names() == 0: return 1
if self.ClosureCount >= MAX_CLOSURE_NAMES: return 1
if self.ClosureCount >= CallGenBase.MAX_CLOSURE_NAMES: return 1
idx: sizet = sizet(self.ClosureCount) * 64
string.strncpy(self.ClosureNames + idx, name, 63)
self.ClosureCount += 1
@@ -695,22 +694,84 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
"""确保函数指针签名表已分配(stdlib.malloc,懒初始化)"""
if self.FuncptrNames is not None:
return 1
self.FuncptrNames = CompilerFactory.factory.PermPool.alloc(64 * MAX_FUNC_PTR_SIGS)
self.FuncptrRets = CompilerFactory.factory.PermPool.alloc(8 * MAX_FUNC_PTR_SIGS)
self.FuncptrNames = CompilerFactory.factory.PermPool.alloc(64 * CallGenBase.MAX_FUNC_PTR_SIGS)
self.FuncptrRets = CompilerFactory.factory.PermPool.alloc(8 * CallGenBase.MAX_FUNC_PTR_SIGS)
if self.FuncptrNames is None or self.FuncptrRets is None:
return 0
string.memset(self.FuncptrNames, 0, 64 * MAX_FUNC_PTR_SIGS)
string.memset(self.FuncptrNames, 0, 64 * CallGenBase.MAX_FUNC_PTR_SIGS)
return 1
def register_func_ptr_sig(self, name: str,
ret_ty: rawptr[llvmlite.LLVMType]) -> int:
"""注册函数指针变量的返回类型签名,0 成功"""
def _func_ptr_sig_fatal(self, pool: rawptr[memhub.MemBuddy],
node: rawptr[ast.AST], name: str,
ret_ty: rawptr[llvmlite.LLVMType],
reason: str) -> None:
"""函数指针签名登记失败 → 编译期报错(文案含变量名/返回类型/上限/处置建议)
原实现满表时静默 return 1:登记被丢弃后,调用点才以
"函数指针变量 'X' 的返回类型未注册(t.Callable 注解缺失)"
(下方 _translate_func_ptr_call:843-850)报错,定位到调用行而非登记处,
掩盖真因(表满/重名冲突)。此处统一在登记点 fail-fast。
fatal_error 终止编译、不返回,故调用方的返回值分支仅为防御性写法。
"""
cap: int = CallGenBase.MAX_FUNC_PTR_SIGS
ret_str: str = "?"
if ret_ty is not None:
typed_buf: str = pool.alloc(96)
if typed_buf is not None:
llvmlite.TypePrint(typed_buf, 96, ret_ty, pool)
if typed_buf[0] != '\0':
ret_str = typed_buf
msg_buf: str = pool.alloc(1024)
if msg_buf is not None:
viperlib.snprintf(
msg_buf, 1024,
"函数指针签名表登记失败:变量 '%s'(返回类型 %s)——%s。"
"当前上限 MAX_FUNC_PTR_SIGS=%d,已登记 %d 条。"
"同名同返回类型的重复登记已做原地命中、不占新槽位;"
"若确实需要更多函数指针变量,请上调 CallGenBase.vp 的 "
"MAX_FUNC_PTR_SIGS(全仓唯一来源,改此一处即全仓生效)",
name, ret_str, reason, cap, self.FuncptrCount)
self.Gen.TypeGenErr.fatal_error(node, msg_buf, "TypeError")
return
self.Gen.TypeGenErr.fatal_error(
node, "函数指针签名表登记失败(签名表已满或变量名冲突)", "TypeError")
def register_func_ptr_sig(self, pool: rawptr[memhub.MemBuddy], name: str,
ret_ty: rawptr[llvmlite.LLVMType],
node: rawptr[ast.AST] = None) -> int:
"""注册函数指针变量的返回类型签名,0=成功(含幂等命中)/ 1=失败(已报错终止)
表按**裸变量名**全局共享(PermPool,跨文件/跨阶段存活),同一变量可能被
多轮登记(模块级:Translator.vp:568-575 的预注册遍与 translate_children
TranslatorGenMixin.vp:130-136 各见同一 AnnAssign 一次):
* 同名同返回类型 → 原地命中、不新增槽位(否则重复登记把表撑爆);
* 同名不同返回类型 → 报错。既不能静默丢弃(查找返回先登记值:先翻译
模块已按旧值生成间接调用,后登记模块却按新值,同一名字两种 ABI),
也不能静默覆盖(先翻译模块的调用点已按旧值生成)——两者都是无声误编译;
* 满表 → 报错(原实现静默丢弃)。
"""
if name is None or ret_ty is None:
return 1
if self._ensure_funcptr_sigs() == 0:
self._func_ptr_sig_fatal(pool, node, name, ret_ty,
"签名表分配失败(PermPool 内存不足)")
return 1
if self.FuncptrCount >= MAX_FUNC_PTR_SIGS:
# 幂等/冲突检查:线性扫已登记裸名(FuncptrCount 上限 64,代价可忽略)
i: sizet
for i in range(self.FuncptrCount):
idx_hit: sizet = sizet(i) * 64
if string.strcmp(self.FuncptrNames + idx_hit, name) == 0:
prev_ret: rawptr[llvmlite.LLVMType] = (
rawptr[llvmlite.LLVMType])(self.FuncptrRets[i])
if prev_ret is not None \
and self.Gen.AssignGen._stg_ty_equal(pool, prev_ret, ret_ty) != 0:
return 0
self._func_ptr_sig_fatal(pool, node, name, ret_ty,
"该名字已按另一种返回类型登记过")
return 1
if self.FuncptrCount >= CallGenBase.MAX_FUNC_PTR_SIGS:
self._func_ptr_sig_fatal(pool, node, name, ret_ty, "签名表已满")
return 1
idx: sizet = sizet(self.FuncptrCount) * 64
# 槽位定长 64 字节,与 register_closure_name 一致用 strncpy 钳制防越界
@@ -743,7 +804,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
mod: rawptr[llvmlite.LLVMModule],
fp_alloca: rawptr[llvmlite.Value],
func_name: str,
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
trans: rawptr[HT.Translator]) -> rawptr[llvmlite.Value]:
"""函数指针变量间接调用,返回结果 Value"""
@@ -819,7 +880,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
cl: rawptr[ast.Call],
trans: rawptr[HT.Translator] = None) -> None:
"""翻译 printf 调用(特殊处理格式字符串)"""
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None:
return
can: sizet = len(cargs)
@@ -880,7 +941,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
cl: rawptr[ast.Call],
trans: rawptr[HT.Translator] = None) -> None:
"""翻译 print 调用(类型感知格式化 + 默认换行)"""
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None:
return
@@ -1119,7 +1180,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
"""初始化全局函数表(用 stdlib.malloc 分配,生命周期=整个程序)"""
if self.GlobalFuncs is not None:
return 1
size: sizet = GLOBAL_FUNC_MAX * GenTypes.FuncEntry.__sizeof__()
size: sizet = CallGenBase.GLOBAL_FUNC_MAX * GenTypes.FuncEntry.__sizeof__()
self.GlobalFuncs = CompilerFactory.factory.PermPool.alloc(size)
if self.GlobalFuncs is None:
return 0
@@ -1154,7 +1215,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
# 已存在则跳过(避免重复)
if self.find_func_in_table(self.GlobalFuncs, self.GlobalFuncCount, name) is not None:
return 0
if self.GlobalFuncCount >= GLOBAL_FUNC_MAX:
if self.GlobalFuncCount >= CallGenBase.GLOBAL_FUNC_MAX:
return 1
# 直接写入全局表(不调用 add_func_to_table,避免递归)
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
@@ -1281,7 +1342,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
"""初始化全局默认参数表"""
if self.GlobalDefaults is not None:
return 1
size: sizet = GLOBAL_DEFAULTS_MAX * GenTypes.FuncDefaultsEntry.__sizeof__()
size: sizet = CallGenBase.GLOBAL_DEFAULTS_MAX * GenTypes.FuncDefaultsEntry.__sizeof__()
self.GlobalDefaults = CompilerFactory.factory.PermPool.alloc(size)
if self.GlobalDefaults is None:
return 0
@@ -1321,7 +1382,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
# 同模块同名已存在则跳过
if self.find_func_defaults(name, cur_sha) is not None:
return 0
if self.GlobalDefaultsCount >= GLOBAL_DEFAULTS_MAX:
if self.GlobalDefaultsCount >= CallGenBase.GLOBAL_DEFAULTS_MAX:
return 1
entry_size: sizet = GenTypes.FuncDefaultsEntry.__sizeof__()
entry_addr: uint64 = uint64(self.GlobalDefaults) + self.GlobalDefaultsCount * entry_size
@@ -1359,7 +1420,7 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
string.memset(kinds, 0, default_count * 4)
string.memset(ints, 0, default_count * 8)
string.memset(strs, 0, default_count * 8)
dl: rawptr[list[rawptr[ast.AST]]] = rawptr[list[rawptr[ast.AST]]](uint64(defaults))
dl: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(uint64(defaults))
di: int
for di in range(default_count):
dnode: rawptr[ast.AST] = dl.get(di)
+3 -3
View File
@@ -193,7 +193,7 @@ class CallGenLib(CallGenBase.CallGenBase):
call_name: str,
head: rawptr[llvmlite.Value],
count: int,
cargs: rawptr[list[rawptr[ast.AST]]] = None,
cargs: list[ast.AST*]* = None,
self_off: int = 0,
trans: rawptr[HT.Translator] = None) -> rawptr[llvmlite.Value]:
"""把跨模块调用实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链
@@ -738,9 +738,9 @@ class CallGenLib(CallGenBase.CallGenBase):
mod: rawptr[llvmlite.LLVMModule],
func_name: str,
found_func: rawptr[llvmlite.Function],
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
keywords: rawptr[list[rawptr[ast.AST]]],
keywords: list[ast.AST*]*,
kw_count: sizet,
trans: rawptr[HT.Translator]) -> rawptr[llvmlite.Value]:
"""带关键字参数的函数调用,按参数顺序构建调用链表
+74 -16
View File
@@ -31,10 +31,12 @@ import lib.core.Gen.GenApi as GenApi
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# 常量(与 TPC 侧 HandlesExprCall 保持一致)
#
# GLOBAL_FUNC_MAX / GLOBAL_DEFAULTS_MAX / MAX_CLOSURE_NAMES /
# MAX_FUNC_PTR_SIGS / ISINSTANCE_CLASS_MAX:唯一来源在 CallGenBase.vp
# (本文件不使用,故不留副本;消费方 CallGenLeafKit.vp 经 CallGenBase.<NAME> 引用)。
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
@@ -43,12 +45,6 @@ ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
@@ -75,7 +71,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
func_name: str = self.Gen.ExprGenBase.get_func_name(cl.func)
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
can: sizet = 0
if cargs is not None:
can = len(cargs)
@@ -159,7 +155,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
binop_ty: rawptr[llvmlite.LLVMType] = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, cl.func, trans._imported_modules, trans._from_imports, trans, 0)
if binop_ty is not None:
binop_args: rawptr[list[rawptr[ast.AST]]] = cl.args
binop_args: list[ast.AST*]* = cl.args
if binop_args is not None and len(binop_args) == 1:
binop_arg_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
builder, pool, mod, binop_args.get(0), funcs_ptr, func_count, trans)
@@ -223,7 +219,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
return None
# 获取关键字参数
keywords: rawptr[list[rawptr[ast.AST]]] = cl.keywords
keywords: list[ast.AST*]* = cl.keywords
kw_count: sizet = 0
if keywords is not None:
kw_count = len(keywords)
@@ -931,6 +927,17 @@ class CallGenMixin(CallGenBase.CallGenBase):
if trans is not None and trans._from_imports is not None:
if self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0) is not None:
bare_resolvable = 1
# M3 会话函数(跨片段):名字命中会话函数表 ⇒ 同样按"可解析"占位,
# 不 fail-fast。返回类型由下方 found_func==None 分支的
# repl_try_session_func_call 按表内签名(宿主从定义片段 IR 解析)构造,
# 与 _infer_external_func_ret_ty 的白名单无关;此处的 call_ret_ty 只是
# 占位(命中后立即 return,不会用到)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 不经过;且限定裸名(Name)——
# 会话函数只以裸名导出,模块限定调用(mod.f)不走本表。
if trans is not None and trans.ReplCtx is not None \
and cl.func is not None and cl.func.kind() == ast.ASTKind.Name \
and self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name) is not None:
bare_resolvable = 1
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(
pool, func_name, node, 1 - bare_resolvable)
@@ -991,6 +998,31 @@ class CallGenMixin(CallGenBase.CallGenBase):
# 其他 i8* 变量按闭包调用路径处理
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca,
cargs, can, trans)
# ---- M3:会话函数表命中 → ctx 函数指针槽取址 + 间接调用 ----
# 位置(为什么在这里):本步位于 `if found_func is None:` 块**末尾**——
# ① found_func 为 None ⇒ 本片段的 LLVM 模块里没有该函数的定义/前向声明
# (会话函数定义在**先前片段**的 DLL 里,代码不在本模块内);
# ② 放在闭包名/局部函数指针变量分支**之后** ⇒ 局部名与闭包正确地**遮蔽**
# 会话函数(Python 的局部优先),只有"既非本模块函数/声明、又非局部/
# 闭包/函数指针变量"的裸名才查会话函数表;
# ③ 仍先于 from_imports 解析与"裸名调用未定义且未导入"的 fail-fast。
# 命中后从 ctx 的函数指针槽 load 地址、按表内签名间接调用:函数代码只在
# 定义片段里一份(不复制/不重编译),调用点只持指针。
sfn_box: t.CPtr = None
if trans is not None and trans.ReplCtx is not None \
and (cl.func is None or cl.func.kind() == ast.ASTKind.Name):
sfn_box = stdlib.malloc(4)
if sfn_box is not None:
sfn_box[0] = 0
sfn_val: rawptr[llvmlite.Value] = self.Gen.VarGen.repl_try_session_func_call(
trans, pool, builder, mod, func_name, cargs, int(can),
int(kw_count), node, sfn_box)
sfn_handled: int = int(sfn_box[0])
stdlib.free(sfn_box)
if sfn_handled != 0:
# 命中(含命中但按 M3 规则明确报错的情形):不再走任何
# "未定义/未导入"路径。void 会话函数返回 None 属预期。
return sfn_val
# 翻译参数并链接
# 有关键字参数且找到函数定义时,按参数名匹配
@@ -1139,7 +1171,26 @@ class CallGenMixin(CallGenBase.CallGenBase):
# 否则下方 _build_const_from_snapshot 读到未初始化栈垃圾指针崩溃。
# 与 _translate_method_call 的 mdf_gd 无条件查询(L2677)保持一致。
gd_entry: rawptr[GenTypes.FuncDefaultsEntry] = self.Gen.CallGenLeafKit.find_func_defaults(func_name, dfx_callee_sha)
if df_defaults is None and gd_entry is not None:
# 包重导出补查(如 json.write 实际定义在 includes/json/__writer.vp):
# AST 侧模块名 'json' 只能解析到包 __init__ 的 SHA1,而默认参数表按
# **定义该函数的模块 SHA1** 登记(_register_global_defaults),于是直接
# 查不到 → 缺参填充整体跳过 → call 实参数 < declare/define 形参数 →
# 被调函数读寄存器/栈残留垃圾(VET `json.write(pool, val, False)` 的
# 缺省 cap 读成指针量级 → pool.alloc(巨量) → [OOM] 根因)。
# 与下方 L1473-1480 调用名重定向同一套 re-export 解析:命中后用
# **定义模块 SHA1** 复查默认参数表(SHA1 已校验,不会跨模块串味)。
if gd_entry is None and dfx_callee_sha is not None:
rr_src_dx: str = self.Gen.ImportsGen.lookup_reexport(dfx_callee_sha, func_name)
if rr_src_dx is None:
rr_src_dx = self._lookup_reexport_from_vpi(pool, dfx_callee_sha, func_name)
if rr_src_dx is not None:
rr_sha_dx: str = self._lookup_module_sha1(rr_src_dx)
if rr_sha_dx is not None:
gd_entry = self.Gen.CallGenLeafKit.find_func_defaults(func_name, rr_sha_dx)
# 覆盖条件含 df_default_count<=0:调用方函数表可能命中一条同名条目但
# Defaults 为空/默认值为 0 条(VpiReader 注入的 declare 条目),
# 此时必须用全局默认参数表的真实条目覆盖,否则缺参填充整体不触发。
if gd_entry is not None and (df_defaults is None or df_default_count <= 0):
df_defaults = gd_entry.Defaults
df_default_count = gd_entry.DefaultCount
df_param_count = gd_entry.ParamCount
@@ -1162,7 +1213,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
df_default_count = 0
df_param_count = 0
# 填充缺失参数
if df_defaults is not None and df_default_count > 0 and can < df_param_count:
if df_default_count > 0 and can < df_param_count and (df_defaults is not None or gd_entry is not None):
df_offset: int = df_param_count - df_default_count
for df_di in range(df_default_count):
df_param_idx: int = df_offset + df_di
@@ -1172,8 +1223,8 @@ class CallGenMixin(CallGenBase.CallGenBase):
if gd_entry is not None:
df_arg_val = self.Gen.CallGenLeafKit._build_const_from_snapshot(
pool, mod, gd_entry, df_di, trans, builder)
if df_arg_val is None:
df_defaults_list: rawptr[list[rawptr[ast.AST]]] = rawptr[list[rawptr[ast.AST]]](uint64(df_defaults))
if df_arg_val is None and df_defaults is not None:
df_defaults_list: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(uint64(df_defaults))
df_default_node: rawptr[ast.AST] = df_defaults_list.get(df_di)
if df_default_node is not None:
df_arg_val = self.Gen.ExprGen.translate_value(
@@ -1185,6 +1236,12 @@ class CallGenMixin(CallGenBase.CallGenBase):
else:
tail.Next = df_arg_val
tail = df_arg_val
# 追加的默认实参必须计入实参数:下方
# _coerce_args_to_def_params/_ensure_method_declare 等
# 均以 can 为界重建/校验实参链,count 不增会把补齐的
# 尾随默认实参整体截断丢弃(VET json.write 缺省 cap
# 丢失 → 被调方读寄存器垃圾 → pool.alloc 巨量 OOM 根因)。
can += 1
elif found_func is not None:
# 默认参数缺失/表信息不全时的零值兜底:函数表有参数数但无默认值记录、或调用方先于定义文件编译导致查不到默认值时,用 found_func 实际参数数(stub declare,与 define 一致)兜底并缺参填零值(对齐 TPC _fill_default_args 语义)。注意排除 EH 输出参数(%__eh_msg_out__/%__eh_code_out__),否则会与 _emit_eh_guarded_call 追加的真实 EH 实参重复导致 ABI 错位
fc_cnt: int = 0
@@ -1213,6 +1270,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
else:
tail.Next = zero_val_z
tail = zero_val_z
can += 1 # 零值兜底实参同样计入实参数(同默认实参)
fp_z = llvmlite.param_get_next(fp_z)
fpi_z += 1
+1 -1
View File
@@ -353,7 +353,7 @@ class CallGenProp(CallGenBase.CallGenBase):
total_count = 1
# 翻译调用参数
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
can: sizet = 0
if cargs is not None:
can = len(cargs)
+5 -5
View File
@@ -78,7 +78,7 @@ class CallGenVa(CallGenBase.CallGenBase):
builder: rawptr[llvmlite.IRBuilder],
mod: rawptr[llvmlite.LLVMModule],
cl: rawptr[ast.Call],
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
@@ -126,7 +126,7 @@ class CallGenVa(CallGenBase.CallGenBase):
builder: rawptr[llvmlite.IRBuilder],
mod: rawptr[llvmlite.LLVMModule],
cl: rawptr[ast.Call],
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
@@ -161,7 +161,7 @@ class CallGenVa(CallGenBase.CallGenBase):
def _translate_va_start_sugar(self, pool: rawptr[memhub.MemBuddy],
builder: rawptr[llvmlite.IRBuilder],
mod: rawptr[llvmlite.LLVMModule],
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
@@ -193,7 +193,7 @@ class CallGenVa(CallGenBase.CallGenBase):
def _translate_va_end_sugar(self, pool: rawptr[memhub.MemBuddy],
builder: rawptr[llvmlite.IRBuilder],
mod: rawptr[llvmlite.LLVMModule],
cargs: rawptr[list[rawptr[ast.AST]]],
cargs: list[ast.AST*]*,
can: sizet,
funcs_ptr: t.CPtr,
func_count: int,
@@ -282,7 +282,7 @@ class CallGenVa(CallGenBase.CallGenBase):
call_name: str,
head: rawptr[llvmlite.Value],
can: sizet,
arg_nodes: rawptr[list[rawptr[ast.AST]]] = None,
arg_nodes: list[ast.AST*]* = None,
self_offset: int = 0,
trans: rawptr[HT.Translator] = None) -> int:
"""检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升
+14 -14
View File
@@ -192,7 +192,7 @@ class ClassGenBase:
return 1
if cd.bases is None:
return 0
bases: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases: list[ast.AST*]* = cd.bases
bn: sizet = len(bases)
if bn == 0:
return 0
@@ -245,7 +245,7 @@ class ClassGenBase:
return 1
if cd.bases is None:
return 0
bases: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases: list[ast.AST*]* = cd.bases
bn: sizet = len(bases)
if bn == 0:
return 0
@@ -298,7 +298,7 @@ class ClassGenBase:
return 1
if cd.bases is None:
return 0
bases: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases: list[ast.AST*]* = cd.bases
bn: sizet = len(bases)
if bn == 0:
return 0
@@ -309,7 +309,7 @@ class ClassGenBase:
if self._is_crenum_base(base_node, trans) == 1:
return 1
return 0
def _has_decorator(self, decorator_list: rawptr[list[rawptr[ast.AST]]],
def _has_decorator(self, decorator_list: list[ast.AST*]*,
deco_name: str) -> int:
"""检查 decorator_list 中是否有指定装饰器名"""
if decorator_list is None:
@@ -356,7 +356,7 @@ class ClassGenBase:
return 0
return 1 if string.strcmp(at2.attr, attr_name) == 0 else 0
return 0
def _class_has_tattr(self, decorator_list: rawptr[list[rawptr[ast.AST]]],
def _class_has_tattr(self, decorator_list: list[ast.AST*]*,
attr_name: str) -> int:
"""检查装饰器列表是否含 t.attr.<attr_name>(@c.Attribute(t.attr.xxx) 或 @t.attr.xxx)。
@@ -413,7 +413,7 @@ class ClassGenBase:
return 0
return int(cn.int_val)
return 0
def _class_aligned_value(self, decorator_list: rawptr[list[rawptr[ast.AST]]]) -> int:
def _class_aligned_value(self, decorator_list: list[ast.AST*]*) -> int:
"""取 @c.Attribute(t.attr.aligned(N)) 的对齐字节数,无 aligned 返回 0"""
if decorator_list is None:
return 0
@@ -445,7 +445,7 @@ class ClassGenBase:
if av2 > 0:
return av2
return 0
def _extract_func_meta(self, decorator_list: rawptr[list[rawptr[ast.AST]]]) -> int:
def _extract_func_meta(self, decorator_list: list[ast.AST*]*) -> int:
"""从装饰器列表提取元数据位掩码,返回 0=无装饰器元数据"""
meta: int = 0
if decorator_list is None:
@@ -734,7 +734,7 @@ class ClassGenBase:
"""类定义是否是异常类:基类是 Exception 或已注册异常类(1=是)"""
if cd is None or cd.bases is None:
return 0
bases_e: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases_e: list[ast.AST*]* = cd.bases
bn_e: sizet = len(bases_e)
for bi_e in range(bn_e):
base_e: rawptr[ast.AST] = bases_e.get(bi_e)
@@ -758,7 +758,7 @@ class ClassGenBase:
"""返回异常类定义的真实父类名(第一个非 Exception 的已注册异常类),无则 None"""
if cd is None or cd.bases is None:
return None
bases_e2: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases_e2: list[ast.AST*]* = cd.bases
bn_e2: sizet = len(bases_e2)
for bi_e2 in range(bn_e2):
base_e2: rawptr[ast.AST] = bases_e2.get(bi_e2)
@@ -795,7 +795,7 @@ class ClassGenBase:
lst_rcm: rawptr[ast.List] = rawptr[ast.List](value_node)
if lst_rcm.elts is None:
return
elts_rcm: rawptr[list[rawptr[ast.AST]]] = lst_rcm.elts
elts_rcm: list[ast.AST*]* = lst_rcm.elts
en_rcm: sizet = len(elts_rcm)
for ei_rcm in range(en_rcm):
el_rcm: rawptr[ast.AST] = elts_rcm.get(ei_rcm)
@@ -960,7 +960,7 @@ class ClassGenBase:
pool: rawptr[memhub.MemBuddy] = None
if trans is not None:
pool = trans.Pool
bases: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases: list[ast.AST*]* = cd.bases
bn: sizet = len(bases)
if bn == 0:
return None
@@ -1012,7 +1012,7 @@ class ClassGenBase:
"""收集所有非 marker 父类名到 parents_buf,返回数量"""
if pool is None or cd is None or cd.bases is None or parents_buf is None:
return 0
bases: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases: list[ast.AST*]* = cd.bases
bn: sizet = len(bases)
count: int = 0
for bi in range(bn):
@@ -1059,7 +1059,7 @@ class ClassGenBase:
if trans is None or cd is None or cd.bases is None:
return
pool: rawptr[memhub.MemBuddy] = trans.Pool
bases: rawptr[list[rawptr[ast.AST]]] = cd.bases
bases: list[ast.AST*]* = cd.bases
bn: sizet = len(bases)
if bn == 0:
return
@@ -1099,7 +1099,7 @@ class ClassGenBase:
"""检查类中是否有任何函数标记了 @t.CVTable,返回 1=有 / 0=无"""
if cd is None or cd.children is None:
return 0
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
cn: sizet = len(children)
for ci in range(cn):
stmt: rawptr[ast.AST] = children.get(ci)
+47 -3
View File
@@ -200,14 +200,24 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
field_types_buf: sizet | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
field_defaults_buf: sizet | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
field_annot_buf: sizet | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段 volatile 标记缓冲(1=字段注解联合含 t.CVolatile);与 field_annot_buf
# 并行,取值 0/1(sizet 数组复用,避免另设类型)。
field_volatile_buf: sizet | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
# 字段「所指物 volatile」标记缓冲(1=字段注解为 rawptr[t.CVolatile | T],
# 等价 C 的 volatile T*);与 field_volatile_buf 并行,取值 0/1。
field_pointee_volatile_buf: sizet | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX)
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None:
return 0
if field_annot_buf is None:
if field_annot_buf is None or field_volatile_buf is None:
return 0
if field_pointee_volatile_buf is None:
return 0
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_annot_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
string.memset(field_pointee_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
field_count: int = 0
@@ -273,9 +283,13 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
field_annot_buf[field_count] = stored_val
else:
field_annot_buf[field_count] = 0
# 继承父类字段的 volatile 标记(字段展平后语义不变)
field_volatile_buf[field_count] = sizet(pfe.IsVolatile)
# 继承父类字段的「所指物 volatile」标记(展平后语义不变)
field_pointee_volatile_buf[field_count] = sizet(pfe.PointeeVolatile)
field_count += 1
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
return 0
@@ -412,8 +426,25 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
field_annot_buf[field_count] = diag_cn_val
else:
field_annot_buf[field_count] = 0
# t.CVolatile 存储类标记 → 该字段经对象访问的 load/store 带
# volatile(如 Spinlock.locked: t.CVolatile | t.CInt)。
if self.Gen.TypeGenErr.has_decorator_marker(
aa.annotation, "CVolatile", 0) != 0:
field_volatile_buf[field_count] = 1
else:
field_volatile_buf[field_count] = 0
# rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*)→ 经该字段解引用(self.ptr[i]/self.ptr.field)
# 的 load/store 带 volatile。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(
aa.annotation) != 0:
field_pointee_volatile_buf[field_count] = 1
else:
field_pointee_volatile_buf[field_count] = 0
else:
field_annot_buf[field_count] = 0
field_volatile_buf[field_count] = 0
field_pointee_volatile_buf[field_count] = 0
field_count += 1
if field_count == 0:
@@ -590,7 +621,20 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
if fannot_addr != 0:
fannot = str(fannot_addr)
if fname is not None and fty2 is not None:
self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
fidx_add: int = self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
# t.CVolatile 字段标记(field_volatile_buf[fi] 与 add_field 顺序一一对应)
if fidx_add >= 0 and field_volatile_buf[fi] != 0:
fe_vol: rawptr[GenTypes.FieldEntry] = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_vol is not None:
fe_vol.IsVolatile = 1
# rawptr[t.CVolatile | T] 字段标记(field_pointee_volatile_buf[fi] 与
# add_field 顺序一一对应):经该字段解引用时 load/store 带 volatile。
if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0:
fe_pv: rawptr[GenTypes.FieldEntry] = \
self.Gen.StructGen._get_field_entry(entry, fidx_add)
if fe_pv is not None:
fe_pv.PointeeVolatile = 1
# Phase 1a 声明模式:只注册 struct + 设置 OOP 标志,不翻译方法体
if trans._declare_only == 1:
+11 -11
View File
@@ -159,7 +159,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
# 成员缺失(FR_OK 找不到)。register_enum 幂等返回已有 entry,
# add_enum_member 成员名去重,重复处理安全合并。
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
VLogger.warning(f"{class_name} has no body", "ENUM")
return 0
@@ -226,7 +226,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
ag: rawptr[ast.Assign] = rawptr[ast.Assign](stmt)
if ag is None or ag.targets is None:
continue
tgts: rawptr[list[rawptr[ast.AST]]] = ag.targets
tgts: list[ast.AST*]* = ag.targets
tn: sizet = len(tgts)
if tn == 0:
continue
@@ -412,7 +412,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
# SHA1 已在 register_struct 时设置,无需再补
return 0
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
VLogger.warning("{class_name} has no body", "UNION")
return 0
@@ -646,7 +646,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
existing.Ty = None
existing.FieldCount = 0
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
# [D32 fail-fast] 此前只 warning + return 0:变体表**静默不注册** →
# _is_renum_class_name 恒 0、变体构造拿不到 tag(__types.vp L703
@@ -680,7 +680,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
if variant_cd is None or variant_cd.children is None:
variant_count += 1
continue
variant_children: rawptr[list[rawptr[ast.AST]]] = variant_cd.children
variant_children: list[ast.AST*]* = variant_cd.children
vcn: sizet = len(variant_children)
field_idx: int = 0
for vi in range(vcn):
@@ -732,7 +732,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
ag_v: rawptr[ast.Assign] = rawptr[ast.Assign](stmt)
if ag_v is None or ag_v.targets is None:
continue
tgts_v: rawptr[list[rawptr[ast.AST]]] = ag_v.targets
tgts_v: list[ast.AST*]* = ag_v.targets
if len(tgts_v) == 0:
continue
t0_v: rawptr[ast.AST] = tgts_v.get(0)
@@ -886,7 +886,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
"""
if trans is None or pool is None or class_name is None or cd is None:
return
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
return
cn: sizet = len(children)
@@ -898,7 +898,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
if variant_cd is None or variant_cd.name is None \
or variant_cd.children is None:
continue
vchildren: rawptr[list[rawptr[ast.AST]]] = variant_cd.children
vchildren: list[ast.AST*]* = variant_cd.children
vcn: sizet = len(vchildren)
field_idx: int = 0
for vi in range(vcn):
@@ -938,7 +938,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
return
if variant_count == 0:
return
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
return
@@ -1011,7 +1011,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
ag_p: rawptr[ast.Assign] = rawptr[ast.Assign](stmt_p)
if ag_p is None or ag_p.targets is None or ag_p.value is None:
continue
tgts_p: rawptr[list[rawptr[ast.AST]]] = ag_p.targets
tgts_p: list[ast.AST*]* = ag_p.targets
if len(tgts_p) == 0:
continue
t0_p: rawptr[ast.AST] = tgts_p.get(0)
@@ -1054,7 +1054,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
ag_rv: rawptr[ast.Assign] = rawptr[ast.Assign](stmt)
if ag_rv is None or ag_rv.targets is None:
continue
tgts_rv: rawptr[list[rawptr[ast.AST]]] = ag_rv.targets
tgts_rv: list[ast.AST*]* = ag_rv.targets
if len(tgts_rv) == 0:
continue
t0_rv: rawptr[ast.AST] = tgts_rv.get(0)
+60 -15
View File
@@ -52,17 +52,61 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
if tp is None:
return 0
return 1
def _dup_perm_str(self, s: str) -> str:
"""深拷贝字符串到永久池(无永久池/分配失败时原样返回入参)"""
if s is None:
return None
perm: rawptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
if perm is None:
return s
n: sizet = string.strlen(s)
b: str = perm.alloc(n + 1)
if b is None:
return s
string.strcpy(b, s)
return b
def _register_generic_template(self, pool: rawptr[memhub.MemBuddy],
cd: rawptr[ast.ClassDef],
module_sha1: str) -> None:
"""注册泛型类模板(类名 + ClassDef 节点 + 来源模块 SHA1)"""
"""注册泛型类模板(类名 + ClassDef 节点 + 来源模块 SHA1)
[池归属修复] 这四张表 + GenericClassTpParams 的语义都是**永久表**:
批量编译下要在 Phase1 的 reset_to(mark1) 与 Phase2 的 reset_to(mark2) 之后
继续可读(读点见 _find_generic_template/_find_generic_template_sha1/
_find_generic_template_file/_find_generic_template_tp_params,均有"遍历
悬空内存 strcmp 即崩溃"的记录),REPL 下还要跨片段存活。但注册点有三个:
- Phase1Scan.vp:322 → perm(永久池);
- Phase2/__init__.vp:321 → perm(永久池);
- ClassGenDef.vp:78 → trans.Pool(**会被 reset_to 回收的池**)。
于是"表在哪个池"取决于哪条路径先注册到表:批量编译里两遍预注册
(_PreRegisterAllGenericTemplates / _PreRegisterAppGenericTemplates)在前,
表被建在永久池 ⇒ 第三条路径只是追加;而两遍预注册都没建成表时(例如
includes 的泛型 marker 文本预筛未命中),第三条路径把表建在 trans.Pool 上,
随 mark 回收 ⇒ 后续 _find_generic_template 遍历悬空表(UAF)。REPL 从不跑
Phase1/Phase2,第三条路径是**唯一**注册点 ⇒ 表必然落在会被每片段 reset 的
池上,M4 一旦在片段里定义泛型类就是必现 UAF。
故此处统一:表体一律用永久池(无永久池时退回入参池,即旧行为),并把
调用方池里的三个串(类名/SHA1/来源文件)深拷贝进永久池——它们同样会随
调用方池回收而悬空(挂名 strcmp 即读悬空内存)。
批量编译不变量:变更只影响"表与串分配在哪个池",写入的**内容**逐字不变
(同样的类名/节点/ SHA1/文件路径,同样的插入顺序与查重),发射的 IR 与
符号名不受池选择影响 ⇒ 逐字不变。
"""
if cd is None or cd.name is None:
return
perm: rawptr[memhub.MemBuddy] = CompilerFactory.factory.PermPool
table_pool: rawptr[memhub.MemBuddy] = perm
if table_pool is None:
table_pool = pool
if self.GenericClassNames is None:
self.GenericClassNames = list[str](pool, 8)
self.GenericClassNodes = list[rawptr[ast.ClassDef]](pool, 8)
self.GenericClassSha1s = list[str](pool, 8)
self.GenericClassFiles = list[str](pool, 8)
self.GenericClassNames = list[str](table_pool, 8)
self.GenericClassNodes = list[rawptr[ast.ClassDef]](table_pool, 8)
self.GenericClassSha1s = list[str](table_pool, 8)
self.GenericClassFiles = list[str](table_pool, 8)
if self.GenericClassTpParams is None:
self.GenericClassTpParams = CompilerFactory.factory.PermPool.alloc(GENERIC_TEMPLATE_MAX * 8)
if perm is None:
return
self.GenericClassTpParams = perm.alloc(GENERIC_TEMPLATE_MAX * 8)
if self.GenericClassTpParams is None:
return
string.memset(self.GenericClassTpParams, 0, GENERIC_TEMPLATE_MAX * 8)
@@ -76,11 +120,12 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
return
# type_params stdlib 深拷贝:模板节点的 type_params 是 pool 分配的
tp_names_buf: str = self._copy_type_params_stdlib(cd.type_params)
self.GenericClassNames.append(cd.name)
self.GenericClassNames.append(self._dup_perm_str(cd.name))
self.GenericClassNodes.append(cd)
self.GenericClassSha1s.append(module_sha1)
self.GenericClassSha1s.append(self._dup_perm_str(module_sha1))
# 同时保存模板来源文件路径(错误定位用,见字段注释)
self.GenericClassFiles.append(self.Gen.TypeGenResolve.get_current_file())
self.GenericClassFiles.append(
self._dup_perm_str(self.Gen.TypeGenResolve.get_current_file()))
if tp_names_buf is not None and cn < GENERIC_TEMPLATE_MAX:
self.GenericClassTpParams[cn] = sizet(tp_names_buf)
def _copy_type_params_stdlib(self, tp: list[str] | t.CPtr) -> str:
@@ -298,9 +343,9 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# Tuple: (T, int) 等
if k == ast.ASTKind.Tuple:
tup: rawptr[ast.Tuple] = rawptr[ast.Tuple](node)
new_elts: rawptr[list[rawptr[ast.AST]]] = list[rawptr[ast.AST]](pool, 8)
new_elts: list[ast.AST*]* = list[rawptr[ast.AST]](pool, 8)
if tup.elts is not None:
tup_elts: rawptr[list[rawptr[ast.AST]]] = tup.elts
tup_elts: list[ast.AST*]* = tup.elts
en: sizet = len(tup_elts)
ei: sizet
for ei in range(en):
@@ -329,9 +374,9 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
return args
old_args: rawptr[ast.Arguments] = rawptr[ast.Arguments](args)
# 拷贝 args 列表,替换每个 Arg 的 annotation
new_arg_list: rawptr[list[rawptr[ast.AST]]] = list[rawptr[ast.AST]](pool, 8)
new_arg_list: list[ast.AST*]* = list[rawptr[ast.AST]](pool, 8)
if old_args.args is not None:
old_arg_list: rawptr[list[rawptr[ast.AST]]] = old_args.args
old_arg_list: list[ast.AST*]* = old_args.args
an: sizet = len(old_arg_list)
ai: sizet
for ai in range(an):
@@ -364,7 +409,7 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
cd.decorator_list, None)
# 拷贝 children
if cd.children is not None:
cd_children: rawptr[list[rawptr[ast.AST]]] = cd.children
cd_children: list[ast.AST*]* = cd.children
cn: sizet = len(cd_children)
ci: sizet
for ci in range(cn):
@@ -389,7 +434,7 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
fd.decorator_list, new_returns2, 0)
# 方法体直接引用原始节点
if fd.children is not None:
fd_children: rawptr[list[rawptr[ast.AST]]] = fd.children
fd_children: list[ast.AST*]* = fd.children
bcn: sizet = len(fd_children)
bci: sizet
for bci in range(bcn):
+7 -7
View File
@@ -87,7 +87,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
pool: rawptr[memhub.MemBuddy] = trans.Pool
mod: rawptr[llvmlite.LLVMModule] = trans.Module
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
return 0
cn: sizet = len(children)
@@ -444,7 +444,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
if trans is None or cd is None or struct_ty is None or class_name is None:
return 0
children: rawptr[list[rawptr[ast.AST]]] = cd.children
children: list[ast.AST*]* = cd.children
if children is None:
return 0
@@ -681,13 +681,13 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
if md_args_node is not None:
md_ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](md_args_node)
if md_ags.args is not None:
md_alist: rawptr[list[rawptr[ast.AST]]] = md_ags.args
md_alist: list[ast.AST*]* = md_ags.args
if is_static_m != 0:
md_param_count = len(md_alist)
else:
md_param_count = len(md_alist) - 1
if md_ags.defaults is not None:
md_dlist: rawptr[list[rawptr[ast.AST]]] = md_ags.defaults
md_dlist: list[ast.AST*]* = md_ags.defaults
md_defaults = md_dlist
md_default_count = len(md_dlist)
@@ -718,7 +718,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
if args_node is not None:
ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if ags.args is not None:
alist: rawptr[list[rawptr[ast.AST]]] = ags.args
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
start_idx: int = 0
if is_static_m == 0:
@@ -868,7 +868,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
if args_node is not None:
ags2: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if ags2.args is not None:
alist2: rawptr[list[rawptr[ast.AST]]] = ags2.args
alist2: list[ast.AST*]* = ags2.args
an2: sizet = len(alist2)
start_idx2: int = 0
if is_static_m == 0:
@@ -948,7 +948,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
# 否则 `global BootInfo` 未注册时 prescan 把 BootInfo 当局部创建 alloca,
# 后续翻译 is_global_name 为真又写全局 → 局部 alloca 永不写入 → 读全 0
# (main.vp __init__ BootInfo memmap 全 0 根因)。
body: rawptr[list[rawptr[ast.AST]]] = fd.children
body: list[ast.AST*]* = fd.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
+140 -4
View File
@@ -801,6 +801,11 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
# load 解引用获取 Ptr(Struct)
obj_ptr = self._deref_if_ptr_ptr(builder, obj_ptr)
# rawptr[t.CVolatile | T] 基址(volatile 指针指向物):经该指针访问
# 字段(p.field)的 load 带 volatile。仅当基址可解析到 VarEntry
# (Name)/ FieldEntry(Attribute)时为 1;解析失败为 0(文本不变)。
vol_ba_rd: int = self.base_pointee_is_volatile(pool, trans, at.value)
# 查找结构体类型信息
# obj_ptr 类型应该是 Ptr(Struct(...))
match obj_ptr.Ty:
@@ -839,7 +844,7 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
llvmlite.build_bitcast(builder, alias_ptr, decl_ptr_rn)
if cast_ptr_rn is not None:
return llvmlite.build_load(builder, decl_ty_rn, cast_ptr_rn)
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr)
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr, vol_ba_rd)
# property getter 回退: obj.attr 是 @property/@x.getter 方法时生成调用
# (字段未找到时先尝试,避免报误导性的"字段未找到"错误)
if field_info is None and at.value is not None and trans is not None:
@@ -904,13 +909,19 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
pool, mod, annot_se.Ty)
if field_info is not None:
field_ty: rawptr[llvmlite.LLVMType] = field_info.Ty
# t.CVolatile 字段:经对象访问的 load 带 volatile 限定符
field_vol_rd: int = self.Gen.StructGen.field_is_volatile(field_info)
# 合并"字段所指物 volatile"(基址是 rawptr[t.CVolatile|T] 时,
# 经其访问的字段 load 亦须 volatile;两者取或)
if vol_ba_rd != 0:
field_vol_rd = 1
# 联合体:bitcast obj_ptr 到 field_ty* 后 load
if self.Gen.StructGen.is_union_by_type(struct_ty) == 1:
field_ptr_ty: rawptr[llvmlite.LLVMType] = llvmlite.Ptr(pool, field_ty)
casted: rawptr[llvmlite.Value] = llvmlite.build_bitcast(
builder, obj_ptr, field_ptr_ty)
if casted is not None:
return llvmlite.build_load(builder, field_ty, casted)
return llvmlite.build_load(builder, field_ty, casted, field_vol_rd)
return None
# 普通结构体:GEP + load
field_idx: int = field_info.Index
@@ -935,12 +946,12 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
cast_ptr_rd: rawptr[llvmlite.Value] = llvmlite.build_bitcast(
builder, field_ptr, decl_ptr_rd)
if cast_ptr_rd is not None:
return llvmlite.build_load(builder, decl_ty_rd, cast_ptr_rd)
return llvmlite.build_load(builder, decl_ty_rd, cast_ptr_rd, field_vol_rd)
# 数组类型字段不能 load 为 SSA value,直接返回字段指针
# 用于后续下标访问: self.state[0] → GEP state 字段 → GEP 数组元素
if self.is_array_type(field_ty) != 0:
return field_ptr
loaded_val: rawptr[llvmlite.Value] = llvmlite.build_load(builder, field_ty, field_ptr)
loaded_val: rawptr[llvmlite.Value] = llvmlite.build_load(builder, field_ty, field_ptr, field_vol_rd)
# 联合类型字段(如 Token | t.CPtr)被编译为 i8*,
# 若 AnnotClassName 指示了具体结构体类型,bitcast 为正确的结构体指针。
# 注意:仅当字段类型本身是指针时才 bitcast(指针→指针);
@@ -1063,6 +1074,131 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
if fe is None:
return None
return self.Gen.StructGen.get_field_annot_class_name(fe)
def attr_field_is_volatile(self, pool: rawptr[memhub.MemBuddy],
trans: rawptr[HT.Translator],
attr_node: rawptr[ast.AST]) -> int:
"""obj.field 访问的字段是否 volatile(1=是 / 0=否)
仅解析一层属性:先定基座(Name 变量注解名 / 属性链字段注解名)得到父类,
再按名查字段取 FieldEntry.IsVolatile。查不到(vtable 方法、影子字段、
依赖未就绪等)返回 0,不影响既有行为(写路径退回非 volatile store)。
"""
if attr_node is None or trans is None:
return 0
if attr_node.kind() != ast.ASTKind.Attribute:
return 0
at: rawptr[ast.Attribute] = rawptr[ast.Attribute](attr_node)
if at is None or at.attr is None or at.value is None:
return 0
parent_class: str = None
if at.value.kind() == ast.ASTKind.Name:
nm: rawptr[ast.Name] = rawptr[ast.Name](at.value)
if nm.id is None:
return 0
ve: rawptr[GenTypes.VarEntry] = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if ve is None:
return 0
parent_class = self.Gen.VarGen.var_annot_class_name(ve)
elif at.value.kind() == ast.ASTKind.Attribute:
parent_class = self._resolve_attr_field_class(pool, trans, at.value)
if parent_class is None:
return 0
fe: rawptr[GenTypes.FieldEntry] = self.Gen.StructGen.lookup_field_by_class(
parent_class, at.attr, trans.ModuleSha1, at, trans._from_imports)
return self.Gen.StructGen.field_is_volatile(fe)
# ============================================================
# 指针指向物 volatile(rawptr[t.CVolatile | T],等价 C 的 volatile T*)
# 基址→VarEntry/FieldEntry 解析与消费点。仅在基址能解析到具体条目时生效,
# 其余(函数返回/复杂表达式等)返回 0(明确不支持,见限制清单)。
# ============================================================
def _resolve_attr_field_entry(self, pool: rawptr[memhub.MemBuddy],
trans: rawptr[HT.Translator],
attr_node: rawptr[ast.AST]) -> rawptr[GenTypes.FieldEntry]:
"""obj.field 访问的 FieldEntry(**非 fatal 只读解析**,失败返回 None)
与 attr_field_is_volatile 的区别:后者走 lookup_field_by_class(模块限定名
分支会 fatal_error),仅用于写路径;本函数要能在读路径无条件调用,故
对齐 ExprGenOps._node_type_unsigned 的字段遍历——解析基座类名后 find_struct
取裸结构体、自遍历字段,未命中一律返回 None,绝不 fatal,不改变既有报错行为。
仅支持单层基座(基座是 Name,如 self.ptr);基座是属性链/调用/下标等返回 None。
"""
if attr_node is None or trans is None:
return None
if attr_node.kind() != ast.ASTKind.Attribute:
return None
at: rawptr[ast.Attribute] = rawptr[ast.Attribute](attr_node)
if at is None or at.attr is None or at.value is None:
return None
if at.value.kind() != ast.ASTKind.Name:
return None
nm: rawptr[ast.Name] = rawptr[ast.Name](at.value)
if nm.id is None:
return None
ve: rawptr[GenTypes.VarEntry] = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
if ve is None:
return None
cls_nm: str = self.Gen.VarGen.var_annot_class_name(ve)
if cls_nm is None:
return None
# 取裸类名(点前部分):只做裸名结构体查找,避免模块限定名解析 fatal
bare: str = cls_nm
dot: str = self.Gen.StructGen._last_dot_outside_brackets(cls_nm)
if dot is not None:
bare = dot + 1
if bare is None:
return None
st: rawptr[GenTypes.StructEntry] = self.Gen.StructGen.find_struct(bare)
if st is None:
return None
fi: int = 0
while fi < st.FieldCount:
fe_u: rawptr[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
fi += 1
return None
def attr_field_pointee_is_volatile(self, pool: rawptr[memhub.MemBuddy],
trans: rawptr[HT.Translator],
attr_node: rawptr[ast.AST]) -> int:
"""obj.field 字段的**所指物**是否 volatile(1=是 / 0=否)
即该字段注解是否为 rawptr[t.CVolatile | T](等价 C 的 volatile T*)。
消费点:经该字段解引用(self.ptr[i] / self.ptr.field)的 load/store
据此加 LLVM volatile 限定符。
"""
fe: rawptr[GenTypes.FieldEntry] = self._resolve_attr_field_entry(
pool, trans, attr_node)
return self.Gen.StructGen.field_pointee_is_volatile(fe)
def base_pointee_is_volatile(self, pool: rawptr[memhub.MemBuddy],
trans: rawptr[HT.Translator],
base_node: rawptr[ast.AST]) -> int:
"""指针基址的所指物是否 volatile(rawptr[t.CVolatile|T] 变量/字段)
仅当基址可解析到具体 VarEntry(Name)/ FieldEntry(Attribute)时生效;
基址是函数返回/下标/调用/更复杂表达式时返回 0(明确不支持)。
消费点:p[i] / p.field / c.Deref(p) 的 load/store 据此加 LLVM volatile。
"""
if base_node is None or trans is None:
return 0
if base_node.kind() == ast.ASTKind.Name:
nm: rawptr[ast.Name] = rawptr[ast.Name](base_node)
if nm.id is None:
return 0
ve: rawptr[GenTypes.VarEntry] = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm.id)
return self.Gen.VarGen.var_pointee_is_volatile(ve)
if base_node.kind() == ast.ASTKind.Attribute:
return self.attr_field_pointee_is_volatile(pool, trans, base_node)
return 0
def get_attribute_ptr(self, builder: rawptr[llvmlite.IRBuilder],
pool: rawptr[memhub.MemBuddy],
mod: rawptr[llvmlite.LLVMModule],
+5 -5
View File
@@ -46,7 +46,7 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
# ============================================================
def _extract_docstring_from_body(self, body: rawptr[list[rawptr[ast.AST]]]) -> str:
def _extract_docstring_from_body(self, body: list[ast.AST*]*) -> str:
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
if body is None or len(body) == 0:
return None
@@ -111,7 +111,7 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
if tree is None or module_sha1 is None:
return
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is None:
return
cn: sizet = len(ch)
@@ -123,7 +123,7 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
if kd == ast.ASTKind.FunctionDef:
fd: rawptr[ast.FunctionDef] = rawptr[ast.FunctionDef](child)
if fd is not None and fd.name is not None:
fd_body: rawptr[list[rawptr[ast.AST]]] = fd.children
fd_body: list[ast.AST*]* = fd.children
fdoc: str = self._extract_docstring_from_body(fd_body)
if fdoc is not None:
self.register_docstring(module_sha1, fd.name, fdoc)
@@ -131,12 +131,12 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
cd: rawptr[ast.ClassDef] = rawptr[ast.ClassDef](child)
if cd is None or cd.name is None:
continue
cd_body0: rawptr[list[rawptr[ast.AST]]] = cd.children
cd_body0: list[ast.AST*]* = cd.children
cdoc: str = self._extract_docstring_from_body(cd_body0)
if cdoc is not None:
self.register_docstring(module_sha1, cd.name, cdoc)
# 类方法 docstring(key="{ClassName}.{method}")
cd_body: rawptr[list[rawptr[ast.AST]]] = cd.children
cd_body: list[ast.AST*]* = cd.children
if cd_body is not None:
cbn: sizet = len(cd_body)
for mi in range(cbn):
+1 -1
View File
@@ -262,7 +262,7 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
if list_node is None:
return None
elts: rawptr[list[rawptr[ast.AST]]] = list_node.elts
elts: list[ast.AST*]* = list_node.elts
elts_count: sizet = 0
if elts is not None:
elts_count = len(elts)
+1 -1
View File
@@ -58,7 +58,7 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
arg_tail: rawptr[llvmlite.Value] = None
arg_count: int = 0
parts: rawptr[list[rawptr[ast.AST]]] = js.values
parts: list[ast.AST*]* = js.values
pn: sizet = len(parts)
for pi in range(pn):
part: rawptr[ast.AST] = parts.get(pi)
+48 -11
View File
@@ -167,7 +167,13 @@ class ExprGenName(ExprGenBase.ExprGenBase):
return mod_alloca
case _:
pass
return llvmlite.build_load(builder, load_ty, mod_alloca)
# t.CVolatile 全局:load 带 volatile 限定符(禁优化器消除重复读)
vol_g: int = 0
if trans is not None:
ve_g: rawptr[GenTypes.VarEntry] = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm_id)
vol_g = self.Gen.VarGen.var_is_volatile(ve_g)
return llvmlite.build_load(builder, load_ty, mod_alloca, vol_g)
# nonlocal 变量:通过闭包 env 访问
if GenApi.is_nonlocal_name(trans, nm_id) != 0:
@@ -206,7 +212,23 @@ class ExprGenName(ExprGenBase.ExprGenBase):
# load [N x i8] 值传给 %s 会读到错误数据崩溃
# (ClassArrayTest TestCharArray 运行时崩溃根因)。
return alloca
return llvmlite.build_load(builder, load_ty, alloca)
# t.CVolatile 局部变量:load 带 volatile 限定符
vol_l: int = 0
if trans is not None:
ve_l: rawptr[GenTypes.VarEntry] = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, nm_id)
vol_l = self.Gen.VarGen.var_is_volatile(ve_l)
return llvmlite.build_load(builder, load_ty, alloca, vol_l)
# REPL 模式(M2)会话变量:局部表未命中的名字若是会话变量,从 ctx 槽按类型读回。
# 位置在两处:① 局部查找之后(片段自己声明的局部名/参数优先,走原路径);
# ② 模块别名/模块函数引用之前(会话名不得被 printf 等本模块 declare 抢走)。
# 非 REPL(trans.ReplCtx is None)时 repl_try_read 立即返回 None ⇒ 原路径逐字不变。
if trans is not None and trans.ReplCtx is not None:
sess_val: rawptr[llvmlite.Value] = self.Gen.VarGen.repl_try_read(
trans, nm_id, node)
if sess_val is not None:
return sess_val
# 模块别名检查:SymTab 查找失败后才检查
# 如果 nm_id 是已导入模块名(如 win32file, fileio)且未被声明为局部变量,
@@ -756,6 +778,11 @@ class ExprGenName(ExprGenBase.ExprGenBase):
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: rawptr[ast.Name] = rawptr[ast.Name](sub.value)
if nm.id is not None and trans is not None:
# rawptr[t.CVolatile | T] 变量(volatile 指针指向物):本分支内
# 的**元素 load**(p[i] 解引用)带 volatile;仅读取指针值自身的
# load(alloca → 指针值)不带(那是指针变量而非所指物)。
vol_sub_rd: int = self.Gen.VarGen.var_pointee_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
# 读路径:元素指针 load 出元素值
@@ -850,7 +877,8 @@ class ExprGenName(ExprGenBase.ExprGenBase):
elem_ptr_pp: rawptr[llvmlite.Value] = llvmlite.build_gep(
builder, inner_ty, ptr_val_pp, idx_val)
if elem_ptr_pp is not None:
return llvmlite.build_load(builder, inner_ty, elem_ptr_pp)
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, inner_ty, elem_ptr_pp, vol_sub_rd)
return None
# 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
# 仅当类定义了 __getitem__(当前模块定义或跨模块 stub declare 注入)
@@ -880,7 +908,8 @@ class ExprGenName(ExprGenBase.ExprGenBase):
elem_rd_g: rawptr[llvmlite.Value] = llvmlite.build_gep(
builder, inner_ty, ptr_val_rd_g, idx_val)
if elem_rd_g is not None:
return llvmlite.build_load(builder, inner_ty, elem_rd_g)
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, inner_ty, elem_rd_g, vol_sub_rd)
return None
# bytes|t.CPtr / str|t.CPtr: alloca 是 i8**,
# 直接 GEP 按 i8* 步长(8 字节),load i8*
@@ -900,7 +929,8 @@ class ExprGenName(ExprGenBase.ExprGenBase):
elem_ptr_ts: rawptr[llvmlite.Value] = llvmlite.build_gep(
builder, pointee, ptr_val_pe_ts, idx_val)
if elem_ptr_ts is not None:
return llvmlite.build_load(builder, pointee, elem_ptr_ts)
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, pointee, elem_ptr_ts, vol_sub_rd)
return None
# 普通指针遍历: 先 load 指针值,再 GEP
ptr_val: rawptr[llvmlite.Value] = llvmlite.build_load(
@@ -909,7 +939,8 @@ class ExprGenName(ExprGenBase.ExprGenBase):
elem_ptr2: rawptr[llvmlite.Value] = llvmlite.build_gep(
builder, inner_ty, ptr_val, idx_val)
if elem_ptr2 is not None:
return llvmlite.build_load(builder, inner_ty, elem_ptr2)
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, inner_ty, elem_ptr2, vol_sub_rd)
return None
case _:
# 联合类型变量(IsPtrElement=1,如 _list.vp 的
@@ -959,7 +990,8 @@ class ExprGenName(ExprGenBase.ExprGenBase):
elem_sub_g: rawptr[llvmlite.Value] = llvmlite.build_gep(
builder, pointee, ptr_val_sub_g, idx_val)
if elem_sub_g is not None:
return llvmlite.build_load(builder, pointee, elem_sub_g)
# volatile 指针指向物:解引用 load 带 volatile
return llvmlite.build_load(builder, pointee, elem_sub_g, vol_sub_rd)
return None
pass
@@ -971,6 +1003,11 @@ class ExprGenName(ExprGenBase.ExprGenBase):
# 检查是否是指针类型
if self.is_ptr_type(ptr_val.Ty) != 0:
# 通用路径基址(如 self.ptr / 复杂表达式):仅当基址可解析到
# VarEntry/FieldEntry 且其为 rawptr[t.CVolatile|T] 时,元素 load 带 volatile;
# 其余基址解析返回 0(明确不支持,见限制清单)。守卫为 0 时文本不变。
vol_sub_gen: int = self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, sub.value)
elem_ty2: rawptr[llvmlite.LLVMType] = ptr_val.Ty.Pointee
if elem_ty2 is not None:
# 数组类型: 双索引 GEP + load 元素
@@ -980,7 +1017,7 @@ class ExprGenName(ExprGenBase.ExprGenBase):
builder, elem_ty2, arr_elem_ty2, ptr_val,
self._zext_index_to_i32(builder, idx_val))
if elem_ptr3 is not None:
return llvmlite.build_load(builder, arr_elem_ty2, elem_ptr3)
return llvmlite.build_load(builder, arr_elem_ty2, elem_ptr3, vol_sub_gen)
return None
case _:
pass
@@ -1014,7 +1051,7 @@ class ExprGenName(ExprGenBase.ExprGenBase):
builder, cell_p_u64, i8pp_ty_u64)
if cast_cell_u64 is not None:
return llvmlite.build_load(
builder, i8p_ty_u64, cast_cell_u64)
builder, i8p_ty_u64, cast_cell_u64, vol_sub_gen)
case _:
pass
# 自定义结构体 (如 self._ht[key]): 仅当类定义了 __getitem__ 才转发方法调用;
@@ -1037,13 +1074,13 @@ class ExprGenName(ExprGenBase.ExprGenBase):
elem_ptr_gen: rawptr[llvmlite.Value] = llvmlite.build_gep(
builder, elem_ty2, ptr_val, idx_val)
if elem_ptr_gen is not None:
return llvmlite.build_load(builder, elem_ty2, elem_ptr_gen)
return llvmlite.build_load(builder, elem_ty2, elem_ptr_gen, vol_sub_gen)
return None
# 指针类型: 单索引 GEP + load
elem_ptr4: rawptr[llvmlite.Value] = llvmlite.build_gep(
builder, elem_ty2, ptr_val, idx_val)
if elem_ptr4 is not None:
return llvmlite.build_load(builder, elem_ty2, elem_ptr4)
return llvmlite.build_load(builder, elem_ty2, elem_ptr4, vol_sub_gen)
return None
# ============================================================
# 获取下标表达式的元素指针(不加载值,用于赋值)
+2 -2
View File
@@ -479,7 +479,7 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
return None
op: int = ops_list.get(0)
comparators: rawptr[list[rawptr[ast.AST]]] = cmp.comparators
comparators: list[ast.AST*]* = cmp.comparators
if comparators is None or len(comparators) == 0:
return None
rhs_node: rawptr[ast.AST] = comparators.get(0)
@@ -966,7 +966,7 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
return None
op: int = bo.op
values: rawptr[list[rawptr[ast.AST]]] = bo.values
values: list[ast.AST*]* = bo.values
if values is None:
return None
count: sizet = len(values)
+7 -7
View File
@@ -32,7 +32,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
# 返回 LLVM IR 属性字符串(如 "alwaysinline nounwind"),无属性返回 None
# ============================================================
def extract_func_attrs(self, pool: rawptr[memhub.MemBuddy],
decorator_list: rawptr[list[rawptr[ast.AST]]],
decorator_list: list[ast.AST*]*,
imported_modules: str) -> str:
"""从 decorator_list 提取 c.Attribute 属性,返回 LLVM IR 属性字符串"""
if decorator_list is None:
@@ -84,7 +84,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
# 遍历参数提取属性
if call_node.args is None:
continue
args_list: rawptr[list[rawptr[ast.AST]]] = call_node.args
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(an):
arg_node: rawptr[ast.AST] = args_list.get(ai)
@@ -108,7 +108,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
return None
return attrs_buf
def extract_func_section(self, pool: rawptr[memhub.MemBuddy],
decorator_list: rawptr[list[rawptr[ast.AST]]],
decorator_list: list[ast.AST*]*,
imported_modules: str) -> str:
"""提取 @c.Attribute(t.attr.section("xxx")) 为属性组文本 '"section"="xxx"'。
@@ -133,7 +133,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
continue
if call_node.args is None:
continue
args_list: rawptr[list[rawptr[ast.AST]]] = call_node.args
args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list)
for ai in range(an):
arg_node: rawptr[ast.AST] = args_list.get(ai)
@@ -331,7 +331,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
"""函数体顶层是否含 raise(非 StopIteration),1=是 / 0=否"""
if fd is None or fd.children is None:
return 0
body_f: rawptr[list[rawptr[ast.AST]]] = fd.children
body_f: list[ast.AST*]* = fd.children
bn_f: sizet = len(body_f)
for bi_f in range(bn_f):
stmt_f: rawptr[ast.AST] = body_f.get(bi_f)
@@ -388,7 +388,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
deco_fa_cr: rawptr[ast.Attribute] = rawptr[ast.Attribute](deco_call_cr.func)
if deco_fa_cr.attr is None or string.strcmp(deco_fa_cr.attr, "CReturn") != 0:
continue
cr_args: rawptr[list[rawptr[ast.AST]]] = deco_call_cr.args
cr_args: list[ast.AST*]* = deco_call_cr.args
if cr_args is None:
continue
cr_an: sizet = len(cr_args)
@@ -427,7 +427,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
if args_node is not None:
ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if ags.args is not None:
alist: rawptr[list[rawptr[ast.AST]]] = ags.args
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
i32_ty: rawptr[llvmlite.LLVMType] = llvmlite.Int32(pool)
for ai in range(an):
+28 -11
View File
@@ -49,7 +49,7 @@ class FuncGenDef(CallGenBase.CallGenBase):
i8_ty: rawptr[llvmlite.LLVMType] = llvmlite.Int8(pool)
i8_ptr_ty: rawptr[llvmlite.LLVMType] = llvmlite.Ptr(pool, i8_ty)
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is None:
return 0
@@ -210,10 +210,10 @@ class FuncGenDef(CallGenBase.CallGenBase):
if fd_args_node is not None:
fd_ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](fd_args_node)
if fd_ags.args is not None:
fd_alist: rawptr[list[rawptr[ast.AST]]] = fd_ags.args
fd_alist: list[ast.AST*]* = fd_ags.args
fd_param_count = len(fd_alist)
if fd_ags.defaults is not None:
fd_dlist: rawptr[list[rawptr[ast.AST]]] = fd_ags.defaults
fd_dlist: list[ast.AST*]* = fd_ags.defaults
fd_defaults = fd_dlist
fd_default_count = len(fd_dlist)
@@ -234,7 +234,7 @@ class FuncGenDef(CallGenBase.CallGenBase):
if args_node is not None:
ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if ags.args is not None:
alist: rawptr[list[rawptr[ast.AST]]] = ags.args
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
for ai in range(an):
arg: rawptr[ast.Arg] = rawptr[ast.Arg](alist.get(ai))
@@ -512,7 +512,7 @@ class FuncGenDef(CallGenBase.CallGenBase):
if args_node_extern is not None:
ags_e: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node_extern)
if ags_e.args is not None:
alist_e: rawptr[list[rawptr[ast.AST]]] = ags_e.args
alist_e: list[ast.AST*]* = ags_e.args
an_e: sizet = len(alist_e)
for ai_e in range(an_e):
arg_e: rawptr[ast.Arg] = rawptr[ast.Arg](alist_e.get(ai_e))
@@ -611,10 +611,10 @@ class FuncGenDef(CallGenBase.CallGenBase):
if args_node is not None:
tfd_ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if tfd_ags.args is not None:
tfd_alist: rawptr[list[rawptr[ast.AST]]] = tfd_ags.args
tfd_alist: list[ast.AST*]* = tfd_ags.args
tfd_param_count = len(tfd_alist)
if tfd_ags.defaults is not None:
tfd_dlist: rawptr[list[rawptr[ast.AST]]] = tfd_ags.defaults
tfd_dlist: list[ast.AST*]* = tfd_ags.defaults
tfd_defaults = tfd_dlist
tfd_default_count = len(tfd_dlist)
@@ -629,7 +629,7 @@ class FuncGenDef(CallGenBase.CallGenBase):
if args_node is not None:
ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if ags.args is not None:
alist: rawptr[list[rawptr[ast.AST]]] = ags.args
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
for ai in range(an):
arg: rawptr[ast.Arg] = rawptr[ast.Arg](alist.get(ai))
@@ -787,7 +787,7 @@ class FuncGenDef(CallGenBase.CallGenBase):
if args_node is not None:
ags2: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if ags2.args is not None:
alist2: rawptr[list[rawptr[ast.AST]]] = ags2.args
alist2: list[ast.AST*]* = ags2.args
an2: sizet = len(alist2)
for ai2 in range(an2):
arg2: rawptr[ast.Arg] = rawptr[ast.Arg](alist2.get(ai2))
@@ -829,12 +829,29 @@ class FuncGenDef(CallGenBase.CallGenBase):
if cls_nm is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, arg2.arg, cls_nm)
# t.CVolatile 形参:声明期标记,函数体内该参数名的
# load/store 带 volatile(volatile 参数对象的读改写不得
# 被优化器提升/合并,如自旋等待入参)。
if arg2.annotation is not None:
if self.Gen.TypeGenErr.has_decorator_marker(
arg2.annotation, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(
trans.SymTab, arg2.arg)
# rawptr[t.CVolatile | T] 形参(volatile 指针指向物):
# 函数体内该参数解引用(p[i]/p.field/c.Deref(p))带 volatile。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(
arg2.annotation) != 0:
self.Gen.VarGen.set_var_pointee_volatile(
trans.SymTab, arg2.arg)
pname2: str = pool.alloc(32)
if pname2 is not None:
viperlib.snprintf(pname2, 32, "%%%s", arg2.arg)
param_val: rawptr[llvmlite.Value] = llvmlite.SSAValue(
pool, param_ty2, pname2)
llvmlite.build_store(func_builder, param_val, alloca)
vol_p2: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(
trans.SymTab, arg2.arg))
llvmlite.build_store(func_builder, param_val, alloca, vol_p2)
# *args 变长参数:创建 va_list 缓冲区 + 调用 va_start + 注册到符号表
# 生成 IR:
@@ -924,7 +941,7 @@ class FuncGenDef(CallGenBase.CallGenBase):
# 1. 先预扫描 Global/Nonlocal 声明(Python 语义:global/nonlocal
# 对整个函数作用域生效,与声明位置无关,故须在任何赋值前注册)
# 2. 再预扫描 AnnAssign 为局部变量提前创建 alloca
body: rawptr[list[rawptr[ast.AST]]] = fd.children
body: list[ast.AST*]* = fd.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
+26 -12
View File
@@ -113,7 +113,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
if fd.args is not None:
ags_n: rawptr[ast.Arguments] = rawptr[ast.Arguments](fd.args)
if ags_n.args is not None:
alist_n: rawptr[list[rawptr[ast.AST]]] = ags_n.args
alist_n: list[ast.AST*]* = ags_n.args
an_n: sizet = len(alist_n)
for ai_n in range(an_n):
arg_n: rawptr[ast.Arg] = rawptr[ast.Arg](alist_n.get(ai_n))
@@ -168,7 +168,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
if fd.args is not None:
ags_p: rawptr[ast.Arguments] = rawptr[ast.Arguments](fd.args)
if ags_p.args is not None:
alist_p: rawptr[list[rawptr[ast.AST]]] = ags_p.args
alist_p: list[ast.AST*]* = ags_p.args
idx_p: int = 0
apn: sizet = len(alist_p)
for api in range(apn):
@@ -196,11 +196,25 @@ class FuncGenMixin(CallGenBase.CallGenBase):
if cls_nm_p is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, arg_p.arg, cls_nm_p)
# t.CVolatile 形参:声明期标记(与 FuncGenDef 主路径同款),
# 函数体内该参数名的 load/store 带 volatile。
if self.Gen.TypeGenErr.has_decorator_marker(
arg_p.annotation, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(
trans.SymTab, arg_p.arg)
# rawptr[t.CVolatile | T] 形参(volatile 指针指向物):
# 函数体内该参数解引用带 volatile(与 FuncGenDef 主路径同款)。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(
arg_p.annotation) != 0:
self.Gen.VarGen.set_var_pointee_volatile(
trans.SymTab, arg_p.arg)
pname2_n: str = pool.alloc(32)
if pname2_n is not None:
viperlib.snprintf(pname2_n, 32, "%%%s", arg_p.arg)
p_val: rawptr[llvmlite.Value] = llvmlite.SSAValue(pool, pty2_n, pname2_n)
llvmlite.build_store(func_builder, p_val, p_alloca)
vol_pn: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, arg_p.arg))
llvmlite.build_store(func_builder, p_val, p_alloca, vol_pn)
idx_p += 1
# 6. 保存父函数作用域状态(仅非变量表相关)
@@ -232,7 +246,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
# 8. 预扫描函数体:
# a. 先预扫描 Global/Nonlocal 声明(Python 语义:对整个函数作用域生效)
# b. 再预扫描 AnnAssign 为局部变量提前创建 alloca
body: rawptr[list[rawptr[ast.AST]]] = fd.children
body: list[ast.AST*]* = fd.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
@@ -951,7 +965,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
fd: rawptr[ast.FunctionDef] = rawptr[ast.FunctionDef](node)
if fd is None or fd.children is None:
return count
ch: rawptr[list[rawptr[ast.AST]]] = fd.children
ch: list[ast.AST*]* = fd.children
cn: sizet = len(ch)
for ci in range(cn):
st: rawptr[ast.AST] = ch.get(ci)
@@ -963,7 +977,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
elif k == ast.ASTKind.Nonlocal:
nl_node: rawptr[ast.Nonlocal] = rawptr[ast.Nonlocal](st)
if nl_node is not None and nl_node.names is not None:
nl_list: rawptr[list[rawptr[ast.AST]]] = nl_node.names
nl_list: list[ast.AST*]* = nl_node.names
nn: sizet = len(nl_list)
for ni in range(nn):
nm_node: rawptr[ast.AST] = nl_list.get(ni)
@@ -1137,7 +1151,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
if lam.args is not None:
ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](lam.args)
if ags.args is not None:
alist: rawptr[list[rawptr[ast.AST]]] = ags.args
alist: list[ast.AST*]* = ags.args
pn: sizet = len(alist)
for pi in range(pn):
if param_count >= 64:
@@ -1482,7 +1496,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
bop2: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
cc2: int = cap_count
if bop2.values is not None:
vs: rawptr[list[rawptr[ast.AST]]] = bop2.values
vs: list[ast.AST*]* = bop2.values
vn: sizet = len(vs)
for vi in range(vn):
cc2 = self._lambda_scan_body(trans, vs.get(vi), param_names, param_count,
@@ -1493,7 +1507,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
cc3: int = self._lambda_scan_body(trans, cmp.left, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
if cmp.comparators is not None:
cl: rawptr[list[rawptr[ast.AST]]] = cmp.comparators
cl: list[ast.AST*]* = cmp.comparators
cn: sizet = len(cl)
for cvi in range(cn):
cc3 = self._lambda_scan_body(trans, cl.get(cvi), param_names, param_count,
@@ -1504,13 +1518,13 @@ class FuncGenMixin(CallGenBase.CallGenBase):
cc4: int = self._lambda_scan_body(trans, cal.func, param_names, param_count,
cap_names, cap_allocas, cap_tys, cap_count)
if cal.args is not None:
als: rawptr[list[rawptr[ast.AST]]] = cal.args
als: list[ast.AST*]* = cal.args
an: sizet = len(als)
for ai in range(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: rawptr[list[rawptr[ast.AST]]] = cal.keywords
kws: list[ast.AST*]* = cal.keywords
kn: sizet = len(kws)
for ki in range(kn):
kw: rawptr[ast.Keyword] = rawptr[ast.Keyword](kws.get(ki))
@@ -1542,7 +1556,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
ls: rawptr[ast.List] = rawptr[ast.List](node)
cc7: int = cap_count
if ls.elts is not None:
es: rawptr[list[rawptr[ast.AST]]] = ls.elts
es: list[ast.AST*]* = ls.elts
en: sizet = len(es)
for ei in range(en):
cc7 = self._lambda_scan_body(trans, es.get(ei), param_names, param_count,
+12 -1
View File
@@ -37,9 +37,20 @@ def set_current_file_imports(file_path: str) -> None:
# ---------------- set_current_file_type(委托 TypeGenResolve.set_current_file) ----------------
def set_current_file_type(file_path: str) -> None:
"""委托到 gen.set_current_file(原 Handles 壳)"""
"""委托到 gen.set_current_file —— 报错归因的"当前文件"写入通道(原 Handles 壳)
与 fatal_error 同因同解:Converter 的 vpi 生成(Phase 1)、Phase A-pre-vpi
跑在 BuildPipeline 注入 GenApi._g_gen 之前(BuildPipeline.vp 的
_GeneratePyiFromAst 调用点早于 GenApi._g_gen 赋值),此时 _g_gen 为空;
写侧若缺兜底会静默 no-op → fatal_error 的 get_current_file 读到的仍是
上一个文件(归因错位)。故回退读 Generator 模块单例(main.vp 启动即注入)。
"""
if _g_gen is not None:
_g_gen.TypeGenResolve.set_current_file(file_path)
return
gg: rawptr[Generator.Generator] = Generator._g_gen
if gg is not None:
gg.TypeGenResolve.set_current_file(file_path)
# ---------------- set_current_module_sha1(委托 TypeGenBase.set_current_module_sha1) ----------------
+220
View File
@@ -72,6 +72,15 @@ class VarEntry:
# set_var_ptr_element 经 SpecWithElemBits 补「指针元素」语义(原
# IsPtrElement 字段已并入 Spec.ElemBits)。
Spec: rawptr[TSpec.TypeSpec]
# 1=该变量的 load/store 带 LLVM volatile 限定符(注解联合含 t.CVolatile)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
IsVolatile: int
# 1=该变量是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):解引用(p[i] / p.field / c.Deref(p))的 load/store 带 LLVM
# volatile,禁止优化器重排序/消除对所指对象的每次访问。注意与 IsVolatile
# 的区别:IsVolatile 限定"指针变量自身"的读写,本字段限定"指针所指物"。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,条目数组步长同步。
PointeeVolatile: int
# ============================================================
@@ -110,6 +119,15 @@ class FieldEntry:
# 注解侧再经 SpecWithSign / SpecWithSource 补符号性与注解来源名
# (Spec.SourceName,原 AnnotClassName 字段已并入)。
Spec: rawptr[TSpec.TypeSpec]
# 1=该字段经对象访问的 load/store 带 LLVM volatile 限定符
# (字段注解联合含 t.CVolatile,如 Spinlock.locked: t.CVolatile | t.CInt)。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
IsVolatile: int
# 1=该字段是 rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):经该字段解引用(self.ptr[i] / self.ptr.field)的 load/store
# 带 LLVM volatile。与 IsVolatile 的区别同 VarEntry.PointeeVolatile。
# 追加在末尾:既有字段偏移不变,__sizeof__() 自动纳入,字段数组步长同步。
PointeeVolatile: int
# ============================================================
@@ -207,3 +225,205 @@ class FuncDefaultsEntry:
ConstKinds: int | t.CPtr # const_kind 数组(DefaultCount 个 i32)
ConstInts: int64 | t.CPtr # int_val 数组(DefaultCount 个 i64)
ConstStrs: sizet | t.CPtr # str_val 指针数组(DefaultCount 个 i8*)
# ============================================================
# REPL 会话(M2/M3):ctx 缓冲区布局 + 会话表条目 + 结果区头
#
# ctx 缓冲区由宿主(lib/core/Repl.vp)在 PermPool 一次性分配,片段函数以首形参
# 接收(注解 bytes ⇒ i8*),同一进程内宿主与翻译器共享同一块内存。
#
# 布局(用户拍板子约定;字节全小端,无符号/有符号按位模式):
#
# 偏移 0 magic(u32 = 'REPL' = 0x5245504C) 4 version(u32 = 2)
# 偏移 8 result_kind(i32) 12 保留(0)
# 偏移 16 result_a(u64) 24 result_b(u64)
# 偏移 32 var_count(i32) 36 last_frag(i32)
# 偏移 40..63 保留(0)
# 偏移 64 + i*16 第 i 个会话变量的槽(步长 16 字节),i < REPL_VAR_CAP
# 偏移 1088 + j*16 第 j 个会话函数的指针槽(步长 16 字节),j < REPL_FUNC_CAP
#
# 版本与向后兼容(version 1 → 2,**纯尾部追加**):
# v1 = 头 64B + 变量槽区 1024B(共 1088B);v2 在 1088 之后追加函数指针槽区
# (REPL_FUNC_CAP*REPL_FUNC_SLOT_SIZE)。前 1088 字节的字段偏移/宽度/语义逐字
# 未变 ⇒ 按 v1 生成的旧片段 DLL 与 v2 宿主混用仍然正确(旧片段只读写
# < 1088 的字节);新片段读函数指针槽时宿主必为 v2 ⇒ 无版本协商需求。
# 头 +4 写 2 仅作"含函数指针槽区"的显式标记(片段侧不校验该字段)。
#
# result_kind:
# 0 = none 本片段无结果(声明类片段)
# 1 = int/uint result_a = 值(补码位模式,宿主按有符号 64 位回显)
# 2 = float64 result_a = IEEE754 double 位模式(宿主重解释后回显)
# 3 = str/bytes result_a = 指针;result_b = 长度(0 = 以 NUL 结尾,宿主按
# C 字符串处理——片段侧不额外调 strlen,故长度未知时写 0)
# 4 = struct/其它 result_a = 值地址(结构体值装箱后的盒子地址 / 非字符串指针)
#
# 槽内存放形态(ReplVarEntry.Kind):
# SCALAR — 标量/指针(≤8 字节)的值直接存槽;
# BOX — 结构体值:片段侧 malloc 盒子(值存盒内),槽内存盒子指针。
# 槽步长 16 字节(结构体/字符串统一走指针 ⇒ 宿主无需知道类型);负载一律
# ≤8 字节且 8 字节对齐即合法(MemBuddy 用户指针实测 8 字节对齐)。
# 槽数上限 REPL_VAR_CAP ⇒ 变量区尺寸 = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE。
#
# 函数指针槽(M3):槽内是会话函数在**定义片段 DLL** 里的入口地址(void*,8 字节;
# DLL 常驻不卸载 ⇒ 地址在会话期内有效)。宿主在片段 DLL 加载成功后
# GetProcAddress(裸名) 取得地址并写入槽;后续片段的调用点从槽 load 地址后
# 间接调用 ⇒ 函数代码只有一份(在定义片段里),调用点不复制代码。
# ============================================================
REPL_CTX_MARKER: t.CDefine = 1397571916 # 宿主侧对象标记(自比对;非 ctx 头字段)
REPL_CTX_MAGIC: t.CDefine = 0x5245504C # 'REPL'(ctx 头 +0,小端字节序为 R E P L)
REPL_CTX_VERSION: t.CDefine = 2 # ctx 头 +4(1 → 2:尾部追加函数指针槽区)
REPL_SLOT_SIZE: t.CDefine = 16 # 每个会话变量占用字节数(步长)
REPL_SLOT_BASE: t.CDefine = 64 # 槽区起点 = 固定头字节数(= +40..63 保留之后)
REPL_CTX_PARAM: t.CDefine = "__repl_ctx__" # 包裹模板里 ctx 形参名(与 Repl.vp 同源)
REPL_VAR_CAP: t.CDefine = 64 # 会话变量上限(变量区 = 64 + 64*16 = 1088 字节)
# 会话函数指针槽区(M3):起点紧跟变量区,步长与变量槽一致(16B,低 8 字节存地址)
REPL_FUNC_BASE: t.CDefine = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
REPL_FUNC_SLOT_SIZE: t.CDefine = 16 # 每个会话函数占用字节数(步长)
REPL_FUNC_CAP: t.CDefine = 32 # 会话函数上限(函数区 = 32*16 = 512 字节)
# ctx 总字节数 = 64 + 64*16 + 32*16 = 1600(宿主 Repl._REPL_CTX_SIZE 必须一致)
REPL_CTX_SIZE: t.CDefine = 1600
# 会话函数的形参上限(签名 token 数组定长;超出 ⇒ 注册期明确报错)
REPL_FUNC_PARAM_MAX: t.CDefine = 8
# 片段入口函数名前缀(Repl.vp 生成 "__repl_frag_%d";用于区分"片段入口函数"与
# 被提升的会话函数本体——前者是会话变量写入的作用域,后者只读会话槽)
REPL_ENTRY_PREFIX: t.CDefine = "__repl_frag_"
# 会话模块 SHA1(M4):片段翻译器的 ModuleSha1 取此**稳定**值 ⇒ 片段内定义的类型
# 名形如 "<16位hex>.<Type>",且**跨片段逐字一致**——这是"先前片段定义的类在后续
# 片段可见/可用"的基础(类型名是跨片段查找的唯一键)。
# 派生:sha1("TransPyV-REPL-Session").hexdigest()[:16]
# 得 71f67b7cb405f0e4(一次性计算,见 repl-design.md §M4 的可复算命令)。
# 形态:16 位小写 hex 与 includes 模块 SHA1 同形 ⇒ 被既有判定
# GenApi.is_sha1_hex16 / StructGen.find_struct 的"模块段即 SHA1"分支
# 直接当作模块限定名处理(无需新增解析规则)。
# 反例(为什么不给每个片段各设一个 sha1):类型名会随片段漂移,先前片段注册的
# 结构体/方法在后续片段的 find_struct_by_module 全部落空。
# 与裸名约定的关系:`export def`(含片段入口与会话函数)走 has_export 分支取裸名
# (FuncGenDef.vp:482-485),**不受**该值影响;受影响的只有类/方法/模块级全局
# 等经 _mangle_name/_mangle_name_with_sha1 命名的符号。
REPL_SESSION_SHA1: t.CDefine = "71f67b7cb405f0e4"
# ctx 头字段偏移(与 ReplResult 字段顺序/宽度一一对应)
REPL_RES_KIND_OFF: t.CDefine = 8 # i32
REPL_RES_A_OFF: t.CDefine = 16 # u64
REPL_RES_B_OFF: t.CDefine = 24 # u64
REPL_RES_VARCOUNT_OFF: t.CDefine = 32 # i32
REPL_RES_LASTFRAG_OFF: t.CDefine = 36 # i32
# 结果 Kind(宿主按此回显)
REPL_RES_NONE: t.CDefine = 0 # 本片段无结果(如仅声明)
REPL_RES_INT: t.CDefine = 1 # 整数(读 result_a 位模式)
REPL_RES_FLOAT: t.CDefine = 2 # float64(读 result_a 的 double 位模式)
REPL_RES_STR: t.CDefine = 3 # str/bytes(读 result_a 指针 + result_b 长度)
REPL_RES_OBJ: t.CDefine = 4 # 结构体/其它指针(读 result_a 地址)
# 会话变量类型标签(ReplVarEntry.TyTag)
REPL_TY_INT: t.CDefine = 1
REPL_TY_FLOAT: t.CDefine = 2
REPL_TY_STR: t.CDefine = 3 # i8*(str/bytes)
REPL_TY_PTR: t.CDefine = 4 # 其他指针(Bits=pointee 位宽 / TyName=结构体名)
REPL_TY_STRUCT: t.CDefine = 5 # 结构体值(按声明类型内联在槽里;≤ 槽容量)
# 会话变量存放形态(ReplVarEntry.Kind)
REPL_KIND_UNTYPED: t.CDefine = 0 # 已登记槽位但尚未定型(未赋值/无注解)
REPL_KIND_SCALAR: t.CDefine = 1 # 值内联存槽(标量/指针/≤16B 结构体值)
# 会话函数签名的返回类型 token(M3;只用于 ReplFuncEntry.RetTag)
REPL_TY_VOID: t.CDefine = 0 # 无返回值(void;不作为形参 token)
# ============================================================
# ReplVarEntry - REPL 会话变量条目(跨片段存活,表本体在 PermPool)
# ============================================================
@t.NoVTable
class ReplVarEntry:
"""REPL 会话变量条目:名字 + ctx 槽偏移 + 定型信息
Name/TyName 必须是 PermPool 深拷贝——宿主的 AST 名字串属于片段 pool,
行末 reset_to 后即失效,直接存指针会读到已回收内存。
"""
Name: str # 变量名(PermPool 深拷贝,NUL 结尾)
TyName: str # 结构体名(TyTag=PTR 且 pointee 为结构体 / TyTag=STRUCT);否则 None
Offset: int # ctx 内槽偏移(REPL_SLOT_BASE + i * REPL_SLOT_SIZE)
Kind: int # REPL_KIND_*
TyTag: int # REPL_TY_*
Bits: int # 标量位宽;TyTag=PTR 时是 pointee 位宽(0=未知)
# ============================================================
# ReplFuncEntry - REPL 会话函数条目(M3;跨片段存活,表本体在 PermPool)
#
# 语义:片段 N 里 `def f(...)` 被提升为片段模块里的**顶层 export def**(裸名导出),
# 该 DLL 加载后宿主 GetProcAddress 取到地址并写入 ctx 的函数指针槽;本条目记录
# "名字 → 符号名 + 定义片段号 + 槽偏移 + 签名 token",后续片段的调用点据此
# 从槽 load 地址并间接调用(与 M2 的会话变量槽同理:单一真相 + 间接寻址)。
#
# 签名 token(ParamTags/ParamBits 每形参 1 字节、RetTag/RetBits)与来源:
# 定义片段翻译出的 IR 里 `define <ret> @f(i8* %__repl_ctx__, <P1> %p1, ...)`
# 是 ABI 的唯一真相(注解 → LLVM 类型由 codegen 解析),宿主在注册前解析该
# define 行并**只接受**可往返重建的 token 集合:
# REPL_TY_INT(bits=1/8/16/32/64) / REPL_TY_FLOAT(bits=32/64) / REPL_TY_STR(i8*)
# 其余类型(结构体值、数组、多级指针等)⇒ 注册期明确报错,绝不静默按 i32 兜底。
# ============================================================
@t.NoVTable
class ReplFuncEntry:
"""REPL 会话函数条目:名字 + 导出符号名 + ctx 函数指针槽 + 签名 token"""
Name: str # 会话函数名(= 用户写的裸名;PermPool 深拷贝,NUL 结尾)
SymName: str # 片段 DLL 里的导出符号名(PermPool 深拷贝;M3 = 裸名)
FragNo: int # 定义该函数的片段号(重定义时更新为最新片段号)
SlotOff: int # ctx 内函数指针槽偏移(REPL_FUNC_BASE + j*REPL_FUNC_SLOT_SIZE)
RetTag: int # REPL_TY_VOID / REPL_TY_INT / REPL_TY_FLOAT / REPL_TY_STR
RetBits: int # 对齐 ReplVarEntry.Bits 的口径(VOID 时为 0)
ParamCount: int # 形参数(**不含**注入的 ctx 首形参)
ParamTags: str # PermPool 字节数组(每形参 1 字节 tag,REPL_FUNC_PARAM_MAX 个)
ParamBits: str # PermPool 字节数组(每形参 1 字节 bits,REPL_FUNC_PARAM_MAX 个)
# ============================================================
# ReplCtx - REPL 会话上下文(宿主建、翻译器读)
#
# 开关语义:trans.ReplCtx != None ⇒ REPL 模式(仅 Repl.vp 翻译片段前置位,
# 翻译后清空)。Gen 侧全部拦截点都以此字段为唯一守卫——普通编译路径下该字段
# 恒为 None,逐字走原路径。
# ============================================================
@t.NoVTable
class ReplCtx:
"""REPL 模式上下文(宿主持有;翻译器只读)"""
Marker: int # REPL_CTX_MARKER(防误传非 ReplCtx 对象)
Table: rawptr[ReplVarEntry] # 会话表数组(PermPool,容量 Cap)
Count: int # 已登记条目数(= 已分配槽数)
Cap: int # 容量(REPL_VAR_CAP)
CtxParam: str # 片段函数 ctx 形参名(PermPool 深拷贝)
SlotSize: int # 槽步长(REPL_SLOT_SIZE)
# 影子条目(宿主建、PermPool):仅供"裸表达式结果"做类型分类用,不占会话槽、
# 不进表(Count 不计入),Name 固定为 "<expr>" 以便错误消息可读。
Scratch: rawptr[ReplVarEntry]
# 宿主提供的永久池:会话表条目名/结构体名等**跨片段存活**的数据必须在此池
# 深拷贝(片段的 trans.Pool 行末 reset_to 即回收,直接存其指针会悬垂)。
Perm: rawptr[memhub.MemBuddy]
# 会话函数表(M3;宿主写、Gen 只读):数组容量 FuncCap,已登记 FuncCount 条。
# 条目只在"定义片段 DLL 加载成功且 GetProcAddress 命中"后由宿主追加 ⇒ 片段
# 翻译期看到的表恒为"先前片段的已就绪函数"(本片段新定义的同名函数走直接调用)。
FuncTable: rawptr[ReplFuncEntry]
FuncCount: int
FuncCap: int # 容量(REPL_FUNC_CAP)
# ============================================================
# ReplResult - ctx 头(宿主按字段读;片段按 REPL_RES_*_OFF 字节偏移写)
#
# 字段顺序/宽度即 ctx 布局:Magic(+0,i32) Version(+4,i32) Kind(+8,i32)
# Reserved0(+12,i32) A(+16,i64) B(+24,i64) VarCount(+32,i32) LastFrag(+36,i32)。
# 宿主另有"同一头的浮点视图"(把 +16 的 8 字节按 double 读回,见 Repl.vp)。
# ============================================================
@t.NoVTable
class ReplResult:
"""ctx 固定头(前 64 字节中的前 40 字节;+40..63 为保留)"""
Magic: int # REPL_CTX_MAGIC('REPL')
Version: int # REPL_CTX_VERSION
Kind: int # REPL_RES_*
Reserved0: int # 保留(0)
A: t.CInt64T # result_a
B: t.CInt64T # result_b
VarCount: int # 已分配槽数(宿主写,供检视)
LastFrag: int # 最近执行的片段号(宿主写,供检视)
+7 -5
View File
@@ -13,7 +13,8 @@ import w32.fileio as fileio
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.VLogger as VLogger
# ============================================================
@@ -731,7 +732,8 @@ class ImportsGenMixin:
stdlib.free(line_buf_err)
f_err.close()
stdio.printf("ImportError: %s\n", msg)
sys.exit(1)
# 终止点收敛:REPL 会话内 longjmp 回 REPL 循环(诊断已打印),批处理下终止
VLogger.repl_abort()
# ============================================================
# _is_local_module_file - 当前文件同目录存在 {mod_name}.vp/.py
@@ -819,7 +821,7 @@ class ImportsGenMixin:
imp: rawptr[ast.Import] = rawptr[ast.Import](node)
if imp is None:
return 0
names: rawptr[list[rawptr[ast.AST]]] = imp.names
names: list[ast.AST*]* = imp.names
if names is None:
return 0
nn: sizet = len(names)
@@ -893,7 +895,7 @@ class ImportsGenMixin:
# 否则别名 "syscall" 精确匹配不到包名 "vpsdk" → is_module_imported 返回 0
# → syscall._syscall1 / syscall.LOAD_SO 跨模块解析失败。
if impf.module is None:
names_m: rawptr[list[rawptr[ast.AST]]] = impf.names
names_m: list[ast.AST*]* = impf.names
if names_m is not None:
nn_m: sizet = len(names_m)
for ni_m in range(nn_m):
@@ -937,7 +939,7 @@ class ImportsGenMixin:
trans.Pool, trans.CurrentPackage,
impf.level, impf.module)
if resolved is not None:
names: rawptr[list[rawptr[ast.AST]]] = impf.names
names: list[ast.AST*]* = impf.names
if names is not None:
nn: sizet = len(names)
for ni in range(nn):
+28 -28
View File
@@ -39,7 +39,7 @@ class MatchGenMixin:
Gen: rawptr[Generator.Generator]
def _case_tail_unreachable(self, trans: rawptr[HT.Translator],
builder: rawptr[llvmlite.IRBuilder],
body_list: rawptr[list[rawptr[ast.AST]]]) -> int:
body_list: list[ast.AST*]*) -> int:
if trans is None or builder is None:
return 0
if llvmlite.builder_cur_block_is_terminated(builder) != 0:
@@ -132,7 +132,7 @@ class MatchGenMixin:
# 2b. 若未确定,遍历 cases 查 MatchClass 变体
if is_renum == 0:
cases_list: rawptr[list[rawptr[ast.AST]]] = m_node.cases
cases_list: list[ast.AST*]* = m_node.cases
if cases_list is not None:
cn: sizet = len(cases_list)
ci: int = 0
@@ -227,7 +227,7 @@ class MatchGenMixin:
#
# 遍历 case body 顶层语句,查找 Expr(Call(Attribute(Name('c'), 'NoBreak')))
# ============================================================
def _has_no_break(self, body_list: rawptr[list[rawptr[ast.AST]]]) -> int:
def _has_no_break(self, body_list: list[ast.AST*]*) -> int:
if body_list is None:
return 0
n: sizet = len(body_list)
@@ -352,7 +352,7 @@ class MatchGenMixin:
pool, func, name_buf)
# 3. 准备收集数组(用 CSizeT 数组存指针)
cases_list: rawptr[list[rawptr[ast.AST]]] = node.cases
cases_list: list[ast.AST*]* = node.cases
if cases_list is None:
# fail-fast: match cases 缺失
self.Gen.TypeGenErr.fatal_error(node, "match cases 缺失", "TypeError")
@@ -452,7 +452,7 @@ class MatchGenMixin:
elif pk == ast.ASTKind.MatchOr:
# MatchOr: 多个值共享一个 BB(取第一个值做 case)
mo: rawptr[ast.MatchOr] = rawptr[ast.MatchOr](pat)
pats: rawptr[list[rawptr[ast.AST]]] = mo.patterns
pats: list[ast.AST*]* = mo.patterns
if pats is not None and len(pats) > 0:
first_pat: rawptr[ast.AST] = pats.get(0)
if first_pat is not None \
@@ -474,7 +474,7 @@ class MatchGenMixin:
bb = default_bb
# 检测 NoBreak
body_list: rawptr[list[rawptr[ast.AST]]] = mc.children
body_list: list[ast.AST*]* = mc.children
nb: int = self._has_no_break(body_list)
# 存入数组
@@ -576,7 +576,7 @@ class MatchGenMixin:
subject_ptr, trans, pool, builder)
# 翻译 case body
body_list: rawptr[list[rawptr[ast.AST]]] = mc.children
body_list: list[ast.AST*]* = mc.children
if body_list is not None:
bc: sizet = len(body_list)
bi: int = 0
@@ -614,7 +614,7 @@ class MatchGenMixin:
rm_merge_reach = 1
else:
# 有显式 default:翻译 default case body(找到 is_def==1 的 case)
dbody: rawptr[list[rawptr[ast.AST]]] = None
dbody: list[ast.AST*]* = None
di: int = 0
while di < collected:
if def_arr[di] == 1:
@@ -627,7 +627,7 @@ class MatchGenMixin:
# (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃,
# gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐
# Converter arg_defaults 内联声明规避模式)。
dbody2: rawptr[list[rawptr[ast.AST]]] = dmc.children
dbody2: list[ast.AST*]* = dmc.children
dbody = dbody2
if dbody2 is not None:
dn: sizet = len(dbody2)
@@ -670,7 +670,7 @@ class MatchGenMixin:
if pat is None or renum_entry is None or renum_ty is None:
return
mc: rawptr[ast.MatchClass] = rawptr[ast.MatchClass](pat)
pats: rawptr[list[rawptr[ast.AST]]] = mc.patterns
pats: list[ast.AST*]* = mc.patterns
if pats is None:
return
# bitcast subject_ptr 到 renum_ptr_ty(确保 GEP 类型匹配)
@@ -821,7 +821,7 @@ class MatchGenMixin:
sq_merge_reach: int = 0
# 1. 求值 subject 各元素
elts: rawptr[list[rawptr[ast.AST]]] = None
elts: list[ast.AST*]* = None
if subject_node.kind() == ast.ASTKind.Tuple:
st: rawptr[ast.Tuple] = rawptr[ast.Tuple](subject_node)
if st is not None:
@@ -841,7 +841,7 @@ class MatchGenMixin:
for ei in range(ecount):
# elts 预声明+重赋值会把 .get 误分派成 list[int].get(i32 截断);
# 用内联别名接收者规避(对齐 Converter arg_defaults 内联模式)
elts_inl: rawptr[list[rawptr[ast.AST]]] = elts
elts_inl: list[ast.AST*]* = elts
elt: rawptr[ast.AST] = elts_inl.get(ei)
sv: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
builder, pool, trans.Module, elt,
@@ -865,7 +865,7 @@ class MatchGenMixin:
pool, func, name_buf)
# 3. 第一遍:收集 case 信息(check/body BB、default 标记、nobreak、guard)
cases_list: rawptr[list[rawptr[ast.AST]]] = node.cases
cases_list: list[ast.AST*]* = node.cases
if cases_list is None:
return 0
case_count: sizet = len(cases_list)
@@ -927,7 +927,7 @@ class MatchGenMixin:
body_arr[collected] = sizet(bbb)
if first_check < 0:
first_check = collected
body_list: rawptr[list[rawptr[ast.AST]]] = mc.children
body_list: list[ast.AST*]* = mc.children
nb: int = self._has_no_break(body_list)
def_arr[collected] = is_def
nb_arr[collected] = nb
@@ -950,8 +950,8 @@ class MatchGenMixin:
if def_arr[ci] == 1:
ci = ci + 1
continue
cbb2: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](check_arr[ci], t.CPtr)
bbb2: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](body_arr[ci], t.CPtr)
cbb2: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](check_arr[ci])
bbb2: rawptr[llvmlite.BasicBlock] = rawptr[llvmlite.BasicBlock](body_arr[ci])
llvmlite.position_at_end(builder, cbb2)
# 计算匹配条件(元素比较 && guard)
@@ -960,7 +960,7 @@ class MatchGenMixin:
mc2: rawptr[ast.MatchCase] = rawptr[ast.MatchCase](cnode)
ms: rawptr[ast.MatchSequence] = (
rawptr[ast.MatchSequence])(mc2.pattern)
pats: rawptr[list[rawptr[ast.AST]]] = None
pats: list[ast.AST*]* = None
if ms is not None:
pats = ms.patterns
pcount: sizet = 0
@@ -973,7 +973,7 @@ class MatchGenMixin:
for pi in range(ecount):
# pats 预声明+重赋值会把 .get 误分派成 list[int].get(i32 截断);
# 用内联别名接收者规避(对齐 Converter arg_defaults 内联模式)
pats_inl: rawptr[list[rawptr[ast.AST]]] = pats
pats_inl: list[ast.AST*]* = pats
pat1: rawptr[ast.AST] = pats_inl.get(pi)
if pat1 is None:
continue
@@ -987,7 +987,7 @@ class MatchGenMixin:
if pv is None:
self.Gen.TypeGenErr.fatal_error(
pat1, f"序列模式元素 {pi} 翻译失败", "TypeError")
sv1: rawptr[llvmlite.Value] = rawptr[llvmlite.Value](subj_vals[pi], t.CPtr)
sv1: rawptr[llvmlite.Value] = rawptr[llvmlite.Value](subj_vals[pi])
c1: rawptr[llvmlite.Value] = self._seq_cmp_eq(trans, builder, sv1, pv)
if c1 is None:
self.Gen.TypeGenErr.fatal_error(pat1, "序列模式元素比较失败", "TypeError")
@@ -1059,7 +1059,7 @@ class MatchGenMixin:
mc3: rawptr[ast.MatchCase] = (
rawptr[ast.MatchCase])(cnode2)
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH)
body_list2: rawptr[list[rawptr[ast.AST]]] = mc3.children
body_list2: list[ast.AST*]* = mc3.children
if body_list2 is not None:
bc: sizet = len(body_list2)
bi: int = 0
@@ -1092,7 +1092,7 @@ class MatchGenMixin:
# 7. default 块
llvmlite.position_at_end(builder, default_bb)
dbody: rawptr[list[rawptr[ast.AST]]] = None
dbody: list[ast.AST*]* = None
if has_default == 1:
di: int = 0
while di < collected:
@@ -1106,7 +1106,7 @@ class MatchGenMixin:
# (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃,
# gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐
# Converter arg_defaults 内联声明规避模式)。
dbody2: rawptr[list[rawptr[ast.AST]]] = dmc.children
dbody2: list[ast.AST*]* = dmc.children
dbody = dbody2
if dbody2 is not None:
dn: sizet = len(dbody2)
@@ -1178,7 +1178,7 @@ class MatchGenMixin:
pool, func, name_buf)
# 3. 收集 case 信息
cases_list: rawptr[list[rawptr[ast.AST]]] = node.cases
cases_list: list[ast.AST*]* = node.cases
if cases_list is None:
# fail-fast: match cases 缺失
self.Gen.TypeGenErr.fatal_error(node, "match cases 缺失", "TypeError")
@@ -1233,7 +1233,7 @@ class MatchGenMixin:
has_default = 1
elif pk == ast.ASTKind.MatchOr:
mo: rawptr[ast.MatchOr] = rawptr[ast.MatchOr](pat)
pats: rawptr[list[rawptr[ast.AST]]] = mo.patterns
pats: list[ast.AST*]* = mo.patterns
if pats is not None and len(pats) > 0:
first_pat: rawptr[ast.AST] = pats.get(0)
if first_pat is not None \
@@ -1266,7 +1266,7 @@ class MatchGenMixin:
else:
bb = default_bb
body_list: rawptr[list[rawptr[ast.AST]]] = mc.children
body_list: list[ast.AST*]* = mc.children
nb: int = self._has_no_break(body_list)
if collected < MAX_MATCH_CASES:
@@ -1316,7 +1316,7 @@ class MatchGenMixin:
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH)
body_list: rawptr[list[rawptr[ast.AST]]] = mc.children
body_list: list[ast.AST*]* = mc.children
if body_list is not None:
bc: sizet = len(body_list)
bi: int = 0
@@ -1346,7 +1346,7 @@ class MatchGenMixin:
trans._ctrl_merge_reachable = 1
sm_merge_reach = 1
else:
dbody: rawptr[list[rawptr[ast.AST]]] = None
dbody: list[ast.AST*]* = None
di: int = 0
while di < collected:
if def_arr[di] == 1:
@@ -1359,7 +1359,7 @@ class MatchGenMixin:
# (i32→inttoptr 截断指针 → translate_stmt 读垃圾节点崩溃,
# gen1 IR 证据)。用 dbody2 内联声明接收者遍历(对齐
# Converter arg_defaults 内联声明规避模式)。
dbody2: rawptr[list[rawptr[ast.AST]]] = dmc.children
dbody2: list[ast.AST*]* = dmc.children
dbody = dbody2
if dbody2 is not None:
dn: sizet = len(dbody2)
+26 -26
View File
@@ -238,7 +238,7 @@ class StmtGenCifMixin:
return 0
if k == ast.ASTKind.BoolOp:
bo: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
vals: rawptr[list[rawptr[ast.AST]]] = bo.values
vals: list[ast.AST*]* = bo.values
if vals is None:
self.CifEvalOk = 0
return 0
@@ -267,7 +267,7 @@ class StmtGenCifMixin:
if self.CifEvalOk == 0:
return 0
ops_list: list[int] | t.CPtr = (list[int] | t.CPtr)(cmp.ops)
comps: rawptr[list[rawptr[ast.AST]]] = cmp.comparators
comps: list[ast.AST*]* = cmp.comparators
if ops_list is None or comps is None:
self.CifEvalOk = 0
return 0
@@ -373,7 +373,7 @@ class StmtGenCifMixin:
return 0
if string.strcmp(vn.id, "ctraits") != 0 or string.strcmp(at.attr, "isptr") != 0:
return 0
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
self.CifEvalOk = 0
return 0
@@ -414,7 +414,7 @@ class StmtGenCifMixin:
kind = nm.id
if kind is None:
return 0
cargs: rawptr[list[rawptr[ast.AST]]] = cl.args
cargs: list[ast.AST*]* = cl.args
if cargs is None or len(cargs) < 1:
return 0
arg0: rawptr[ast.AST] = cargs.get(0)
@@ -473,7 +473,7 @@ class StmtGenCifMixin:
# return a, b → 打包匿名 struct 值(多返回值,函数返回类型 tuple[T1,T2])
if rt.value.kind() == ast.ASTKind.Tuple:
rtp: rawptr[ast.Tuple] = rawptr[ast.Tuple](rt.value)
rtp_elts: rawptr[list[rawptr[ast.AST]]] = rtp.elts
rtp_elts: list[ast.AST*]* = rtp.elts
rt_n: sizet = 0
if rtp_elts is not None:
rt_n = len(rtp_elts)
@@ -714,7 +714,7 @@ class StmtGenCifMixin:
cif_val: int = self._evaluate_cif_test(pool, trans, if_node.test)
if self.CifEvalOk != 0:
if cif_val != 0:
body_cif: rawptr[list[rawptr[ast.AST]]] = if_node.children
body_cif: list[ast.AST*]* = if_node.children
if body_cif is not None:
bc_cif: sizet = len(body_cif)
bi_cif: sizet
@@ -723,7 +723,7 @@ class StmtGenCifMixin:
if stmt_cif is not None:
self.Gen.StmtGen.translate_stmt(trans, stmt_cif)
else:
orelse_cif: rawptr[list[rawptr[ast.AST]]] = if_node.orelse
orelse_cif: list[ast.AST*]* = if_node.orelse
if orelse_cif is not None and len(orelse_cif) == 1:
sub_cif: rawptr[ast.AST] = orelse_cif.get(0)
if sub_cif is not None and sub_cif.kind() == ast.ASTKind.If:
@@ -776,7 +776,7 @@ class StmtGenCifMixin:
# 检查是否有 else 分支
has_else: int = 0
orelse_list: rawptr[list[rawptr[ast.AST]]] = if_node.orelse
orelse_list: list[ast.AST*]* = if_node.orelse
if orelse_list is not None:
if len(orelse_list) > 0:
has_else = 1
@@ -798,7 +798,7 @@ class StmtGenCifMixin:
# 末句 If 全 return 被 fail-fast 误判"流程掉出末尾"的根因链一环)
trans._ctrl_merge_reachable = 1
llvmlite.position_at_end(builder, then_bb)
body: rawptr[list[rawptr[ast.AST]]] = if_node.children
body: list[ast.AST*]* = if_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -929,7 +929,7 @@ class StmtGenCifMixin:
trans._break_bb = end_bb
trans._continue_bb = cond_bb
body: rawptr[list[rawptr[ast.AST]]] = while_node.children
body: list[ast.AST*]* = while_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -980,7 +980,7 @@ class StmtGenCifMixin:
"""递归扫描节点子树(跳过嵌套 While/For 子树),含 Break 返回 1"""
if node is None:
return 0
ch: rawptr[list[rawptr[ast.AST]]] = node.children
ch: list[ast.AST*]* = node.children
if ch is not None:
n: sizet = len(ch)
for bi in range(n):
@@ -996,7 +996,7 @@ class StmtGenCifMixin:
return 1
return 0
def _StmtHasBreak(self, body: rawptr[list[rawptr[ast.AST]]]) -> int:
def _StmtHasBreak(self, body: list[ast.AST*]*) -> int:
"""语句列表是否含本层 break(嵌套循环的 break 归内层)"""
if body is None:
return 0
@@ -1194,7 +1194,7 @@ class StmtGenCifMixin:
call: rawptr[ast.Call] = rawptr[ast.Call](iter_node)
# 3. 解析 range 参数: range(stop) / range(start, stop) / range(start, stop, step)
args: rawptr[list[rawptr[ast.AST]]] = call.args
args: list[ast.AST*]* = call.args
if args is None:
return 0
arg_count: sizet = len(args)
@@ -1429,7 +1429,7 @@ class StmtGenCifMixin:
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: rawptr[list[rawptr[ast.AST]]] = for_node.children
body: list[ast.AST*]* = for_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -1479,7 +1479,7 @@ class StmtGenCifMixin:
"""从 Tuple target 提取前两个 Name 变量名,写入 out1/out2,成功返回 1"""
if tup is None or tup.elts is None or out1 is None or out2 is None:
return 0
elts: rawptr[list[rawptr[ast.AST]]] = tup.elts
elts: list[ast.AST*]* = tup.elts
n: sizet = len(elts)
if n < 2:
return 0
@@ -1566,7 +1566,7 @@ class StmtGenCifMixin:
r1_stop = llvmlite.const_int64(pool, 0)
# 先赋中间变量再取 args(cast 链式属性访问会推断错字段偏移)
call_src1: rawptr[ast.Call] = rawptr[ast.Call](src1)
cargs1: rawptr[list[rawptr[ast.AST]]] = call_src1.args
cargs1: list[ast.AST*]* = call_src1.args
if cargs1 is not None:
n1: sizet = len(cargs1)
if n1 >= 1:
@@ -1636,7 +1636,7 @@ class StmtGenCifMixin:
r2_stop = llvmlite.const_int64(pool, 0)
# 先赋中间变量再取 args(cast 链式属性访问会推断错字段偏移)
call_src2: rawptr[ast.Call] = rawptr[ast.Call](src2)
cargs2: rawptr[list[rawptr[ast.AST]]] = call_src2.args
cargs2: list[ast.AST*]* = call_src2.args
if cargs2 is not None:
n2: sizet = len(cargs2)
if n2 >= 1:
@@ -1808,7 +1808,7 @@ class StmtGenCifMixin:
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: rawptr[list[rawptr[ast.AST]]] = for_node.children
body: list[ast.AST*]* = for_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -1969,7 +1969,7 @@ class StmtGenCifMixin:
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: rawptr[list[rawptr[ast.AST]]] = for_node.children
body: list[ast.AST*]* = for_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -2136,7 +2136,7 @@ class StmtGenCifMixin:
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: rawptr[list[rawptr[ast.AST]]] = for_node.children
body: list[ast.AST*]* = for_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -2180,7 +2180,7 @@ class StmtGenCifMixin:
lst: rawptr[ast.List] = rawptr[ast.List](list_node)
if lst is None or lst.elts is None:
self.Gen.TypeGenErr.fatal_error(for_node, "列表字面量迭代: 空列表")
elts: rawptr[list[rawptr[ast.AST]]] = lst.elts
elts: list[ast.AST*]* = lst.elts
elts_count: sizet = len(elts)
if elts_count <= 0:
self.Gen.TypeGenErr.fatal_error(for_node, "列表字面量迭代: 空列表")
@@ -2334,7 +2334,7 @@ class StmtGenCifMixin:
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: rawptr[list[rawptr[ast.AST]]] = for_node.children
body: list[ast.AST*]* = for_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -2445,7 +2445,7 @@ class StmtGenCifMixin:
trans._break_bb = end_bb
trans._continue_bb = incr_bb
body: rawptr[list[rawptr[ast.AST]]] = for_node.children
body: list[ast.AST*]* = for_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -2637,7 +2637,7 @@ class StmtGenCifMixin:
old_continue: rawptr[llvmlite.BasicBlock] = trans._continue_bb
trans._break_bb = end_bb
trans._continue_bb = step_bb
body: rawptr[list[rawptr[ast.AST]]] = for_node.children
body: list[ast.AST*]* = for_node.children
if body is not None:
body_count: sizet = len(body)
for bi in range(body_count):
@@ -2664,7 +2664,7 @@ class StmtGenCifMixin:
"""enumerate 迭代: for i, v in enumerate(x)"""
if call is None or call.args is None:
return 0
cargs: rawptr[list[rawptr[ast.AST]]] = call.args
cargs: list[ast.AST*]* = call.args
if len(cargs) < 1:
self.Gen.TypeGenErr.fatal_error(for_node, "enumerate 需要至少一个参数")
return self._build_seq_loop(trans, for_node, t1, t2, cargs.get(0), None, 1)
@@ -2677,7 +2677,7 @@ class StmtGenCifMixin:
if call is None or call.args is None:
# fail-fast: zip 参数缺失
self.Gen.TypeGenErr.fatal_error(call, "zip 参数缺失", "SyntaxError")
cargs: rawptr[list[rawptr[ast.AST]]] = call.args
cargs: list[ast.AST*]* = call.args
if len(cargs) < 2:
self.Gen.TypeGenErr.fatal_error(for_node, "zip 需要至少两个参数")
return self._build_seq_loop(trans, for_node, t1, t2, cargs.get(0), cargs.get(1), 2)
+34 -2
View File
@@ -46,7 +46,7 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
gn: rawptr[ast.Global] = rawptr[ast.Global](node)
if gn is None:
return 0
names: rawptr[list[rawptr[ast.AST]]] = gn.names
names: list[ast.AST*]* = gn.names
if names is None:
return 0
n: sizet = len(names)
@@ -67,7 +67,7 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
nl: rawptr[ast.Nonlocal] = rawptr[ast.Nonlocal](node)
if nl is None:
return 0
names: rawptr[list[rawptr[ast.AST]]] = nl.names
names: list[ast.AST*]* = nl.names
if names is None:
return 0
n: sizet = len(names)
@@ -105,6 +105,14 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
return 0
ck: int = call_node.kind()
if ck != ast.ASTKind.Call:
# REPL 模式(M2):片段函数本体内的裸表达式语句(如 1+2 / x / x + 1)
# 求值后写入 ctx 结果区,由宿主回显。调用表达式仍走下方原路径
# (执行 + 丢弃返回值,同 print/printf 语义)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ "表达式语句除调用外一律忽略"
# 的原行为逐字不变。
if trans is not None and trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
self.Gen.VarGen.repl_expr_result(trans, call_node)
return 0
cl: rawptr[ast.Call] = rawptr[ast.Call](call_node)
@@ -126,6 +134,30 @@ class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
trans.Pool, trans._cur_builder, trans.Module, cl, trans)
else:
# 通用函数调用(返回值丢弃)
# M3:裸名调用**会话函数**(跨片段)时额外把返回值写进 ctx 结果区回显
# (M2 的限制条目"调用表达式的结果不回显"对会话函数放开——用户在
# `>>> f(41)` 后期望看到 `=> 42`)。结果区的类型分类直接用会话函数表里
# 记录的返回类型 token(宿主从定义片段 IR 解析而来,是定义端 ABI 真相),
# 不用影子条目推断 ⇒ 与调用点签名同源。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 原"执行 + 丢弃返回值"逐字不变;
# 仅裸名(Name)且表中登记的会话函数走本分支(printf/print 等在上方已分流)。
if trans is not None and trans.ReplCtx is not None \
and func_node.kind() == ast.ASTKind.Name \
and self.Gen.VarGen.repl_in_frag_scope(trans.SymTab) != 0:
sfn_ent: rawptr[GenTypes.ReplFuncEntry] = \
self.Gen.VarGen.repl_find_func_entry(trans.ReplCtx, func_name)
if sfn_ent is not None and sfn_ent.RetTag != GenTypes.REPL_TY_VOID:
sfn_v: rawptr[llvmlite.Value] = self.Gen.CallGen.translate_call(
trans.Pool, trans._cur_builder, trans.Module, call_node,
trans._funcs, trans._func_count, trans)
if sfn_v is not None:
ctx_v: rawptr[llvmlite.Value] = self.Gen.VarGen.repl_ctx_ptr(
trans, trans.ReplCtx, call_node)
if ctx_v is not None:
self.Gen.VarGen.repl_emit_result(
trans, ctx_v, sfn_v, sfn_ent.RetTag, sfn_ent.RetBits,
call_node)
return 0
self.Gen.CallGen.translate_call(
trans.Pool, trans._cur_builder, trans.Module, call_node,
trans._funcs, trans._func_count, trans)
+2 -2
View File
@@ -597,7 +597,7 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
mod: rawptr[llvmlite.LLVMModule] = trans.Module
if pool is None or builder is None or mod is None:
return 0
items: rawptr[list[rawptr[ast.AST]]] = wt.items
items: list[ast.AST*]* = wt.items
if items is None:
return 0
n: sizet = len(items)
@@ -695,7 +695,7 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
trans, pool, builder, mod, fd_cls_bare, ctx_val)
# 2. 翻译 body
body: rawptr[list[rawptr[ast.AST]]] = wt.children
body: list[ast.AST*]* = wt.children
if body is not None:
bn: sizet = len(body)
for bi in range(bn):
+23 -2
View File
@@ -684,10 +684,10 @@ class StructGenMixin:
dst_l.end_col_offset = src_l.end_col_offset
dst_l.ctx = src_l.ctx
# 深拷贝 elts:stdlib 手动构造 list[AST | t.CPtr] 容器
src_elts_l: rawptr[list[rawptr[ast.AST]]] = src_l.elts
src_elts_l: list[ast.AST*]* = src_l.elts
if src_elts_l is not None:
cnt_l: sizet = len(src_elts_l)
lst_copy_l: rawptr[list[rawptr[ast.AST]]] = rawptr[list[rawptr[ast.AST]]](
lst_copy_l: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(
pool.alloc(list[rawptr[ast.AST]].__sizeof__()))
if lst_copy_l is not None:
string.memset(lst_copy_l, 0, list[rawptr[ast.AST]].__sizeof__())
@@ -1906,3 +1906,24 @@ class StructGenMixin:
if fe is None:
return None
return TSpec.SpecSourceName(fe.Spec)
def field_is_volatile(self, fe: rawptr[GenTypes.FieldEntry]) -> int:
"""字段是否 volatile(FieldEntry.IsVolatile 承载),1=是 / 0=否
由 ClassGenDef 收集字段注解时检测 t.CVolatile 写入;消费点(成员读写的
load/store)据此加 LLVM volatile 限定符。
"""
if fe is None:
return 0
return fe.IsVolatile
def field_pointee_is_volatile(self, fe: rawptr[GenTypes.FieldEntry]) -> int:
"""字段所指物是否 volatile(FieldEntry.PointeeVolatile 承载),1=是 / 0=否
字段注解为 rawptr[t.CVolatile | T](等价 C 的 volatile T*)时由 ClassGenDef
写入;消费点(经该字段解引用 self.ptr[i] / self.ptr.field 的 load/store)
据此加 LLVM volatile 限定符。
"""
if fe is None:
return 0
return fe.PointeeVolatile
+15 -11
View File
@@ -137,16 +137,13 @@ class Translator:
_cur_builder: rawptr[llvmlite.IRBuilder]
# 当前赋值语句的注解节点(AnnAssign 翻译 rhs 前设置,Assign 翻译前清空)。
# 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → bitcast i32* load
# (对齐 TPC HandlesSpecialCall._infer_deref_target_type;否则固定读 1 字节 + sext 截断)。
# 供裸名泛型构造从注解推断类型实参:`s: Stack[int] = Stack()` 用注解里的
# int 特化(消费者见 CallGenCtor.vp 的 CurAssignNode/CurAssignAnnot 判定)。
CurAssignAnnot: rawptr[ast.AST]
# 当前赋值语句的 RHS 节点(AnnAssign 翻译 rhs 前设置,rhs 翻译后清除)。
# 供 c.Deref 判定"是否为赋值直接 RHS":仅 v: int = c.Deref(p) 时用
# CurAssignAnnot 推断读取宽度;嵌套在 while 条件/比较/binop/类型转换
# 调用中(如 while c.Deref(p) != 0、total += int(c.Deref(p)))不推断,
# 否则 i8* 会被残留 CurAssignAnnot 误判为 i32* 读 4 字节(对齐 TPC
# _infer_deref_target_type 的直接 RHS 检查)。
# 供裸名泛型构造判定"构造调用是否为赋值直接 RHS":仅该 identity 命中时才用
# CurAssignAnnot 推断类型实参(对齐 TPC 的直接 RHS 检查)。
CurAssignNode: rawptr[ast.AST]
# 当前函数的返回注解节点(translate_function_def 进入函数体前设置,退出时
@@ -254,6 +251,11 @@ class Translator:
# 嵌套作用域符号表
SymTab: rawptr[GenTypes.SymbolTable]
# REPL 模式上下文(M2 会话变量):None ⇒ 非 REPL(普通编译路径逐字不变)。
# 仅 Repl.vp 在翻译片段前置位、翻译后清空;Gen 侧全部会话槽拦截点以
# `trans.ReplCtx is not None` 为唯一守卫。
ReplCtx: rawptr[GenTypes.ReplCtx]
def __init__(self):
self.Module = None
self.Pool = None
@@ -293,6 +295,8 @@ class Translator:
self.GenericTypeArgs = None
self._declare_only = 0
self.SymTab = None
# REPL 模式上下文:默认未设置(= 非 REPL,Gen 侧拦截全部旁路)
self.ReplCtx = None
self._async_funcs = None
self._async_func_count = 0
self._coro_var_async = None
@@ -475,7 +479,7 @@ class Translator:
# 检查用户是否定义了 main 函数
has_user_main: int = 0
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn_count: sizet = len(ch)
for ci in range(cn_count):
@@ -523,7 +527,7 @@ class Translator:
# (前向声明需要解析类型注解,如 t.CArray[str] 依赖 t 模块已导入)
# 记录最后处理的 import 行号,供 vpi 注入错误上下文帧定位
cur_imp_lineno: int = 0
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn_count: sizet = len(ch)
for ci in range(cn_count):
@@ -711,7 +715,7 @@ class Translator:
if fd_s.args is not None:
args_s: rawptr[ast.Arguments] = rawptr[ast.Arguments](fd_s.args)
if args_s.args is not None:
args_list_s: rawptr[list[rawptr[ast.AST]]] = args_s.args
args_list_s: list[ast.AST*]* = args_s.args
an_s: sizet = len(args_list_s)
ai_s: sizet
for ai_s in range(an_s):
@@ -729,7 +733,7 @@ class Translator:
# translate_list_literal 阶段 fail-fast 提示 _list 未注册(需显式注解)。
# 递归语句块子节点
if node.children is not None:
ch_s: rawptr[list[rawptr[ast.AST]]] = node.children
ch_s: list[ast.AST*]* = node.children
n_s: sizet = len(ch_s)
for ci_s in range(n_s):
self._scan_container_usage(ch_s.get(ci_s), need_list_box, need_dict_box)
+135 -6
View File
@@ -83,7 +83,7 @@ class TranslatorGenMixin:
Returns:
int: 新增的变量数
"""
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is None:
return 0
@@ -164,7 +164,7 @@ class TranslatorGenMixin:
if fd.args is not None:
ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](fd.args)
if ags.args is not None:
al: rawptr[list[rawptr[ast.AST]]] = ags.args
al: list[ast.AST*]* = ags.args
an: sizet = len(al)
ai: sizet
for ai in range(an):
@@ -220,7 +220,7 @@ class TranslatorGenMixin:
is_int_elem = 0
# 获取列表元素(通过 ln.elts 而非 list_node.elts,确保类型正确识别)
elts: rawptr[list[rawptr[ast.AST]]] = ln.elts
elts: list[ast.AST*]* = ln.elts
if elts is None:
return None
elts_count: sizet = len(elts)
@@ -575,6 +575,81 @@ class TranslatorGenMixin:
viperlib.snprintf(buf, body_len + 8, "{ %s }", body)
return buf
# ============================================================
# 模块级全局变量 t.attr.* 属性提取
#
# 语法:属性写在类型注解的联合里(`T | t.attr.section(".x")`),因为
# Assign/AnnAssign 节点没有 decorator_list(解析器对 `@c.Attribute(...)`
# 后接赋值会直接报 "decorator must be followed by def/class")。
# 与函数侧口径差异:函数属性来自 decorator_list,提取为属性组文本
# `"section"="xxx"`(LLVM 字符串属性必须带引号,走 attributes #N);
# 全局变量属性没有属性组,直接写入 GlobalVariable.Section/Align/Linkage
# 字段,打印时由 includes/llvmlite/__module.vp 的 _print_global 补引号
# (`, section "xxx"` / `, align N`),故此处 Section 值不带引号。
# ============================================================
def _scan_global_tattr(self, node: rawptr[ast.AST],
want_name: str) -> rawptr[ast.AST]:
"""在全局变量类型注解中查找 t.attr.<want_name> 节点。
注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)),
递归左右子节点。命中返回该 t.attr 节点(Call: t.attr.aligned(16) /
Attribute: t.attr.weak),未命中返回 None。属性名识别复用类侧
ClassGenBase._tattr_name(Call→func→attr / Attribute→attr)。
"""
if node is None:
return None
if node.kind() == ast.ASTKind.BinOp:
bop: rawptr[ast.BinOp] = rawptr[ast.BinOp](node)
if bop.op == ast.OpKind.BitOr:
hit_l: rawptr[ast.AST] = self._scan_global_tattr(bop.left, want_name)
if hit_l is not None:
return hit_l
return self._scan_global_tattr(bop.right, want_name)
return None
if self.Gen.ClassGenBase._tattr_name(node, want_name) != 0:
return node
return None
def _extract_global_attr_section(self, node: rawptr[ast.AST],
var_name: str) -> str:
"""取 `T | t.attr.section("xxx")` 的段名(不含引号)。
缺参/空段名属非法值 → fail-fast(与 handle_module_level_var 既有
TypeGenErr.fatal_error 风格一致):空段名会生成 `section ""` 这种
无意义/非法的 IR,静默忽略只会掩盖拼写错误。
"""
hit: rawptr[ast.AST] = self._scan_global_tattr(node, "section")
if hit is None:
return None
sec_val: str = self.Gen.FuncGenBase._get_attr_call_arg_str(hit)
if sec_val is None or sec_val[0] == 0:
self.Gen.TypeGenErr.fatal_error(
node, f"模块级变量 '{var_name}' 的 t.attr.section(...) 段名缺失或为空",
"ValueError")
return None
return sec_val
def _extract_global_attr_aligned(self, node: rawptr[ast.AST],
var_name: str) -> int:
"""取 `T | t.attr.aligned(N)` 的对齐字节数(无该属性返回 0)。
N 必须是正整数字面量:aligned(0)/aligned(-4)/aligned("x") 均属非法值
→ fail-fast(llc 亦拒绝 `align 0`,静默忽略会掩盖参数错误)。
"""
hit: rawptr[ast.AST] = self._scan_global_tattr(node, "aligned")
if hit is None:
return 0
al_val: int = self.Gen.ClassGenBase._tattr_aligned_int(hit)
if al_val <= 0:
self.Gen.TypeGenErr.fatal_error(
node, f"模块级变量 '{var_name}' 的 t.attr.aligned(N) 需要正整数字面量 N",
"ValueError")
return 0
return al_val
def _extract_global_attr_weak(self, node: rawptr[ast.AST]) -> int:
"""检测 `T | t.attr.weak` / `t.attr.weak()`(1=有,0=无)"""
if self._scan_global_tattr(node, "weak") is not None:
return 1
return 0
# ============================================================
# handle_module_level_var - 模块级变量声明 → 创建 LLVM 全局变量
#
@@ -643,6 +718,28 @@ class TranslatorGenMixin:
nm: rawptr[ast.Name] = rawptr[ast.Name](aa.target)
var_name = nm.id
annot_node = aa.annotation
# t.Callable 模块级函数指针:登记 (变量名 → 返回类型) 签名。此路径原先
# 只创建全局变量、不登记签名,模块级 t.Callable 变量被间接调用时在
# CallGenLeafKit._translate_func_ptr_call 报"返回类型未注册(t.Callable
# 注解缺失)"(CallGenLeafKit.vp:843-850)。与函数内 AnnAssign 同一登记函数
# (AssignGenMixin.vp:1465-1474),参数形态照抄该处:
# (pool, 注解, trans._imported_modules, trans._from_imports, trans)。
# 时机:_translate_module_level 在 translate_children 之前先遍历全部模块级
# AnnAssign/Assign(Translator.vp:568-575,其后才是 :578 translate_children),
# 故登记早于本文件任何函数体翻译;签名表本体与返回类型深拷贝都在 PermPool
# (CallGenLeafKit.vp:698-699、:783-788),跨文件查找可见。
# t.CDefine/t.CTypedef 等分支已在 :675-689 提前返回,行为不变。
if annot_node is not None:
ml_callable_ret: rawptr[llvmlite.LLVMType] = (
self.Gen.TypeGenErr.try_extract_callable_ret(
pool, annot_node, trans._imported_modules,
trans._from_imports, trans))
if ml_callable_ret is not None:
# 返回值:0=成功/同类型幂等命中;非 0 已在 register_func_ptr_sig
# 内部 fatal_error 终止编译,此处分支仅防御(见 AssignGenMixin 同处)。
if self.Gen.CallGenLeafKit.register_func_ptr_sig(
pool, var_name, ml_callable_ret, aa) != 0:
return 0
# 解析类型
if aa.annotation is not None:
resolved: rawptr[llvmlite.LLVMType] = self.Gen.TypeGenResolve.resolve_annotation_type(
@@ -711,7 +808,7 @@ class TranslatorGenMixin:
asgn: rawptr[ast.Assign] = rawptr[ast.Assign](node)
if asgn is None or asgn.targets is None:
return 0
targets: rawptr[list[rawptr[ast.AST]]] = asgn.targets
targets: list[ast.AST*]* = asgn.targets
if len(targets) < 1:
return 0
t0: rawptr[ast.AST] = targets.get(0)
@@ -807,6 +904,16 @@ class TranslatorGenMixin:
if annot_node is not None:
is_global_cexport = self.Gen.TypeGenErr.has_decorator_marker(
annot_node, "CExport", 0)
# 全局变量侧 t.attr.* 属性(`T | t.attr.xxx` 注解联合形态):
# section→gv.Section、aligned→gv.Align、weak→gv.Linkage(见下方应用点)。
# 仅在 AnnAssign(annot_node 非 None)时存在,与 t.CExport 同一条路。
gv_attr_section: str = None
gv_attr_align: int = 0
gv_attr_weak: int = 0
if annot_node is not None:
gv_attr_section = self._extract_global_attr_section(annot_node, var_name)
gv_attr_align = self._extract_global_attr_aligned(annot_node, var_name)
gv_attr_weak = self._extract_global_attr_weak(annot_node)
mangled_name: str = var_name
if is_global_cexport == 0:
mangled_name = self.Gen.FuncGenBase._mangle_name(trans, var_name)
@@ -815,8 +922,14 @@ class TranslatorGenMixin:
return 0
llvmlite.module_add_global(mod, gv)
# 段名:直接写字段(_print_global 会补 `, section "xxx"` 的引号)。
if gv_attr_section is not None:
llvmlite.global_set_section(gv, gv_attr_section)
# B5:@c.Attribute(t.attr.aligned(N)) 类的全局实例施加对齐(如 fpu_context 16 对齐,
# FXSAVE 区域要求 16 字节对齐,否则 fxsave/fxrstor #GP)。
# 优先级:变量级显式 `T | t.attr.aligned(N)` 比类级(struct entry Align)更具体,
# 显式值胜出;无显式属性时沿用类级对齐(既有行为不变)。
al_ty: rawptr[llvmlite.LLVMType] = var_ty
match var_ty:
case llvmlite.LLVMType.Ptr(pointee_al):
@@ -826,11 +939,16 @@ class TranslatorGenMixin:
al_entry: rawptr[GenTypes.StructEntry] = None
if al_ty is not None and al_ty.kind() == llvmlite.LLVMType.Struct:
al_entry = self.Gen.StructGen.find_struct_by_type(al_ty)
if al_entry is not None and al_entry.Align > 0:
if gv_attr_align > 0:
gv.Align = gv_attr_align
elif al_entry is not None and al_entry.Align > 0:
gv.Align = al_entry.Align
# 设置初始值(有初始值时清除 external linkage,因为 LLVM 22+ 不允许 external global 带初始值)
gv.Linkage = None
# t.attr.weak → weak linkage(须在 Linkage 清空之后设置)。
if gv_attr_weak != 0:
llvmlite.global_set_linkage(gv, "weak")
if init_kind == 1:
# 整数初始值
init_buf: str = pool.alloc(48)
@@ -1015,6 +1133,17 @@ class TranslatorGenMixin:
# 注册到模块作用域
if self.Gen.VarGen.define_module_var(trans.SymTab, var_name, gv_ref) == 0:
# t.CVolatile 存储类标记(注解联合含 t.CVolatile)→ 标记全局变量
# volatile:所有 load/store 带 LLVM volatile,禁止优化器把忙等循环
# 的重复 load 提升/消除(如 `locked: t.CVolatile | t.CInt`)。
if annot_node is not None:
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(trans.SymTab, var_name)
# rawptr[t.CVolatile | T](volatile 指针指向物):解引用(p[i]/
# p.field/c.Deref(p))的 load/store 带 volatile。与上一分支互斥:
# 前者是变量自身限定(BinOp 联合),后者是 rawptr 下标实参标记。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot_node) != 0:
self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, var_name)
# 设置 AnnotClassName(联合类型如 Logger|t.CPtr 简化为 Ptr(i8) 时,
# 属性访问需要通过 AnnotClassName 回退到类名查找结构体)
if k == ast.ASTKind.AnnAssign:
@@ -1089,7 +1218,7 @@ class TranslatorGenMixin:
# 预处理模块级变量:为 AnnAssign/Assign 创建 LLVM 全局变量
# 必须在 pre_scan_allocas 之前执行,否则 PreScan 会创建局部 alloca,
# 导致其他函数通过 SSA 编号引用 wrapper main 的局部变量(无效 IR)
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn_count: sizet = len(ch)
for ci in range(cn_count):
+114 -24
View File
@@ -120,6 +120,28 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
else:
self.CDefineNameIdxBuilt = -1
# ============================================================
# CDefine 表本体的池归一
#
# 表指针挂在**进程级单例** _g_gen.TypeGenCDef 上,不在任何 pool 的管辖
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
# (Phase1 1a/1b、Phase2 PhaseA、StubMerger 扫描、Repl 每片段都按 mark/reset
# 轮转)。表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None`
# 守卫失效 ⇒ 下一次 register_cdefine_* 的 append 写进已回收内存(堆损坏;
# 检测模式下该区被 free 填成 0xCD ⇒ append 的接收者 = 0xcdcdcdcdcdcdcdcd)。
# 故表本体(四个 list + 名索引)一律落 PermPool(进程级永久池,从不 reset),
# 与"文件级/片段级 reset"彻底解耦。
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
# ============================================================
def _cdefine_table_pool(self,
pool: rawptr[memhub.MemBuddy]) -> rawptr[memhub.MemBuddy]:
"""CDefine 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
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: rawptr[memhub.MemBuddy],
name: str, value: int64) -> None:
"""注册 CDefine 编译期常量到本地表
@@ -139,11 +161,29 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
if name is None:
return
if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64)
self._cdef_idx_init(pool)
tp: rawptr[memhub.MemBuddy] = self._cdefine_table_pool(pool)
tn: list[str] | t.CPtr = list[str](tp, 64)
tv: list[str] | t.CPtr = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
# 落到 None 或未初始化地址(原实现无检查 ⇒ append 写进 NULL/0xCD 区崩)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(
None, "CDefine 表分配失败(表本体池不可用或耗尽)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self._cdef_idx_init(tp)
# 表就绪守卫:表本体缺失/半初始化(建表失败或外部置空)⇒ fail-fast,
# 不写垃圾(append 之前必有非空断言)
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(
None, "CDefine 表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
return
name_copy: str = pool.alloc(string.strlen(name) + 1)
if name_copy is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' 名称缓冲区分配失败", "MemoryError")
@@ -177,11 +217,26 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
if name is None:
return
if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64)
self._cdef_idx_init(pool)
tp: rawptr[memhub.MemBuddy] = self._cdefine_table_pool(pool)
tn: list[str] | t.CPtr = list[str](tp, 64)
tv: list[str] | t.CPtr = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(
None, "CDefine 浮点表分配失败(表本体池不可用或耗尽)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self._cdef_idx_init(tp)
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(
None, "CDefine 浮点表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
return
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
if name_copy_d is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 浮点 '{name}' 名称缓冲区分配失败", "MemoryError")
@@ -220,11 +275,26 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串值缺失: {name}", "InternalCompilerError")
return
if self.CDefineNames is None:
self.CDefineNames = list[str](pool, 64)
self.CDefineValues = list[str](pool, 64)
self.CDefineDoubleFlags = list[int](pool, 64)
self.CDefineStrFlags = list[int](pool, 64)
self._cdef_idx_init(pool)
tp: rawptr[memhub.MemBuddy] = self._cdefine_table_pool(pool)
tn: list[str] | t.CPtr = list[str](tp, 64)
tv: list[str] | t.CPtr = list[str](tp, 64)
td: list[int] | t.CPtr = list[int](tp, 64)
tsf: list[int] | t.CPtr = list[int](tp, 64)
if tn is None or tv is None or td is None or tsf is None:
self.Gen.TypeGenErr.fatal_error(
None, "CDefine 字符串表分配失败(表本体池不可用或耗尽)", "MemoryError")
return
self.CDefineNames = tn
self.CDefineValues = tv
self.CDefineDoubleFlags = td
self.CDefineStrFlags = tsf
self._cdef_idx_init(tp)
# 表就绪守卫(同 register_cdefine_constant):append 之前必有非空断言
if self.CDefineNames is None or self.CDefineValues is None \
or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
self.Gen.TypeGenErr.fatal_error(
None, "CDefine 字符串表未就绪(CDefineNames/Values/Flags 为空)", "MemoryError")
return
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
if ncopy_s is None:
self.Gen.TypeGenErr.fatal_error(None, f"CDefine 字符串 '{name}' 名称缓冲区分配失败", "MemoryError")
@@ -459,13 +529,33 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
"""设置 cdefine_found 标志(供跨模块查找函数使用)"""
self.CDefineFound = val
def clear_cdefine_constants(self) -> None:
"""清空 CDefine 常量表(模块切换时调用)"""
self.CDefineNames = None
self.CDefineValues = None
self.CDefineDoubleFlags = None
self.CDefineStrFlags = None
# 名索引随表复位(下次 register 重建);不同步会使 Built 与空表错配
self.CDefineNameIdx = None
self.CDefineNameIdxCap = 0
self.CDefineNameIdxBuilt = -1
"""清空 CDefine 常量表(模块切换时调用)
表本体自首次创建起常驻 PermPool(见 _cdefine_table_pool)⇒ 此处只
**清空内容**、不丢弃表指针:
- 丢指针会退回"下一次 register_cdefine_* 按**调用方 pool** 重建",
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
初始化内存的根因);
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
register 首次 append 即写回第 0 槽。
"""
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()
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
# 与"新建表后首次 register"的索引状态逐字一致)
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
string.memset(self.CDefineNameIdx, 0,
sizet(self.CDefineNameIdxCap) * 8)
self.CDefineNameIdxBuilt = 0
else:
self.CDefineNameIdx = None
self.CDefineNameIdxCap = 0
self.CDefineNameIdxBuilt = -1
self.CDefineIsStr = 0
+78 -23
View File
@@ -121,6 +121,28 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
else:
self.CTypedefNameIdxBuilt = -1
# ============================================================
# CTypedef 表本体的池归一
#
# 表指针挂在**进程级单例** _g_gen.TypeGenCType 上,不在任何 pool 的管辖
# 范围内;而调用方传进来的 pool 在翻译期是「每文件 reset_to 回收」的瞬时池
# (Phase1 1a/1b、Phase2 PhaseA、Repl 每片段/每模块都按 mark/reset 轮转)。
# 表本体若落在该池:reset 之后表指针**悬空但非 None** ⇒ `is None` 守卫失效
# ⇒ 下一次 register_ctypedef 的 append 写进已回收内存(堆损坏;检测模式下
# 该区被 free 填成 0xCD ⇒ append 收到 rdi=0xcdcdcdcdcdcdcdcd 后崩)。
# 故表本体(表对象 + 名索引 + visiting 表)一律落 PermPool(进程级永久池,
# 从不 reset),与"文件级/片段级 reset"彻底解耦。
# PermPool 不可用(无 CompilerFactory.factory 的极简单元程序)时退回入参
# pool —— 与既有语义一致,只是不再有"表本体跨 reset 存活"的隐式假设。
# ============================================================
def _ctypedef_table_pool(self,
pool: rawptr[memhub.MemBuddy]) -> rawptr[memhub.MemBuddy]:
"""CTypedef 表本体的池:恒取 PermPool(不可用则退回入参 pool)"""
if CompilerFactory.factory is not None \
and CompilerFactory.factory.PermPool is not None:
return CompilerFactory.factory.PermPool
return pool
def register_ctypedef(self, pool: rawptr[memhub.MemBuddy],
name: str, type_str: str) -> None:
"""注册 CTypedef 到本地表
@@ -152,20 +174,31 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
return
string.strcpy(type_copy, type_str)
if self.CTypedefNames is None:
self.CTypedefNames = list[str](pool, 64)
self.CTypedefTypeStrs = list[str](pool, 64)
# 名索引随表创建(同池;分配失败则标记不可信,查找回退线性扫描)
self._ctypedef_idx_init(pool)
tp: rawptr[memhub.MemBuddy] = self._ctypedef_table_pool(pool)
tn: list[str] | t.CPtr = list[str](tp, 64)
ts: list[str] | t.CPtr = list[str](tp, 64)
# 建表失败(池不可用/耗尽)⇒ 明确报错并返回,绝不让下面的 append
# 落到 None 或未初始化地址(原实现无检查 ⇒ 写进 0xCDCD 区崩)
if tn is None or ts is None:
VLogger.error("CTypedef 表分配失败(表本体池不可用或耗尽)", "TypeGen")
return
self.CTypedefNames = tn
self.CTypedefTypeStrs = ts
# 名索引随表创建(同池 = PermPool;分配失败则标记不可信,查找回退线性扫描)
self._ctypedef_idx_init(tp)
# 表就绪守卫:表本体缺失(建表失败/被外部置空)⇒ fail-fast,不写垃圾
if self.CTypedefNames is None or self.CTypedefTypeStrs is None:
VLogger.error("CTypedef 表未就绪(CTypedefNames/CTypedefTypeStrs 为空)", "TypeGen")
return
# 去重:若已存在同名 CTypedef,更新其类型字符串
if self.CTypedefNames is not None:
exist_n: sizet = len(self.CTypedefNames)
ei: sizet
for ei in range(exist_n):
en: str = self.CTypedefNames.get(ei)
if en is not None and string.strcmp(en, name_copy) == 0:
# 已存在,更新类型字符串
self.CTypedefTypeStrs.set(ei, type_copy)
return
exist_n: sizet = len(self.CTypedefNames)
ei: sizet
for ei in range(exist_n):
en: str = self.CTypedefNames.get(ei)
if en is not None and string.strcmp(en, name_copy) == 0:
# 已存在,更新类型字符串
self.CTypedefTypeStrs.set(ei, type_copy)
return
self.CTypedefNames.append(name_copy)
self.CTypedefTypeStrs.append(type_copy)
# 追加后同步名索引(槽下标 = 追加前 len = 新 len-1)
@@ -387,15 +420,34 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
return TSpec.TS_SIGN_UNKNOWN
return self._spec_sign_of_span(ts, 0, string.strlen(ts), depth + 1)
def clear_ctypedefs(self) -> None:
"""清空 CTypedef 表(模块切换时调用)"""
self.CTypedefNames = None
self.CTypedefTypeStrs = None
# 名索引随表复位(下次 register 重建);不同步会使 Built 与空表错配
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
"""清空 CTypedef 表(模块切换时调用)
表本体自首次创建起常驻 PermPool(见 _ctypedef_table_pool)⇒ 此处只
**清空内容**、不丢弃表指针:
- 丢指针会退回"下一次 register_ctypedef 按**调用方 pool** 重建",
表本体又落回会被 reset_to 回收的瞬时池(冷 temp 下翻译 includes
时才走到注册路径 ⇒ 表本体跨 reset 悬空 ⇒ append 写进已回收/未
初始化内存的根因);
- 空表与既有语义完全等价:lookup 命中空表即"未命中",
register 首次 append 即写回第 0 槽。
"""
if self.CTypedefNames is not None:
self.CTypedefNames.clear()
if self.CTypedefTypeStrs is not None:
self.CTypedefTypeStrs.clear()
# 名索引清槽(槽存 index+1,0=空槽)+ Built 归零(空表与索引同步;
# 与"新建表后首次 register"的索引状态逐字一致)
if self.CTypedefNameIdx is not None and self.CTypedefNameIdxCap > 0:
string.memset(self.CTypedefNameIdx, 0,
sizet(self.CTypedefNameIdxCap) * 8)
self.CTypedefNameIdxBuilt = 0
else:
self.CTypedefNameIdx = None
self.CTypedefNameIdxCap = 0
self.CTypedefNameIdxBuilt = -1
self.CTypedefResolveDepth = 0
self.CTypedefVisiting = None
if self.CTypedefVisiting is not None:
self.CTypedefVisiting.clear()
# builtins 源扫描标记随表清空复位:每文件最多读一次 includes/builtins.vp
# (_lookup_builtins_typedef 的"最后匹配"兜底,见 TypeGenResolve)
self.BuiltinsTypedefScanned = 0
@@ -421,7 +473,10 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
if name is None:
return 0
if self.CTypedefVisiting is None:
self.CTypedefVisiting = list[str](pool, 32)
# 与 CTypedef 表同池(PermPool):clear_ctypedefs 只清空、指针不丢,
# 避免这张表也落在会被 reset_to 回收的调用方池上
vp: rawptr[memhub.MemBuddy] = self._ctypedef_table_pool(pool)
self.CTypedefVisiting = list[str](vp, 32)
if self.CTypedefVisiting is None:
return 0
vn: sizet = len(self.CTypedefVisiting)
@@ -499,7 +554,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
"""
if pool is None or tree is None:
return 0
ch: rawptr[list[rawptr[ast.AST]]] = tree.children
ch: list[ast.AST*]* = tree.children
if ch is None:
return 0
n: sizet = len(ch)
+43 -15
View File
@@ -81,9 +81,9 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
def dump_parse_traceback(self, file_path: str) -> None:
"""解析失败时以 Python 风格输出 traceback(仿 fatal_error)"""
err_line: int = ast.get_parse_err_line()
err_col: int = ast.get_parse_err_col()
err_msg: str = ast.get_parse_err_msg()
stdio.printf("Traceback (most recent call last):\n")
err_col: int = ast.get_parse_err_col()
err_msg: str = ast.get_parse_err_msg()
stdio.printf("Traceback (most recent call last, compile-time error):\n")
if file_path is None or file_path[0] == 0:
file_path = "<unknown>"
if err_line > 0:
@@ -95,8 +95,7 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
# 前缀 4 空格 + err_col 个空格后落点恰好是该 token 首字符)
if err_col > 0:
stdio.printf(" ")
c: int
for c in range(err_col):
for _ in range(err_col):
stdio.printf(" ")
stdio.printf("^\n")
stdlib.free(src)
@@ -112,9 +111,8 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。
"""
stdio.printf("Traceback (most recent call last):\n")
stdio.printf("Traceback (most recent call last, compile-time error):\n")
# 输出调用栈帧(从底到顶)
i: int
for i in range(self.Gen.TypeGenBase.TraceCount):
frame: rawptr[GenTypes.TraceFrame] = self.Gen.TypeGenBase._get_trace_frame(i)
if frame is not None and frame.file_name is not None:
@@ -176,10 +174,12 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
if err_type is None or err_type[0] == 0:
err_type = "SyntaxError"
stdio.printf("%s: %s\n", err_type, msg)
# 退出前强制刷出:stdout 重定向到文件时是全缓冲,sys.exit 直接终止进程
# 会把整段 traceback 丢掉(实测 2.log 只剩一行 VLogger 输出)。
# 退出前强制刷出:stdout 重定向到文件时是全缓冲,直接终止进程会把整段
# traceback 丢掉(实测 2.log 只剩一行 VLogger 输出)。
stdio.fflush(None)
sys.exit(1)
# 终止点收敛:REPL 会话内经 VLogger.repl_abort longjmp 回 REPL 循环
# (Traceback 已打印,进程存活);批处理下该出口即 sys.exit(1),逐字不变。
VLogger.repl_abort()
def fatal_type_error(self, node: rawptr[ast.AST], msg: str,
col_start: int = 0, col_end: int = 0):
@@ -241,7 +241,35 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
return 0
return 0
def is_pass_body(self, body: rawptr[list[rawptr[ast.AST]]]) -> int:
def has_pointee_volatile_marker(self, node: rawptr[ast.AST]) -> int:
"""检测指针注解 rawptr[...] 的下标实参是否含 t.CVolatile 标记,1=是 / 0=否
语义:rawptr[t.CVolatile | T] 等价 C 的 volatile T*,即"指针所指对象按
volatile 访问"——解引用(p[i] / p.field / c.Deref(p))的 load/store 带
LLVM volatile。与 has_decorator_marker 的区别:后者检测变量/字段**自身**
的可见性限定(如 `locked: t.CVolatile | t.CInt`),对 Subscript 节点返回 0;
本函数专检 rawptr 的 pointee 侧标记(下标实参子树),两者互不串味。
标注形态(vast.parse 实证):
rawptr[t.CVolatile | t.CInt]
→ Subscript(value=Name('rawptr'),
slice=BinOp(BitOr, Attribute(t,CVolatile), Attribute(t,CInt)))
多级指针 rawptr[rawptr[t.CVolatile|T]] 的 slice 是 Subscript,
has_decorator_marker 不递归 Subscript ⇒ 返回 0(明确不支持,见限制清单)。
"""
if node is None or node.kind() != ast.ASTKind.Subscript:
return 0
sub: rawptr[ast.Subscript] = rawptr[ast.Subscript](node)
if sub.value is None or sub.value.kind() != ast.ASTKind.Name:
return 0
rp_nm: rawptr[ast.Name] = rawptr[ast.Name](sub.value)
if rp_nm.id is None or string.strcmp(rp_nm.id, "rawptr") != 0:
return 0
if sub.slice is None:
return 0
return self.has_decorator_marker(sub.slice, "CVolatile", 0)
def is_pass_body(self, body: list[ast.AST*]*) -> int:
"""检测函数体是否为 pass(空体或仅含 pass 语句)"""
if body is None:
return 1
@@ -319,7 +347,7 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
return llvmlite.Int32(pool)
def infer_return_type(self, pool: rawptr[memhub.MemBuddy],
body: rawptr[list[rawptr[ast.AST]]],
body: list[ast.AST*]*,
param_types: str) -> rawptr[llvmlite.LLVMType]:
"""扫描函数体中的 return 语句,推断返回类型"""
if body is None:
@@ -341,7 +369,7 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
ret_ty = expr_ty
return ret_ty
def body_has_value_return(self, pool: rawptr[memhub.MemBuddy],
body: rawptr[list[rawptr[ast.AST]]]) -> int:
body: list[ast.AST*]*) -> int:
"""扫描函数体,判断是否存在返回非 None 值的 return
递归检查控制流语句(if/while/for/try/with/match)内层 return,但跳过
@@ -373,7 +401,7 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
elif k == ast.ASTKind.If or k == ast.ASTKind.While or \
k == ast.ASTKind.For or k == ast.ASTKind.Try or \
k == ast.ASTKind.With or k == ast.ASTKind.Match:
sub: rawptr[list[rawptr[ast.AST]]] = stmt.children
sub: list[ast.AST*]* = stmt.children
if sub is not None and len(sub) > 0:
if self.body_has_value_return(pool, sub) != 0:
return 1
@@ -395,7 +423,7 @@ class TypeGenErr(TypeGenBase.TypeGenBase):
ags: rawptr[ast.Arguments] = rawptr[ast.Arguments](args_node)
if ags.args is None:
return None
alist: rawptr[list[rawptr[ast.AST]]] = ags.args
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
if an == 0:
return None
+143 -7
View File
@@ -17,6 +17,7 @@ import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger as StubMerger
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.core.CompilerFactory as CompilerFactory
import lib.Projectrans.Config as Config
@@ -151,7 +152,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return self._list_elts_to_type_str(pool, tup.elts, "", "")
return None
def _list_elts_to_type_str(self, pool: rawptr[memhub.MemBuddy],
elts: rawptr[list[rawptr[ast.AST]]],
elts: list[ast.AST*]*,
open_ch: str, close_ch: str) -> str:
"""元素列表转逗号分隔字符串(List/Tuple 共用),包上 open/close 括号
@@ -230,6 +231,42 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
string.strcat(buf, ".")
string.strcat(buf, cls_name)
return buf
def _repl_opaque_class_ty(self, pool: rawptr[memhub.MemBuddy],
mod_sha1: str,
cls_name: str) -> rawptr[llvmlite.LLVMType]:
"""(REPL 会话专用)未登记类别的"仅类型"占位:`%"{sha1}.{类名}"` opaque 具名结构体
背景:REPL 的 includes 类注册(M4.2)只登记**布局可信**的类;缺 `.stub.ll`
的模块其带父类/虚表的类被拒 ⇒ 该类的类型不在会话 StructTable 里。而其它模块
的 `.vpi` 一旦在形参/返回/字段注解里引用它(如
`def __new__(self, pool: rawptr[memhub.MemManager])`),注入侧的 S4
fail-fast(`VpiReader._inject_method_declare` 的"类型注解解析失败")就会
中断**整个模块**的 vpi 注入——报错点在该注解,真因是"被引类未登记"。
本函数按定义侧命名(`{sha1}.{类名}`,与 ClassGenDef / ClassGenVTable 逐字
一致)造 opaque 具名结构体:指针引用与宿主签名逐字兼容(`%"{sha1}.{类名}"*`),
且**不写 StructTable** ⇒ `lookup_field_by_class`/`find_struct_by_module`
仍按"类未注册"明确报错,绝不按(未知的)字段偏移静默访问(M4.2 的
"宁可报错"口径不变)。
调用面严格限定在 `trans.ReplCtx != None`(REPL 会话)且**模块已知**
(sha1 已由 `StubMergerStore` 解析出)的路径:批处理 `trans.ReplCtx`
恒 None ⇒ 本函数不被调用,批处理逐字不变;模块名未登记时调用方不接受
本兜底(那是真实的"未编译/未扫描"错误,仍 fail-fast)。
"""
if pool is None or mod_sha1 is None or cls_name is None:
return None
if mod_sha1[0] == '\0' or cls_name[0] == '\0':
return None
full: str = self._concat_sha1_class(pool, mod_sha1, cls_name)
if full is None:
return None
ex: rawptr[llvmlite.LLVMType] = self.Gen.StructGen.get_struct_type(full)
if ex is not None:
return ex
# fcount=-1 = opaque 哨兵(区别于空结构体 {}:后者 fcount==0 是"完整定义、
# 可 alloca");与 llvmlite 的跨模块 opaque 声明同一表示。
return llvmlite.Struct(pool, None, -1, full)
def _norm_type_arg_name(self, pool: rawptr[memhub.MemBuddy],
type_name: str,
from_imports: str) -> str:
@@ -610,7 +647,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# parse_tokens 成功,故此处用文件级 parse 兜底)
tree_s: rawptr[ast.AST] = ast.parse(type_str, pool)
if tree_s is not None:
ch_s: rawptr[list[rawptr[ast.AST]]] = tree_s.children
ch_s: list[ast.AST*]* = tree_s.children
if ch_s is not None and len(ch_s) > 0:
st0: rawptr[ast.AST] = ch_s.get(0)
if st0 is not None and st0.kind() == ast.ASTKind.Expr:
@@ -1766,7 +1803,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# Tuple slice: dict[str, int] → "str,int"
sub_tup: rawptr[ast.Tuple] = rawptr[ast.Tuple](sub.slice)
if sub_tup.elts is not None:
tup_elts_sn: rawptr[list[rawptr[ast.AST]]] = sub_tup.elts
tup_elts_sn: list[ast.AST*]* = sub_tup.elts
tn_sn: sizet = len(tup_elts_sn)
# 计算总长度
tup_total: sizet = 1
@@ -1827,7 +1864,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 用于函数 `-> tuple[str, t.CUnsignedShort]`(如 binascii.b2a_hqx)
# 与调用端 `a, b = f()` 的打包/解包。
if string.strcmp(gen_cls_name, "tuple") == 0:
tp_elts: rawptr[list[rawptr[ast.AST]]] = None
tp_elts: list[ast.AST*]* = None
tp_count: sizet = 0
if sub.slice.kind() == ast.ASTKind.Tuple:
tp_tup: rawptr[ast.Tuple] = rawptr[ast.Tuple](sub.slice)
@@ -1837,7 +1874,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
else:
# [FIX] elem_size=8(rawptr[ast.AST] 指针 8 字节),原传 1 → get 只读
# 1 字节拼指针(高 56 位残留)→ tuple 元素解析垃圾
tp_single: rawptr[list[rawptr[ast.AST]]] = list[rawptr[ast.AST]](pool, 8)
tp_single: list[ast.AST*]* = list[rawptr[ast.AST]](pool, 8)
if tp_single is not None:
tp_single.append(sub.slice)
tp_elts = tp_single
@@ -1870,7 +1907,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
gen_arg_names: list[str] | t.CPtr = list[str](pool, 8)
if sub.slice.kind() == ast.ASTKind.Tuple:
gen_tp: rawptr[ast.Tuple] = rawptr[ast.Tuple](sub.slice)
gen_elts: rawptr[list[rawptr[ast.AST]]] = gen_tp.elts
gen_elts: list[ast.AST*]* = gen_tp.elts
if gen_elts is not None:
gen_en: sizet = len(gen_elts)
gen_ei: sizet
@@ -1909,6 +1946,45 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if gen_atn is not None:
string.strcat(gen_mangled, gen_atn)
string.strcat(gen_mangled, "]")
# [REPL 注入期] 只做类型/签名层面的事,**绝不触发模板体翻译**(特化延后)。
#
# 现场:Repl 会话初始化先**真注册**了 includes 泛型模板(Repl.vp 步骤③''),
# 随后逐模块注入 .vpi(ReplStubLayoutBegin → ReplInitIncludesClasses,逐模块
# _setjmp)。注入的只是类字段/方法**签名**,此刻会话 StructTable 只含"已注入
# 模块的已注册类":模板体引用的**跨模块**类型(_list.vp 的 memhub.MemManager)
# 与**同模块**类型(linkedlist.vp 的 NameEntry)都可能尚未注册(模板是全局
# 预注册的,其归属模块未必已注入、同模块兄弟类也未必已注册)。
# 故一旦在此特化,_specialize_generic_class 就会 translate_class_def 翻译模板
# 类体源码(字段/方法注解解析)⇒ 命中未注册类型 ⇒ lookup_field_by_class /
# 形参注解解析失败 fatal ⇒ 该模块整个 .vpi 注入中断(异常被逐模块 _setjmp
# 隔离,但该模块的类全部注册不上 + Traceback 刷屏)。
# 结论:注入期**不特化**,直接返回**可用的 opaque 特化名**
# (`%"{模板模块 sha1}.{mangled}"`,与定义侧特化命名逐字一致)——注解解析
# 成功、不触碰任何未注册类型、不写 StructTable(字段访问照旧按"类未注册"
# 明确报错)。真正需要布局/方法时在**片段期**走下方既有特化路径:ReplCtx
# 仍在但 ReplStubLayoutMode 已归零 ⇒ 本分支不经过,特化照常发生(届时按需
# 注册的依赖已就绪;确实缺失则明确报错,落在片段内、不影响启动)。
# 批处理 ReplCtx 恒 None 且 ReplStubLayoutMode 恒 0 ⇒ 本分支不经过,逐字不变。
if trans is not None and trans.ReplCtx is not None \
and VpiReader.ReplStubLayoutMode != 0:
# 已登记的特化结构体(会话期先前注册)直接复用:本分支只读、不翻译
spec_ty_inj: rawptr[llvmlite.LLVMType] = self.Gen.StructGen.get_struct_type(gen_mangled)
if spec_ty_inj is not None:
return spec_ty_inj
# 归属模块 sha1:优先取**全局模板表**(步骤③'' 已按 includes 全量注册,
# 与模块注入顺序无关 ⇒ 跨模块泛型如 GSList 也能拼出定义侧特化名);
# 退回注入期逐模块登记的归属表(StubMergerStore.RegisterGenericOwner)。
inj_owner: str = self.Gen.ClassGenGeneric._find_generic_template_sha1(gen_cls_name)
if inj_owner is None:
inj_owner = StubMergerStore.LookupGenericOwnerSha1(gen_cls_name)
if inj_owner is not None:
ty_inj: rawptr[llvmlite.LLVMType] = self._repl_opaque_class_ty(
pool, inj_owner, gen_mangled)
if ty_inj is not None:
return ty_inj
# 归属未知(模板/归属均未登记)⇒ 保持原口径返回 None,由上层按
# "注解解析失败"明确报错(不伪造无模块前缀的类名)
return None
# 查找已特化的 struct(特化由实例化调用 list[int](pool) 触发)
gen_spec_ty: rawptr[llvmlite.LLVMType] = self.Gen.StructGen.get_struct_type(gen_mangled)
if gen_spec_ty is not None:
@@ -1971,6 +2047,26 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
f"泛型下标 '{gen_mangled}' 未解析出特化结构体(泛型模板='{gen_cls_name}');"
f"该注解将退化为裸基类/i8* → 后续 .get() 按错误元素类型分派并截断指针",
"TypeError")
# [REPL 会话] includes 泛型模板(list/dict/Vector/…)在会话里**未注册**:会话
# 类注册只注入 `.vpi` 的类(泛型类被跳过),不跑批处理的
# _PreRegister*GenericTemplates ⇒ 泛型下标的特化恒 miss。若该注解位于形参/
# 返回位置,S4 fail-fast 会中断**整个模块**的 `.vpi` 注入(实测:93da… 的
# `targets: rawptr[list[rawptr[AST]]]`;9 个 `.vpi` / 78 条 def 签名行含
# list[/dict[,其中 93da… 30 条)。故按定义侧特化命名
# "%{模板模块 sha1}.{mangled}" 造 opaque 具名类型占位(实证
# `%"b56d2f995104690d.list[rawptr[AST]]"`):方法调用按类名仍可解析(符号在
# 宿主里),字段访问按"类未注册/字段未找到"明确报错(不静默按错偏移访问)。
# 模板模块 sha1 取**登记事实**(VpiReader 注入期扫 ClassDef 登记,见
# StubMergerStore.RegisterGenericOwner),不按名字猜。
# 批处理 trans.ReplCtx 恒 None ⇒ 不经过;本分支只在上述**全部**查找(含特化
# 触发)失败后到达 ⇒ 不改变任何既有成功路径。
if trans is not None and trans.ReplCtx is not None:
gowner_sha1: str = StubMergerStore.LookupGenericOwnerSha1(gen_cls_name)
if gowner_sha1 is not None:
ty_repl_g: rawptr[llvmlite.LLVMType] = self._repl_opaque_class_ty(
pool, gowner_sha1, gen_mangled)
if ty_repl_g is not None:
return ty_repl_g
# 未特化:返回 None(不报错,让后续流程处理)
return None
def resolve_annotation_type(self, pool: rawptr[memhub.MemBuddy],
@@ -2134,12 +2230,14 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# trans.ModuleSha1 是"正在翻译的文件",而注解里的类归属 stub 源模块
# (from_imports 即该 stub 的名字→模块映射)。仅靠"当前模块 sha1 + 全局可见性"
# 在 App 严格模式下会查不到(如 ast.stmts.Expression 经 ast.__init__.vpi 注入)。
fi_sha1_rn: str = None
if trans is not None and from_imports is not None:
fi_mod: str = StubMergerStore.LookupModNameByAlias(from_imports, nm.id)
if fi_mod is not None:
fi_sha1: str = StubMergerStore.LookupSha1ByModName(fi_mod)
if fi_sha1 is None:
fi_sha1 = StubMergerStore.LookupSha1ByModNameSuffix(fi_mod)
fi_sha1_rn = fi_sha1
stdlib.free(fi_mod)
if fi_sha1 is not None:
fi_entry: rawptr[GenTypes.StructEntry] = self.Gen.StructGen.find_struct_by_module(nm.id, fi_sha1)
@@ -2237,6 +2335,29 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
pool, lib_node_n, imported_modules, from_imports, trans, depth + 1)
if lib_ty_n is not None:
return lib_ty_n
# [REPL 会话] 归属模块已知(from_imports 解析出 sha1;或本模块自身 =
# 注入期 trans.ModuleSha1 = 当前 stub 的源模块 sha1)但该类未在会话
# StructTable 登记(M4.2 布局不可信被拒 / 字段类型不可解析被跳过)⇒
# 用 opaque 具名类型占位,使该注解可解析(不写 StructTable,字段访问
# 仍按"类未注册"明确报错)。批处理 trans.ReplCtx 恒 None ⇒ 不经过;
# 本分支在上述全部查找失败后才到达 ⇒ 不改变任何既有成功路径。
if trans is not None and trans.ReplCtx is not None:
own_sha_rn: str = fi_sha1_rn
# 本模块裸类名:仅当 trans.ModuleSha1 是"当前 stub 的源模块 sha1"时可用
# (REPL 会话初始化注入期由 ReplInitIncludesClasses 逐模块设置)。
# 片段翻译期 trans.ModuleSha1 = 会话 sha1(GenTypes.REPL_SESSION_SHA1):
# 此刻裸名找不到归属就**不能**拿会话 sha1 当归属(会造出
# "%{会话sha1}.{类名}" 这种宿主里不存在的类名 → 方法调用符号名误导),
# 故排除该情形,保持原 fail-fast。
if own_sha_rn is None:
if trans.ModuleSha1 is not None \
and string.strcmp(trans.ModuleSha1, GenTypes.REPL_SESSION_SHA1) != 0:
own_sha_rn = trans.ModuleSha1
if own_sha_rn is not None:
ty_repl_n: rawptr[llvmlite.LLVMType] = self._repl_opaque_class_ty(
pool, own_sha_rn, nm.id)
if ty_repl_n is not None:
return ty_repl_n
return None
# Attribute 节点: int, uint64, namespace_defs.PlainStruct 等
@@ -2306,9 +2427,14 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# SHA1 前缀形态优先用完整名 "{sha1}.{Class}" 精确命中定义侧
# struct(get_struct_type 类名回退可能命中跨模块同名类)
full_attr_ty: rawptr[llvmlite.LLVMType] = None
# mod_sha1_rv:把本分支解析出的"模块段 sha1"带到末尾(REPL 会话的
# "仅类型"占位兜底要用它;声明在此以覆盖下方 SHA1 前缀形态与枚举/
# typedef 尝试都在同一作用域)。
mod_sha1_rv: str = None
# val_name.id 是模块名段:恰为 16 位小写 hex 才是 SHA1 别名
#(判定走唯一真源 GenApi.is_sha1_hex16,C10 收敛)。
if is_builtin_attr == 0 and GenApi.is_sha1_hex16(val_name.id) != 0:
mod_sha1_rv = val_name.id
full_q: str = self._concat_sha1_class(pool, val_name.id, at.attr)
if full_q is not None:
full_attr_ty = self.Gen.StructGen.get_struct_type(full_q)
@@ -2324,6 +2450,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# StructGenMixin._resolve_module_sha1 逐级尝试)
mod_sha1_v: str = self.Gen.StructGen._resolve_module_sha1(
val_name.id, from_imports)
mod_sha1_rv = mod_sha1_v
if mod_sha1_v is not None:
full_q_v: str = self._concat_sha1_class(pool, mod_sha1_v, at.attr)
if full_q_v is not None:
@@ -2366,6 +2493,15 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return mod_td_ty
else:
self.Gen.TypeGenCType._ctypedef_pop_visiting()
# [REPL 会话] 模块限定名(memhub.MemManager 等)的归属模块已知,
# 但该类未在会话 StructTable 登记(M4.2 布局不可信被拒 / 字段类型
# 不可解析被跳过)⇒ 用 opaque 具名类型占位(不写 StructTable ⇒
# 字段访问仍按"类未注册"明确报错)。批处理 trans.ReplCtx 恒 None。
if trans is not None and trans.ReplCtx is not None and mod_sha1_rv is not None:
ty_repl_a: rawptr[llvmlite.LLVMType] = self._repl_opaque_class_ty(
pool, mod_sha1_rv, at.attr)
if ty_repl_a is not None:
return ty_repl_a
return None
# 嵌套 Attribute: w32.win32base.SECURITY_ATTRIBUTES 等三级及以上限定名
# at.value 是 Attribute 而非 Name,递归提取最终类名(at.attr)查结构体表
@@ -2755,7 +2891,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
tup: rawptr[ast.Tuple] = rawptr[ast.Tuple](sub.slice)
if tup.elts is None:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray Tuple 缺少 elts")
elts_list: rawptr[list[rawptr[ast.AST]]] = tup.elts
elts_list: list[ast.AST*]* = tup.elts
elts_n: sizet = len(elts_list)
if elts_n < 2:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray Tuple 元素不足")
File diff suppressed because it is too large. Load diff
+2980
View File
File diff suppressed because it is too large. Load diff
+62 -4
View File
@@ -14,6 +14,53 @@ import w32.win32base as win32base
# 输出格式: ├ LEVEL: message / ├ LEVEL[category]: message(与 TPC lib/core/VLogger.py 对齐)
# ============================================================
# 错误恢复(REPL/JIT 模式):error 打印后 longjmp 跳回 REPL 的 setjmp 点。
# mingw-w64 x86_64:longjmp 原型为 void longjmp(jmp_buf, int);setjmp 宏展开为
# _setjmp(buf, NULL)(jmp_buf 实为 _JBTYPE[16],见 Repl.vp 的 _REPL_JMPBUF_SIZE)。
extern def longjmp(buf: t.CPtr, val: int)
# REPL 恢复点指针(由 Repl 在会话开始时注册、会话结束时清除)。
# 生命周期 = 整个 REPL 会话:Repl 每轮片段都会重新武装(Repl.run 每轮 _setjmp +
# Repl.ReplExecFragment 入口重注册),故它覆盖整条片段流水线(翻译 → 写 .ll →
# llc → clang++ → LoadLibrary → GetProcAddress → 调用 → 回显),而不只是翻译期。
# 批处理路径从不调用 set_repl_jmpbuf ⇒ 恒为 None ⇒ 所有出口退回 sys.exit(1)。
_repl_jmpbuf: t.CPtr = None
# ============================================================
# repl_abort - TPV 内"诊断后终止"的唯一出口
#
# 约定:调用方**已打印**诊断(Traceback / VLogger.error / [OOM] 等),本函数只决定
# "跳回 REPL 继续"还是"终止进程":
# * REPL 会话(_repl_jmpbuf 已注册):fflush 后 longjmp 回 Repl.run 的恢复点,
# 进程存活、提示符继续(宽容 Traceback);
# * 批处理 / 未武装:sys.exit(1),与改动前的 fail-fast 行为逐字一致。
# 收敛点:Logger.error、模块级 error、TypeGenErr.fatal_error、
# ImportsGenMixin._report_import_error、CallGenBase/VpiReader 的链表哨兵。
# ============================================================
def repl_abort() -> int:
"""诊断后的统一出口:已注册恢复点则 longjmp 回 REPL,否则终止进程。"""
if _repl_jmpbuf is not None:
stdio.fflush(None)
longjmp(_repl_jmpbuf, 1)
sys.exit(1)
return 0
def set_repl_jmpbuf(buf: t.CPtr) -> int:
"""注册 REPL 的 jmp_buf(会话开始;每片段入口可幂等重注册)。"""
global _repl_jmpbuf
_repl_jmpbuf = buf
return 0
def clear_repl_jmpbuf() -> int:
"""清除已注册的 jmp_buf(REPL 会话结束时调用;中途不清)。"""
global _repl_jmpbuf
_repl_jmpbuf = None
return 0
# 日志级别
class LogLevel(t.CEnum):
DEBUG = 0
@@ -47,12 +94,14 @@ class Logger:
_level: int
_console_handle: win32base.HANDLE
_use_color: int
jit_flag: bool
__mbuddy__: rawptr[memhub.MemBuddy]
def __init__(self, level: int = 1):
self._level = level
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
self._use_color = 1
self.jit_flag = False
self.__mbuddy__ = _mbuddy
def _set_color(self, attr: WORD) -> int:
@@ -108,7 +157,11 @@ class Logger:
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg,
FOREGROUND_RED,
FOREGROUND_RED | FOREGROUND_INTENSITY, category)
# 错误立即终止进程(避免错误后继续执行导致连锁崩溃)
# JIT/REPL 模式:诊断已打印,经统一出口跳回 REPL 循环(未武装时该出口仍终止进程)
if self.jit_flag:
repl_abort()
return ret
# 批处理模式:错误立即终止进程(行为与改动前逐字一致)
sys.exit(1)
return ret
@@ -128,6 +181,11 @@ class Logger:
self._level = level
return 0
def set_jit_flag(self, v: bool) -> int:
"""设置 JIT/REPL 模式标志:True 时 error 改为 longjmp 回 REPL 循环。"""
self.jit_flag = v
return 0
# ============================================================
# 高级日志方法
# ============================================================
@@ -211,10 +269,10 @@ def warning(msg: str, category: str = "") -> int:
def error(msg: str, category: str = "") -> int:
"""输出 ERROR 级别日志并立即终止进程。"""
"""输出 ERROR 级别日志:批处理模式终止进程;JIT/REPL 模式 longjmp 回 REPL。"""
if log is not None: return log.error(msg, category)
# logger 不可用时也直接退出
sys.exit(1)
# logger 不可用时也走统一出口(REPL 已注册恢复点时仍可恢复)
repl_abort()
return 0
+72 -4
View File
@@ -1,5 +1,6 @@
import lib.core.Gen.GenApi as GenApi
import lib.core.VLogger as VLogger
import lib.core.Repl as Repl
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.core.IncludesScanner as IncludesScanner
@@ -41,13 +42,16 @@ CODE_PAGE: t.CDefine = 65001
SRC_BUF_SIZE: t.CDefine = 1048576
# Version (dev/beta/rc)
V_VIPER_VERSION: t.CDefine = "1.0.3-dev.31 2026-07-27 Non-LTS"
V_TPV_VERSION: t.CDefine = "0.0.3-dev.428"
V_TPV_VERSION: t.CArray[char] | t.attr.section(".PRVERSN") = "0.0.3-dev.428"
V_RUNTIME_VERSION: t.CDefine = "0.2.3+19-Non-LTS"
V_TPC_COMMIT: t.CDefine = "e376b949c9"
V_TPV_COMMIT: t.CDefine = "ab0f37e1a8"
V_RUNTIME_COMMIT: t.CDefine = "f087aa88e2"
V_TARGET: t.CDefine = "x86_64-w64-windows-gnu"
V_LLVM: t.CDefine = "22.1.8"
V_TARGET: t.CDefine = "x86_64-w64-windows-gnu"
V_LLVM: t.CDefine = "22.1.8"
# --repl 模板工程查找缓冲(repl.vpj 命中路径 / 未命中诊断文本)
REPL_PROJ_PATH_CAP: t.CDefine = 1024
REPL_PROJ_DIAG_CAP: t.CDefine = 2048
# ============================================================
# main: Command-line entry
@@ -152,6 +156,7 @@ def main() -> int:
# 早于 parse_args,值取自 L70-72 的原始命令行 strstr —— 与 --detection 同范式)
parser.add_argument("--detection-deep", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Deep detection: probe every allocation and enable the periodic full-table scan (implies --detection; extremely slow, diagnostics only)")
parser.add_argument("--show-args", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show all parsed arguments")
parser.add_argument("--repl", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Start the interactive REPL (M2 session variables + M3 cross-fragment functions: fragment -> DLL -> call)")
parser.add_argument("--build-dir", None, argparse.STRING, 0, None, False, argparse.STORE, "Override project.vpj build_dir (default ./.tpv_build; relative paths resolve against the project dir)")
parser.add_argument("--no-llvmpeephole", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Disable all local llvmlite peephole passes (T2 store/load forward & ret-zero, T3 no-op bitcast) in FunctionPrint serialization")
parser.add_argument("--version", "-v", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show version and exit")
@@ -183,6 +188,37 @@ Target: %s
""", V_VIPER_VERSION, V_RUNTIME_VERSION, V_TPV_VERSION, V_TPV_COMMIT, V_TPC_COMMIT, V_RUNTIME_COMMIT, V_TARGET, V_LLVM)
return 0
# === --repl 守卫:与 --phase2command / --asan 互斥 ===
# 位置:在下面 --asan / --phase2command 的全局状态设置与清理副作用之前
#(--asan 会切 includes 缓存后缀并清空 .binary_asan、--phase2command 会设置
# BuildPipeline._phase2_cmd_path),故必须最先判定。
# ① --phase2command:_phase2_cmd_path 非空时 compile_ll_to_obj 只把 opt/llc
# 命令写进 bat 而**不执行**(BuildPipeline.vp:322-344)⇒ 片段 .obj 不会生成,
# Repl 第 ⑩ 步的 clang++ -shared 必然失败(Repl.vp 的 ReplExecFragment)。
# ② --asan:会给片段 .ll 先做 opt -passes=asan 插桩
# (BuildPipeline.vp:346-361),而片段产物是运行期 LoadLibraryA 载入的 DLL,
# 宿主进程并未挂 ASan 运行库,"片段 obj 被插桩"语义不明。
repl_flag: bool = args.get_bool("repl")
if repl_flag:
if args.get_str("phase2command") is not None:
stdio.printf("FAIL: --repl 与 --phase2command 互斥\n")
stdio.printf(" 原因: --phase2command 下 compile_ll_to_obj 只写 bat 不执行 llc,\n")
stdio.printf(" 片段 .obj 不会生成,随后的 clang++ -shared 必然失败\n")
stdio.printf(" 处理: 去掉 --phase2command(REPL 需要立即产出并加载片段 DLL)\n")
argparse.release(args)
return 2
if args.get_bool("asan"):
stdio.printf("FAIL: --repl 与 --asan 互斥\n")
stdio.printf(" 原因: --asan 先对片段 .ll 做 ASan 插桩,而片段产物是运行期\n")
stdio.printf(" LoadLibraryA 载入的 DLL,宿主进程未挂 ASan 运行库,插桩语义不明\n")
stdio.printf(" 处理: 去掉 --asan(如需排查内存问题,请用工程模式构建 ASan 版 TransPyV)\n")
argparse.release(args)
return 2
# === --repl: M1 交互式循环(表达式片段 → DLL → 调用回显)===
# 分支位置:见下方"工程配置加载"之后(Repl 需要 Config 的 compiler/linker/target 通道),
# 仍在 Phase1/Phase2 之前;该分支直接返回。
# 打印解析结果
# log.banner("TransPyV 参数解析结果")
@@ -249,9 +285,30 @@ Target: %s
llvmlite.G_PeepEnable = 0
log.info("--no-llvmpeephole 已启用:关闭 FunctionPrint 本地窥孔(RenumberSSA 保留)", "peep")
# 加载 project.vpj 配置
# === --repl 启动便利:未给 --project 时按 cwd → exe 同目录 查找 repl.vpj ===
# 命中后按 --project 等价路径喂给下面既有的 Config.Load_project_config 流程
#(不另写一套加载器);两处都没有则 fail-fast,诊断文本含"需要 --project"、
# 两个已查位置与模板位置(由 Repl.ReplFindProject 写入 repl_diag_buf)。
proj_loaded: int = 0
proj_path: str = project
if repl_flag and proj_path is None:
repl_proj_buf: bytes = stdlib.malloc(REPL_PROJ_PATH_CAP)
repl_diag_buf: bytes = stdlib.malloc(REPL_PROJ_DIAG_CAP)
if repl_proj_buf is None or repl_diag_buf is None:
stdio.printf("FAIL: --repl 查找 repl.vpj 的路径缓冲分配失败\n")
argparse.release(args)
return 1
if Repl.ReplFindProject(repl_proj_buf, REPL_PROJ_PATH_CAP,
repl_diag_buf, REPL_PROJ_DIAG_CAP) != 0:
proj_path = repl_proj_buf
log.info(f"--repl: 未指定 --project,使用查找到的模板工程 {proj_path}", "repl")
else:
stdio.printf("FAIL: --repl 未找到工程文件 repl.vpj\n")
stdio.printf("%s", repl_diag_buf)
stdio.printf(" 也可用 --project 显式指定工程文件(如本仓库的 TransPyV/project.vpj)\n")
argparse.release(args)
return 1
# 加载工程配置(显式 --project / --repl 查找到的 repl.vpj)
if proj_path is not None:
log.banner("工程配置")
cfg_ret: int = Config.Load_project_config(proj_path)
@@ -290,6 +347,17 @@ Target: %s
Config.resolve_paths(proj_dir)
Config.print_config()
# === --repl: M1 交互式循环(表达式片段 → IR → llc → clang++ -shared → DLL → 调用回显)===
# 位置:工程配置加载之后(Repl 经 Config 取 compiler/linker 命令与 target triple;
# 未加载 project.vpj 时 Repl 退回内置默认 llc/clang++),仍在 Phase1/Phase2 之前。
if args.get_bool("repl"):
Repl._perm_pool = perm_pool # jmp_buf/解析表/会话缓冲须跨行存活,必须用 PermPool
if log is not None:
log.set_jit_flag(True) # error 改为 longjmp 回 REPL(Repl.run 退出时复位 False)
rc_repl: int = Repl.run(mb)
argparse.release(args)
return rc_repl
# --asan 要求工程在 project.vpj 的 linker.static_libs 声明 ASan 运行库(TPV 不硬编码路径),缺失则 fail-fast
if BuildPipeline._asan_mode != 0 and Config.asan_runtime_declared() == 0:
log.error("启用 --asan 需要在 project.vpj 的 linker.static_libs 里声明 ASan 运行库:", "asan")
+2
View File
@@ -138,6 +138,7 @@ exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
| `--rebuild-includes` | 只删 includes 预编译缓存(`includes.binary`),强制重编 includes |
| `--clear-cache` | 只清 temp 缓存(`.vpi` / `.stub.ll` / `.text.ll`),强制重生成 stub |
| `--run` | 编译成功后立即执行产物 |
| `--repl` | 交互式 REPL(M1:表达式片段 → DLL → 调用回显);未给 `--project` 时按 cwd → `TransPyV.exe` 同目录查找 `repl.vpj` 模板工程(找不到则报错并提示 `--project`);与 `--asan` / `--phase2command` 互斥 |
| `--asan` | ASan 模式:先 `opt -passes=asan` 插桩;运行库须由 `project.vpj` 的 `linker.static_libs` 声明,否则 fail-fast |
| `--phase2command <bat>` | 不执行 `llc`/链接,只把命令写进指定 `.bat` |
| `--nowarn-register` | 抑制 Phase A 依赖类 WARN(跨模块 `text.ll` 未生成,下遍收敛) |
@@ -349,6 +350,7 @@ exe\TransPyV.exe --project TransPyV\Test\project.vpj --clean --run
| `--rebuild-includes` | delete only the includes cache (`includes.binary`), forcing a full includes rebuild |
| `--clear-cache` | clear only temp (`.vpi` / `.stub.ll` / `.text.ll`), forcing stub regeneration |
| `--run` | execute the produced binary on success |
| `--repl` | interactive REPL (M1: expression fragment → DLL → call and echo); without `--project` it looks for the `repl.vpj` template project in cwd, then next to `TransPyV.exe` (errors out with a `--project` hint if neither exists); mutually exclusive with `--asan` / `--phase2command` |
| `--asan` | ASan mode: `opt -passes=asan` first; runtimes must be declared in `project.vpj` `linker.static_libs`, else fail-fast |
| `--phase2command <bat>` | do not run `llc`/link; write the commands into the given `.bat` |
| `--nowarn-register` | suppress Phase A dependency WARNs (cross-module `text.ll` not yet emitted, converges next pass) |
+1
View File
@@ -24,6 +24,7 @@
"-Wl,--allow-multiple-definition",
"-Wl,--stack,0x1000000",
"-Wl,--no-insert-timestamp",
"-Wl,--export-all-symbols",
"-g"
],
"static_libs": [
+41
View File
@@ -0,0 +1,41 @@
{
"$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json",
"name": "viperepl",
"version": "1.0.0",
"build_dir": "%temp%/viperepl/.tpv_build",
"compiler": {
"cmd": "llc",
"flags": [
"-filetype=obj",
"-relocation-model=pic",
"-O0"
]
},
"linker": {
"cmd": "D:/msys64/mingw64/bin/clang++.exe",
"flags": [
"-fuse-ld=lld",
"-lmsvcrt",
"-lucrt",
"-lpthread",
"-lmingwex",
"-lkernel32",
"-Wl,--allow-multiple-definition",
"-Wl,--stack,0x1000000",
"-Wl,--no-insert-timestamp",
"-g"
],
"output": "repl.dll"
},
"includes": [
"../includes"
],
"target": {
"triple": "x86_64-pc-windows-gnu",
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"sha1_slice_level": 1,
"strict_mode": true
}
}