fix(phasec): 可达模块 .obj 缺失误杀恢复,纯声明模块跳过

Phase B+ 对纯声明模块(text.ll 无 define 行,如 stdio.vp 全 pass)
按设计不生成 .obj,但 Phase C 遍历可达模块时无条件要求 .obj 存在,
导致误杀。新增 StubMerger.IsDeclOnlyModule 判断(与 Phase B+ 内联
逻辑一致),.obj 缺失时声明文件静默跳过,实现文件缺失仍 fail-fast。
This commit is contained in:
TermiNexus committed 2026-08-13 03:11:13 +08:00
1 parent c9de06e76d
commit b4c01808bf
2 files changed
+63 -4

No files matched your search

+8 -4
View File
@@ -980,6 +980,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# 不再全量收集 includes.binary 下的所有 .obj
if reachable_set is not None and reachable_count > 0:
ibd_len_pc: t.CSizeT = string.strlen(includes_binary_dir)
td_len_pc: t.CSizeT = string.strlen(temp_dir)
ri_pc: int
for ri_pc in range(reachable_count):
rsha1_pc: str = reachable_set + t.CSizeT(ri_pc) * 17
@@ -1001,10 +1002,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
fop_pos += op_len_pc
final_obj_paths[fop_pos] = '\0'
else:
fb_pc: str = VLogger.fmt_buf()
if fb_pc is not None:
viperlib.snprintf(fb_pc, 1024, "可达模块 .obj 缺失: %s", obj_path_pc)
VLogger.error(fb_pc, "PhaseC")
# .obj 缺失:纯声明模块(text.ll 无 define 行,如 stdio.vp 全 pass)
# 无需 .obj,静默跳过;实现文件缺失才是编译产物异常(fail-fast)
if StubMerger.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1:
fb_pc: str = VLogger.fmt_buf()
if fb_pc is not None:
viperlib.snprintf(fb_pc, 1024, "可达模块 .obj 缺失: %s", obj_path_pc)
VLogger.error(fb_pc, "PhaseC")
stdlib.free(obj_path_pc)
final_obj_paths[fop_pos] = '\0'
+55
View File
@@ -729,6 +729,61 @@ def LookupSha1RelPath(sha1: str) -> str:
return None
# ============================================================
# IsDeclOnlyModule - 判断模块是否为纯声明文件
#
# 读取 {temp_dir}/{sha1}.text.ll,扫描是否含 "define " 行:
# 返回 1 = 纯声明(无函数体,如 stdio.vp 全 pass),无需 .obj
# 返回 0 = 实现文件(含 define 函数体)
# 返回 -1 = text.ll 不存在/无法判断
#
# 与 Phase B+(Phase2.vp 编译缺失 includes 时)的内联判断逻辑一致,
# 供 Phase C 链接时区分"声明文件无需 .obj"与"实现文件缺失"。
# ============================================================
def IsDeclOnlyModule(temp_dir: str, td_len: t.CSizeT, sha1: str) -> int:
"""判断模块是否为纯声明文件(text.ll 无 define 行)"""
if temp_dir is None or sha1 is None:
return -1
tpath: str = _sliced_path(temp_dir, td_len, sha1, "text.ll")
if tpath is None:
return -1
tf_d: fileio.File | t.CPtr = fileio.File(tpath, fileio.MODE.R)
stdlib.free(tpath)
if tf_d.closed:
return -1
tbuf_d: bytes = stdlib.malloc(STUB_READ_BUF_SIZE)
tbr_d: t.CInt64T = 0
if tbuf_d is not None:
tbr_d = tf_d.read_all(tbuf_d, STUB_READ_BUF_SIZE)
tf_d.close()
if tbuf_d is None:
return -1
if tbr_d <= 0:
# 空 text.ll:无函数体,纯声明
stdlib.free(tbuf_d)
return 1
if tbr_d < STUB_READ_BUF_SIZE:
tbuf_d[tbr_d] = '\0'
else:
tbuf_d[STUB_READ_BUF_SIZE - 1] = '\0'
# 逐行扫描:找 "define " 行即为实现文件
tpos_d: t.CSizeT = 0
while tpos_d < tbr_d:
tls_d: t.CSizeT = tpos_d
while tpos_d < tbr_d:
if tbuf_d[tpos_d] == '\n':
break
tpos_d += 1
tll_d: t.CSizeT = tpos_d - tls_d
if tpos_d < tbr_d:
tpos_d += 1
if tll_d >= 7 and string.strncmp(tbuf_d + tls_d, "define ", 7) == 0:
stdlib.free(tbuf_d)
return 0
stdlib.free(tbuf_d)
return 1
# ============================================================
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
#