diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index 3645570..166e5bd 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -243,6 +243,13 @@ class CallGenMixin: # 第二遍:后缀匹配(.name) # 不跳过 declare:方法 stub 也是 declare,需要通过后缀匹配找到 # 调用处(translate_call 等)负责判断 IsDeclared 并决定使用裸名还是 sha1 前缀名 + # + # 修复:本模块 define 优先于跨模块 stub declare。 + # 根因:跨模块同名函数(如 lexer.vp 的 _advance 与 parser.vp 的 _advance) + # 的 stub declare 在 stub 注入时排在 FuncHead 前面,裸名调用 _advance 后缀 + # 匹配按链表顺序先命中跨模块 declare → 参数按 Lexer* coerce → Parser* 被 + # bitcast 成 Lexer* → lexer 版 _advance 按 Lexer 布局操作 Parser 指针, + # cur 永不推进 → parse 无限循环。本模块同名 define 必须优先。 cur = mod.FuncHead while cur is not None: cur_name2: t.CChar | t.CPtr = llvmlite.function_get_name(cur) @@ -257,6 +264,24 @@ class CallGenMixin: match = 0 break if match == 1: + if cur.IsDeclared == 0: + return cur + cur = llvmlite.function_get_next(cur) + # 本模块无同名 define:回退到 stub declare(跨模块方法/函数调用) + cur = mod.FuncHead + while cur is not None: + cur_name3: t.CChar | t.CPtr = llvmlite.function_get_name(cur) + if cur_name3 is not None: + cur_len3: t.CSizeT = string.strlen(cur_name3) + if cur_len3 > name_len + 1: + suffix_start3: t.CSizeT = cur_len3 - name_len + if cur_name3[suffix_start3 - 1] == '.': + match3: int = 1 + for i in range(name_len): + if cur_name3[suffix_start3 + i] != name[i]: + match3 = 0 + break + if match3 == 1: return cur cur = llvmlite.function_get_next(cur) return None @@ -564,6 +589,28 @@ class CallGenMixin: return None + def _is_sha1_mangled_call(self, call_name: str) -> int: + """判断 call_name 是否为跨模块 Viper 函数("{16位hex}.func" 形式) + + C 库函数(strlen 等裸名)和 Win32 API(VirtualAlloc 等裸名)返回 0, + 不参与精确返回类型解析(走 _infer_external_func_ret_ty 白名单)。 + """ + if call_name is None: + return 0 + first_dot: t.CChar | t.CPtr = string.strchr(call_name, '.') + if first_dot is None: + return 0 + pre_len: t.CSizeT = t.CSizeT(t.CUInt64T(first_dot) - t.CUInt64T(call_name)) + if pre_len != 16: + return 0 + for hx in range(16): + ch: int = call_name[hx] + if ch < 48 or ch > 57: + if ch < 97 or ch > 102: + if ch < 65 or ch > 70: + return 0 + return 1 + def _emit_llvm_memcpy_intrinsic(self, pool: memhub.MemBuddy | t.CPtr, builder: llvmlite.IRBuilder | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -2917,9 +2964,14 @@ class CallGenMixin: def _LookupMethodRetTyFromTextLL(self, pool: memhub.MemBuddy | t.CPtr, class_sha1: str, class_name: str, - method_name: str) -> llvmlite.LLVMType | t.CPtr: - """从依赖模块的 text.ll 中查找方法返回类型""" - if pool is None or class_sha1 is None or class_name is None or method_name is None: + method_name: str, + use_bare: int = 0) -> llvmlite.LLVMType | t.CPtr: + """从依赖模块的 text.ll 中查找方法返回类型 + + use_bare: 1=按模块级函数查找(搜索 "{sha1}.{method_name}" define, + 用于 new_lexer/tokenize 等模块函数;class_name 忽略)。 + """ + if pool is None or class_sha1 is None or method_name is None: return None if self.TempDir is None or self.TempDirLen == 0: return None @@ -2968,16 +3020,24 @@ class CallGenMixin: else: read_buf[_TEXT_LL_READ_BUF_SIZE - 1] = '\0' - # 构造搜索模式: "{sha1}.{class_name}.{method_name}" - cls_len: t.CSizeT = string.strlen(class_name) + # 构造搜索模式: "{sha1}.{class_name}.{method_name}" 或 bare 模式 "{sha1}.{method_name}" + cls_len: t.CSizeT = 0 + if class_name is not None: + cls_len = string.strlen(class_name) meth_len: t.CSizeT = string.strlen(method_name) - search_len: t.CSizeT = sha1_len + cls_len + meth_len + 4 + search_len: t.CSizeT = sha1_len + meth_len + 2 + if use_bare == 0: + search_len = sha1_len + cls_len + meth_len + 4 search_buf: bytes = stdlib.malloc(search_len) if search_buf is None: stdlib.free(read_buf) return None - viperlib.snprintf(search_buf, search_len, "%s.%s.%s", - class_sha1, class_name, method_name) + if use_bare != 0: + viperlib.snprintf(search_buf, search_len, "%s.%s", + class_sha1, method_name) + else: + viperlib.snprintf(search_buf, search_len, "%s.%s.%s", + class_sha1, class_name, method_name) search_str_len: t.CSizeT = string.strlen(search_buf) # 在 text.ll 中搜索 define 行包含 search_buf @@ -3010,8 +3070,15 @@ class CallGenMixin: # 检查是否匹配 search_buf if name_start + search_str_len <= ls + ll: if string.strncmp(read_buf + name_start, search_buf, search_str_len) == 0: - # 匹配!提取返回类型: [ls+7, at_pos) - result_ty = self._ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos) + # 函数名边界检查:search 后必须是 '"'(带引号名)、'('(参数表)或行尾, + # 否则 "sha1.func" 会误匹配 "sha1.func_helper" 等前缀同名函数 + name_end: t.CSizeT = name_start + search_str_len + end_ch: int = 0 + if name_end < ls + ll: + end_ch = read_buf[name_end] + if end_ch == 34 or end_ch == 40 or end_ch == 0: + # 匹配!提取返回类型: [ls+7, at_pos) + result_ty = self._ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos) pos = le + 1 @@ -3019,6 +3086,45 @@ class CallGenMixin: stdlib.free(read_buf) return result_ty + # ============================================================ + # _dep_text_ll_exists - 探测依赖模块 text.ll 是否已生成 + # + # 用于区分跨模块返回类型精确解析失败的两种场景: + # 1. text.ll 存在但函数未命中 → 真错误(依赖已编译,函数缺失) + # 2. text.ll 不存在 → 依赖模块尚未翻译(Phase A 按文件顺序翻译, + # main.vp 先于其 import 的 mbuddytest.vp),标记重试待下遍 + # ============================================================ + def _dep_text_ll_exists(self, pool: memhub.MemBuddy | t.CPtr, + sha1: str) -> int: + """探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在,1=存在""" + if self.TempDir is None or self.TempDirLen == 0 or sha1 is None: + return 0 + sha1_len: t.CSizeT = string.strlen(sha1) + if sha1_len < 2: + return 0 + path_len: t.CSizeT = self.TempDirLen + sha1_len + 14 + path_buf: bytes = stdlib.malloc(path_len) + subdir_buf: bytes = stdlib.malloc(3) + if path_buf is None or subdir_buf is None: + if path_buf is not None: + stdlib.free(path_buf) + if subdir_buf is not None: + stdlib.free(subdir_buf) + return 0 + subdir_buf[0] = sha1[0] + subdir_buf[1] = sha1[1] + subdir_buf[2] = '\0' + viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll", + self.TempDir, subdir_buf, sha1) + stdlib.free(subdir_buf) + tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R) + exists: int = 0 + if not tf.closed: + tf.close() + exists = 1 + stdlib.free(path_buf) + return exists + # ============================================================ # 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助 # @@ -5997,6 +6103,71 @@ class CallGenMixin: re_inferred_ty: llvmlite.LLVMType | t.CPtr = self._infer_external_func_ret_ty(pool, orig_func_name) if re_inferred_ty is not None: call_ret_ty = re_inferred_ty + # ============================================================ + # 跨模块 Viper 函数返回类型精确解析(对齐方法路径,fail-fast) + # 根因:_infer_external_func_ret_ty 白名单未覆盖返回指针的模块函数 + # (new_lexer→Lexer* / tokenize→Token* / parse_tokens→AST*),兜底 i32 + # 导致 64 位指针被截断高 32 位 → inttoptr 无效指针 → 解引用崩溃 + # (AstTest 的 _lexer_init 崩溃链)。declare 也可能是错误 i32(_ensure_method_declare + # 曾用兜底类型创建),因此不信任 found_func 的 declare 返回类型。 + # 权威来源:text.ll define;次选:vpi stub 返回注解;均未命中立即终止。 + # 仅对 mangled("{16位hex}.func")调用生效;C 库/Win32 裸名走原白名单。 + # Phase 1a(declare_only)期间依赖 text.ll 未生成,保持原白名单行为不 fail-fast。 + # ============================================================ + if self._is_sha1_mangled_call(call_name) != 0: + if trans is None or trans._declare_only == 0: + cf_dot: t.CChar | t.CPtr = string.strchr(call_name, '.') + cf_last: t.CChar | t.CPtr = string.strrchr(call_name, '.') + if cf_dot is not None and cf_last is not None: + cf_sha1: t.CChar | t.CPtr = stdlib.malloc(17) + cf_bare_len: t.CSizeT = string.strlen(call_name) - \ + (t.CSizeT(t.CUInt64T(cf_last) - t.CUInt64T(call_name))) - 1 + cf_bare: t.CChar | t.CPtr = stdlib.malloc(cf_bare_len + 1) + if cf_sha1 is not None and cf_bare is not None: + string.strncpy(cf_sha1, call_name, 16) + cf_sha1[16] = '\0' + string.strcpy(cf_bare, cf_last + 1) + cf_ret: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL( + pool, cf_sha1, None, cf_bare, 1) + if cf_ret is not None: + call_ret_ty = cf_ret + else: + cf_vpi: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi( + pool, cf_sha1, None, cf_bare) + if cf_vpi is not None: + call_ret_ty = cf_vpi + else: + # 区分两种失败场景: + # 1. 依赖模块 text.ll 存在但函数未命中 → 真错误,fail-fast + # 2. 依赖模块 text.ll 不存在(模块尚未翻译)→ Phase A 按 + # 文件顺序翻译,main.vp 先于其 import 的 mbuddytest.vp + # (同项目多源文件)。此时标记 trans._deps_pending=1, + # 保持白名单类型继续,Phase A 多遍循环下遍重译—— + # 届时依赖已生成 text.ll,可精确解析(治本:依赖先翻译)。 + if self._dep_text_ll_exists(pool, cf_sha1) != 0: + cf_err: t.CChar | t.CPtr = VLogger.fmt_buf() + if cf_err is not None: + viperlib.snprintf( + cf_err, 1024, + "跨模块函数 '%s' 返回类型无法精确获取(text.ll/vpi 均未命中)," + "默认 i32 会截断指针/i64,请检查依赖模块是否已编译/注册", call_name) + VLogger.error(cf_err, "HandlesExprCall") + HandlesType.fatal_error( + node, + f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}", + "TypeError") + else: + if trans is not None: + trans._deps_pending = 1 + cf_dbg: t.CChar | t.CPtr = VLogger.fmt_buf() + if cf_dbg is not None: + viperlib.snprintf( + cf_dbg, 1024, + "跨模块函数 '%s' 依赖模块 text.ll 尚未生成(Phase A 顺序)," + "标记重试,本遍保持默认类型", call_name) + VLogger.warning(cf_dbg, "HandlesExprCall") + stdlib.free(cf_sha1) + stdlib.free(cf_bare) # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can) # 检查被调用函数是否为变长函数(*args),若是则对变长参数进行 C ABI 提升 diff --git a/App/lib/core/Handles/HandlesTranslator.vp b/App/lib/core/Handles/HandlesTranslator.vp index 6ac244f..f8f211e 100644 --- a/App/lib/core/Handles/HandlesTranslator.vp +++ b/App/lib/core/Handles/HandlesTranslator.vp @@ -132,6 +132,11 @@ class Translator: # Phase 1a 声明模式标志:1=只注册 struct/enum/union 不翻译代码体,0=全量翻译 _declare_only: t.CInt + # Phase A 依赖未就绪标志:1=本文件翻译时依赖模块的 text.ll 尚未生成 + # (同项目源文件按顺序翻译,main.vp 先于其 import 的 mbuddytest.vp), + # 跨模块函数返回类型无法精确解析。Phase A 多遍循环据此下遍重试。 + _deps_pending: t.CInt + # 子 Handle 指针(每个 Handle 一个槽,通过 Mixin 回指针访问本结构体) AssignH: HandlesAssign.AssignHandle | t.CPtr ReturnH: HandlesReturn.ReturnHandle | t.CPtr @@ -175,6 +180,7 @@ class Translator: self.GenericTypeParamNames = None self.GenericTypeArgs = None self._declare_only = 0 + self._deps_pending = 0 self.AssignH = None self.ReturnH = None self.ImportsH = None diff --git a/App/lib/core/Phase2.vp b/App/lib/core/Phase2.vp index 6f7dae1..fc42dd9 100644 --- a/App/lib/core/Phase2.vp +++ b/App/lib/core/Phase2.vp @@ -415,84 +415,139 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if app_deps_buf is not None: app_deps_buf[0] = '\0' - for i in range(file_count): - ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size - ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) - if ent is None or ent.Path is None: - continue - fb_loop: t.CChar | t.CPtr = VLogger.fmt_buf() - if fb_loop is not None: - viperlib.snprintf(fb_loop, 1024, "Phase A 开始翻译: %s", ent.Path) - VLogger.info(fb_loop, "PhaseA-DBG") - # 计算源文件的包名(相对 source_dir 的目录部分) - src_dir_len_pa: t.CSizeT = string.strlen(source_dir) - pkg_pa: str = None - if string.strlen(ent.Path) > src_dir_len_pa + 1: - rel_path_pa: str = ent.Path + src_dir_len_pa + 1 - pkg_pa = HandlesImports.compute_package_from_relpath(mb, rel_path_pa) - tr_a: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent.Path, ent.Sha1, pkg_pa, 0, temp_dir) - if tr_a is None: - continue - fb_ok: t.CChar | t.CPtr = VLogger.fmt_buf() - if fb_ok is not None: - viperlib.snprintf(fb_ok, 1024, "Phase A 翻译完成: %s", ent.Path) - VLogger.info(fb_ok, "PhaseA-DBG") + # Phase A 多遍重试:同项目多源文件时,main.vp 可能先于其 import 的 + # mbuddytest.vp 翻译(文件系统顺序),跨模块函数返回类型精确解析会因 + # 依赖 text.ll 未生成而标记 _deps_pending=1(见 CallGenMixin 跨模块 + # 返回类型解析段)。下遍重译即可命中(依赖已生成 text.ll)。 + # 每遍至少完成一个文件,最多 file_count 遍收敛。 + done_flags: bytes = stdlib.malloc(file_count) + if done_flags is None: + VLogger.error("Phase A done_flags 分配失败", "PhaseA") + stdlib.free(entries) + return 1 + string.memset(done_flags, 0, file_count) + PHASE_A_MAX_PASSES: t.CInt = 32 + for pass_a in range(PHASE_A_MAX_PASSES): + completed_this_pass: int = 0 + for i in range(file_count): + if done_flags[i] != 0: + continue + ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size + ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) + if ent is None or ent.Path is None: + done_flags[i] = 1 + completed_this_pass += 1 + continue + fb_loop: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_loop is not None: + viperlib.snprintf(fb_loop, 1024, "Phase A 开始翻译: %s", ent.Path) + VLogger.info(fb_loop, "PhaseA-DBG") + # 计算源文件的包名(相对 source_dir 的目录部分) + src_dir_len_pa: t.CSizeT = string.strlen(source_dir) + pkg_pa: str = None + if string.strlen(ent.Path) > src_dir_len_pa + 1: + rel_path_pa: str = ent.Path + src_dir_len_pa + 1 + pkg_pa = HandlesImports.compute_package_from_relpath(mb, rel_path_pa) + tr_a: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(mb, ent.Path, ent.Sha1, pkg_pa, 0, temp_dir) + if tr_a is None: + # 翻译失败(语法/解析错误):记录待下遍重试,收敛检查统一报错 + continue + # 收集 _imported_modules 到 app_deps_buf(供 Phase B+ 按需编译)。 + # 依赖在翻译早期已填充,即使 _deps_pending 也收集。 + if tr_a._imported_modules is not None and app_deps_buf is not None: + im_len_a: t.CSizeT = string.strlen(tr_a._imported_modules) + if im_len_a > 0 and app_deps_len + im_len_a + 1 < APP_DEPS_BUF_SIZE: + if app_deps_len > 0: + app_deps_buf[app_deps_len] = ' ' + app_deps_len = app_deps_len + 1 + string.strcpy(app_deps_buf + app_deps_len, tr_a._imported_modules) + app_deps_len = app_deps_len + im_len_a + app_deps_buf[app_deps_len] = '\0' + if tr_a._deps_pending != 0: + # 依赖模块 text.ll 未生成(同项目文件顺序),不写盘,下遍重试 + fb_dp: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_dp is not None: + viperlib.snprintf(fb_dp, 1024, + "Phase A 依赖未就绪,下遍重试: %s", ent.Path) + VLogger.warning(fb_dp, "PhaseA-DBG") + continue + fb_ok: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_ok is not None: + viperlib.snprintf(fb_ok, 1024, "Phase A 翻译完成: %s", ent.Path) + VLogger.info(fb_ok, "PhaseA-DBG") - # dump stub IR (declarations only) - stub_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE) - if stub_buf_a is None: - continue - tr_a.dump_ir(stub_buf_a, PHASE_A_IR_SIZE, llvmlite.OUTPUT_STUB) - stub_len_a: t.CSizeT = string.strlen(stub_buf_a) + # dump stub IR (declarations only) + stub_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE) + if stub_buf_a is None: + continue + tr_a.dump_ir(stub_buf_a, PHASE_A_IR_SIZE, llvmlite.OUTPUT_STUB) + stub_len_a: t.CSizeT = string.strlen(stub_buf_a) - # save stub.ll (切片路径: temp_dir/{sha1前缀}/{sha1}.stub.ll) - stub_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "stub.ll") - if stub_path_a is not None: - sf_a: fileio.File | t.CPtr = fileio.File(stub_path_a, fileio.MODE.W) - if not sf_a.closed: - sf_a.write(stub_buf_a, stub_len_a) - sf_a.close() - stdlib.free(stub_path_a) - stdlib.free(stub_buf_a) + # save stub.ll (切片路径: temp_dir/{sha1前缀}/{sha1}.stub.ll) + stub_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "stub.ll") + if stub_path_a is not None: + sf_a: fileio.File | t.CPtr = fileio.File(stub_path_a, fileio.MODE.W) + if not sf_a.closed: + sf_a.write(stub_buf_a, stub_len_a) + sf_a.close() + stdlib.free(stub_path_a) + stdlib.free(stub_buf_a) - # dump text IR (definitions only) - text_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE) - if text_buf_a is None: - continue - tr_a.dump_ir(text_buf_a, PHASE_A_IR_SIZE, llvmlite.OUTPUT_TEXT) - text_len_a: t.CSizeT = string.strlen(text_buf_a) + # dump text IR (definitions only) + text_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE) + if text_buf_a is None: + continue + tr_a.dump_ir(text_buf_a, PHASE_A_IR_SIZE, llvmlite.OUTPUT_TEXT) + text_len_a: t.CSizeT = string.strlen(text_buf_a) - # save text.ll (切片路径) - text_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "text.ll") - if text_path_a is not None: - tf_a: fileio.File | t.CPtr = fileio.File(text_path_a, fileio.MODE.W) - if not tf_a.closed: - tf_a.write(text_buf_a, text_len_a) - tf_a.close() - stdlib.free(text_path_a) - stdlib.free(text_buf_a) + # save text.ll (切片路径) + text_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "text.ll") + if text_path_a is not None: + tf_a: fileio.File | t.CPtr = fileio.File(text_path_a, fileio.MODE.W) + if not tf_a.closed: + tf_a.write(text_buf_a, text_len_a) + tf_a.close() + stdlib.free(text_path_a) + stdlib.free(text_buf_a) - # save dependencies (_imported_modules) for Phase B (切片路径) - deps_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "deps.txt") - if deps_path_a is not None: - df_a: fileio.File | t.CPtr = fileio.File(deps_path_a, fileio.MODE.W) - if not df_a.closed: - if tr_a._imported_modules is not None: - dl_a: t.CSizeT = string.strlen(tr_a._imported_modules) - df_a.write(tr_a._imported_modules, dl_a) - df_a.close() - stdlib.free(deps_path_a) + # save dependencies (_imported_modules) for Phase B (切片路径) + deps_path_a: str = StubMerger._sliced_path(temp_dir, td_len_pa, ent.Sha1, "deps.txt") + if deps_path_a is not None: + df_a: fileio.File | t.CPtr = fileio.File(deps_path_a, fileio.MODE.W) + if not df_a.closed: + if tr_a._imported_modules is not None: + dl_a: t.CSizeT = string.strlen(tr_a._imported_modules) + df_a.write(tr_a._imported_modules, dl_a) + df_a.close() + stdlib.free(deps_path_a) - # 收集 _imported_modules 到 app_deps_buf(供 Phase B+ 按需编译) - if tr_a._imported_modules is not None and app_deps_buf is not None: - im_len_a: t.CSizeT = string.strlen(tr_a._imported_modules) - if im_len_a > 0 and app_deps_len + im_len_a + 1 < APP_DEPS_BUF_SIZE: - if app_deps_len > 0: - app_deps_buf[app_deps_len] = ' ' - app_deps_len = app_deps_len + 1 - string.strcpy(app_deps_buf + app_deps_len, tr_a._imported_modules) - app_deps_len = app_deps_len + im_len_a - app_deps_buf[app_deps_len] = '\0' + done_flags[i] = 1 + completed_this_pass += 1 + # 收敛检查:所有文件完成则结束 + all_done: int = 1 + for i_ck in range(file_count): + if done_flags[i_ck] == 0: + all_done = 0 + break + if all_done != 0: + break + if completed_this_pass == 0: + # 无进展:剩余文件无法完成(真实编译错误),终止并列出未完成文件 + for i_err in range(file_count): + if done_flags[i_err] == 0: + ea_err: t.CUInt64T = t.CUInt64T(entries) + i_err * entry_size + ent_err: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_err, t.CPtr)) + if ent_err is not None and ent_err.Path is not None: + fb_err: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_err is not None: + viperlib.snprintf(fb_err, 1024, + "Phase A 无法完成翻译: %s(请检查依赖模块是否已编译/注册)", ent_err.Path) + VLogger.error(fb_err, "PhaseA") + stdlib.free(done_flags) + VLogger.error("Phase A 多遍收敛失败,终止编译", "PhaseA") + stdlib.free(entries) + return 1 + stdlib.free(done_flags) if do_phase2 == 0: VLogger.info("Phase A 完成(仅 stub 生成)", "project")