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

+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:
# 跳过 . 和 ..