diff --git a/App/lib/Phase/BuildPipeline.vp b/App/lib/Phase/BuildPipeline.vp index c8a6326..f63e358 100644 --- a/App/lib/Phase/BuildPipeline.vp +++ b/App/lib/Phase/BuildPipeline.vp @@ -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") diff --git a/App/lib/Phase/Phase2/__init__.vp b/App/lib/Phase/Phase2/__init__.vp index e89a19f..aa34079 100644 --- a/App/lib/Phase/Phase2/__init__.vp +++ b/App/lib/Phase/Phase2/__init__.vp @@ -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,实现按需编译 diff --git a/App/lib/Projectrans/Config.vp b/App/lib/Projectrans/Config.vp index 2d035ab..f2c61c4 100644 --- a/App/lib/Projectrans/Config.vp +++ b/App/lib/Projectrans/Config.vp @@ -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 diff --git a/App/lib/Projectrans/Utils.vp b/App/lib/Projectrans/Utils.vp index 162bc1f..7327866 100644 --- a/App/lib/Projectrans/Utils.vp +++ b/App/lib/Projectrans/Utils.vp @@ -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: # 跳过 . 和 .. diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index fdaab29..dfbe4b6 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -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( diff --git a/App/lib/Stub/StubMerger/StubMergerCollect.vp b/App/lib/Stub/StubMerger/StubMergerCollect.vp index 4457867..5671d00 100644 --- a/App/lib/Stub/StubMerger/StubMergerCollect.vp +++ b/App/lib/Stub/StubMerger/StubMergerCollect.vp @@ -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: # 跳过 "." 和 ".." diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 1cd7494..acf133e 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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) diff --git a/App/lib/core/Gen/CallGenCall.vp b/App/lib/core/Gen/CallGenCall.vp index 4a75c68..edf27e9 100644 --- a/App/lib/core/Gen/CallGenCall.vp +++ b/App/lib/core/Gen/CallGenCall.vp @@ -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 diff --git a/App/lib/core/Gen/CallGenCtor.vp b/App/lib/core/Gen/CallGenCtor.vp index bd94e02..4694b19 100644 --- a/App/lib/core/Gen/CallGenCtor.vp +++ b/App/lib/core/Gen/CallGenCtor.vp @@ -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 diff --git a/App/lib/core/Gen/CallGenDispatch.vp b/App/lib/core/Gen/CallGenDispatch.vp index ef745e3..99529b7 100644 --- a/App/lib/core/Gen/CallGenDispatch.vp +++ b/App/lib/core/Gen/CallGenDispatch.vp @@ -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: diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index 924e189..3980e2b 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -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( diff --git a/App/lib/core/Gen/ClassGenDef.vp b/App/lib/core/Gen/ClassGenDef.vp index 2759dd8..680a9bc 100644 --- a/App/lib/core/Gen/ClassGenDef.vp +++ b/App/lib/core/Gen/ClassGenDef.vp @@ -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 diff --git a/App/lib/core/Gen/ClassGenGeneric.vp b/App/lib/core/Gen/ClassGenGeneric.vp index ccba23b..194c9af 100644 --- a/App/lib/core/Gen/ClassGenGeneric.vp +++ b/App/lib/core/Gen/ClassGenGeneric.vp @@ -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 diff --git a/App/lib/core/Gen/ClassGenVTable.vp b/App/lib/core/Gen/ClassGenVTable.vp index f42a464..f8e14c2 100644 --- a/App/lib/core/Gen/ClassGenVTable.vp +++ b/App/lib/core/Gen/ClassGenVTable.vp @@ -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( diff --git a/App/lib/core/Gen/ExprGenCls.vp b/App/lib/core/Gen/ExprGenCls.vp index 9c6fbe2..e9fd8fd 100644 --- a/App/lib/core/Gen/ExprGenCls.vp +++ b/App/lib/core/Gen/ExprGenCls.vp @@ -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 \ No newline at end of file diff --git a/App/lib/core/Gen/FuncGenDef.vp b/App/lib/core/Gen/FuncGenDef.vp index 013d9a7..b18ebb7 100644 --- a/App/lib/core/Gen/FuncGenDef.vp +++ b/App/lib/core/Gen/FuncGenDef.vp @@ -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) diff --git a/App/lib/core/Gen/FuncGenMixin.vp b/App/lib/core/Gen/FuncGenMixin.vp index 6b3cb83..60a316a 100644 --- a/App/lib/core/Gen/FuncGenMixin.vp +++ b/App/lib/core/Gen/FuncGenMixin.vp @@ -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) diff --git a/App/lib/core/Gen/StmtGenCifMixin.vp b/App/lib/core/Gen/StmtGenCifMixin.vp index c3e01cd..4ebd74b 100644 --- a/App/lib/core/Gen/StmtGenCifMixin.vp +++ b/App/lib/core/Gen/StmtGenCifMixin.vp @@ -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 diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 337d9b8..a4b1d4c 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -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) diff --git a/App/lib/core/Gen/TypeGenResolve.vp b/App/lib/core/Gen/TypeGenResolve.vp index 392bf41..4819b6b 100644 --- a/App/lib/core/Gen/TypeGenResolve.vp +++ b/App/lib/core/Gen/TypeGenResolve.vp @@ -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 "" 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 diff --git a/App/main.vp b/App/main.vp index 7fc2e3e..eb3f22b 100644 --- a/App/main.vp +++ b/App/main.vp @@ -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)