fix(tpv): 泛型类型参数 SHA1 规范化 + 自举链接修复

- 类型参数统一 "{定义模块SHA1}.{Class}"(ast.AST/AST → f467978339422f32.AST),
  消除跨模块特化名分裂导致的 undefined symbol(list[AST|t.CPtr].set 根因)
- TypeGenResolve 新增 _norm_type_arg_name/_is_sha1_prefixed/_concat_sha1_class;
  resolve_annotation_type 兼容 "{16hex}.Class" Attribute 形态(跳过
  imported_modules 检查,优先完整名命中定义侧 struct)
- CallGenCtor._extract_type_args_from_slice / extract_class_name_from_annotation
  新增 from_imports 参数,8 处调用方传 trans._from_imports 保持注册/查找一致
- 修复模块级函数链式调用推断(get_perm_pool() 联合返回 → .alloc() 裸名 alloc)
- 跨模块方法 declare self 改为类指针 %"sha1.Class"*,调用侧按形参 coerce bitcast
- 撤销泛型特化双 SHA1 前缀,恢复单前缀(模板 SHA1)
- 新增 --phase2command xxx.bat:不执行 llc/链接,命令写入 bat
This commit is contained in:
TermiNexus committed 2026-08-23 12:00:04 +08:00
1 parent a6e28c71f5
commit a12dcafed2
21 files changed
+533 -108

No files matched your search

+80
View File
@@ -41,6 +41,13 @@ _mbuddy: memhub.MemBuddy | t.CPtr
# ASan 模式(--asan):对 .ll 先 opt -passes=asan 插桩,链接加 -fsanitize=address
_asan_mode: bool = 0
# --phase2command xxx.bat:不执行 llc/链接命令,只把 Phase B/B+/C 的
# llc(含 ASan opt)与 clang 链接命令写入指定 .bat 路径(由用户手动执行)。
# None = 正常执行(默认)。
_phase2_cmd_path: str = None
# bat 文件是否已写入 header(@echo off),首次写入时置 1
_phase2_bat_init: int = 0
# 源代码缓冲区大小(1MB)
SRC_BUF_SIZE: t.CDefine = 1048576 # 2^20 = 1MB
@@ -337,6 +344,31 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
# -asan-globals=false 关闭全局插桩:TPV 对全局变量(如各模块 _g_gen)
# 用直接地址寻址,ASan 全局 redzone 重定位后引用错位会读到垃圾指针。
compile_input: str = ir_path
# --phase2command 模式:不执行 opt/llc,把 opt(ASan)+ llc 命令写入 bat
if _phase2_cmd_path is not None:
p2_opt_cmd: str = None
if _asan_mode != 0:
p2_asan: str = build_sliced_path(output_dir, module_name, "asan.ll")
if p2_asan is not None:
p2_olen: t.CSizeT = len(p2_asan) + len(ir_path) + 192
p2_opt_cmd = _mbuddy.alloc(p2_olen)
if p2_opt_cmd is not None:
viperlib.snprintf(p2_opt_cmd, p2_olen,
"opt -passes=forceattrs,asan -force-attribute=sanitize_address -asan-globals=false -o %s %s",
p2_asan, ir_path)
compile_input = p2_asan
p2_clen: t.CSizeT = len(cc_cmd) + len(cc_flags) + len(obj_path) + len(compile_input) + 64
p2_cmd: bytes = _mbuddy.alloc(p2_clen)
if p2_cmd is not None:
viperlib.snprintf(p2_cmd, p2_clen, "%s %s -o %s %s",
cc_cmd, cc_flags, obj_path, compile_input)
if p2_opt_cmd is not None:
_phase2_write_cmd(p2_opt_cmd)
if p2_cmd is not None:
_phase2_write_cmd(p2_cmd)
return 0
if _asan_mode != 0:
asan_path: str = build_sliced_path(output_dir, module_name, "asan.ll")
if asan_path is None:
@@ -453,6 +485,29 @@ def free_llc_task(task: LlcTask | t.CPtr):
task.ModuleSha1 = None
def _phase2_write_cmd(cmd: str) -> None:
"""--phase2command 模式:把命令追加写入 bat(不执行)
首次调用写入 header(@echo off + 说明),后续命令以 \r\n 分行追加,
与 cmd.exe 批处理格式一致。bat 路径由模块级 _phase2_cmd_path 指定。
"""
if _phase2_cmd_path is None or cmd is None:
return
f: fileio.File | t.CPtr = fileio.File(_phase2_cmd_path, fileio.MODE.A)
if f.closed:
VLogger.error("phase2command: bat 打开失败", "phase2cmd")
return
if _phase2_bat_init == 0:
_phase2_bat_init = 1
hdr1: str = "@echo off\r\n"
hdr2: str = "rem TransPyV --phase2command generated commands\r\n"
f.write(hdr1, string.strlen(hdr1))
f.write(hdr2, string.strlen(hdr2))
f.write(cmd, string.strlen(cmd))
f.write("\r\n", 2)
f.close()
def _run_cmd_wait(cmd: str) -> int:
"""CreateProcessA 同步执行命令并等待退出,返回退出码。
@@ -525,6 +580,22 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
"""
if tasks is None or task_count <= 0:
return 0
# --phase2command 模式:不 spawn 线程,把 opt(ASan)+ llc 命令全部写入 bat
if _phase2_cmd_path is not None:
for pi2 in range(task_count):
tsk_p: LlcTask | t.CPtr = (LlcTask | t.CPtr)(t.CVoid(
t.CUInt64T(tasks) + t.CSizeT(pi2) * LlcTask.__sizeof__(), t.CPtr))
if tsk_p is None:
continue
if tsk_p.OptCmd is not None:
_phase2_write_cmd(tsk_p.OptCmd)
if tsk_p.LlcCmd is not None:
_phase2_write_cmd(tsk_p.LlcCmd)
fb_p2: str = VLogger.fmt_buf()
if fb_p2 is not None:
viperlib.snprintf(fb_p2, 1024, "phase2command 模式:%d 条 llc 命令已写入 %s", task_count, _phase2_cmd_path)
VLogger.info(fb_p2, "phase2cmd")
return 0
if max_threads <= 0:
max_threads = 1
# 线程句柄数组:list[str](str=bytes=i8*,elem_size=8 显式)存句柄值。
@@ -734,6 +805,11 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
linker_cmd, obj_path, output_dir, linker_output,
use_flags)
# --phase2command 模式:不执行链接,命令写入 bat
if _phase2_cmd_path is not None:
_phase2_write_cmd(cmd)
return 0
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
if result is None:
VLogger.error("subprocess.run 返回 None", "link")
@@ -857,6 +933,10 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT,
# 用 subprocess.run 捕获 clang++ 的 stdout/stderr 输出
# 之前用 stdlib.system() 无法捕获具体错误(undefined reference 等),
# 导致 1.txt 中只看到"链接失败,返回码: 1"而看不到未解析符号
# --phase2command 模式:不执行链接,命令写入 bat
if _phase2_cmd_path is not None:
_phase2_write_cmd(cmd)
return 0
result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
if result is None:
VLogger.error("subprocess.run 返回 None", "link")
+91
View File
@@ -28,6 +28,7 @@ import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
import lib.Stub.StubGen.Converter as StubConverter
# 全局 mbuddy 指针
_mbuddy: memhub.MemBuddy | t.CPtr
@@ -386,6 +387,96 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# 不再需要构建独立的 SHA1 映射表副本。
# === Phase A-pre-vpi: 预生成所有 App 源文件的 .vpi stub ===
#
# 与 Phase1 1b-pre 同理:依赖模块的 .vpi 必须在依赖方翻译前可用。
# Phase A 主循环按扫描顺序翻译(非拓扑),若 StubMergerCollect/IR/Store
# 先于 __init__.vp 翻译,__init__.vpi 尚未生成 → LoadDepVpiIntoModule
# 找不到 vpi → 跨模块全局变量 declare 未注入 → ExprGenAttr fallback
# 以 i8* 创建带前缀 global(@b0b568._sha1_store_count = external global i8*)
# → llc "icmp slt 类型不匹配"(22fc/2e23/daad 根因,见 vpi 时间戳顺序)。
# 此处先为所有 App 源文件预生成 vpi,保证 Phase A 翻译任意模块时其依赖
# vpi 已存在(App 源文件自身的 vpi 随后由 TranslateFileGetTrans 覆盖重写,
# 内容一致,此处仅保证"先于依赖方")。
td_len_vpa: t.CSizeT = string.strlen(temp_dir)
PYI_BUF_SIZE_P2: t.CSizeT = 262144
vpi_failed_p2: int = 0
for i_vp2 in range(file_count):
ea_vp2: t.CUInt64T = t.CUInt64T(entries) + i_vp2 * entry_size
ent_vp2: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_vp2, t.CPtr))
if ent_vp2 is None or ent_vp2.Path is None or ent_vp2.Sha1 is None:
vpi_failed_p2 += 1
continue
# vpi 已存在则跳过(sha1 基于内容,同名 vpi 必然有效;跨次编译缓存)
vpi_path_vp2: str = StubMergerCollect._sliced_path(temp_dir, td_len_vpa, ent_vp2.Sha1, "vpi")
if vpi_path_vp2 is None:
vpi_failed_p2 += 1
continue
vf_vp2: fileio.File | t.CPtr = fileio.File(vpi_path_vp2, fileio.MODE.R)
if not vf_vp2.closed:
vf_vp2.close()
stdlib.free(vpi_path_vp2)
continue
stdlib.free(vpi_path_vp2)
# 读取源文件 → 解析 AST → 生成 vpi
# (rel_path 传 None:与 TranslateFileGetTrans 后续覆盖写的内容一致)
f_vp2: fileio.File | t.CPtr = fileio.File(ent_vp2.Path, fileio.MODE.R)
if f_vp2.closed:
vpi_failed_p2 += 1
continue
src_buf_vp2: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf_vp2 is None:
f_vp2.close()
vpi_failed_p2 += 1
continue
bytes_read_vp2: LONG = f_vp2.read_all(src_buf_vp2, SRC_BUF_SIZE)
f_vp2.close()
if bytes_read_vp2 <= 0:
stdlib.free(src_buf_vp2)
vpi_failed_p2 += 1
continue
if bytes_read_vp2 < SRC_BUF_SIZE:
src_buf_vp2[bytes_read_vp2] = 0
else:
src_buf_vp2[SRC_BUF_SIZE - 1] = 0
lx_vp2: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if lx_vp2 is None:
stdlib.free(src_buf_vp2)
vpi_failed_p2 += 1
continue
ast._lexer_init(lx_vp2, src_buf_vp2, mb)
tokens_vp2: ast.Token | t.CPtr = ast.tokenize(lx_vp2)
tree_vp2: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens_vp2)
if tree_vp2 is None:
stdlib.free(src_buf_vp2)
vpi_failed_p2 += 1
continue
pyi_buf_vp2: bytes = stdlib.malloc(PYI_BUF_SIZE_P2)
if pyi_buf_vp2 is not None:
pyi_pos_vp2: t.CSizeT = StubConverter._GeneratePyiFromAst(
mb, tree_vp2, None, 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:
pf_vp2: fileio.File | t.CPtr = fileio.File(pyi_path_vp2, fileio.MODE.W)
if not pf_vp2.closed:
pf_vp2.write(pyi_buf_vp2, pyi_pos_vp2)
pf_vp2.close()
stdlib.free(pyi_path_vp2)
stdlib.free(pyi_buf_vp2)
else:
vpi_failed_p2 += 1
stdlib.free(src_buf_vp2)
# OOM 清理:回收本文件 AST(vpi 已写盘;mark2 后持久数据已 stdlib 化)
mb.reset_to(mark2)
if vpi_failed_p2 > 0:
fb_vp2: str = VLogger.fmt_buf()
if fb_vp2 is not None:
viperlib.snprintf(fb_vp2, 1024, "Phase A-pre-vpi 有 %d 个文件 vpi 预生成失败", vpi_failed_p2)
VLogger.error(fb_vp2, "PhaseA")
return 1
# === 2. Phase A: 为每个文件生成 stub + text ===
# app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔)
# 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist,实现按需编译
+29 -40
View File
@@ -35,8 +35,8 @@ LinkerFlags: str = None # 链接器参数(空格分隔,从 linker.
LinkerOutput: str = None
TargetTriple: str = None
TargetDataLayout: str = None
Sha1SliceLevel: t.CInt = 0 # SHA1 切片层数(目录分散等级)
StrictMode: t.CInt = 0
Sha1SliceLevel: int = 0 # SHA1 切片层数(目录分散等级)
StrictMode: int = 0
IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景)
IncludesDirs: str = None # 全部 includes 目录(空格分隔,多 includes 目录支持)
@@ -60,17 +60,13 @@ def Load_project_config(path: str) -> int:
global TargetTriple, TargetDataLayout, Sha1SliceLevel, StrictMode
global IncludesDir, IncludesDirs
if path is None:
return 1
if _mbuddy is None:
return 1
if path is None: return 1
if _mbuddy is None: return 1
# 打开文件
f: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R)
if f is None:
return 1
if f.closed:
return 1
if f is None: return 1
if f.closed: return 1
# 分配读取缓冲区
CFG_BUF_SIZE: t.CSizeT = 8192
@@ -82,56 +78,53 @@ def Load_project_config(path: str) -> int:
bytes_read: t.CInt64T = f.read_all(buf, CFG_BUF_SIZE)
f.close()
if bytes_read <= 0:
return 1
if bytes_read <= 0: return 1
# 解析 JSON
root: JsonValue | t.CPtr = json_parse(_mbuddy, buf)
if root is None:
return 1
if not root.is_object():
return 1
if root is None: return 1
if not root.is_object(): return 1
# 读取顶层字段(使用显式 __getitem__ 调用,兼容旧编译器二进制)
sd_val: JsonValue | t.CPtr = root.__getitem__("source_dir")
sd_val: JsonValue | t.CPtr = root["source_dir"]
SourceDir = sd_val.as_string() if sd_val is not None else None
# build_dir: 统一构建目录(默认 ./.tpv_build),temp 和 output 作为其子目录
bd_val: JsonValue | t.CPtr = root.__getitem__("build_dir")
bd_val: JsonValue | t.CPtr = root["build_dir"]
if bd_val is not None and bd_val.is_string():
BuildDir = bd_val.as_string()
else:
BuildDir = "./.tpv_build"
name_val: JsonValue | t.CPtr = root.__getitem__("name")
name_val: JsonValue | t.CPtr = root["name"]
ProjectName = name_val.as_string() if name_val is not None else None
ver_val: JsonValue | t.CPtr = root.__getitem__("version")
ver_val: JsonValue | t.CPtr = root["version"]
ProjectVersion = ver_val.as_string() if ver_val is not None else None
# 读取 compiler 子对象
compiler: JsonValue | t.CPtr = root.__getitem__("compiler")
compiler: JsonValue | t.CPtr = root["compiler"]
if compiler is not None and compiler.is_object():
cc_cmd_val: JsonValue | t.CPtr = compiler.__getitem__("cmd")
cc_cmd_val: JsonValue | t.CPtr = compiler["cmd"]
CompilerCmd = cc_cmd_val.as_string() if cc_cmd_val is not None else None
# 读取 compiler.flags 数组,拼接成空格分隔的字符串
cc_flags_val: JsonValue | t.CPtr = compiler.__getitem__("flags")
cc_flags_val: JsonValue | t.CPtr = compiler["flags"]
CompilerFlags = _join_flags_array(cc_flags_val)
# 读取 linker 子对象
linker: JsonValue | t.CPtr = root.__getitem__("linker")
linker: JsonValue | t.CPtr = root["linker"]
if linker is not None and linker.is_object():
ld_cmd_val: JsonValue | t.CPtr = linker.__getitem__("cmd")
ld_cmd_val: JsonValue | t.CPtr = linker["cmd"]
LinkerCmd = ld_cmd_val.as_string() if ld_cmd_val is not None else None
ld_out_val: JsonValue | t.CPtr = linker.__getitem__("output")
ld_out_val: JsonValue | t.CPtr = linker["output"]
LinkerOutput = ld_out_val.as_string() if ld_out_val is not None else None
# 读取 linker.flags 数组,拼接成空格分隔的字符串
ld_flags_val: JsonValue | t.CPtr = linker.__getitem__("flags")
ld_flags_val: JsonValue | t.CPtr = linker["flags"]
LinkerFlags = _join_flags_array(ld_flags_val)
# 读取 target 子对象
target: JsonValue | t.CPtr = root.__getitem__("target")
target: JsonValue | t.CPtr = root["target"]
if target is not None and target.is_object():
tgt_triple_val: JsonValue | t.CPtr = target.__getitem__("triple")
tgt_triple_val: JsonValue | t.CPtr = target["triple"]
TargetTriple = tgt_triple_val.as_string() if tgt_triple_val is not None else None
tgt_dl_val: JsonValue | t.CPtr = target.__getitem__("datalayout")
tgt_dl_val: JsonValue | t.CPtr = target["datalayout"]
TargetDataLayout = tgt_dl_val.as_string() if tgt_dl_val is not None else None
# 读取 options 子对象
@@ -139,19 +132,19 @@ def Load_project_config(path: str) -> int:
# level=0 → 不切片,文件直接放 base_dir
# level=1 → b7/{sha1}.ext
# level=2 → b7/90/{sha1}.ext
options: JsonValue | t.CPtr = root.__getitem__("options")
options: JsonValue | t.CPtr = root["options"]
Sha1SliceLevel = 1
StrictMode = 1
if options is not None and options.is_object():
sl: JsonValue | t.CPtr = options.__getitem__("sha1_slice_level")
sl: JsonValue | t.CPtr = options["sha1_slice_level"]
if sl is not None and sl.is_int():
Sha1SliceLevel = sl.as_int()
sm: JsonValue | t.CPtr = options.__getitem__("strict_mode")
sm: JsonValue | t.CPtr = options["strict_mode"]
if sm is not None and sm.is_bool():
StrictMode = 1 if sm.as_bool() else 0
# 读取 includes 数组(支持多目录:首元素存 IncludesDir,全部拼接存 IncludesDirs)
includes_val: JsonValue | t.CPtr = root.__getitem__("includes")
includes_val: JsonValue | t.CPtr = root["includes"]
if includes_val is not None and includes_val.is_array():
arr_len: t.CSizeT = includes_val.__len__()
inc_buf: bytes = stdlib.malloc(2048)
@@ -204,14 +197,12 @@ def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str:
# 第一遍:计算总长度(所有 flag 长度 + 空格分隔符)
total_len: t.CSizeT = 0
i: t.CSizeT = 0
while i < arr_len:
for i in range(arr_len):
item: JsonValue | t.CPtr = flags_val.get_item(i)
if item is not None and item.is_string():
s: str = item.as_string()
if s is not None:
total_len += string.strlen(s) + 1 # +1 for space
i += 1
if total_len == 0:
return None
@@ -223,8 +214,7 @@ def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str:
# 第二遍:拼接(flag 之间用空格分隔)
pos: t.CSizeT = 0
i = 0
while i < arr_len:
for i in range(arr_len):
item = flags_val.get_item(i)
if item is not None and item.is_string():
s = item.as_string()
@@ -235,7 +225,6 @@ def _join_flags_array(flags_val: JsonValue | t.CPtr) -> str:
pos += 1
string.strcpy(buf + pos, s)
pos += slen
i += 1
buf[pos] = '\0'
return buf
+11 -20
View File
@@ -25,11 +25,9 @@ _mbuddy: memhub.MemBuddy | t.CPtr
# ============================================================
def nibble_to_hex_char(n: int, uppercase: int) -> t.CChar:
"""0-15 → '0'-'9'/'a'-'f'(uppercase=1 时用 'A'-'F')"""
if n < 10:
return '0' + n
if uppercase != 0:
return 'A' + (n - 10)
return 'a' + (n - 10)
if n < 10: return '0' + n
if uppercase != 0: return 'A' + (n - 10)
return 'a' + (n - 10)
# ============================================================
@@ -78,14 +76,12 @@ def compute_sha1(pool: memhub.MemBuddy | t.CPtr, content: str) -> str:
# 构造 sha1 对象(__init__ 会初始化 state/count/buf)
# ctx 在栈上分配(alloca),函数内使用完即丢弃,无需堆分配
ctx: hashlib.sha1 | t.CPtr = hashlib.sha1()
if ctx is None:
return None
if ctx is None: return None
# 计算摘要
ctx.update(content)
digest: bytes = pool.alloc(hashlib.SHA1_DIGEST_LEN)
if digest is None:
return None
if digest is None: return None
ctx.final(digest)
# 转为十六进制字符串(取前 8 字节 = 16 个十六进制字符)
@@ -101,10 +97,8 @@ def is_vp_or_py_at(p: str, start: t.CSizeT, plen: t.CSizeT) -> int:
return 0
base: t.CSizeT = start + plen - 3
if p[base] == '.':
if p[base + 1] == 'v' and p[base + 2] == 'p':
return 1
if p[base + 1] == 'p' and p[base + 2] == 'y':
return 1
if p[base + 1] == 'v' and p[base + 2] == 'p': return 1
if p[base + 1] == 'p' and p[base + 2] == 'y': return 1
return 0
@@ -119,8 +113,7 @@ def JoinPath(a: str, b: str) -> str:
b_len: t.CSizeT = string.strlen(b)
total_len: t.CSizeT = a_len + b_len + 2
buf: str = stdlib.malloc(total_len)
if buf is None:
return None
if buf is None: return None
viperlib.snprintf(buf, total_len, "%s/%s", a, b)
return buf
@@ -134,11 +127,9 @@ def read_file_to_buf(path: str, buf_size: t.CSizeT) -> bytes:
返回缓冲区(调用方负责 free);打开失败/空文件/分配失败返回 None。
合并自 Phase2/StubMerger/IncludesScanner 的重复"打开+读取+终止"序列。
"""
if path is None or buf_size <= 0:
return None
if path is None or buf_size <= 0: return None
sf: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R)
if sf.closed:
return None
if sf.closed: return None
buf: bytes = stdlib.malloc(buf_size)
if buf is None:
sf.close()
@@ -238,7 +229,7 @@ def CleanDir(dir_path: str) -> int:
return 0
deleted: int = 0
while 1:
while True:
fname: str = find_data.cFileName
if fname is not None:
# 跳过 . 和 ..
+11 -1
View File
@@ -1289,7 +1289,17 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr,
pname: str = ag.arg
if pname is None:
pname = "arg"
# 解析参数类型
# self/cls 参数(首参):跨模块声明用类指针(%"sha1.Class"*)而非
# 默认 i8* —— 与定义侧 _translate_method 的 self 参数 Ptr(StructTy)
# 签名一致,消除跨模块声明/定义歧义。调用侧按形参 coerce 自动
# bitcast(见 _call_method_on_ptr/_translate_method_call 的 coerce
# 块)。类类型未注册时兜底走下方注解解析/i8*(保持原行为)。
if pi == 0 and (string.strcmp(pname, "self") == 0
or string.strcmp(pname, "cls") == 0):
cls_ty_sd: llvmlite.LLVMType | t.CPtr = GenApi.get_struct_type(cls_name)
if cls_ty_sd is not None:
llvmlite.add_param(pool, func, llvmlite.Ptr(pool, cls_ty_sd), pname)
continue
param_ty: llvmlite.LLVMType | t.CPtr = i8_ptr_ty
if ag.annotation is not None:
resolved_ty: llvmlite.LLVMType | t.CPtr = GenApi.resolve_annotation_type(
+3 -3
View File
@@ -134,7 +134,7 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
stdlib.free(pattern)
if handle != win32base.INVALID_HANDLE_VALUE:
while 1:
while True:
if out_count >= max_count:
break
fname: str = find_data.cFileName
@@ -157,10 +157,10 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int,
handle = win32file.FindFirstFileA(pattern, find_data)
stdlib.free(pattern)
if handle != win32base.INVALID_HANDLE_VALUE:
while 1:
while True:
# 检查是否是目录(FILE_ATTRIBUTE_DIRECTORY = 0x10 = 16)
attrs: t.CUInt32T = find_data.dwFileAttributes
if(attrs & 16) != 0:
if (attrs & 16) != 0:
fname = find_data.cFileName
if fname is not None:
# 跳过 "." 和 ".."
+3 -3
View File
@@ -1088,7 +1088,7 @@ class AssignGenMixin:
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
spec_name_ps: str = None
if template_cd is not None:
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_annot.slice)
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_annot.slice, trans._from_imports)
if type_args_ps is not None and len(type_args_ps) > 0:
spec_name_ps = self.Gen.ClassGenGeneric._specialize_generic_class(
trans, sub_nm.id, type_args_ps)
@@ -1127,7 +1127,7 @@ class AssignGenMixin:
# __function.vp blocks.Tail 根因;对齐 ClassGenMixin L3159 BUG 1 方案3)
if annot is not None:
cls_nm_ps: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
annot, trans._imported_modules, pool)
annot, trans._imported_modules, pool, trans._from_imports)
if cls_nm_ps is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, nm.id, cls_nm_ps)
@@ -1329,7 +1329,7 @@ class AssignGenMixin:
# 传 pool:泛型 Subscript 返回 mangled name(对齐 L401 同根因修复)
if annot is not None:
cls_nm_hd: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
annot, trans._imported_modules, pool)
annot, trans._imported_modules, pool, trans._from_imports)
if cls_nm_hd is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, nm.id, cls_nm_hd)
+38 -5
View File
@@ -277,10 +277,11 @@ class CallGenCall(CallGenBase.CallGenBase):
viperlib.snprintf(err_msg, 256, "类 '%s' 方法 '%s' 找不到模块 SHA1", class_name, method_name)
self.Gen.TypeGenErr.fatal_error(node, err_msg)
self.Gen.TypeGenErr.fatal_error(node, "找不到模块 SHA1")
mangled_len2: t.CSizeT = string.strlen(cls_sha1) + string.strlen(lookup_name) + 2
mangled2: str = pool.alloc(mangled_len2)
# 生成调用名: {cls_sha1}.{lookup_name}(Hash Namespace 单前缀)
mangled2: str = pool.alloc(string.strlen(cls_sha1) + string.strlen(lookup_name) + 2)
if mangled2 is not None:
viperlib.snprintf(mangled2, mangled_len2, "%s.%s", cls_sha1, lookup_name)
viperlib.snprintf(mangled2, string.strlen(cls_sha1) + string.strlen(lookup_name) + 2,
"%s.%s", cls_sha1, lookup_name)
call_name = mangled2
# 确定返回类型(__before_init__ 和 __init__ 返回 void)
@@ -769,10 +770,28 @@ class CallGenCall(CallGenBase.CallGenBase):
seg_buf_g[seg_len_g] = '\0'
ta_list_g.append(seg_buf_g)
cur_arg_g = comma_g + 1
# base_buf_g 可能已带模板模块 SHA1 前缀({tpl}.GSList):
# 剥离后再查模板/特化,保证 _find_generic_template 命中裸模板名。
base_clean_g: str = base_buf_g
if base_len_g > 17:
is_sha1_pfx_g: int = 1
si_g: int
for si_g in range(16):
cg_g: t.CChar = base_buf_g[si_g]
if not (('0' <= cg_g <= '9') or ('a' <= cg_g <= 'f')):
is_sha1_pfx_g = 0
break
if is_sha1_pfx_g != 0 and base_buf_g[16] == '.':
base_clean_g = base_buf_g + 17
spec_name_g: str = self.Gen.ClassGenGeneric._specialize_generic_class(
trans, base_buf_g, ta_list_g)
trans, base_clean_g, ta_list_g)
if spec_name_g is not None:
# 特化后重新查找特化函数(特化名 = 调用点类名)
# 特化后重新查找特化函数(特化名 = 调用点类名)。
# find_func_in_module 后缀匹配:定义符号名
# {模板}.spec.method(Hash Namespace 单前缀),
# 同时以裸名(spec.method)和带模板前缀名
# ({tpl}.spec.method)结尾,两种 class_name 形式
# 都能命中本地 define。
spec_lk_g: str = pool.alloc(
string.strlen(class_name) + string.strlen(method_name) + 2)
if spec_lk_g is not None:
@@ -1064,6 +1083,20 @@ class CallGenCall(CallGenBase.CallGenBase):
cl,
f"this-adjust 父类结构体未注册: 方法'{method_name}'定义类'{mc_anc}'的 struct 未找到",
"InternalCompilerError")
# self 参数按声明首参类型 coerce:跨模块 declare self 是类指针
# (%"sha1.Class"*,见 VpiReader._inject_method_declare),而对象指针
# 可能是 i8*(X|t.CPtr 联合简化)或 this-adjust 后的祖先指针,需
# bitcast 匹配声明签名(对齐 _call_method_on_ptr 的 coerce 块),
# 否则 llc 报 call 参数类型与 declare 不一致。
if found_func is not None:
pp_self: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
if pp_self is not None:
pty_self: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(pp_self)
if pty_self is not None:
cself_tm: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, self_arg, pty_self, None)
if cself_tm is not None:
self_arg = cself_tm
# 构建参数链表: self → arg1 → arg2 → ...
llvmlite.value_set_next(self_arg, None)
head: llvmlite.Value | t.CPtr = self_arg
+11 -4
View File
@@ -786,12 +786,17 @@ class CallGenCtor(CallGenBase.CallGenBase):
# HandlesExprCall 通过 HandlesType._get_type_name_from_annotation 调用
def _extract_type_args_from_slice(self, pool: memhub.MemBuddy | t.CPtr,
slice_node: ast.AST | t.CPtr) -> list[str] | t.CPtr:
slice_node: ast.AST | t.CPtr,
from_imports: str = None) -> list[str] | t.CPtr:
"""从 Subscript.slice 提取类型实参名列表
list[int] -> ["int"]
dict[str, int] -> ["str", "int"]
list[AST | t.CPtr] -> ["AST|t.CPtr"]
list[AST | t.CPtr] -> ["f467978339422f32.AST|t.CPtr"]
from_imports 可用时,各类型实参经 TypeGenResolve._norm_type_arg_name
规范化为 "{定义模块SHA1}.{Class}"(ast.AST/AST → f467978339422f32.AST),
与特化注册名一致,消除跨模块特化名分裂(list[AST].set undefined symbol)。
"""
if slice_node is None or pool is None:
return None
@@ -807,11 +812,13 @@ class CallGenCtor(CallGenBase.CallGenBase):
i: t.CSizeT
for i in range(n):
elt: ast.AST | t.CPtr = elts.get(i)
tn: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, elt)
tn_raw: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, elt)
tn: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, tn_raw, from_imports)
if tn is not None:
result.append(tn)
else:
tn: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, slice_node)
tn_raw: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, slice_node)
tn: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, tn_raw, from_imports)
if tn is not None:
result.append(tn)
return result
+1 -1
View File
@@ -561,7 +561,7 @@ class CallGenDispatch(CallGenBase.CallGenBase):
if sub_sz.value is not None and sub_sz.value.kind() == ast.ASTKind.Name:
gen_nm_sz: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_sz.value)
if gen_nm_sz.id is not None:
gen_args_sz: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_sz.slice)
gen_args_sz: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_sz.slice, trans._from_imports)
if gen_args_sz is not None and len(gen_args_sz) > 0:
gargs_resolved: list[str] | t.CPtr = list[str](pool, 8)
if gargs_resolved is not None:
+1 -1
View File
@@ -118,7 +118,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(gen_class_name)
if template_cd is not None:
# 提取类型实参
gen_type_args: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_node.slice)
gen_type_args: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_node.slice, trans._from_imports)
if gen_type_args is not None and len(gen_type_args) > 0:
# 特化泛型类,返回特化类名
spec_name: str = self.Gen.ClassGenGeneric._specialize_generic_class(
+1 -1
View File
@@ -380,7 +380,7 @@ class ClassGenDef(ClassGenBase.ClassGenBase):
# BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST|t.CPtr]")
if aa.annotation is not None:
annot_cn: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
aa.annotation, trans._imported_modules, pool)
aa.annotation, trans._imported_modules, pool, trans._from_imports)
if annot_cn is not None:
diag_cn_val: t.CSizeT = t.CSizeT(annot_cn)
field_annot_buf[field_count] = diag_cn_val
+3 -3
View File
@@ -512,7 +512,7 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 特化触发文件 sha1:进入函数时 trans.ModuleSha1 是触发方文件,
# 翻译特化类时会被切换为模板 sha1(见下方),作废重译时按此重置
# 特化类 MethodsEmitted(见 _record_spec_trigger / reset_module_methods_emitted)
spec_trigger_sha1: str = trans.ModuleSha1
spec_trigger_sha1: str = trans.ModuleSha1
if self.GenericSpecDepth > GENERIC_SPEC_MAX_DEPTH:
err_spec: str = trans.Pool.alloc(256)
if err_spec is not None:
@@ -659,8 +659,8 @@ class ClassGenGeneric(ClassGenBase.ClassGenBase):
# 7. 翻译特化类
# 切换 trans.ModuleSha1 为泛型模板定义模块的 SHA1,
# 使结构体类型名和方法定义名都用定义模块的 SHA1 前缀,
# 与调用端 get_struct_sha1(class_name) 查到的 SHA1 一致。
# 使结构体类型名与特化方法函数符号名都用定义模块的 SHA1 前缀
# (跨模块一致,单态化特化名 = {模板SHA1}.Class[param].method)。
# 同时并入模板模块的 imports(见 _merge_template_imports 注释),
# 避免模板方法体内的跨模块调用被误报"模块未导入"。
saved_module_sha1: str = trans.ModuleSha1
+6 -6
View File
@@ -403,7 +403,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
func: llvmlite.Function | t.CPtr = self.Gen.CallGenLeafKit.find_func_global(full_name)
else:
# 当前类方法: 用当前模块 SHA1
mangled_name: str = self.Gen.FuncGenBase._mangle_name(trans, full_name)
mangled_name: str = self._mangle_name(trans, full_name)
func: llvmlite.Function | t.CPtr = self.Gen.CallGenLeafKit.find_func_in_table(
trans._funcs, trans._func_count, full_name)
# 当前模块表找不到时,回退到全局表
@@ -472,7 +472,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
if vtable_name_buf is None:
return 0
viperlib.snprintf(vtable_name_buf, 128, "%s.vtable", class_name)
vtable_mangled: str = self.Gen.FuncGenBase._mangle_name(trans, vtable_name_buf)
vtable_mangled: str = self._mangle_name(trans, vtable_name_buf)
# 5. 创建全局变量
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(
@@ -713,7 +713,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
full_name: str = full_name_buf
# SHA1 命名空间混淆
mangled_name: str = self.Gen.FuncGenBase._mangle_name(trans, full_name)
mangled_name: str = self._mangle_name(trans, full_name)
# 推断返回类型
ret_ty: llvmlite.LLVMType | t.CPtr = None
@@ -968,7 +968,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
# 传 pool:泛型 Subscript 返回 mangled name(对齐 HandlesAnnAssign 修复)
if arg2.annotation is not None:
cls_nm_oc: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
arg2.annotation, imported_modules, pool)
arg2.annotation, imported_modules, pool, trans._from_imports)
if cls_nm_oc is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, arg2.arg, cls_nm_oc)
@@ -1147,7 +1147,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
viperlib.snprintf(full_name_buf, 128, "%s.__before_init__", class_name)
full_name: str = full_name_buf
mangled_name: str = self.Gen.FuncGenBase._mangle_name(trans, full_name)
mangled_name: str = self._mangle_name(trans, full_name)
# 创建函数
func: llvmlite.Function | t.CPtr = llvmlite.create_function(
@@ -1224,7 +1224,7 @@ class ClassGenVTable(ClassGenBase.ClassGenBase):
if vt_name_buf is not None:
viperlib.snprintf(vt_name_buf, 128, "%s.vtable", class_name)
vt_full_name: str = vt_name_buf
vt_mangled: str = self.Gen.FuncGenBase._mangle_name(trans, vt_full_name)
vt_mangled: str = self._mangle_name(trans, vt_full_name)
# GEP 到 __vtable__ 字段(索引 0)
vt_slot: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
+36
View File
@@ -336,6 +336,28 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if ca is None or ca.attr is None or ca.value is None:
return None
# 模块级函数调用(Module.func()):接收者是已导入模块名而非类对象
# (CompilerFactory.get_perm_pool())。返回注解可能是联合类型
# (memhub.MemBuddy | t.CPtr),查 vpi 模块函数返回注解(cls=None)
# 提取返回类,解决链式方法调用 .alloc() 推断失败退化裸名 @alloc
# (链接 undefined symbol: alloc 根因)。
if ca.value.kind() == ast.ASTKind.Name:
mod_nm2: str = ((ast.Name | t.CPtr)(ca.value)).id
if mod_nm2 is not None and trans._from_imports is not None:
mod_sha1_2: str = self.Gen.CallGenBase._lookup_mod_sha1(
pool, trans._from_imports, mod_nm2)
if mod_sha1_2 is not None:
temp_dir2: str = None
if self.Gen.CallGenBase.TempDir is not None:
temp_dir2 = self.Gen.CallGenBase.TempDir
if temp_dir2 is not None:
VpiReader._ensure_vpi_ret_annot_loaded(mod_sha1_2, temp_dir2)
annot_ret2: str = VpiReader._lookup_vpi_ret_annot(
mod_sha1_2, None, ca.attr)
if annot_ret2 is not None:
ret_cls2: str = self._extract_annot_class_name(pool, annot_ret2)
if ret_cls2 is not None:
return ret_cls2
obj_cls: str = self.infer_expr_class(pool, mod, ca.value, trans)
if obj_cls is None:
return None
@@ -476,6 +498,13 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
seg[seglen] = '\0'
if self.Gen.StructGen.find_struct(seg) is not None:
return seg
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中
# 时去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
seg_dot2: str = string.strrchr(seg, 46)
if seg_dot2 is not None and seg_dot2 != seg:
seg_bare2: str = seg_dot2 + 1
if self.Gen.StructGen.find_struct(seg_bare2) is not None:
return seg_bare2
start = i + 1
# 最后一段(无 '|' 后缀)
s: t.CSizeT = start
@@ -490,4 +519,11 @@ class ExprGenCls(ExprGenBase.ExprGenBase):
seg[seglen] = '\0'
if self.Gen.StructGen.find_struct(seg) is not None:
return seg
# 模块前缀(memhub.MemBuddy → MemBuddy):find_struct 未命中时
# 去最后一个 '.' 前的模块名再试(vpi 返回注解常带模块限定)
seg_dot3: str = string.strrchr(seg, 46)
if seg_dot3 is not None and seg_dot3 != seg:
seg_bare3: str = seg_dot3 + 1
if self.Gen.StructGen.find_struct(seg_bare3) is not None:
return seg_bare3
return None
+1 -1
View File
@@ -677,7 +677,7 @@ class FuncGenDef(CallGenBase.CallGenBase):
cls_nm: str = None
if arg2.annotation is not None:
cls_nm = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
arg2.annotation, imported_modules, pool)
arg2.annotation, imported_modules, pool, trans._from_imports)
if cls_nm is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, arg2.arg, cls_nm)
+1 -1
View File
@@ -193,7 +193,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
self.Gen.VarGen.define_var(trans.SymTab, arg_p.arg, p_alloca)
if arg_p.annotation is not None:
cls_nm_p: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
arg_p.annotation, imported_modules, pool)
arg_p.annotation, imported_modules, pool, trans._from_imports)
if cls_nm_p is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, arg_p.arg, cls_nm_p)
+1 -1
View File
@@ -912,7 +912,7 @@ class StmtGenCifMixin:
# 到达 end 块(merge 结构性不可达)→ 置 0,函数收尾对末句 While 生成
# unreachable(追加90:无 br 到达 merge 时才安全;否则 llc O2 把
# br-to-unreachable 优化为 fall-through → 双分支执行崩溃)。
# 修复 vrandom.gammavariate 的 while 1 全 return 被误判"流程掉出末尾"。
# 修复 vrandom.gammavariate 的 while True 全 return 被误判"流程掉出末尾"。
if self._IsConstTrue(while_node.test) != 0 and self._StmtHasBreak(body) == 0:
trans._ctrl_merge_reachable = 0
return 0
+1 -1
View File
@@ -568,7 +568,7 @@ class TranslatorGenMixin:
aa_ml: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
if aa_ml is not None and aa_ml.annotation is not None:
cls_nm_ml: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(
aa_ml.annotation, trans._imported_modules, pool)
aa_ml.annotation, trans._imported_modules, pool, trans._from_imports)
if cls_nm_ml is not None:
self.Gen.VarGen.set_var_annot_class_name(
trans.SymTab, var_name, cls_nm_ml)
+191 -16
View File
@@ -87,6 +87,143 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return buf2
return None
return None
def _is_sha1_prefixed(self, s: str) -> int:
"""判断 s 是否已是 "{16位hex}." 前缀(已规范化,防二次前缀)
泛型类型参数规范化为 "{SHA1}.Class" 后,可能再次进入本提取路径
(如特化方法体内 T 替换为 "f467978339422f32.AST"),必须识别并保持,
否则会把 SHA1 当模块名再查一次(查不到)或重复加前缀(名不符)。
"""
if s is None:
return 0
if string.strlen(s) < 17:
return 0
if s[16] != '.':
return 0
i: t.CSizeT
for i in range(16):
c: int = s[i]
# 48-57 = '0'-'9',97-102 = 'a'-'f'
if not ((c >= 48 and c <= 57) or (c >= 97 and c <= 102)):
return 0
return 1
def _concat_sha1_class(self, pool: memhub.MemBuddy | t.CPtr,
sha1: str, cls_name: str) -> str:
"""拼接 "{sha1}.{cls_name}"(pool 分配),失败返回 None"""
if pool is None or sha1 is None or cls_name is None:
return None
total: t.CSizeT = string.strlen(sha1) + 1 + string.strlen(cls_name) + 1
buf: str = pool.alloc(total)
if buf is None:
return None
buf[0] = '\0'
string.strcat(buf, sha1)
string.strcat(buf, ".")
string.strcat(buf, cls_name)
return buf
def _norm_type_arg_name(self, pool: memhub.MemBuddy | t.CPtr,
type_name: str,
from_imports: str) -> str:
"""泛型类型参数名 SHA1 规范化:统一为 "{定义模块SHA1}.{Class}" 形式
收敛同一类型的不同书写(ast.AST / AST → f467978339422f32.AST),
消除跨模块特化名分裂(undefined symbol: 7cadfe07e983fd90.list[AST].set 根因:
一侧注解写 "list[ast.AST | t.CPtr]"、另一侧写 "list[AST | t.CPtr]",
生成两个不同特化名 → 结构体/方法注册与调用不一致)。
规则(自顶向下):
1. 联合类型 "A|B"('|' 分割):左右段各自规范化后重组
2. 已是 "{16位hex}.Class"(_is_sha1_prefixed):保持(防二次前缀)
3. "t.XXX" / "c.XXX":保持(内置命名空间无模块 SHA1)
4. "mod.Class"(Attribute):mod 是 import 别名时先经 from_imports 反查
完整模块名 → SHA1;再尝试 mod 直接查 SHA1(无别名 import 的完整路径)
5. 裸名 "Class":经 from_imports 查所属模块 SHA1
6. 均查不到:返回原串(当前模块自身类 / 内置类型名,保持原样)
返回 None 表示 type_name 为 None。
"""
if type_name is None:
return None
# ---- 1. 联合类型 "A|B" ----
seg_len: t.CSizeT = string.strlen(type_name)
pipe_idx: t.CSizeT = seg_len
pi: t.CSizeT = 0
while pi < seg_len:
if type_name[pi] == '|':
pipe_idx = pi
break
pi += 1
if pipe_idx < seg_len:
left_buf: str = pool.alloc(pipe_idx + 1)
if left_buf is None:
return type_name
string.strncpy(left_buf, type_name, pipe_idx)
left_buf[pipe_idx] = '\0'
right_str: str = type_name + pipe_idx + 1
right_len: t.CSizeT = string.strlen(right_str)
right_buf: str = pool.alloc(right_len + 1)
if right_buf is None:
return type_name
string.strcpy(right_buf, right_str)
l_n: str = self._norm_type_arg_name(pool, left_buf, from_imports)
r_n: str = self._norm_type_arg_name(pool, right_buf, from_imports)
if l_n is None:
l_n = left_buf
if r_n is None:
r_n = right_buf
total: t.CSizeT = string.strlen(l_n) + 1 + string.strlen(r_n) + 1
out: str = pool.alloc(total)
if out is None:
return type_name
out[0] = '\0'
string.strcat(out, l_n)
string.strcat(out, "|")
string.strcat(out, r_n)
return out
# ---- 2. 已规范化(SHA1 前缀)----
if self._is_sha1_prefixed(type_name) != 0:
return type_name
# ---- 3. t.XXX / c.XXX 内置命名空间 ----
if (type_name[0] == 't' or type_name[0] == 'c') and type_name[1] == '.':
return type_name
# ---- 4. "mod.Class" Attribute ----
dot_pos: str = string.strchr(type_name, '.')
if dot_pos is not None:
mod_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(type_name))
attr: str = dot_pos + 1
if mod_len > 0:
mod_buf: str = pool.alloc(mod_len + 1)
if mod_buf is not None:
string.strncpy(mod_buf, type_name, mod_len)
mod_buf[mod_len] = '\0'
# 4a. mod 是 import 别名(import X as mod 注册 "mod:X")→ 完整模块名 → SHA1
if from_imports is not None:
orig_mod: str = self.Gen.ImportsGen.lookup_from_import(
from_imports, mod_buf, 0)
if orig_mod is not None:
sha1a: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, orig_mod)
if sha1a is not None:
res_a: str = self._concat_sha1_class(pool, sha1a, attr)
if res_a is not None:
return res_a
# 4b. mod 本身是完整模块路径(无别名 import,如 w32.fileio)
sha1b: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_buf)
if sha1b is not None:
res_b: str = self._concat_sha1_class(pool, sha1b, attr)
if res_b is not None:
return res_b
return type_name
return type_name
# ---- 5. 裸名 "Class":查 from_imports 所属模块 SHA1 ----
if from_imports is not None:
sha1c: str = self.Gen.CallGenBase._lookup_mod_sha1(
pool, from_imports, type_name, 0)
if sha1c is not None:
res_c: str = self._concat_sha1_class(pool, sha1c, type_name)
if res_c is not None:
return res_c
# ---- 6. 查不到:保持原样 ----
return type_name
def _parse_type_string(self, pool: memhub.MemBuddy | t.CPtr,
type_str: str,
ctx: t.CInt) -> ast.AST | t.CPtr:
@@ -617,12 +754,16 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
return "<unknown>"
def extract_class_name_from_annotation(self, node: ast.AST | t.CPtr,
imported_modules: str,
pool: memhub.MemBuddy | t.CPtr = None) -> str:
pool: memhub.MemBuddy | t.CPtr = None,
from_imports: str = None) -> str:
"""从类型注解 AST 中提取类名,返回 None=无法提取
pool 可用时,对泛型 Subscript(如 list[AST | t.CPtr])返回 mangled name
(如 "list[ast.AST|t.CPtr]"),与 resolve_annotation_type 的 mangled name 格式一致,
使 find_struct 能找到特化的泛型结构体。
(如 "list[f467978339422f32.AST|t.CPtr]"),与 resolve_annotation_type 的
mangled name 格式一致,使 find_struct 能找到特化的泛型结构体。
from_imports 可用时,slice 类型参数经 _norm_type_arg_name 规范化为
"{定义模块SHA1}.{Class}"(ast.AST/AST → f467978339422f32.AST),
与特化注册名一致(消除 list[AST].set undefined symbol)。
pool 为 None 时,对 Subscript 只返回裸类名(如 "list"),保持向后兼容。
"""
if node is None:
@@ -705,15 +846,15 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
right_is_cptr: int = self._is_cptr_marker(bop.right, imported_modules)
# 提取非 CPtr 一边的类名
if left_is_cptr != 0 and right_is_cptr == 0:
return self.extract_class_name_from_annotation(bop.right, imported_modules, pool)
return self.extract_class_name_from_annotation(bop.right, imported_modules, pool, from_imports)
if right_is_cptr != 0 and left_is_cptr == 0:
return self.extract_class_name_from_annotation(bop.left, imported_modules, pool)
return self.extract_class_name_from_annotation(bop.left, imported_modules, pool, from_imports)
# 无 CPtr 标记:尝试两边
if left_is_cptr == 0 and right_is_cptr == 0:
left_name: str = self.extract_class_name_from_annotation(bop.left, imported_modules, pool)
left_name: str = self.extract_class_name_from_annotation(bop.left, imported_modules, pool, from_imports)
if left_name is not None:
return left_name
return self.extract_class_name_from_annotation(bop.right, imported_modules, pool)
return self.extract_class_name_from_annotation(bop.right, imported_modules, pool, from_imports)
return None
# BUG 1 方案3:Subscript 节点拼接 mangled name
@@ -745,7 +886,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if ti_sn > 0:
tup_total += 1
elt_node: ast.AST | t.CPtr = tup_elts_sn.get(ti_sn)
elt_name: str = self._get_type_name_from_annotation(pool, elt_node)
elt_name_raw: str = self._get_type_name_from_annotation(pool, elt_node)
elt_name: str = self._norm_type_arg_name(pool, elt_name_raw, from_imports)
if elt_name is not None:
tup_total += string.strlen(elt_name)
tup_buf: str = pool.alloc(tup_total)
@@ -755,13 +897,17 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if ti_sn > 0:
string.strcat(tup_buf, ",")
elt_node2: ast.AST | t.CPtr = tup_elts_sn.get(ti_sn)
elt_name2: str = self._get_type_name_from_annotation(pool, elt_node2)
elt_name2_raw: str = self._get_type_name_from_annotation(pool, elt_node2)
elt_name2: str = self._norm_type_arg_name(pool, elt_name2_raw, from_imports)
if elt_name2 is not None:
string.strcat(tup_buf, elt_name2)
slice_name = tup_buf
else:
# 非 Tuple slice: list[AST | t.CPtr] → "ast.AST|t.CPtr"
slice_name = self._get_type_name_from_annotation(pool, sub.slice)
# 类型参数经 _norm_type_arg_name 规范化为 "{SHA1}.Class",
# 与特化注册名一致(list[f467978339422f32.AST|t.CPtr])
slice_raw: str = self._get_type_name_from_annotation(pool, sub.slice)
slice_name = self._norm_type_arg_name(pool, slice_raw, from_imports)
if slice_name is None:
return base_name
# 拼接 "base_name[slice_name]"
@@ -838,10 +984,14 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
gen_ei: t.CSizeT
for gen_ei in range(gen_en):
gen_elt: ast.AST | t.CPtr = gen_elts.get(gen_ei)
gen_tn: str = self._get_type_name_from_annotation(pool, gen_elt)
gen_tn_raw: str = self._get_type_name_from_annotation(pool, gen_elt)
# 类型参数 SHA1 规范化(ast.AST/AST → f467978339422f32.AST),
# 与特化注册名一致,消除跨模块特化名分裂
gen_tn: str = self._norm_type_arg_name(pool, gen_tn_raw, from_imports)
gen_arg_names.append(gen_tn)
else:
gen_tn_single: str = self._get_type_name_from_annotation(pool, sub.slice)
gen_tn_raw_single: str = self._get_type_name_from_annotation(pool, sub.slice)
gen_tn_single: str = self._norm_type_arg_name(pool, gen_tn_raw_single, from_imports)
gen_arg_names.append(gen_tn_single)
# 计算总长度并分配缓冲区
gen_total: t.CSizeT = string.strlen(gen_cls_name) + 2 # "[" 和 "]"
@@ -892,7 +1042,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
template_cd_chk: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(gen_cls_name)
if template_cd_chk is not None:
type_args_chk: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(
pool, sub.slice)
pool, sub.slice, from_imports)
if type_args_chk is not None and len(type_args_chk) > 0:
# 错误定位:压入触发特化的调用方帧(注解所在文件+行号)
gen_cur_f2: str = self.Gen.TypeGenResolve.get_current_file()
@@ -1070,14 +1220,39 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
if string.strcmp(val_name.id, "t") == 0 or string.strcmp(val_name.id, "c") == 0:
is_builtin_attr = 1
if is_builtin_attr == 0:
mod_imp: int = self.Gen.ImportsGen.is_module_imported(imported_modules, val_name.id)
if mod_imp == 0:
return None
# SHA1 前缀("{16hex}.Class",跨模块特化类型参数规范化名):
# 模块段即 SHA1,天然"已导入"(SHA1 唯一对应已编译模块),
# 跳过 imported_modules 检查("f467978339422f32" 不在列表中)
sha1_attr: int = 0
if string.strlen(val_name.id) == 16:
sha1_ok: int = 1
i_sa: t.CSizeT
for i_sa in range(16):
c_sa: int = val_name.id[i_sa]
# 48-57 = '0'-'9',97-102 = 'a'-'f'
if not ((c_sa >= 48 and c_sa <= 57) or (c_sa >= 97 and c_sa <= 102)):
sha1_ok = 0
break
if sha1_ok != 0:
sha1_attr = 1
if sha1_attr == 0:
mod_imp: int = self.Gen.ImportsGen.is_module_imported(imported_modules, val_name.id)
if mod_imp == 0:
return None
# 先尝试内置类型映射(int 等)
mapped_ty: llvmlite.LLVMType | t.CPtr = self.map_t_type(pool, at.attr)
if mapped_ty is not None:
return mapped_ty
# 模块限定类类型:Module.ClassName(命名空间隔离)
# SHA1 前缀形态优先用完整名 "{sha1}.{Class}" 精确命中定义侧
# struct(get_struct_type 类名回退可能命中跨模块同名类)
full_attr_ty: llvmlite.LLVMType | t.CPtr = None
if is_builtin_attr == 0 and string.strlen(val_name.id) == 16:
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)
if full_attr_ty is not None:
return full_attr_ty
mod_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(at.attr)
if mod_struct_ty is not None:
return mod_struct_ty
+13
View File
@@ -134,6 +134,7 @@ def main() -> int:
parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Delete the includes.binary precompiled cache and rebuild all the includes")
parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Clear .transpyc_cache global cache")
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan mode: .ll first opt -passes=asan then link -fsanitize=address")
parser.add_argument("--phase2command", None, argparse.STRING, 0, None, False, argparse.STORE, "Do not run llc/link: write Phase2 llc(opt+llc) and clang link commands to the given .bat path")
parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Suppress Phase A dependency WARN")
parser.add_argument("--version", "-v", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show version and exit")
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show help and exit")
@@ -221,6 +222,18 @@ Target: %s
BuildPipeline._asan_mode = False
Config._inc_bin_suffix = ".binary"
# === --phase2command: 只生成命令写入 bat,不执行 llc/链接 ===
# Phase B/B+ 的 llc(含 ASan opt)与 Phase C 的 clang 链接命令写入指定
# .bat 路径(BuildPipeline._phase2_write_cmd 首次写入 header,后续追加)。
p2c: str = args.get_str("phase2command")
if p2c is not None:
BuildPipeline._phase2_cmd_path = p2c
if log is not None:
fb_p2c: str = VLogger.fmt_buf()
if fb_p2c is not None:
viperlib.snprintf(fb_p2c, 1024, "--phase2command: 不执行 llc/链接,命令写入 %s", p2c)
log.info(fb_p2c, "phase2cmd")
# --nowarn-register:抑制"外部函数返回类型未登记"类 WARN 输出
if args.get_bool("nowarn-register"):
VLogger.set_nowarn_register(1)