From 6b368b46a7d8be0d25f056b78a97eeb14440449f Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 5 Sep 2026 14:13:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(tpv):=20App=20=E4=B8=8E=20TPVAppBK=20?= =?UTF-8?q?=E6=96=B0=E4=BB=A3=E7=89=88=E6=9C=AC=E5=AF=B9=E9=BD=90=EF=BC=88?= =?UTF-8?q?rowptr=20=E9=85=8D=E5=A5=97=E5=AE=9E=E7=8E=B0=E5=85=A8=E5=A5=97?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把 TransPyV/App 覆盖为 TPVAppBK 内容(39 文件):6 参注册式 _BuildReachableSha1Set 全套(StubMergerBuild/Store/Collect/__init__/IncludesScanner/Phase1Scan 等)、CallGen 与 TypeGenResolve 等新代实现。配套根 includes 为 rowptr 工作区(另有 includes_rowptr_bk 备份)。 --- App/lib/Phase/BuildPipeline.vp | 4 +- App/lib/Phase/Phase1/Phase1Main.vp | 118 +--- App/lib/Phase/Phase1/Phase1Scan.vp | 39 +- App/lib/Phase/Phase2/__init__.vp | 6 +- App/lib/Stub/StubGen/VpiReader.vp | 309 +++++++- App/lib/Stub/StubMerger/StubMergerBuild.vp | 699 ++++++++++++------- App/lib/Stub/StubMerger/StubMergerCollect.vp | 12 +- App/lib/Stub/StubMerger/StubMergerStore.vp | 75 ++ App/lib/Stub/StubMerger/__init__.vp | 2 +- App/lib/core/Gen/AssignGenMixin.vp | 175 ++++- App/lib/core/Gen/AsyncGenMixin.vp | 14 +- App/lib/core/Gen/Call/CallGenBase.vp | 37 +- App/lib/core/Gen/Call/CallGenCall.vp | 67 ++ App/lib/core/Gen/Call/CallGenCtor.vp | 17 +- App/lib/core/Gen/Call/CallGenDispatch.vp | 20 +- App/lib/core/Gen/Call/CallGenMixin.vp | 24 +- App/lib/core/Gen/Call/CallGenProp.vp | 5 + App/lib/core/Gen/Class/ClassGenBase.vp | 107 +++ App/lib/core/Gen/Class/ClassGenDef.vp | 72 +- App/lib/core/Gen/Class/ClassGenVTable.vp | 32 + App/lib/core/Gen/Expr/ExprGenAttr.vp | 57 ++ App/lib/core/Gen/Expr/ExprGenBase.vp | 34 +- App/lib/core/Gen/Expr/ExprGenMixin.vp | 12 + App/lib/core/Gen/Expr/ExprGenName.vp | 97 ++- App/lib/core/Gen/Expr/ExprGenOps.vp | 15 + App/lib/core/Gen/Func/FuncGenBase.vp | 63 ++ App/lib/core/Gen/Func/FuncGenDef.vp | 91 ++- App/lib/core/Gen/GenTypes.vp | 2 + App/lib/core/Gen/Stmt/StmtGenCifMixin.vp | 24 +- App/lib/core/Gen/Stmt/StmtGenMixin.vp | 7 + App/lib/core/Gen/StructGenMixin.vp | 52 +- App/lib/core/Gen/Translator.vp | 116 ++- App/lib/core/Gen/TranslatorGenMixin.vp | 74 +- App/lib/core/Gen/Type/TypeGenBase.vp | 19 + App/lib/core/Gen/Type/TypeGenCType.vp | 40 +- App/lib/core/Gen/Type/TypeGenErr.vp | 38 + App/lib/core/Gen/Type/TypeGenResolve.vp | 554 ++++++++++++++- App/lib/core/IncludesScanner.vp | 180 ++++- App/main.vp | 9 +- 39 files changed, 2813 insertions(+), 505 deletions(-) diff --git a/App/lib/Phase/BuildPipeline.vp b/App/lib/Phase/BuildPipeline.vp index a2d1d6e..11786e8 100644 --- a/App/lib/Phase/BuildPipeline.vp +++ b/App/lib/Phase/BuildPipeline.vp @@ -50,9 +50,7 @@ _phase2_bat_init: int = 0 SRC_BUF_SIZE: t.CDefine = 1048576 # 2^20 = 1MB # .vpi 存根缓冲区大小(256KB,与 Phase1 一致) -# 用 t.CDefine 宏而非模块级全局:模块级全局是跨模块共享裸符号(@PYI_BUF_SIZE), -# Phase1/__init__.vp 已定义同名全局 → 两模块各定义强符号 → ld.lld duplicate symbol。 -PYI_BUF_SIZE: t.CDefine = 262144 +PYI_BUF_SIZE: t.CSizeT = 262144 # ============================================================ # TranslateFileGetTrans - 翻译单个文件,返回 Translator 对象 diff --git a/App/lib/Phase/Phase1/Phase1Main.vp b/App/lib/Phase/Phase1/Phase1Main.vp index 611f26d..327fd2d 100644 --- a/App/lib/Phase/Phase1/Phase1Main.vp +++ b/App/lib/Phase/Phase1/Phase1Main.vp @@ -244,14 +244,8 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int: # ScanResult 指针(stdlib 分配,调用方负责释放);失败返回 None # ============================================================ def _BuildReachableResult(mb: memhub.MemBuddy | t.CPtr, - includes_dir: str, - reachable_set: str = None, - reachable_count: int = 0) -> IncludesScanner.ScanResult | t.CPtr: - """从 Sha1Store(可达 includes + App)构建可达 FileEntry 列表 - - reachable_set/reachable_count 非空时按可达集合过滤:includes 预注册是 - 全量 store(PopulateSha1MapStore),1a/1b 只处理 reachable 子集。 - """ + includes_dir: str) -> IncludesScanner.ScanResult | t.CPtr: + """从 Sha1Store(仅可达 includes + App)构建可达 FileEntry 列表""" if mb is None: return None store_count: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) @@ -270,10 +264,6 @@ def _BuildReachableResult(mb: memhub.MemBuddy | t.CPtr, iidx: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_INC_DIR_LEN if StubMerger._sha1_store_incdir[iidx] == '\0': continue - # 可达过滤(预注册为全量 store,result 只保留 reachable 子集) - if reachable_count > 0 and reachable_set is not None: - if StubMergerCollect._is_in_sha1_set(StubMerger._sha1_store_arr + sidx, reachable_set, reachable_count) == 0: - continue eaddr: t.CUInt64T = t.CUInt64T(result.Entries) + t.CUInt64T(result.Count) * entry_size entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(eaddr, t.CPtr)) if entry is None: @@ -371,105 +361,6 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 悬空 → 后续 tokenize 的 _kw_lookup strcmp 访问 0x4 崩溃(1.log ASan)。 ast._init_tables(mb) - # ============================================================ - # 旧 5 参 def 兼容预注册(补回 178ea40 重构时移除的 1a-pre) - # - # 旧 _BuildReachableSha1Set 只读全局存储器:BFS 前必须已有全量 - # includes 的 sha1→module 映射(PopulateSha1MapStore)与每文件的 - # import 依赖内存(PopulateIncludesDeps)。注册式新 def 是在 BFS 中 - # 自行登记,旧 def 没有该逻辑,故在 reachable 之前全量扫描注册。 - # ============================================================ - pp_t0: t.CUInt64T = w32.win32base.GetTickCount64() - pp_res: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir) - if pp_res is None: - VLogger.error("includes 预扫描失败(scan_includes 返回 None),终止编译", "Phase1") - return 1 - pp_es: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() - pp_store_count: int = StubMergerStore.PopulateSha1MapStore(pp_res) - if pp_store_count <= 0: - VLogger.error(f"includes 预注册失败(PopulateSha1MapStore={pp_store_count}),终止编译", "Phase1") - return 1 - pp_fail: int = 0 - pp_ok: int = 0 - for pp_i in range(pp_res.Count): - pp_mark: t.CSizeT = mb.mark() - pp_ea: t.CUInt64T = t.CUInt64T(pp_res.Entries) + t.CUInt64T(pp_i) * pp_es - pp_ent: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(pp_ea, t.CPtr)) - if pp_ent is None or pp_ent.Sha1 is None or pp_ent.Path is None: - pp_fail += 1 - mb.reset_to(pp_mark) - continue - pp_sha1: str = pp_ent.Sha1 - pp_f: fileio.File | t.CPtr = fileio.File(pp_ent.Path, fileio.MODE.R) - if pp_f.closed: - pp_fail += 1 - mb.reset_to(pp_mark) - continue - pp_buf: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE) - if pp_buf is None: - pp_f.close() - pp_fail += 1 - mb.reset_to(pp_mark) - continue - pp_br: LONG = pp_f.read_all(pp_buf, Phase1.SRC_BUF_SIZE) - pp_f.close() - if pp_br <= 0: - stdlib.free(pp_buf) - pp_fail += 1 - mb.reset_to(pp_mark) - continue - if pp_br < Phase1.SRC_BUF_SIZE: - pp_buf[pp_br] = 0 - else: - pp_buf[Phase1.SRC_BUF_SIZE - 1] = 0 - pp_lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) - pp_tree: ast.AST | t.CPtr = None - if pp_lx is not None: - ast._lexer_init(pp_lx, pp_buf, mb) - pp_tok: ast.Token | t.CPtr = ast.tokenize(pp_lx) - pp_tree = ast.parse_tokens(mb, pp_tok) - if pp_tree is None: - stdlib.free(pp_buf) - pp_fail += 1 - VLogger.error(f"includes 预注册解析失败: {pp_ent.Path}", "Phase1") - mb.reset_to(pp_mark) - continue - pp_tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() - if pp_tr is None: - stdlib.free(pp_buf) - pp_fail += 1 - mb.reset_to(pp_mark) - continue - pp_tr.ModuleSha1 = pp_sha1 - pp_tr._declare_only = 2 - pp_tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, pp_ent.RelPath) - GenApi.set_current_file_type(pp_ent.Path) - GenApi.set_current_file_imports(pp_ent.Path) - GenApi.set_current_module_sha1(pp_sha1) - GenApi.clear_cdefine_constants() - GenApi.clear_ctypedefs() - GenApi.reset_visible_structs(mb, 0) - pp_rc: int = pp_tr.translate(pp_tree) - if pp_rc == 0: - pp_tr._inject_auto_container_visibility(mb, pp_tree) - if pp_tr._imported_modules is not None: - StubMergerStore.PopulateIncludesDeps(pp_sha1, pp_tr._imported_modules) - pp_ok += 1 - else: - pp_fail += 1 - VLogger.error(f"includes 预注册翻译失败: {pp_ent.Path}", "Phase1") - if pp_tr._global_names is not None: - stdlib.free(pp_tr._global_names) - if pp_tr._nonlocal_names is not None: - stdlib.free(pp_tr._nonlocal_names) - stdlib.free(pp_buf) - mb.reset_to(pp_mark) - pp_t1: t.CUInt64T = w32.win32base.GetTickCount64() - VLogger.info(f"includes 预注册: {pp_ok} 个(失败 {pp_fail},{pp_t1 - pp_t0} ms)", "Phase1") - if pp_fail > 0: - VLogger.error(f"includes 预注册有 {pp_fail} 个失败,终止编译", "Phase1") - return 1 - # 依赖图按需扫描(App 优先模型): # 1) 先扫 App 源文件(Config.SourceDir)的 import 依赖 → 初始队列 # 2) BFS 沿队列按文件系统解析 includes 文件,只对可达 includes @@ -485,7 +376,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, if reachable_set is not None: string.memset(reachable_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) reachable_count = StubMergerBuild._BuildReachableSha1Set( - mb, Config.SourceDir, temp_dir, reachable_set, None) + mb, Config.SourceDir, temp_dir, reachable_set, None, includes_dir) if reachable_count < 0: VLogger.error("可达集合构建失败(依赖图扫描失败),终止编译", "Phase1") return 1 @@ -507,8 +398,7 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, # 从 Sha1Store(仅可达 includes + App)构造可达 FileEntry 列表: # 后续 1a/1b-pre/1b 直接遍历 result(incdir 非空条目=includes), # use_reachable 过滤幂等保留(result 即可达子集) - result: IncludesScanner.ScanResult | t.CPtr = _BuildReachableResult( - mb, includes_dir, reachable_set, reachable_count) + result: IncludesScanner.ScanResult | t.CPtr = _BuildReachableResult(mb, includes_dir) if result is None: VLogger.error("可达 FileEntry 列表构建失败,终止编译", "Phase1") return 1 diff --git a/App/lib/Phase/Phase1/Phase1Scan.vp b/App/lib/Phase/Phase1/Phase1Scan.vp index 93375a2..3fc360a 100644 --- a/App/lib/Phase/Phase1/Phase1Scan.vp +++ b/App/lib/Phase/Phase1/Phase1Scan.vp @@ -374,6 +374,31 @@ def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, # 因此每个节点先递归其表达式承载字段,再递归 children 语句块。 # 命中即置 need_list/need_dict;误报无害(最多多编译两个空壳模块)。 # ============================================================ + +def _AnnIsContainer(ann: ast.AST | t.CPtr) -> int: + """注解是否为 list/dict 容器(Name 裸名或 Subscript list[...]/dict[...]) + + 数组初始化(t.CArray[...] / 裸数组注解)返回 0,避免 _ScanTreeForContainers + 把数组字面量误判为 list 容器使用(FIX 2026-08-30,联动 Translator.vp 同修)。 + """ + while ann is not None: + ak_c: int = ann.kind() + if ak_c == ast.ASTKind.Name: + nm_c: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ann) + if nm_c.id is not None: + if string.strcmp(nm_c.id, "list") == 0: + return 1 + if string.strcmp(nm_c.id, "dict") == 0: + return 1 + return 0 + elif ak_c == ast.ASTKind.Subscript: + sub_c: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(ann) + ann = sub_c.value + else: + return 0 + return 0 + + def _ScanTreeForContainers(node: ast.AST | t.CPtr, need_list_box: t.CPtr, need_dict_box: t.CPtr, @@ -489,12 +514,14 @@ def _ScanTreeForContainers(node: ast.AST | t.CPtr, i_v2: t.CSizeT for i_v2 in range(nv_s): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1) - elif k_s == ast.ASTKind.List: - # [] 字面量 - need_list_box[0] = 1 - elif k_s == ast.ASTKind.Dict: - # {} 字面量 - need_dict_box[0] = 1 + # [FIX 2026-08-30] `[...]`/`{}` 字面量不再无条件触发 _list/_dict 导入: + # 字面量广泛用于编译期常量列表(c.Asm 的 op/inp/out CLOBBER 参数、 + # 数组数据初始化),并非 list 容器使用。list/dict 容器的真实使用由 + # 类型位置检测覆盖(Name/Subscript 注解、Call.func 构造名)。 + # elif k_s == ast.ASTKind.List: + # need_list_box[0] = 1 + # elif k_s == ast.ASTKind.Dict: + # need_dict_box[0] = 1 # 递归语句块子节点 if node.children is not None: ch_s: list[ast.AST | t.CPtr] | t.CPtr = node.children diff --git a/App/lib/Phase/Phase2/__init__.vp b/App/lib/Phase/Phase2/__init__.vp index c842d9d..16b770e 100644 --- a/App/lib/Phase/Phase2/__init__.vp +++ b/App/lib/Phase/Phase2/__init__.vp @@ -763,7 +763,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, do_phase1: int, do_phase2: int, log: VLogger.Logger | t.CPtr, args: argparse.ParsedArgs | t.CPtr, - linker_static_libs: str = None, + linker_static_libs: str = None, linker_dynamic_libs: str = None) -> int: """多文件项目编译 @@ -837,7 +837,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, else: string.memset(rs_05, 0, StubMerger.MAX_INCLUDES_SHA1 * 17) rc_05: int = StubMergerBuild._BuildReachableSha1Set( - mb, source_dir, temp_dir, rs_05, None) + mb, source_dir, temp_dir, rs_05, None, includes_dir) if rc_05 < 0: VLogger.error("依赖图扫描失败,无法填充 Sha1Store", "project") elif rc_05 > 0: @@ -1615,7 +1615,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, if reachable_set is not None: string.memset(reachable_set, 0, REACHABLE_SET_SIZE) reachable_count = StubMergerBuild._BuildReachableSha1Set( - mb, source_dir, temp_dir, reachable_set, app_deps_buf) + mb, source_dir, temp_dir, reachable_set, app_deps_buf, includes_dir) VLogger.info(f"可达 includes SHA1: {reachable_count} 个", "PhaseB+") # fast-fail: 可达集合构建失败(依赖未找到),永不回退,直接终止 if reachable_count < 0: diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 6bc5833..021dd80 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -1186,6 +1186,219 @@ def _stub_add_from(pool: memhub.MemBuddy | t.CPtr, return buf +# ============================================================ +# 递归 typedef 预注册(import 闭包) +# +# 背景:vpi stub 注入(_inject_function_declare)解析参数注解(如 +# SetConsoleOutputCP(codepage: UINT))时,UINT 等 typedef 定义在 stdint.vp, +# 但 CTypedef 表是每文件隔离的——当前模块若未直接 import stdint,其 stub +# 未注入 → 表里没有 UINT → 参数类型解析失败退化为 i8_ptr_ty(指针)→ +# 调用侧整数实参触发"整数隐式转指针已禁止"CastError 误报 +# (testcheck.SetConsoleOutputCP(CP_UTF8) 根因)。 +# 本函数在被读 stub 的 import 闭包上递归注册 CTypedef/CDefine(防环、 +# 限深),使注解解析上下文完整,参数/返回类型不再静默退化。 +# ============================================================ +_TYPEDEF_VISIT_MAX: t.CDefine = 1024 + +_g_vpi_typedef_visited: bytes = None +_g_vpi_typedef_visited_count: int = 0 + + +def ResetVpiTypedefVisited() -> None: + """清空递归 typedef 注册的防环访问标记(每个模块的 stub 注入前调用)""" + global _g_vpi_typedef_visited, _g_vpi_typedef_visited_count + if _g_vpi_typedef_visited is not None: + stdlib.free(_g_vpi_typedef_visited) + _g_vpi_typedef_visited = None + _g_vpi_typedef_visited_count = 0 + + +def _vpi_typedef_visited_check(sha1: str) -> int: + """检查 sha1 是否已递归注册过(1=已处理,0=未处理;纯查询不标记)""" + global _g_vpi_typedef_visited, _g_vpi_typedef_visited_count + if sha1 is None: + return 1 + if _g_vpi_typedef_visited is None: + return 0 + vi: int + for vi in range(_g_vpi_typedef_visited_count): + if string.strcmp(_g_vpi_typedef_visited + vi * 17, sha1) == 0: + return 1 + return 0 + + +def _vpi_typedef_visited_mark(sha1: str) -> None: + """标记 sha1 为已递归注册(成功后调用,防环)""" + global _g_vpi_typedef_visited, _g_vpi_typedef_visited_count + if sha1 is None: + return + if _g_vpi_typedef_visited is None: + _g_vpi_typedef_visited = stdlib.malloc(_TYPEDEF_VISIT_MAX * 17) + if _g_vpi_typedef_visited is None: + return + string.memset(_g_vpi_typedef_visited, 0, _TYPEDEF_VISIT_MAX * 17) + _g_vpi_typedef_visited_count = 0 + if _g_vpi_typedef_visited_count >= _TYPEDEF_VISIT_MAX: + return + string.strcpy(_g_vpi_typedef_visited + _g_vpi_typedef_visited_count * 17, sha1) + _g_vpi_typedef_visited_count += 1 + + +def _vpi_register_typedef_closure(pool: memhub.MemBuddy | t.CPtr, + import_mods: str, + temp_dir: str, + depth: int) -> int: + """递归注册 import 闭包模块的 CTypedef/CDefine,返回 0 恒(副作用注册) + + 仅注册 AnnAssign 的 t.CTypedef/t.CDefine,不注入函数 declare(避免 + 传递依赖的函数表膨胀)。vpi 文件缺失(Phase A 早期 stub 未生成)时 + 静默跳过,Phase 2 stub 就绪后自然命中。 + """ + if pool is None or import_mods is None or temp_dir is None: + return 0 + if depth > 8: + return 0 + td_len: t.CSizeT = string.strlen(temp_dir) + total_len: t.CSizeT = string.strlen(import_mods) + pos: t.CSizeT = 0 + while pos < total_len: + while pos < total_len and import_mods[pos] == ' ': + pos += 1 + if pos >= total_len: + break + m_start: t.CSizeT = pos + while pos < total_len and import_mods[pos] != ' ': + pos += 1 + m_len: t.CSizeT = pos - m_start + if m_len == 0: + continue + # t/c 内建库不编译,无 vpi stub + if m_len == 1 and (import_mods[m_start] == 't' or import_mods[m_start] == 'c'): + continue + mod_buf: bytes = stdlib.malloc(m_len + 1) + if mod_buf is None: + continue + string.strncpy(mod_buf, import_mods + m_start, m_len) + mod_buf[m_len] = '\0' + sha1_r: str = StubMergerStore.LookupSha1ByModName(mod_buf) + stdlib.free(mod_buf) + if sha1_r is None: + continue + if _vpi_typedef_visited_check(sha1_r) != 0: + continue + # 构造 {temp_dir}/{sha1[0:2]}/{sha1}.vpi(手写拼接,与 + # _ensure_vpi_ret_annot_loaded 一致,避免引入 StubMergerCollect 环依赖) + path_len_r: t.CSizeT = td_len + 16 + 12 + vpi_path_r: bytes = stdlib.malloc(path_len_r) + if vpi_path_r is None: + continue + string.strcpy(vpi_path_r, temp_dir) + vpi_path_r[td_len] = '/' + vpi_path_r[td_len + 1] = sha1_r[0] + vpi_path_r[td_len + 2] = sha1_r[1] + vpi_path_r[td_len + 3] = '/' + string.strcpy(vpi_path_r + td_len + 4, sha1_r) + string.strcpy(vpi_path_r + td_len + 4 + 16, ".vpi") + f_r: fileio.File | t.CPtr = fileio.File(vpi_path_r, fileio.MODE.R) + if f_r.closed: + stdlib.free(vpi_path_r) + continue + vpi_buf_r: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) + if vpi_buf_r is None: + f_r.close() + stdlib.free(vpi_path_r) + continue + bytes_read_r: LONG = f_r.read_all(vpi_buf_r, VPI_READ_BUF_SIZE) + f_r.close() + if bytes_read_r > 0: + # 成功读到 vpi 才标记已处理(文件缺失/读取失败不标记, + # 后续 pass 的 stub 注入可重试) + _vpi_typedef_visited_mark(sha1_r) + if bytes_read_r < VPI_READ_BUF_SIZE: + vpi_buf_r[bytes_read_r] = 0 + else: + vpi_buf_r[VPI_READ_BUF_SIZE - 1] = 0 + lx_r: ast.Lexer | t.CPtr = ast.new_lexer(pool) + if lx_r is not None: + ast._lexer_init(lx_r, vpi_buf_r, pool) + tokens_r: ast.Token | t.CPtr = ast.tokenize(lx_r) + tree_r: ast.AST | t.CPtr = ast.parse_tokens(pool, tokens_r) + if tree_r is not None and tree_r.children is not None: + ch_r: list[ast.AST | t.CPtr] | t.CPtr = tree_r.children + cn_r: t.CSizeT = len(ch_r) + # 第一遍:收集子模块 imports(递归闭包) + sub_imports: bytes = stdlib.malloc(4096) + if sub_imports is not None: + sub_imports[0] = '\0' + sub_len: t.CSizeT = 0 + ci_r: t.CSizeT + for ci_r in range(cn_r): + child_r: ast.AST | t.CPtr = ch_r.get(ci_r) + if child_r is None: + continue + kk_r: int = child_r.kind() + if kk_r == ast.ASTKind.Import: + imp_r: ast.Import | t.CPtr = (ast.Import | t.CPtr)(child_r) + if imp_r.names is not None: + in_n: t.CSizeT = len(imp_r.names) + ii_r: t.CSizeT + for ii_r in range(in_n): + al_r: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(imp_r.names.get(ii_r)) + if al_r is not None and al_r.name is not None: + il_r: t.CSizeT = string.strlen(al_r.name) + if sub_len + il_r + 2 < 4096: + if sub_len > 0: + sub_imports[sub_len] = ' ' + sub_len += 1 + string.strcpy(sub_imports + sub_len, al_r.name) + sub_len += il_r + sub_imports[sub_len] = '\0' + elif kk_r == ast.ASTKind.ImportFrom: + ifr_r: ast.ImportFrom | t.CPtr = (ast.ImportFrom | t.CPtr)(child_r) + if ifr_r.module is not None: + ml_r: t.CSizeT = string.strlen(ifr_r.module) + if sub_len + ml_r + 2 < 4096: + if sub_len > 0: + sub_imports[sub_len] = ' ' + sub_len += 1 + string.strcpy(sub_imports + sub_len, ifr_r.module) + sub_len += ml_r + sub_imports[sub_len] = '\0' + # 第二遍:注册本模块 AnnAssign 的 CTypedef/CDefine + ci2_r: t.CSizeT + for ci2_r in range(cn_r): + child2_r: ast.AST | t.CPtr = ch_r.get(ci2_r) + if child2_r is None: + continue + if child2_r.kind() != ast.ASTKind.AnnAssign: + continue + aa_r: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(child2_r) + if aa_r.annotation is None or aa_r.target is None or aa_r.value is None: + continue + if GenApi._is_ctypedef_annotation(aa_r.annotation) != 0: + _register_ctypedef_from_annassign(pool, aa_r) + elif GenApi.is_cdefine_annotation(aa_r.annotation) != 0: + if aa_r.target.kind() != ast.ASTKind.Name: + continue + cdef_t: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa_r.target) + if cdef_t.id is None: + continue + if aa_r.value.kind() == ast.ASTKind.Constant: + cn_v: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa_r.value) + if cn_v.const_kind == ast.CONST_FLOAT: + GenApi.register_cdefine_double(pool, cdef_t.id, cn_v.float_val) + elif cn_v.const_kind == ast.CONST_INT: + cdef_val: t.CInt64T = GenApi.extract_cdefine_int_value(aa_r.value) + GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val) + # 递归处理子模块 imports(防环由 visited 标记保证) + if sub_imports[0] != '\0': + _vpi_register_typedef_closure(pool, sub_imports, temp_dir, depth + 1) + stdlib.free(sub_imports) + stdlib.free(vpi_buf_r) + stdlib.free(vpi_path_r) + return 0 + + # ============================================================ # ParseVpiIntoModule - 解析 .vpi 文件,将函数声明注入到当前模块 # @@ -1399,6 +1612,13 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr, imported_modules = _stub_add_imported(pool, imported_modules, src_mod_buf) stdlib.free(src_mod_buf) + # 递归预注册 stub import 闭包的 CTypedef/CDefine:注解(如 codepage: + # UINT)的 typedef 定义在 stdint.vp,但 CTypedef 表每文件隔离——当前 + # 模块未直接 import stdint 时表里没有 UINT → 参数类型退化为 i8_ptr_ty + # → 调用侧整数实参误报 CastError(testcheck.SetConsoleOutputCP 根因)。 + if imported_modules is not None: + _vpi_register_typedef_closure(pool, imported_modules, GenApi.get_temp_dir(), 0) + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) @@ -1578,6 +1798,61 @@ def _inject_reexport_class_declare(pool: memhub.MemBuddy | t.CPtr, return 1 +# ============================================================ +# _vpi_tattr_name / _vpi_class_has_packed - vpi 类装饰器 packed 判定 +# +# vpi 存根保留 @c.Attribute(t.attr.packed) 装饰器(Converter 原样输出)。 +# 跨模块 stub 注入 struct 时必须同步 packed 标记(<{...}>),否则引用 +# 模块按非 packed 布局 GEP,与定义侧 packed 布局错位(B5)。 +# ============================================================ +def _vpi_tattr_name(node: ast.AST | t.CPtr, attr_name: str) -> int: + """从 t.attr. / t.attr.(args) 提取最外层属性名并比较""" + if node is None: + return 0 + if node.kind() == ast.ASTKind.Call: + cl2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl2.func is None: + return 0 + return _vpi_tattr_name(cl2.func, attr_name) + if node.kind() == ast.ASTKind.Attribute: + at2: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + if at2.attr is None: + return 0 + return 1 if string.strcmp(at2.attr, attr_name) == 0 else 0 + return 0 + + +def _vpi_class_has_packed(decorator_list: list[ast.AST | t.CPtr] | t.CPtr) -> int: + """检查 vpi ClassDef 装饰器列表是否含 t.attr.packed""" + if decorator_list is None: + return 0 + dn: t.CSizeT = len(decorator_list) + if dn == 0: + return 0 + for di in range(dn): + deco: ast.AST | t.CPtr = decorator_list.get(di) + if deco is None: + continue + if deco.kind() == ast.ASTKind.Call: + cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco) + if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: + continue + at0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: + continue + if cl.args is None: + continue + an: t.CSizeT = len(cl.args) + for ai in range(an): + ag: ast.AST | t.CPtr = cl.args.get(ai) + if ag is not None and _vpi_tattr_name(ag, "packed") != 0: + return 1 + else: + if _vpi_tattr_name(deco, "packed") != 0: + return 1 + return 0 + + # ============================================================ # _inject_class_struct_declare - 从 vpi ClassDef 注册类为 struct 类型 # @@ -1646,15 +1921,15 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy | t.CPtr, field_names_buf[field_count] = t.CSizeT(m_tgt_c.id) field_types_buf[field_count] = t.CSizeT(field_ty_c) field_count += 1 - # 3. vpi 存根不含类字段(只序列化 class 名 + def 签名,见 _cache_vpi_return_ - # annots_text 的 class/def 行解析)→ field_count 恒为 0。 - # 注册空类(FieldCount=0)会让源模块编译时 find_struct_by_module 命中空类 → - # "结构体已注册但字段数为0" InternalCompilerError(win32base.SECURITY_ATTRIBUTES - # 根因:fileio/subprocess 先编译时注入空类,win32base 后编译命中)。 - # 改为不注册:依赖模块已编译时命中真实类(上方 L1604-1608 已处理),未编译时 - # 由 _deps_pending 多遍收敛(依赖编译后真实类注册,字段访问/裸类名引用走 pending)。 - if field_count == 0: - return 0 + # 3. vpi 存根不含类字段(只序列化 class 名 + def 签名,见 _cache_vpi_return_ + # annots_text 的 class/def 行解析)→ field_count 恒为 0。 + # 注册空类(FieldCount=0)会让源模块编译时 find_struct_by_module 命中空类 → + # "结构体已注册但字段数为0" InternalCompilerError(win32base.SECURITY_ATTRIBUTES + # 根因:fileio/subprocess 先编译时注入空类,win32base 后编译命中)。 + # 改为不注册:依赖模块已编译时命中真实类(上方 L1604-1608 已处理),未编译时 + # 由 _deps_pending 多遍收敛(依赖编译后真实类注册,字段访问/裸类名引用走 pending)。 + if field_count == 0: + return 0 # 4. 构建 ParamNode 链表 first_node_c: llvmlite.ParamNode | t.CPtr = None prev_node_c: llvmlite.ParamNode | t.CPtr = None @@ -1691,10 +1966,20 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy | t.CPtr, struct_ty_c = st_copy_c # 6. 注册到当前模块(输出类型定义行)+ 全局 StructTable + 可见性 llvmlite.module_add_named_type(mod, pool, struct_ty_c) + # B5:vpi 保留 @c.Attribute(t.attr.packed) 装饰器,stub 注入的跨模块 + # struct 必须同样 packed(<{...}>),否则引用模块 GEP 按非 packed 布局 + # 算偏移,与定义侧 packed 布局错位(gdt/elf/fat32 二进制格式字段错读)。 + if _vpi_class_has_packed(class_cd.decorator_list) != 0: + llvmlite.module_set_named_type_packed(mod, pool, struct_ty_c, 1) entry_c: GenTypes.StructEntry | t.CPtr = GenApi.register_struct( pool, cls_name, struct_ty_c, src_sha1) if entry_c is None: return 0 + # B5:vpi 注入注册的 entry 同步 IsPacked——后续跨模块 stub 注入 + # (ensure_struct_def_in_module)按 entry.IsPacked 判定 packed,否则读 0 + # → 注入的跨模块 struct 非 packed(GEP 布局错位)。 + if _vpi_class_has_packed(class_cd.decorator_list) != 0: + entry_c.IsPacked = 1 # [FIX] vpi 注入类时同步注册字段到 StructEntry 字段表(名字+类型)。 # 仅设置 Ty(LLVMType 含字段)而字段表空(FieldCount=0)时,使用端 # lookup_field / lookup_field_by_class 按名字查字段失败 → 属性访问走 @@ -1733,6 +2018,12 @@ def _ensure_full_value_types(mod: llvmlite.LLVMModule | t.CPtr, full_se_h: GenTypes.StructEntry | t.CPtr = GenApi.find_struct_by_type(ty) if full_se_h is not None and full_se_h.Ty is not None: llvmlite.module_add_named_type(mod, pool, full_se_h.Ty) + # B5:值类型注入同步 packed 标记(对齐 ensure_struct_def_in_module)—— + # vpi 全局变量(@_per_cpu = global %per_cpu_data)/值返回类型注入完整 + # 定义时若只 add 不 set_packed → 引用模块节点 IsPacked=0 → 输出 {…} + # 非 packed(GEP 与定义侧 packed 布局错位,stub packed 失效根因)。 + if full_se_h.IsPacked != 0: + llvmlite.module_set_named_type_packed(mod, pool, full_se_h.Ty, 1) case llvmlite.LLVMType.Array(aelem_h, _): if aelem_h is not None: _ensure_full_value_types(mod, pool, aelem_h) diff --git a/App/lib/Stub/StubMerger/StubMergerBuild.vp b/App/lib/Stub/StubMerger/StubMergerBuild.vp index 2e3c2ff..095299e 100644 --- a/App/lib/Stub/StubMerger/StubMergerBuild.vp +++ b/App/lib/Stub/StubMerger/StubMergerBuild.vp @@ -702,87 +702,139 @@ def _LoadAndAppendTextDeclares(temp_dir: str, td_len: t.CSizeT, dep_sha1: str, # 跳过其他行 return out_pos # ============================================================ -# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需翻译) +# _IsAppModule - 模块名是否为 App 源文件(store 条目 incdir 为空) # -# 从 source_dir 的源文件开始,解析 import 语句,递归收集可达的 -# includes 文件 SHA1。Phase 1b 只翻译可达集合中的文件,避免 -# 翻译不需要的 includes(如 Test 不依赖 llvmlite,则不翻译)。 +# 依赖图 BFS 中识别 App 中间节点:App 文件不入 reachable_set(Phase B +# 已编译其 .obj),但其 includes 依赖必须继续展开。App 条目由 App 扫描 +# 阶段用 AppendToSha1MapStore 登记(incdir 留空);includes 条目用 +# AddIncludeToSha1MapStore 登记(incdir 非空)。 +# ============================================================ +def _IsAppModule(mod_name: str) -> int: + """mod_name 是否为 App 源文件(store 命中且 incdir 为空)""" + if mod_name is None: + return 0 + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: + return 0 + for i in range(StubMerger._sha1_store_count): + midx: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(StubMerger._sha1_store_mod + midx, mod_name) == 0: + iidx: t.CSizeT = t.CSizeT(i) * StubMerger.MAX_INC_DIR_LEN + if StubMerger._sha1_store_incdir[iidx] == '\0': + return 1 + return 0 + return 0 +# ============================================================ +# _AppendDepsToWorklist - 追加空格分隔模块名到 worklist(逐模块去重) # -# 算法(工作列表): -# 1. 扫描 source_dir 下的 .vp/.py 文件,解析 AST 获取 _imported_modules -# 2. 构建 SHA1→module_name 映射(从全局内存存储器) -# 3. 工作列表递归:模块名 → 查 SHA1 → 加入 reachable_set → 读 .deps.txt → 追加新模块名 +# worklist 是 BFS 队列(空格分隔)。deps_str 中的模块若已在 worklist/ +# processed 中不重复追加,否则 BFS 沿共享依赖(builtins/stdlib 等) +# 反复累积到 REACH_WORKLIST_BUF 溢出("依赖 worklist 溢出"根因)。 +# +# Args: +# worklist: 队列缓冲(原地修改) +# wl_len: 当前长度(值) +# deps_str: 要追加的模块名(空格分隔) +# processed: 已处理集合(空格分隔,查重) +# +# Returns: +# 追加后的新长度(>=0);溢出/参数错误返回 -1(调用方 fail-fast) +# ============================================================ +def _AppendDepsToWorklist(worklist: bytes, wl_len: t.CSizeT, + deps_str: str, processed: str) -> int: + if worklist is None or deps_str is None: + return -1 + cur_len: t.CSizeT = wl_len + dl: t.CSizeT = string.strlen(deps_str) + dpos: t.CSizeT = 0 + while dpos < dl: + dstart: t.CSizeT = dpos + while dpos < dl and deps_str[dpos] != ' ': + dpos += 1 + dlen: t.CSizeT = dpos - dstart + if dpos < dl: + dpos += 1 + if dlen == 0: + continue + dmod: bytes = stdlib.malloc(dlen + 1) + if dmod is None: + continue + string.strncpy(dmod, deps_str + dstart, dlen) + dmod[dlen] = '\0' + in_wl: int = 0 + if cur_len > 0: + if GenApi.is_module_imported(worklist, dmod) != 0: + in_wl = 1 + if in_wl == 0 and processed is not None: + if GenApi.is_module_imported(processed, dmod) != 0: + in_wl = 1 + if in_wl == 0: + if cur_len + dlen + 2 < StubMerger.REACH_WORKLIST_BUF: + if cur_len > 0 and worklist[cur_len - 1] != ' ': + worklist[cur_len] = ' ' + cur_len = cur_len + 1 + string.strcpy(worklist + cur_len, dmod) + cur_len = cur_len + dlen + worklist[cur_len] = ' ' + cur_len = cur_len + 1 + worklist[cur_len] = '\0' + else: + stdlib.free(dmod) + VLogger.error("依赖 worklist 溢出", "StubMerger") + return -1 + stdlib.free(dmod) + return t.CInt(cur_len) +# ============================================================ +# _BuildReachableSha1Set - 构建可达 SHA1 集合(依赖图按需扫描) +# +# App 优先模型:先扫 App 源文件的 import 依赖,从 app_deps 出发 BFS, +# 对每个模块名按文件系统解析到 includes 文件(ResolveModuleToPath), +# 只对可达 includes 读文件+算 SHA1+解析 import,补充依赖图并登记 +# Sha1Store/deps 存储器/写 deps.txt。未引用的 includes 从不读取。 +# Phase 1b/Phase B+ 只处理可达集合中的文件。 +# +# 算法(工作列表 BFS): +# 1. App 扫描(app_deps 为空时):递归扫 source_dir,declare_only=2 解析 +# import(含相对导入 resolve 成绝对模块名)+ 容器注入(_list/_dict) +# → worklist 初始队列;App 文件登记 store(incdir 空)+ 填 deps +# 2. builtins 预加入队列(内置函数模块始终编译) +# 3. BFS:模块名 → ResolveModuleToPath 按文件系统解析 → 读+hash+parse +# import → 登记 reachable_set/store/deps → 追加依赖到队列,直到闭包 # # Args: # mb: 内存池 -# source_dir: 源文件目录(Config.SourceDir) -# temp_dir: 临时目录(.deps.txt 所在位置;_sha1_map.txt 仅人类可读输出,不读取) +# source_dir: App 源文件目录(Config.SourceDir) +# temp_dir: 临时目录(deps.txt 所在位置) # reachable_set: 输出参数,可达 SHA1 集合缓冲区(MAX_INCLUDES_SHA1 * 17 字节) +# app_deps: App 源文件的直接依赖模块名(空格分隔),非空则跳过 App 扫描 +# includes_dirs: includes 根目录(空格分隔),依赖图按需解析必须 # # Returns: -# 可达 SHA1 数量(>0 成功,<=0 失败) +# 可达 SHA1 数量(>0 成功;0 无 includes 依赖;<0 失败) # ============================================================ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, temp_dir: str, reachable_set: str, - app_deps: str = None) -> int: - """构建可达 SHA1 集合(依赖图按需翻译) - - app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤 - """ + app_deps: str = None, + includes_dirs: str = None) -> int: + """构建可达 SHA1 集合(依赖图按需扫描,App 优先)""" if source_dir is None or temp_dir is None or reachable_set is None: return -1 + if includes_dirs is None: + VLogger.error("includes_dirs 缺失,无法按需解析依赖图", "Reachable") + return -1 SRC_BUF_SIZE_R: t.CSizeT = 1048576 - # 1. 构建 SHA1→module_name 映射 - td_len_r: t.CSizeT = string.strlen(temp_dir) - sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) - mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) - if sha1_arr is None or mod_arr is None: - if sha1_arr is not None: - stdlib.free(sha1_arr) - if mod_arr is not None: - stdlib.free(mod_arr) - return -1 - map_count: int = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_r, sha1_arr, mod_arr) - if map_count <= 0: - VLogger.error("无法构建 SHA1 映射", "Reachable") - stdlib.free(sha1_arr) - stdlib.free(mod_arr) - return -1 - VLogger.debug(f"SHA1 映射: {map_count} 个", "Reachable") - # 工作列表(空格分隔的模块名) worklist: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF) if worklist is None: - stdlib.free(sha1_arr) - stdlib.free(mod_arr) return -1 worklist[0] = '\0' wl_len: t.CSizeT = 0 - reachable_count: int = 0 - - # 2. 如果 app_deps 非空,直接使用它作为初始 worklist(跳过自己的非递归扫描) - # 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层) - if app_deps is not None and app_deps[0] != '\0': - ad_len: t.CSizeT = string.strlen(app_deps) - if ad_len > 0 and ad_len < StubMerger.REACH_WORKLIST_BUF: - string.strcpy(worklist, app_deps) - wl_len = ad_len - else: - VLogger.error("app_deps 过长,无法构建可达集合", "StubMerger") - else: - # app_deps 缺失:Phase1 场景(未翻译 App 源文件)为设计性回退, - # 显式 warning 暴露回退,不再静默。 - # 原实现仅非递归扫描 source_dir 顶层("%s/*"),漏掉 lib/ 等子目录的 - # App 文件 → 其依赖(如 lib/Projectrans/Config.vp 的 json)不进入 - # worklist → json 不可达 → Phase1 不 stub 不翻译 → Phase2 Phase A - # 翻译 Config.vp 时 root.is_object() 跨模块调用无 stub 可查 → 静默 - # translate_value 返回 None → "if 条件表达式翻译失败"(追加82 根因)。 - # 改为递归扫描(复用 IncludesScanner.scan_includes),覆盖全部子目录。 - VLogger.warning("app_deps 缺失,递归扫描 source_dir", "Reachable") + # 1. App 扫描(app_deps 非空时跳过,直接用调用方提供的初始队列) + if app_deps is None or app_deps[0] == '\0': + VLogger.info("App 扫描:解析 source_dir 的 import 依赖", "Reachable") scan_res_r: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, source_dir) if scan_res_r is not None: es_r: t.CSizeT = IncludesScanner.FileEntry.__sizeof__() @@ -791,119 +843,137 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, t.CVoid(t.CUInt64T(scan_res_r.Entries) + t.CUInt64T(e_i_r) * es_r, t.CPtr)) if e_r is None or e_r.Path is None: continue + # 文件级 mark:parse 完立即回收 AST(App 全量 parse 驻留会 OOM) + app_mark: t.CSizeT = mb.mark() sbuf: bytes = Utils.read_file_to_buf(e_r.Path, SRC_BUF_SIZE_R) if sbuf is None: + mb.reset_to(app_mark) continue - # 解析 AST 获取 _imported_modules(与原顶层扫描一致) lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) + tree: ast.AST | t.CPtr = None if lx is not None: ast._lexer_init(lx, sbuf, mb) tokens: ast.Token | t.CPtr = ast.tokenize(lx) - tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) - if tree is not None: - tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() - if tr is not None: - tr._declare_only = 2 - # 源文件包名:用 RelPath(相对 source_dir,支持子目录), - # 保证 from .xxx import 相对导入解析正确 - tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, e_r.RelPath) - GenApi.reset_visible_structs(mb, 0) - tr.translate(tree) - # 容器使用检测:App 源文件用 list/dict(裸名注解/构造/[] - # /{} 字面量)时注入 _list/_dict 依赖(与原实现一致) + tree = ast.parse_tokens(mb, tokens) + if tree is not None: + tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() + if tr is not None: + tr._declare_only = 2 + # 源文件包名:用 RelPath(相对 source_dir,支持子目录), + # 保证 from .xxx import 相对导入解析正确 + tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, e_r.RelPath) + GenApi.set_current_file_type(e_r.Path) + GenApi.set_current_file_imports(e_r.Path) + GenApi.set_current_module_sha1(e_r.Sha1) + GenApi.clear_cdefine_constants() + GenApi.clear_ctypedefs() + GenApi.reset_visible_structs(mb, 0) + ret_a: int = tr.translate(tree) + if ret_a == 0: + # 容器使用检测:App 文件用 list/dict(裸名注解/构造) + # 时注入 _list/_dict 依赖(对齐原实现) tr._inject_auto_container_visibility(mb, tree) - # 获取 _imported_modules + # App 文件登记 store(incdir 空)+ 填 deps: + # BFS 展开 App 中间节点的 includes 依赖时经 + # LookupIncludesDeps(app_sha1) 读取 + if e_r.Sha1 is not None: + StubMergerStore.AppendToSha1MapStore(e_r.Sha1, e_r.RelPath) + StubMergerStore.PopulateIncludesDeps(e_r.Sha1, tr._imported_modules) + # 收集 deps 到初始队列(逐模块去重) if tr._imported_modules is not None: - # 逐模块去重追加(同 app_deps 拼接,防 worklist 膨胀溢出) - im_len: t.CSizeT = string.strlen(tr._imported_modules) - im_pos: t.CSizeT = 0 - while im_pos < im_len: - im_start: t.CSizeT = im_pos - while im_pos < im_len and tr._imported_modules[im_pos] != ' ': - im_pos += 1 - im_mod_len: t.CSizeT = im_pos - im_start - if im_pos < im_len: - im_pos += 1 + im_len_a: t.CSizeT = string.strlen(tr._imported_modules) + im_pos_a: t.CSizeT = 0 + while im_pos_a < im_len_a: + im_start_a: t.CSizeT = im_pos_a + while im_pos_a < im_len_a and tr._imported_modules[im_pos_a] != ' ': + im_pos_a += 1 + im_mod_len: t.CSizeT = im_pos_a - im_start_a + if im_pos_a < im_len_a: + im_pos_a += 1 if im_mod_len == 0: continue - im_buf: bytes = stdlib.malloc(im_mod_len + 1) - if im_buf is None: + im_buf_a: bytes = stdlib.malloc(im_mod_len + 1) + if im_buf_a is None: continue - string.strncpy(im_buf, tr._imported_modules + im_start, im_mod_len) - im_buf[im_mod_len] = '\0' - if wl_len > 0 and GenApi.is_module_imported(worklist, im_buf) != 0: - stdlib.free(im_buf) - continue - if wl_len + im_mod_len + 2 < StubMerger.REACH_WORKLIST_BUF: - if wl_len > 0: + string.strncpy(im_buf_a, tr._imported_modules + im_start_a, im_mod_len) + im_buf_a[im_mod_len] = '\0' + in_wl_a: int = 0 + if wl_len > 0 and GenApi.is_module_imported(worklist, im_buf_a) != 0: + in_wl_a = 1 + if in_wl_a == 0: + if wl_len + im_mod_len + 2 < StubMerger.REACH_WORKLIST_BUF: + if wl_len > 0: + worklist[wl_len] = ' ' + wl_len += 1 + string.strcpy(worklist + wl_len, im_buf_a) + wl_len += im_mod_len worklist[wl_len] = ' ' wl_len += 1 - string.strcpy(worklist + wl_len, im_buf) - wl_len += im_mod_len - worklist[wl_len] = ' ' - wl_len += 1 - worklist[wl_len] = '\0' - stdlib.free(im_buf) - # 计算 App 源文件 SHA1,填充依赖到内存存储器 - # worklist 遍历时,LookupIncludesDeps 能找到 App 源文件的依赖 - if e_r.Sha1 is not None: - StubMergerStore.PopulateIncludesDeps(e_r.Sha1, tr._imported_modules) - # 释放 Translator 资源 - if tr._global_names is not None: - stdlib.free(tr._global_names) - if tr._nonlocal_names is not None: - stdlib.free(tr._nonlocal_names) + worklist[wl_len] = '\0' + stdlib.free(im_buf_a) + # 释放 Translator 资源 + if tr._global_names is not None: + stdlib.free(tr._global_names) + if tr._nonlocal_names is not None: + stdlib.free(tr._nonlocal_names) stdlib.free(sbuf) + mb.reset_to(app_mark) + else: + # app_deps 已提供(Phase2 场景):直接作为初始队列 + ad_len: t.CSizeT = string.strlen(app_deps) + if ad_len > 0 and ad_len < StubMerger.REACH_WORKLIST_BUF: + string.strcpy(worklist, app_deps) + wl_len = ad_len + else: + VLogger.error("app_deps 过长,无法构建可达集合", "StubMerger") + stdlib.free(worklist) + return -1 - # 去掉末尾多余空格 - if wl_len > 0 and worklist[wl_len - 1] == ' ': - worklist[wl_len - 1] = '\0' - wl_len -= 1 + # 去掉末尾多余空格 + if wl_len > 0 and worklist[wl_len - 1] == ' ': + worklist[wl_len - 1] = '\0' + wl_len -= 1 VLogger.debug(f"源文件直接依赖: '{worklist}'", "Reachable") - # 2.5. 预加入基础模块(builtins) - # - # builtins(内置函数模块:ord/chr 等)始终纳入编译链,调用时自动关联。 - # _list/_dict 不再无条件预加入:Phase A 翻译 App 源文件时 - # _inject_auto_container_visibility 已按需检测 list/dict 使用并注入 - # _imported_modules(含 []/{} 字面量),json 由 _dict 的真实 import 链带入。 - if GenApi.is_module_imported(worklist, "builtins") == 0: - bsha1_b: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, "builtins") - if bsha1_b is not None: - if wl_len + 9 < StubMerger.REACH_WORKLIST_BUF: - if wl_len > 0: - worklist[wl_len] = ' ' - wl_len += 1 - string.strcpy(worklist + wl_len, "builtins") - wl_len += 8 - worklist[wl_len] = ' ' - wl_len += 1 - worklist[wl_len] = '\0' + # 2. 预加入基础模块(builtins) + # builtins(内置函数模块)始终纳入编译链。_list/_dict 仅在显式 + # list/dict 注解/构造时经 _inject_auto_container_visibility 注入 + # (list 字面量赋给 t.CArray 走数组初始化,不触发 _list)。 + # processed 尚未分配,此处传 None 仅按 worklist 查重。 + wl_len = _AppendDepsToWorklist(worklist, wl_len, "builtins", None) + if wl_len < 0: + stdlib.free(worklist) + return -1 VLogger.debug(f"预加入容器模块后: '{worklist}'", "Reachable") - # 3. 工作列表算法:递归收集可达 SHA1 + # 3. 工作列表 BFS:模块名 → 文件系统解析 → 读+hash+parse import processed: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF) if processed is None: - stdlib.free(sha1_arr) - stdlib.free(mod_arr) stdlib.free(worklist) return -1 processed[0] = '\0' proc_len: t.CSizeT = 0 + reachable_count: int = 0 + wl_pos: t.CPtr = stdlib.malloc(8) + if wl_pos is None: + stdlib.free(worklist) + stdlib.free(processed) + return -1 + wl_pos_slot: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(wl_pos, t.CPtr)) + wl_pos_slot[0] = 0 - wl_pos: t.CSizeT = 0 - wl_iter: int = 0 - while wl_pos < wl_len: - wl_iter += 1 + while wl_pos_slot[0] < wl_len: + pos: t.CSizeT = wl_pos_slot[0] # 提取一个模块名(到空格或末尾) - name_start: t.CSizeT = wl_pos - while wl_pos < wl_len and worklist[wl_pos] != ' ': - wl_pos += 1 - name_len: t.CSizeT = wl_pos - name_start - if wl_pos < wl_len: - wl_pos += 1 + name_start: t.CSizeT = pos + while pos < wl_len and worklist[pos] != ' ': + pos += 1 + name_len: t.CSizeT = pos - name_start + if pos < wl_len: + pos += 1 + wl_pos_slot[0] = pos if name_len == 0: continue @@ -914,120 +984,257 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, string.strncpy(mod_buf, worklist + name_start, name_len) mod_buf[name_len] = '\0' - # 检查是否已处理 - already: int = 0 + # 已处理去重 if GenApi.is_module_imported(processed, mod_buf) != 0: - already = 1 - if already == 0: - # 加入 processed(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug) - if processed is not None: - if proc_len + name_len + 1 < StubMerger.REACH_WORKLIST_BUF: - string.strcpy(processed + proc_len, mod_buf) - proc_len = proc_len + name_len - processed[proc_len] = ' ' - proc_len = proc_len + 1 - processed[proc_len] = '\0' - else: - VLogger.error("依赖 worklist 溢出", "StubMerger") + stdlib.free(mod_buf) + continue + # 加入 processed + if proc_len + name_len + 1 < StubMerger.REACH_WORKLIST_BUF: + string.strcpy(processed + proc_len, mod_buf) + proc_len = proc_len + name_len + processed[proc_len] = ' ' + proc_len = proc_len + 1 + processed[proc_len] = '\0' + else: + VLogger.error("依赖 processed 溢出", "StubMerger") + stdlib.free(mod_buf) + continue - # 查找 SHA1 - found_sha1: str = StubMergerIR._FindSha1ByModName(sha1_arr, mod_arr, map_count, mod_buf) - if found_sha1 is not None: - # App 源文件(Sha1Store 中 IncDir 为空,AppendToSha1MapStore 规则) - # 不加入 reachable_set:Phase B 已编译其 .obj,若加入会导致 Phase B+ - # 把 App 文件重复编译进 includes.binary,Phase C 链接时与 Phase B 的 - # App obj 重复符号(SysLibTest ld.lld duplicate symbol: StubMerger._mbuddy 根因)。 - # 依赖仍继续展开(App 文件是依赖图中间节点,其 includes 依赖必须可达)。 - is_app_r: int = 0 - if StubMerger._sha1_store_incdir is not None: - for ai_r in range(StubMerger._sha1_store_count): - asi_r: t.CSizeT = t.CSizeT(ai_r) * 17 - if string.strcmp(StubMerger._sha1_store_arr + asi_r, found_sha1) == 0: - aii_r: t.CSizeT = t.CSizeT(ai_r) * StubMerger.MAX_INC_DIR_LEN - if StubMerger._sha1_store_incdir[aii_r] == '\0': - is_app_r = 1 - break - if is_app_r == 0: - reachable_count = StubMergerIR._AddSha1ToSet(reachable_set, reachable_count, found_sha1) - - # 从内存查找依赖(不读 deps.txt 过程文件) - # Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存 - deps_str: str = StubMergerStore.LookupIncludesDeps(found_sha1) - if deps_str is not None: - dl: t.CSizeT = string.strlen(deps_str) - if dl > 0: - # 逐模块追加 + 去重:worklist 初始已含全部 App 直接依赖, - # deps_str 中的模块若已在 worklist/processed 不重复追加, - # 否则 BFS 沿共享依赖(builtins/stdlib 等)反复累积到 - # 64KB 溢出("依赖 worklist 溢出"根因)。 - dpos: t.CSizeT = 0 - while dpos < dl: - dstart: t.CSizeT = dpos - while dpos < dl and deps_str[dpos] != ' ': - dpos += 1 - dlen: t.CSizeT = dpos - dstart - if dpos < dl: - dpos += 1 - if dlen == 0: - continue - dmod: bytes = stdlib.malloc(dlen + 1) - if dmod is None: - continue - string.strncpy(dmod, deps_str + dstart, dlen) - dmod[dlen] = '\0' - in_wl: int = 0 - if GenApi.is_module_imported(worklist, dmod) != 0: - in_wl = 1 - if in_wl == 0 and processed is not None: - if GenApi.is_module_imported(processed, dmod) != 0: - in_wl = 1 - if in_wl == 0: - if wl_len + dlen + 2 < StubMerger.REACH_WORKLIST_BUF: - if wl_len > 0 and worklist[wl_len - 1] != ' ': - worklist[wl_len] = ' ' - wl_len = wl_len + 1 - string.strcpy(worklist + wl_len, dmod) - wl_len = wl_len + dlen - worklist[wl_len] = ' ' - wl_len = wl_len + 1 - worklist[wl_len] = '\0' - else: - VLogger.error("依赖 worklist 溢出", "StubMerger") - stdlib.free(dmod) - # deps_str 为空字符串视为无依赖(合法情况,如叶子模块) - else: - # 依赖未在内存中找到 - # 按需过滤: 检查是否是 App 源文件(source_dir 下的 .vp/.py 文件) - # App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过 - sd_len_ff: t.CSizeT = string.strlen(source_dir) - mb_len_ff: t.CSizeT = string.strlen(mod_buf) - app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6) - is_app_src: int = 0 - if app_mod_path is not None: - # 仅识别 .vp 源文件(不再做 vp -> py 后缀识别回退) - viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.vp", source_dir, mod_buf) - af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R) - if not af.closed: - is_app_src = 1 - af.close() - stdlib.free(app_mod_path) - if is_app_src == 0: - # fast-fail: includes 文件依赖未在内存中找到,Phase 1a-pre 未正确填充 - VLogger.error(f"依赖未找到 fast-fail: 模块 {mod_buf}(sha1={found_sha1}) 的依赖未在内存中注册", "Reachable") - stdlib.free(sha1_arr) - stdlib.free(mod_arr) + # App 中间节点:只展开其 includes 依赖,不入 reachable_set + # (Phase B 已编译 App 的 .obj,加入会导致 Phase B+ 重复编译 → 重复符号) + if _IsAppModule(mod_buf) != 0: + app_sha1: str = StubMergerStore.LookupSha1ByModName(mod_buf) + if app_sha1 is not None: + app_deps_str: str = StubMergerStore.LookupIncludesDeps(app_sha1) + if app_deps_str is not None: + ret_ap: int = _AppendDepsToWorklist(worklist, wl_len, app_deps_str, processed) + if ret_ap < 0: + stdlib.free(mod_buf) stdlib.free(worklist) stdlib.free(processed) - stdlib.free(mod_buf) + stdlib.free(wl_pos) return -1 + wl_len = t.CSizeT(ret_ap) + stdlib.free(mod_buf) + continue + + # includes:按文件系统按需解析(未引用的 includes 根本不触碰) + out_abs: t.CPtr = stdlib.malloc(8) + out_rel: t.CPtr = stdlib.malloc(8) + out_inc: t.CPtr = stdlib.malloc(8) + if out_abs is None or out_rel is None or out_inc is None: + if out_abs is not None: + stdlib.free(out_abs) + if out_rel is not None: + stdlib.free(out_rel) + if out_inc is not None: + stdlib.free(out_inc) + stdlib.free(mod_buf) + continue + # 输出槽防御性初始化(成功路径必全写;未找到/未命中时仅 free 槽本身) + # 槽联合必须用 (t.CSizeT | t.CPtr):t.CPtr|t.CPtr 会被折叠成 t.CPtr, + # [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为 + # 8 字节,经 t.CVoid 显式做 u64↔指针转换。 + abs_slot: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(out_abs, t.CPtr)) + rel_slot: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(out_rel, t.CPtr)) + inc_slot: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(out_inc, t.CPtr)) + abs_slot[0] = 0 + rel_slot[0] = 0 + inc_slot[0] = 0 + rcode: int = IncludesScanner.ResolveModuleToPath( + includes_dirs, mod_buf, out_abs, out_rel, out_inc) + if rcode != 0: + # 内建模块(t/c/builtins 等内部库)或未找到:静默跳过 + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(mod_buf) + continue + abs_path: str = (str)(t.CVoid(abs_slot[0], t.CPtr)) + rel_path: str = (str)(t.CVoid(rel_slot[0], t.CPtr)) + inc_dir: str = (str)(t.CVoid(inc_slot[0], t.CPtr)) + + # 读文件 + 计算 SHA1(CRLF→LF,与 IncludesScanner 一致) + sha1_r2: str = IncludesScanner.compute_file_sha1(mb, abs_path) + if sha1_r2 is None: + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(mod_buf) + continue + + # 已在可达集合(前序 BFS 已 parse 过):只展开其 deps + if StubMergerCollect._is_in_sha1_set(sha1_r2, reachable_set, reachable_count) != 0: + deps_ex: str = StubMergerStore.LookupIncludesDeps(sha1_r2) + if deps_ex is not None: + ret_ex: int = _AppendDepsToWorklist(worklist, wl_len, deps_ex, processed) + if ret_ex < 0: + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(mod_buf) + stdlib.free(worklist) + stdlib.free(processed) + stdlib.free(wl_pos) + return -1 + wl_len = t.CSizeT(ret_ex) + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(mod_buf) + continue + + # 加入可达集合 + 登记 Sha1Store(仅可达 includes) + reachable_count = StubMergerIR._AddSha1ToSet(reachable_set, reachable_count, sha1_r2) + StubMergerStore.AddIncludeToSha1MapStore(sha1_r2, rel_path, inc_dir, mod_buf) + + # 文件级 mark:parse import 后立即回收 AST(可达 includes 全量 + # parse 的 AST 若驻留 → 主池 OOM) + scan_mark_r: t.CSizeT = mb.mark() + sbuf_r: bytes = Utils.read_file_to_buf(abs_path, SRC_BUF_SIZE_R) + if sbuf_r is None: + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(mod_buf) + mb.reset_to(scan_mark_r) + continue + lx_r: ast.Lexer | t.CPtr = ast.new_lexer(mb) + tree_r: ast.AST | t.CPtr = None + if lx_r is not None: + ast._lexer_init(lx_r, sbuf_r, mb) + tokens_r: ast.Token | t.CPtr = ast.tokenize(lx_r) + tree_r = ast.parse_tokens(mb, tokens_r) + if tree_r is None: + VLogger.error(f"可达 includes 解析失败: {abs_path}", "Reachable") + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(sbuf_r) + stdlib.free(mod_buf) + stdlib.free(worklist) + stdlib.free(processed) + stdlib.free(wl_pos) + mb.reset_to(scan_mark_r) + return -1 + tr_r2: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() + if tr_r2 is None: + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(sbuf_r) + stdlib.free(mod_buf) + mb.reset_to(scan_mark_r) + continue + tr_r2.ModuleSha1 = sha1_r2 + tr_r2._declare_only = 2 + # 包名:相对 includes 根目录的 RelPath(支持 from .xxx import 相对导入) + tr_r2.CurrentPackage = GenApi.compute_package_from_relpath(mb, rel_path) + GenApi.set_current_file_type(abs_path) + GenApi.set_current_file_imports(abs_path) + GenApi.set_current_module_sha1(sha1_r2) + GenApi.clear_cdefine_constants() + GenApi.clear_ctypedefs() + GenApi.reset_visible_structs(mb, 0) + ret_r2: int = tr_r2.translate(tree_r) + if ret_r2 != 0: + VLogger.error(f"import 依赖扫描失败(可达 includes): {abs_path}", "Reachable") + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(sbuf_r) + if tr_r2._global_names is not None: + stdlib.free(tr_r2._global_names) + if tr_r2._nonlocal_names is not None: + stdlib.free(tr_r2._nonlocal_names) + stdlib.free(mod_buf) + stdlib.free(worklist) + stdlib.free(processed) + stdlib.free(wl_pos) + mb.reset_to(scan_mark_r) + return -1 + # 容器按需注入(includes 用 list/dict 泛型时追加 _list/_dict 到依赖, + # 对齐 App 层 _inject_auto_container_visibility;内联破环防 _list/_dict 环) + tr_r2._inject_auto_container_visibility(mb, tree_r) + + # 填内存依赖 + 写 deps.txt(供依赖图展开与 Phase B 按需加载) + deps_r2: str = tr_r2._imported_modules + StubMergerStore.PopulateIncludesDeps(sha1_r2, deps_r2) + if deps_r2 is not None: + td_len_w: t.CSizeT = string.strlen(temp_dir) + deps_path_w: str = StubMergerCollect._sliced_path(temp_dir, td_len_w, sha1_r2, "deps.txt") + if deps_path_w is not None: + df_w: fileio.File | t.CPtr = fileio.File(deps_path_w, fileio.MODE.W) + if not df_w.closed: + dl_w: t.CSizeT = string.strlen(deps_r2) + if dl_w > 0: + df_w.write(deps_r2, dl_w) + df_w.close() + stdlib.free(deps_path_w) + # 追加依赖到 worklist(去重防溢出) + ret_w: int = _AppendDepsToWorklist(worklist, wl_len, deps_r2, processed) + if ret_w < 0: + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) + stdlib.free(sbuf_r) + stdlib.free(mod_buf) + stdlib.free(worklist) + stdlib.free(processed) + stdlib.free(wl_pos) + mb.reset_to(scan_mark_r) + return -1 + wl_len = t.CSizeT(ret_w) + else: + VLogger.debug(f"无依赖: {rel_path}", "Reachable") + + # 释放 Translator 的 C malloc 资源 + 回收本文件 AST + if tr_r2._global_names is not None: + stdlib.free(tr_r2._global_names) + if tr_r2._nonlocal_names is not None: + stdlib.free(tr_r2._nonlocal_names) + stdlib.free(sbuf_r) + mb.reset_to(scan_mark_r) + + stdlib.free(abs_path) + stdlib.free(rel_path) + stdlib.free(inc_dir) + stdlib.free(out_abs) + stdlib.free(out_rel) + stdlib.free(out_inc) stdlib.free(mod_buf) - stdlib.free(sha1_arr) - stdlib.free(mod_arr) + stdlib.free(worklist) stdlib.free(processed) + stdlib.free(wl_pos) VLogger.debug(f"可达 SHA1: {reachable_count} 个", "Reachable") return reachable_count + # ============================================================ # BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR # @@ -1512,6 +1719,12 @@ def LoadDepVpiIntoModule(pool: memhub.MemBuddy | t.CPtr, if imported_modules is None: return 0 + # 每个模块的 stub 注入前重置 typedef 递归注册的防环标记: + # CTypedef 表每文件隔离(translate 前 clear),visited 若跨模块残留会 + # 导致 stdint 等 typedef 只注册一次、后续模块注入再次解析时表里为空 + # → 参数类型退化 i8_ptr_ty → 整数实参误报 CastError(FuncTest 根因)。 + VpiReader.ResetVpiTypedefVisited() + td_len: t.CSizeT = string.strlen(temp_dir) total_len: t.CSizeT = string.strlen(imported_modules) success_count: int = 0 diff --git a/App/lib/Stub/StubMerger/StubMergerCollect.vp b/App/lib/Stub/StubMerger/StubMergerCollect.vp index 1f222c2..a2bbe36 100644 --- a/App/lib/Stub/StubMerger/StubMergerCollect.vp +++ b/App/lib/Stub/StubMerger/StubMergerCollect.vp @@ -119,7 +119,7 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int, if out_count >= max_count: return out_count - find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__()) + find_data: rowptr[win32file.WIN32_FIND_DATAA] = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__()) if find_data is None: return out_count string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__()) @@ -189,7 +189,7 @@ def _collect_stub_sha1s(base_dir: str, bd_len: t.CSizeT, level: int, # 数据来源是 PopulateSha1MapStore 填充的全局存储器(不读取 _sha1_map.txt)。 # 用于过滤 stub 文件,避免加载 TransPyV 自身源文件(lib/*)的 stub 导致链接失败。 # ============================================================ -def _load_includes_sha1_set(pool: memhub.MemBuddy | t.CPtr, +def _load_includes_sha1_set(pool: rowptr[memhub.MemBuddy], temp_dir: str, sha1_set: str) -> int: """从全局内存存储器将 includes 的 SHA1 写入 sha1_set @@ -235,8 +235,8 @@ def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int: # Returns: # 0 成功,非 0 失败 # ============================================================ -def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, - scan_result: IncludesScanner.ScanResult | t.CPtr, +def WriteIncludesSha1Map(mb: rowptr[memhub.MemBuddy], temp_dir: str, + scan_result: rowptr[IncludesScanner.ScanResult], filter_set: str, filter_count: int) -> int: """将 includes 扫描结果写入 _sha1_map.txt(人类可读输出,程序内部使用内存存储器) @@ -254,7 +254,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir) # 打开文件写入(CREATE_ALWAYS) - f: fileio.File | t.CPtr = fileio.File(map_path, fileio.MODE.W) + f: rowptr[fileio.File] = fileio.File(map_path, fileio.MODE.W) if f.closed: return 1 @@ -268,7 +268,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy | t.CPtr, temp_dir: str, written_count: int = 0 for i in range(scan_result.Count): ea_w: t.CUInt64T = t.CUInt64T(scan_result.Entries) + i * entry_size_w - ent_w: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(ea_w, t.CPtr)) + ent_w: rowptr[IncludesScanner.FileEntry] = (rowptr[IncludesScanner.FileEntry])(t.CVoid(ea_w, t.CPtr)) if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue # 若提供过滤集合,只写入集合中的条目 diff --git a/App/lib/Stub/StubMerger/StubMergerStore.vp b/App/lib/Stub/StubMerger/StubMergerStore.vp index bd8afe7..93efb27 100644 --- a/App/lib/Stub/StubMerger/StubMergerStore.vp +++ b/App/lib/Stub/StubMerger/StubMergerStore.vp @@ -207,6 +207,81 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int: StubMerger._sha1_store_count += 1 return 0 # ============================================================ +# AddIncludeToSha1MapStore - 单条登记 includes 条目到全局 SHA1 映射存储器 +# +# 依赖图按需扫描(_BuildReachableSha1Set)解析到可达 includes 文件后 +# 逐条登记,替代 PopulateSha1MapStore 的全量登记——未引用的 includes +# 不进 store。inc_dir 非空(所属 includes 根目录,多 includes 支持); +# App 源文件仍用 AppendToSha1MapStore(incdir 留空)。 +# +# Args: +# sha1: SHA1 字符串(16 字符 + null) +# rel_path: 相对 includes 根目录的路径(如 "ast/parser.vp") +# inc_dir: 所属 includes 根目录 +# mod_name: 模块名(如 "ast.parser") +# +# Returns: +# 0 成功(含去重跳过),1 失败 +# ============================================================ +def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: str) -> int: + """单条登记 includes 条目到全局 SHA1 映射存储器(依赖图按需扫描用)""" + if sha1 is None or rel_path is None or mod_name is None: + return 1 + # 存储器未初始化:自动初始化 + if StubMerger._sha1_store_arr is None: + StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) + StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) + StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN) + StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN) + if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: + return 1 + string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17) + string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64) + string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN) + string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN) + StubMerger._sha1_store_count = 0 + if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: + return 1 + # 去重:SHA1 与模块名都相同才跳过(对齐 AppendToSha1MapStore) + for i in range(StubMerger._sha1_store_count): + sidx_d: t.CSizeT = t.CSizeT(i) * 17 + if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0: + midx_d: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(StubMerger._sha1_store_mod + midx_d, mod_name) == 0: + return 0 + # 写 SHA1 + idx_a: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 17 + string.strcpy(StubMerger._sha1_store_arr + idx_a, sha1) + # 写模块名 + idx_m: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * 64 + mn_len: t.CSizeT = string.strlen(mod_name) + if mn_len < 64: + string.strcpy(StubMerger._sha1_store_mod + idx_m, mod_name) + else: + string.strncpy(StubMerger._sha1_store_mod + idx_m, mod_name, 63) + StubMerger._sha1_store_mod[idx_m + 63] = '\0' + # 写相对路径 + idx_r: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_REL_PATH_LEN + rp_len: t.CSizeT = string.strlen(rel_path) + if rp_len < StubMerger.MAX_REL_PATH_LEN: + string.strcpy(StubMerger._sha1_store_rel + idx_r, rel_path) + else: + string.strncpy(StubMerger._sha1_store_rel + idx_r, rel_path, StubMerger.MAX_REL_PATH_LEN - 1) + StubMerger._sha1_store_rel[idx_r + StubMerger.MAX_REL_PATH_LEN - 1] = '\0' + # 写所属 includes 根目录(非空 = includes 条目;App 条目留空) + idx_inc: t.CSizeT = t.CSizeT(StubMerger._sha1_store_count) * StubMerger.MAX_INC_DIR_LEN + if inc_dir is not None: + icd_len: t.CSizeT = string.strlen(inc_dir) + if icd_len < StubMerger.MAX_INC_DIR_LEN: + string.strcpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir) + else: + string.strncpy(StubMerger._sha1_store_incdir + idx_inc, inc_dir, StubMerger.MAX_INC_DIR_LEN - 1) + StubMerger._sha1_store_incdir[idx_inc + StubMerger.MAX_INC_DIR_LEN - 1] = '\0' + else: + StubMerger._sha1_store_incdir[idx_inc] = '\0' + StubMerger._sha1_store_count += 1 + return 0 +# ============================================================ # GetSha1StoreCount - 获取全局存储器条目数 # ============================================================ def GetSha1StoreCount() -> int: diff --git a/App/lib/Stub/StubMerger/__init__.vp b/App/lib/Stub/StubMerger/__init__.vp index 56e7485..539b27b 100644 --- a/App/lib/Stub/StubMerger/__init__.vp +++ b/App/lib/Stub/StubMerger/__init__.vp @@ -34,7 +34,7 @@ import lib.Projectrans.Config as Config # ============================================================ # 全局 mbuddy 指针 -_mbuddy: memhub.MemBuddy | t.CPtr +_mbuddy: rowptr[memhub.MemBuddy] # 最大 includes SHA1 数(用于过滤) MAX_INCLUDES_SHA1: t.CDefine = 1024 diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 6b98b2c..1be6c35 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -399,6 +399,62 @@ class AssignGenMixin: return 1 return 0 + def _stg_ty_equal(self, pool: memhub.MemBuddy | t.CPtr, + ta: llvmlite.LLVMType | t.CPtr, + tb: llvmlite.LLVMType | t.CPtr) -> int: + """类型内容相等比较(storage 绑定判定用)。 + + llvmlite 类型非 canonical:Ptr/Struct 每次构造都 pool.alloc 新实例 + (includes/llvmlite/__types.vp),同类型不同实例地址不同。 + 此前绑定分支用 t.CUInt64T(ta) == t.CUInt64T(tb)(地址比较)判定相等, + 对象构造 X = T() 的 rhs 类型实例(CallGenCtor 的 struct_ty)与 storage + 类型实例(handle_module_level_var 的 resolved_stg_ty)不同 → 绑定失败 + → 构造结果落栈 alloca 并 store 栈地址到全局(悬垂),storage 保持零 + (VKernel devfs devfs_init 双 storage + 栈临时 self 根因)。改为 + TypePrint 内容比较(命名结构体比名字、匿名结构体比字段布局)。 + """ + if ta is None or tb is None: + return 0 + # 快路径:同一实例直接相等 + if t.CUInt64T(ta) == t.CUInt64T(tb): + return 1 + buf_a: str = pool.alloc(256) + buf_b: str = pool.alloc(256) + if buf_a is None or buf_b is None: + return 0 + llvmlite.TypePrint(buf_a, 256, ta, pool) + llvmlite.TypePrint(buf_b, 256, tb, pool) + if buf_a[0] == 0 or buf_b[0] == 0: + return 0 + return 1 if string.strcmp(buf_a, buf_b) == 0 else 0 + + def _is_addrlike_call(self, node: ast.AST | t.CPtr) -> int: + """检查 rhs 是否为 c.Addr/c.Deref 等显式指针操作调用。 + + 此类调用返回指针(如 c.Addr(obj) 返回对象 storage 地址,类型 Ptr(Struct)), + 但语义是"取地址/解引用",不是对象值引用/构造——storage 绑定分支不应捕获: + X = c.Addr(Y) 应直接 store &Y 到 @X(X 与 Y 指向同一对象),若误绑定则 + @X 自指 X.stg 并值复制 Y 的当前值(devfs DevFSObject 脱钩根因)。 + """ + if node is None or node.kind() != ast.ASTKind.Call: + return 0 + cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: + return 0 + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at.value is None or at.value.kind() != ast.ASTKind.Name: + return 0 + vn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if vn.id is None or string.strcmp(vn.id, "c") != 0: + return 0 + if at.attr is None: + return 0 + if string.strcmp(at.attr, "Addr") == 0: return 1 + if string.strcmp(at.attr, "Deref") == 0: return 1 + if string.strcmp(at.attr, "Cast") == 0: return 1 + if string.strcmp(at.attr, "Ptr") == 0: return 1 + return 0 + def _is_64bit_scalar_ty(self, node: ast.AST | t.CPtr) -> int: """判断类型节点是否为 64 位标量数值类型(CUInt64T/CInt64T/CSizeT),元素 8 字节""" if node is None: @@ -437,6 +493,16 @@ class AssignGenMixin: """ if ty_name is None: return 0 + # [FIX 2026-08-31] 前缀归一化:剥离 "t."/"c." 内建命名空间前缀,使 + # "t.CInt" 与裸名 "CInt" 判定一致。泛型实参经 _norm_type_arg_name 规则 3 + # 保留 "t." 前缀(list[t.CInt] 的 T → "t.CInt"),不剥离则 _is_scalar_ty_name + # 匹配不到白名单裸名 "CInt" → 误判为非标量 → _is_ptr_element_annotation 返回 + # 1(8 字节指针槽)→ _list.vp elem_ptr[0]=item 的 GEP 元素类型退化为指针 → + # 整数存指针误报 CastError(ast/base.vp L391 list[t.CInt] 根因)。 + if ty_name[0] == 't' and ty_name[1] == '.': + ty_name += 2 + elif ty_name[0] == 'c' and ty_name[1] == '.': + ty_name += 2 if string.strchr(ty_name, '|') is not None: return 0 if string.strchr(ty_name, '*') is not None: @@ -445,14 +511,13 @@ class AssignGenMixin: return 0 if string.strcmp(ty_name, "bytes") == 0: return 0 - if string.strcmp(ty_name, "t.CPtr") == 0: - return 0 if string.strcmp(ty_name, "CPtr") == 0: return 0 if string.strstr(ty_name, "Ptr") is not None: return 0 # === 白名单:已知纯标量(基础名 + stdint 大写别名) === if string.strcmp(ty_name, "int") == 0: return 1 + if string.strcmp(ty_name, "char") == 0: return 1 if string.strcmp(ty_name, "CInt") == 0: return 1 if string.strcmp(ty_name, "CChar") == 0: return 1 if string.strcmp(ty_name, "CUnsignedChar") == 0: return 1 @@ -563,21 +628,34 @@ class AssignGenMixin: # 为每个元素生成 GEP + store for i in range(elts_count): elem_node: ast.AST | t.CPtr = elts.get(i) + idx_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, i) + elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_ty, elem_ty, array_alloca, idx_val) + if elem_ptr is None: + continue + # 内层嵌套数组:元素类型是数组且元素节点是列表字面量 → 递归数组 + # 初始化(逐元素 GEP + store)。list 字面量赋给 t.CArray 属数组 + # 语义,不应特化 list 泛型(gfx.vp faces: t.CArray[t.CArray[int,3],12] + # 根因)——否则 _list 未注册时 "list literal generic specialization + # failed";只有显式 list[T] 注解才引入 _list。 + inner_arr: int = 0 + if self.Gen.ExprGenBase.is_array_type(elem_ty) != 0: + if elem_node is not None and elem_node.kind() == ast.ASTKind.List: + inner_arr = 1 + if inner_arr != 0: + self._init_global_array_from_list(builder, pool, mod, elem_ptr, elem_node, trans) + continue elem_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value( builder, pool, mod, elem_node, None, 0, trans) if elem_val is None: # 元素翻译失败:直接报错终止(fail-fast),暴露上游根因 self.Gen.TypeGenErr.fatal_error(elem_node, "全局数组初始化元素翻译失败", "TypeError") - idx_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, i) - elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( - builder, arr_ty, elem_ty, array_alloca, idx_val) - if elem_ptr is not None: - # 类型转换(如 i32 → i8 截断) - store_val: llvmlite.Value | t.CPtr = elem_val - if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None: - store_val = self.Gen.ExprGenBase.coerce_to_type( - builder, elem_val, elem_ptr.Ty.Pointee, elem_node) - llvmlite.build_store(builder, store_val, elem_ptr) + # 类型转换(如 i32 → i8 截断) + store_val: llvmlite.Value | t.CPtr = elem_val + if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None: + store_val = self.Gen.ExprGenBase.coerce_to_type( + builder, elem_val, elem_ptr.Ty.Pointee, elem_node) + llvmlite.build_store(builder, store_val, elem_ptr) return 0 @@ -909,9 +987,9 @@ class AssignGenMixin: if mv.id is None or string.strcmp(mv.id, "t") != 0: return 0 elif sub.value.kind() == ast.ASTKind.Name: - # 裸名 CArray(from t import *):与 t.CArray 等价 + # 裸名 CArray(from t import *)/ 内建 rawarray:与 t.CArray 等价 bn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value) - if bn.id is None or string.strcmp(bn.id, "CArray") != 0: + if bn.id is None or (string.strcmp(bn.id, "CArray") != 0 and string.strcmp(bn.id, "rawarray") != 0): return 0 else: return 0 @@ -954,6 +1032,40 @@ class AssignGenMixin: return llvmlite.Array(pool, elem_ty_arr, cnt_arr) return resolved_ty + # ============================================================ + # _build_ann_fail_msg - 构建"变量类型注解解析失败"详细报错 + # + # 含注解文本(_get_type_name_from_annotation 重建)、节点类型(kind 映射)、 + # 以及 builtins 最后匹配的诊断结果(_lookup_builtins_typedef 记录), + # 便于直接定位是"注解本身未注册"还是"builtins 扫描未命中/未展开"。 + # ============================================================ + def _build_ann_fail_msg(self, pool: memhub.MemBuddy | t.CPtr, + annot: ast.AST | t.CPtr, nm_id: str) -> str: + """构建变量类型注解解析失败的详细报错信息 + + 注意:f-string 翻译产物是栈上 alloca 缓冲,在助手函数返回后帧销毁, + 缓冲会被后续调用覆盖成垃圾(实测输出 '$')。必须用堆缓冲 + (stdlib.malloc + viperlib.snprintf)保证跨帧存活。 + """ + ann_txt_x: str = None + if annot is not None: + ann_txt_x = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, annot) + if ann_txt_x is None: + ann_txt_x = "?" + btd_nm_x: str = self.Gen.TypeGenCType.LastBuiltinsTypedefName + btd_rs_x: str = self.Gen.TypeGenCType.LastBuiltinsTypedefResult + msg_len: t.CSizeT = 640 + msg_buf: str = stdlib.malloc(msg_len) + if msg_buf is None: + return ann_txt_x + if btd_nm_x is not None and btd_rs_x is not None: + viperlib.snprintf(msg_buf, msg_len, "变量类型注解解析失败,变量名: %s,注解: %s。builtins 最后匹配: %s 展开为 %s,仍无法解析", nm_id, ann_txt_x, btd_nm_x, btd_rs_x) + elif btd_nm_x is not None: + viperlib.snprintf(msg_buf, msg_len, "变量类型注解解析失败,变量名: %s,注解: %s。builtins 最后匹配: %s 未命中(builtins.vp 无该 typedef 或扫描未运行)", nm_id, ann_txt_x, btd_nm_x) + else: + viperlib.snprintf(msg_buf, msg_len, "变量类型注解解析失败,变量名: %s,注解: %s。builtins 最后匹配: 未触发", nm_id, ann_txt_x) + return msg_buf + # ============================================================ # translate_annassign_prescan - 预扫描 AnnAssign(迁移自 # HandlesAnnAssign.AnnAssignHandle.PreScan) @@ -1050,7 +1162,7 @@ class AssignGenMixin: # fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32) self.Gen.TypeGenErr.fatal_error( aa, - f"变量类型注解解析失败,变量名: {nm.id}", + self._build_ann_fail_msg(pool, annot, nm.id), "TypeError") else: # fail-fast: AnnAssign 必须有 annotation,缺少时立即终止(禁止兜底 i32) @@ -1141,7 +1253,7 @@ class AssignGenMixin: # fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32) self.Gen.TypeGenErr.fatal_error( aa, - f"变量类型注解解析失败,变量名: {nm.id}", + self._build_ann_fail_msg(pool, annot, nm.id), "TypeError") else: # fail-fast: AnnAssign 必须有 annotation,缺少时立即终止(禁止兜底 i32) @@ -1172,7 +1284,8 @@ class AssignGenMixin: if stg_key_aa is not None: viperlib.snprintf(stg_key_aa, t.CSizeT(string.strlen(nm.id)) + 8, "%s.__stg", nm.id) stg_ref_aa = self.Gen.VarGen.lookup_module_var(trans.SymTab, stg_key_aa) - if stg_ref_aa is not None and rhs_val.Ty is not None \ + if self._is_addrlike_call(aa.value) == 0 \ + and stg_ref_aa is not None and rhs_val.Ty is not None \ and stg_ref_aa.Ty is not None and stg_ref_aa.Ty.Pointee is not None: stg_ty_aa: llvmlite.LLVMType | t.CPtr = stg_ref_aa.Ty.Pointee rhs_inner_aa: llvmlite.LLVMType | t.CPtr = None @@ -1182,7 +1295,7 @@ class AssignGenMixin: case _: pass if rhs_inner_aa is not None and rhs_inner_aa.kind() == llvmlite.LLVMType.Struct \ - and stg_ty_aa == rhs_inner_aa: + and self._stg_ty_equal(pool, stg_ty_aa, rhs_inner_aa) != 0: llvmlite.build_store(builder, stg_ref_aa, mod_alloca) stg_obj_val_aa: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, stg_ty_aa, rhs_val) @@ -1193,7 +1306,7 @@ class AssignGenMixin: if mod_alloca.Ty is not None: target_ty = mod_alloca.Ty.Pointee if target_ty is not None: - rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node) + rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans) llvmlite.build_store(builder, rhs_val, mod_alloca) return 0 @@ -1249,7 +1362,8 @@ class AssignGenMixin: if stg_key_mg is not None: viperlib.snprintf(stg_key_mg, t.CSizeT(string.strlen(nm.id)) + 8, "%s.__stg", nm.id) stg_ref_mg = self.Gen.VarGen.lookup_module_var(trans.SymTab, stg_key_mg) - if stg_ref_mg is not None and rhs_val_mg.Ty is not None \ + if self._is_addrlike_call(aa.value) == 0 \ + and stg_ref_mg is not None and rhs_val_mg.Ty is not None \ and stg_ref_mg.Ty is not None and stg_ref_mg.Ty.Pointee is not None: stg_ty_mg: llvmlite.LLVMType | t.CPtr = stg_ref_mg.Ty.Pointee rhs_inner_mg: llvmlite.LLVMType | t.CPtr = None @@ -1259,7 +1373,7 @@ class AssignGenMixin: case _: pass if rhs_inner_mg is not None and rhs_inner_mg.kind() == llvmlite.LLVMType.Struct \ - and t.CUInt64T(stg_ty_mg) == t.CUInt64T(rhs_inner_mg): + and self._stg_ty_equal(pool, stg_ty_mg, rhs_inner_mg) != 0: llvmlite.build_store(builder, stg_ref_mg, mod_glob) stg_obj_val_mg: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, stg_ty_mg, rhs_val_mg) @@ -1429,7 +1543,7 @@ class AssignGenMixin: if alloca.Ty is not None: target_ty = alloca.Ty.Pointee if target_ty is not None: - rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node) + rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans) llvmlite.build_store(builder, rhs_val, alloca) # FakeDuck: 初始化影子结构体 __data__ 字段 = 初始字符串指针 # (对齐 TPC HandlesAnnAssign.py: 仅当有初值时写入 __data__) @@ -1618,7 +1732,7 @@ class AssignGenMixin: sval: llvmlite.Value | t.CPtr = fv2 if tgt_alloca.Ty is not None and tgt_alloca.Ty.Pointee is not None: sval = self.Gen.ExprGenBase.coerce_to_type( - builder, fv2, tgt_alloca.Ty.Pointee, node) + builder, fv2, tgt_alloca.Ty.Pointee, node, trans) llvmlite.build_store(builder, sval, tgt_alloca) case _: pass @@ -1650,7 +1764,7 @@ class AssignGenMixin: # 守卫短路:elem 类型完整才做类型转换 if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None: store_val = self.Gen.ExprGenBase.coerce_to_type( - builder, rhs_val, elem_ptr.Ty.Pointee, node) + builder, rhs_val, elem_ptr.Ty.Pointee, node, trans) llvmlite.build_store(builder, store_val, elem_ptr) continue # get_subscript_ptr 返回 None: 尝试 __setitem__ 运算符重载 @@ -1831,7 +1945,7 @@ class AssignGenMixin: store_val: llvmlite.Value | t.CPtr = rhs_val if field_ptr.Ty is not None and field_ptr.Ty.Pointee is not None: store_val = self.Gen.ExprGenBase.coerce_to_type( - builder, rhs_val, field_ptr.Ty.Pointee, node) + builder, rhs_val, field_ptr.Ty.Pointee, node, trans) llvmlite.build_store(builder, store_val, field_ptr) continue # 守卫:field_ptr 为 None → 构造详细错误信息 @@ -1884,7 +1998,8 @@ class AssignGenMixin: if stg_key_as is not None: viperlib.snprintf(stg_key_as, t.CSizeT(string.strlen(nm.id)) + 8, "%s.__stg", nm.id) stg_ref_as = self.Gen.VarGen.lookup_module_var(trans.SymTab, stg_key_as) - if stg_ref_as is not None and rhs_val is not None and rhs_val.Ty is not None \ + if self._is_addrlike_call(asgn.value) == 0 \ + and stg_ref_as is not None and rhs_val is not None and rhs_val.Ty is not None \ and stg_ref_as.Ty is not None and stg_ref_as.Ty.Pointee is not None: stg_ty_as: llvmlite.LLVMType | t.CPtr = stg_ref_as.Ty.Pointee rhs_inner_as: llvmlite.LLVMType | t.CPtr = None @@ -1894,7 +2009,7 @@ class AssignGenMixin: case _: pass if rhs_inner_as is not None and rhs_inner_as.kind() == llvmlite.LLVMType.Struct \ - and t.CUInt64T(stg_ty_as) == t.CUInt64T(rhs_inner_as): + and self._stg_ty_equal(pool, stg_ty_as, rhs_inner_as) != 0: # 1) @g 引用绑定到 storage(丢弃临时 alloca,避免悬垂) llvmlite.build_store(builder, stg_ref_as, mod_alloca) # 2) 复制构造结果(临时对象)到 storage(函数内复制,alloca 仍有效) @@ -1907,7 +2022,7 @@ class AssignGenMixin: if mod_alloca.Ty is not None: target_ty = mod_alloca.Ty.Pointee if target_ty is not None: - rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node) + rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans) llvmlite.build_store(builder, rhs_val, mod_alloca) continue @@ -1933,7 +2048,7 @@ class AssignGenMixin: f"nonlocal 变量 '{nm.id}' 类型信息丢失,无法写入", "TypeError") rhs_coerced: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type( - builder, rhs_val, nl_target_ty, node) + builder, rhs_val, nl_target_ty, node, trans) llvmlite.build_store(builder, rhs_coerced, nl_ptr) continue @@ -1961,7 +2076,7 @@ class AssignGenMixin: store_val: llvmlite.Value | t.CPtr = rhs_val if alloca.Ty is not None and alloca.Ty.Pointee is not None: store_val = self.Gen.ExprGenBase.coerce_to_type( - builder, rhs_val, alloca.Ty.Pointee, node) + builder, rhs_val, alloca.Ty.Pointee, node, trans) llvmlite.build_store(builder, store_val, alloca) # lambda 表达式赋给变量:注册为闭包变量名, # 调用分派(is_closure_name)才走闭包间接调用路径; diff --git a/App/lib/core/Gen/AsyncGenMixin.vp b/App/lib/core/Gen/AsyncGenMixin.vp index f826d13..bb4ab84 100644 --- a/App/lib/core/Gen/AsyncGenMixin.vp +++ b/App/lib/core/Gen/AsyncGenMixin.vp @@ -1937,10 +1937,20 @@ class AsyncGenMixin(CallGenBase.CallGenBase): check_bbs[0] = t.CSizeT(except_bb) hi: int = 0 for hi in range(nhandlers): + # [FIX 2026-08-31] 块名用 snprintf 生成(对齐本文件其余块命名): + # 原先 "coro.exc_check_%d" % hi 的 % 是 Python 字符串格式化,本语言 + # 未实现 → translate_binop 按整数 srem 编译出 i32 垃圾值 → create_block + # 的 name: str 参数收 i32 → int→ptr 禁令误报(CastError 根因)。 + chk_name: str = pool.alloc(64) + exc_name: str = pool.alloc(64) + if chk_name is None or exc_name is None: + return + viperlib.snprintf(chk_name, 64, "coro.exc_check_%d", hi) + viperlib.snprintf(exc_name, 64, "coro.except_%d", hi) check_bbs[hi + 1] = t.CSizeT(llvmlite.create_block( - pool, func, "coro.exc_check_%d" % hi)) + pool, func, chk_name)) handler_bbs[hi] = t.CSizeT(llvmlite.create_block( - pool, func, "coro.except_%d" % hi)) + pool, func, exc_name)) unmatched_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block( pool, func, "coro.try_reraise") codes_buf: t.CInt | t.CPtr = pool.alloc(256) diff --git a/App/lib/core/Gen/Call/CallGenBase.vp b/App/lib/core/Gen/Call/CallGenBase.vp index 35edbba..61a3db1 100644 --- a/App/lib/core/Gen/Call/CallGenBase.vp +++ b/App/lib/core/Gen/Call/CallGenBase.vp @@ -208,6 +208,9 @@ class CallGenBase: else: buf[READ_SZ - 1] = 0 name_len: t.CSizeT = string.strlen(func_name) + # 模块自含定义标志:vpi 中行首 "def {func_name}(" 命中且未被同名 + # from-import 覆盖 → 该模块自身定义 func_name,不是 re-export 源。 + has_own_def: int = 0 total: t.CSizeT = string.strlen(buf) pos: t.CSizeT = 0 while pos < total: @@ -217,6 +220,29 @@ class CallGenBase: ll: t.CSizeT = pos - ls if pos < total: pos += 1 + # 自含定义检测(须在 from-import 行匹配之前,对所有行生效): + # __values.vp 自含 def ConstFloat 且 `from stdint import *`—— + # star 候选 stdint 会把函数归属劫持为 f5522571bcce7bcb.ConstFloat + # (跨模块返回类型解析 fail-fast 根因)。模块级 def 行首无缩进 + # (类内方法 def 有缩进,首字符为空格,不会误命中)。 + if ll > 4 and buf[ls] == 'd' and buf[ls + 1] == 'e' \ + and buf[ls + 2] == 'f' and buf[ls + 3] == ' ': + fstart_od: t.CSizeT = ls + 4 + fend_od: t.CSizeT = fstart_od + while fend_od < ls + ll and buf[fend_od] != '(' and buf[fend_od] != ' ': + fend_od += 1 + fl2_od: t.CSizeT = fend_od - fstart_od + if fl2_od == name_len: + fd_eq_od: int = 1 + fdi_od: t.CSizeT = 0 + while fdi_od < fl2_od: + if buf[fstart_od + fdi_od] != func_name[fdi_od]: + fd_eq_od = 0 + break + fdi_od += 1 + if fd_eq_od != 0: + has_own_def = 1 + continue # 行必须以 "from " 开头 if ll < 6: continue @@ -337,10 +363,17 @@ class CallGenBase: return mod_buf scan = n_end # 全部行扫描完毕:显式 from-import 精确匹配已提前返回;仅有 star - # import 时返回累积候选(空格分隔,调用方逐个验证定义模块) + # import 时返回累积候选(空格分隔,调用方逐个验证定义模块)。 + # 但模块自含 def {func_name}(未遇同名 from-import 覆盖,否则上面 + # 已提前返回 import 源)时本模块就是定义者,不是 re-export——star + # 候选(如 __values.vp `from stdint import *` 的 stdint)会劫持归属, + # 生成错误 SHA1(f5522571bcce7bcb.ConstFloat)→ 跨模块返回类型解析 + # fail-fast。返回 None 保持原模块定义归属。 if star_cands is not None: stdlib.free(buf) - return star_cands + if has_own_def == 0: + return star_cands + return None stdlib.free(buf) return None diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index 06ef07f..cdacc56 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -341,6 +341,24 @@ class CallGenCall(CallGenBase.CallGenBase): # → vpi 精确解析返回类型(对齐 _translate_method_call L4641); # 都失败则 fail-fast,绝不静默 void。 ret_cm: llvmlite.LLVMType | t.CPtr = None + # is_local_mod 豁免(对齐 _translate_method_call mro_ret 段): + # 目标模块 sha1 == 当前正在翻译的模块 sha1 时,自己的 text.ll + # 尚未生成 → _dep_text_ll_exists 恒 0 → 误判 pending 死锁。 + # 从当前 IR 已声明的本地函数取返回类型,不查自身 text.ll/vpi。 + if cls_sha1 is not None and trans is not None \ + and trans.ModuleSha1 is not None \ + and string.strncmp(cls_sha1, trans.ModuleSha1, 16) == 0: + local_lk_cm2: str = pool.alloc( + string.strlen(class_name) + string.strlen(method_name) + 2) + if local_lk_cm2 is not None: + viperlib.snprintf(local_lk_cm2, + string.strlen(class_name) + string.strlen(method_name) + 2, + "%s.%s", class_name, method_name) + local_fn_cm2: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, local_lk_cm2) + if local_fn_cm2 is not None: + lrt_cm2: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(local_fn_cm2) + if lrt_cm2 is not None: + ret_cm = lrt_cm2 if cls_sha1 is not None: ret_cm = self._LookupMethodRetTyFromTextLL( pool, cls_sha1, class_name, method_name) @@ -366,6 +384,11 @@ class CallGenCall(CallGenBase.CallGenBase): stdio.printf("[DEPDBG] pending(method_ret): mod=%s cls=%s method=%s sha1=%s\n", dbg_ms1, class_name, method_name, cls_sha1 if cls_sha1 is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans is not None and trans.ModuleSha1 is not None: + self._AppendPhaseAPending( + trans.ModuleSha1, "method_ret", class_name, + method_name, cls_sha1, "-") # 依赖未就绪:保持默认 i32(build_call(Void) 返回 None 会 # 破坏表达式翻译;pending 文件本遍不写盘,下遍重译) call_ret_ty = llvmlite.Int32(pool) @@ -939,6 +962,32 @@ class CallGenCall(CallGenBase.CallGenBase): # mixin_hit:跨模块 Mixin 兜底已锁定定义类与返回类型,跳过 # text.ll/vpi/pending 重查段,直接构造 {sha1}.{class}.{method} 调用名 if gen_done == 0 and mixin_hit == 0: + # ============================================================ + # is_local_mod 豁免(对齐 CallGenMixin L1336-1343 的 is_local_mod): + # 方法定义在当前正在翻译的模块(ret_anc_sha1 == trans.ModuleSha1) + # 时,自己的 text.ll 在翻译成功前物理上不存在 → mro_ret 的 + # _dep_text_ll_exists 探测恒 0 → 误判"依赖未就绪" → 本模块类 + # 方法前向引用(gfx.vp Scene3D.Render → self._DrawCube/ + # _DrawSphere/_Draw3DSinWave,ret_anc_sha1=0ae)永久 pending + # 死锁(Phase A 收敛失败根因)。从当前 IR 已声明的函数取返回 + # 类型(本模块方法已发射,find_func_in_module 后缀匹配优先本 + # 模块 define),不走跨模块 text.ll/vpi 查找。 + # ============================================================ + if trans is not None and ret_anc_sha1 is not None \ + and trans.ModuleSha1 is not None \ + and string.strncmp(ret_anc_sha1, trans.ModuleSha1, 16) == 0: + local_lk_lm: str = pool.alloc( + string.strlen(ret_anc_name) + string.strlen(method_name) + 2) + if local_lk_lm is not None: + viperlib.snprintf(local_lk_lm, + string.strlen(ret_anc_name) + string.strlen(method_name) + 2, + "%s.%s", ret_anc_name, method_name) + local_fn_lm: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, local_lk_lm) + if local_fn_lm is not None: + lrt_lm: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(local_fn_lm) + if lrt_lm is not None: + call_ret_ty = lrt_lm + gen_done = 1 # 尝试从 text.ll 查找实际返回类型 # 避免指针返回值被默认 i32 导致 inttoptr 截断 looked_up_ty = self._LookupMethodRetTyFromTextLL( @@ -1006,6 +1055,15 @@ class CallGenCall(CallGenBase.CallGenBase): dbg_ms2, class_name, method_name, ret_anc_sha1 if ret_anc_sha1 is not None else "none", self._dep_text_ll_exists(pool, ret_anc_sha1) if ret_anc_sha1 is not None else -1) + # 持久化到 _phaseA_pending.log,供 Phase2 收敛失败最终诊断按调用方过滤 + txt_s: str = "text=-1" + if ret_anc_sha1 is not None: + txt_s = "text=0" + if self._dep_text_ll_exists(pool, ret_anc_sha1) != 0: + txt_s = "text=1" + self._AppendPhaseAPending( + dbg_ms2, "mro_ret", class_name, method_name, + ret_anc_sha1, txt_s) # 依赖未就绪:保持默认 i32 继续而非 Void——build_call(Void) # 返回 None,方法调用结果喂进表达式(if 条件/比较)时 fatal # "if 条件表达式翻译失败"(ExprGenAttr._get_cderef_attr_base @@ -1043,6 +1101,10 @@ class CallGenCall(CallGenBase.CallGenBase): dbg_ms3, class_name, method_name, anc_p2 if anc_p2 is not None else "none", anc_sha1_p2 if anc_sha1_p2 is not None else "none") + # 持久化到 _phaseA_pending.log,供 Phase2 收敛失败最终诊断按调用方过滤 + self._AppendPhaseAPending( + dbg_ms3, "mro_anc", class_name, method_name, + anc_sha1_p2, anc_p2 if anc_p2 is not None else "none") # 依赖未就绪:保持默认 i32(同上,build_call(Void) 返回 # None 会破坏表达式翻译,pending 文件本遍不写盘) call_ret_ty = llvmlite.Int32(pool) @@ -1766,6 +1828,11 @@ class CallGenCall(CallGenBase.CallGenBase): dbg_ms4, anc if anc is not None else "?", method_name, anc_sha1_sp if anc_sha1_sp is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans is not None and trans.ModuleSha1 is not None: + self._AppendPhaseAPending( + trans.ModuleSha1, "special_ret", anc if anc is not None else "-", + method_name, anc_sha1_sp, "-") # 依赖未就绪:保持默认 i32(build_call(Void) 返回 None # 会破坏表达式翻译;pending 文件本遍不写盘,下遍重译) ret_ty_sp = llvmlite.Int32(pool) diff --git a/App/lib/core/Gen/Call/CallGenCtor.vp b/App/lib/core/Gen/Call/CallGenCtor.vp index 0114fe7..0a8d50c 100644 --- a/App/lib/core/Gen/Call/CallGenCtor.vp +++ b/App/lib/core/Gen/Call/CallGenCtor.vp @@ -848,11 +848,14 @@ class CallGenCtor(CallGenBase.CallGenBase): "TypeError") return "int" k: int = arg_node.kind() - # 常量: 42→int, 3.14→double, "abc"→str + # 常量: 42→int, 3.14→double, "abc"→str, 'x'→char if k == ast.ASTKind.Constant: cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(arg_node) if cn.const_kind == ast.CONST_FLOAT: return "double" + if cn.const_kind == ast.CONST_CHAR: + # 单字符字面量 'x' 是 char(i8),与 int/float/double 并列 + return "char" if cn.const_kind == ast.CONST_STR: return "str" return "int" @@ -869,9 +872,14 @@ class CallGenCtor(CallGenBase.CallGenBase): if string.strcmp(fn_nm.id, "float") == 0 or string.strcmp(fn_nm.id, "double") == 0 \ or string.strstr(fn_nm.id, "Float") is not None or string.strstr(fn_nm.id, "Double") is not None: return "double" + # char 内置类型(char(x) 或含 "Char" 的 t/c 类名:CChar/UChar 等) + # → "char",与 float/double 并列(此前归 int 组丢失 i8 语义) + if string.strcmp(fn_nm.id, "char") == 0 \ + or string.strstr(fn_nm.id, "Char") is not None: + return "char" if string.strcmp(fn_nm.id, "int") == 0 \ or string.strstr(fn_nm.id, "Int") is not None or string.strstr(fn_nm.id, "UInt") is not None \ - or string.strstr(fn_nm.id, "Char") is not None or string.strstr(fn_nm.id, "Short") is not None \ + or string.strstr(fn_nm.id, "Short") is not None \ or string.strstr(fn_nm.id, "Long") is not None: return "int" elif cl_f.func.kind() == ast.ASTKind.Attribute: @@ -879,7 +887,10 @@ class CallGenCtor(CallGenBase.CallGenBase): if fn_at.attr is not None: if string.strstr(fn_at.attr, "Float") is not None or string.strstr(fn_at.attr, "Double") is not None: return "double" - if string.strstr(fn_at.attr, "Int") is not None or string.strstr(fn_at.attr, "UInt") is not None or string.strstr(fn_at.attr, "Char") is not None or string.strstr(fn_at.attr, "Short") is not None or string.strstr(fn_at.attr, "Long") is not None: + # char 内置类型(t.CChar(x) 等含 "Char" 的类名)→ "char" + if string.strstr(fn_at.attr, "Char") is not None: + return "char" + if string.strstr(fn_at.attr, "Int") is not None or string.strstr(fn_at.attr, "UInt") is not None or string.strstr(fn_at.attr, "Short") is not None or string.strstr(fn_at.attr, "Long") is not None: return "int" # 类型转换调用(Call)但函数名无法识别:明确的类型意图却无法解析, # fail-fast(区别于普通变量 Name——那是非 T 参数,默认 int 被过滤)。 diff --git a/App/lib/core/Gen/Call/CallGenDispatch.vp b/App/lib/core/Gen/Call/CallGenDispatch.vp index 8ad8d17..431a985 100644 --- a/App/lib/core/Gen/Call/CallGenDispatch.vp +++ b/App/lib/core/Gen/Call/CallGenDispatch.vp @@ -620,7 +620,15 @@ class CallGenDispatch(CallGenBase.CallGenBase): # [阶段3] 统一尺寸口径:库 typedef(BYTE/DWORD 等)先经正常 # 库分析解析(resolve_typedef_or_map → 位宽/8),miss 才走 # 下方硬编码尺寸表(t/c 核心原语)。 - dyn_sz_c: int = self.Gen.TypeGenResolve.get_type_size_bytes(pool, sz_cls_name, None, None) + # 必须传入 trans 的 from_imports/imported_modules:本模块 typedef + # 值可能引用 from-import 别名(MyWord: t.CTypedef = MyInt, + # MyInt 来自 from t import CInt as MyInt),不传则别名链解析失败 + # → get_type_size_bytes 返回 0 → 落到"回退 2"把 Name 当值翻译 + # → NameError: 未定义名称 'MyWord'(T8 根因)。 + dyn_sz_c: int = self.Gen.TypeGenResolve.get_type_size_bytes( + pool, sz_cls_name, + trans._imported_modules if trans is not None else None, + trans._from_imports if trans is not None else None) if dyn_sz_c > 0: return llvmlite.const_int64(pool, dyn_sz_c) # 内置类型 sizeof(int=4, str/bytes=8 指针, double=8 等) @@ -1352,6 +1360,11 @@ class CallGenDispatch(CallGenBase.CallGenBase): stdio.printf("[DEPDBG] pending(dispatch_ret): mod=%s cls=%s method=%s sha1=%s\n", dbg_ms6, cls_nm2, func_name, sha1_fc if sha1_fc is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans is not None and trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "dispatch_ret", cls_nm2, + func_name, sha1_fc, "-") # 依赖未就绪:保持默认 i32(build_call(Void) # 返回 None 破坏表达式翻译;本遍不写盘) ret_ty3 = llvmlite.Int32(pool) @@ -1380,6 +1393,11 @@ class CallGenDispatch(CallGenBase.CallGenBase): dbg_ms7: str = trans.ModuleSha1 if (trans is not None and trans.ModuleSha1 is not None) else "?" stdio.printf("[DEPDBG] pending(dispatch_sp): mod=%s cls=%s method=%s\n", dbg_ms7, cls_nm2, func_name) + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans is not None and trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "dispatch_sp", cls_nm2, + func_name, "-", "-") if hit_box is not None: hit_box[0] = 1 return self.Gen.CallGenCall._translate_method_call( diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index 36c944e..1f612fc 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -1248,11 +1248,25 @@ class CallGenMixin(CallGenBase.CallGenBase): rs_found = 1 call_name = func_name else: - # 原模块未定义:回退首个候选(旧行为,普通跨模块 - # 函数带 sha1 前缀,如 ast._lexer_init 定义在 ast.lexer) - reexport_sha1: str = self._lookup_module_sha1(reexport_src) - if reexport_sha1 is not None: - mod_sha1 = reexport_sha1 + # 原模块 vpi 自含普通 def(非 CExport/State):如 + # string.vp 自身定义 strncmp,而其 `from stdint + # import *` 产生的 star 候选 stdint 会劫持解析。 + # vpi 命中 → 保持原模块 sha(生成 @{mod_sha1}.func, + # 普通跨模块函数带 sha1 前缀),不走候选回退。 + orig_has_def: int = 0 + if self.Gen.CallGenBase.TempDir is not None \ + and self.Gen.CallGenBase.TempDirLen > 0: + VpiReader._ensure_vpi_ret_annot_loaded( + mod_sha1, self.Gen.CallGenBase.TempDir) + if VpiReader._lookup_vpi_ret_annot( + mod_sha1, None, func_name) is not None: + orig_has_def = 1 + if orig_has_def == 0: + # 原模块未定义:回退首个候选(旧行为,普通跨模块 + # 函数带 sha1 前缀,如 ast._lexer_init 定义在 ast.lexer) + reexport_sha1: str = self._lookup_module_sha1(reexport_src) + if reexport_sha1 is not None: + mod_sha1 = reexport_sha1 # CExport 函数定义用裸名(@strlen),跨模块调用需用裸名而非 @{sha1}.func if rs_found != 0: pass # 已验证 reexport 定义模块并置裸名 diff --git a/App/lib/core/Gen/Call/CallGenProp.vp b/App/lib/core/Gen/Call/CallGenProp.vp index 1e154e4..0bd1146 100644 --- a/App/lib/core/Gen/Call/CallGenProp.vp +++ b/App/lib/core/Gen/Call/CallGenProp.vp @@ -121,6 +121,11 @@ class CallGenProp(CallGenBase.CallGenBase): stdio.printf("[DEPDBG] pending(prop_ret): mod=%s cls=%s method=%s sha1=%s\n", dbg_ms8, class_name, method_name, cls_sha1_rt if cls_sha1_rt is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans is not None and trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "prop_ret", class_name, + method_name, cls_sha1_rt, "-") # 依赖未就绪:保持默认 i32(build_call(Void) 返回 None # 会破坏表达式翻译;pending 文件本遍不写盘,下遍重译) call_ret_ty = llvmlite.Int32(pool) diff --git a/App/lib/core/Gen/Class/ClassGenBase.vp b/App/lib/core/Gen/Class/ClassGenBase.vp index a313e25..ae531ef 100644 --- a/App/lib/core/Gen/Class/ClassGenBase.vp +++ b/App/lib/core/Gen/Class/ClassGenBase.vp @@ -340,6 +340,113 @@ class ClassGenBase: if nm.id is not None and string.strcmp(nm.id, deco_name) == 0: return 1 return 0 + def _tattr_name(self, node: ast.AST | t.CPtr, attr_name: str) -> int: + """从 t.attr. / t.attr.(args) 节点提取最外层属性名并比较。 + + Call(t.attr.aligned(16))→ 递归取 func;Attribute(t.attr.packed)→ 取 attr。 + """ + if node is None: + return 0 + if node.kind() == ast.ASTKind.Call: + cl2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl2.func is None: + return 0 + return self._tattr_name(cl2.func, attr_name) + if node.kind() == ast.ASTKind.Attribute: + at2: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + if at2.attr is None: + return 0 + return 1 if string.strcmp(at2.attr, attr_name) == 0 else 0 + return 0 + def _class_has_tattr(self, decorator_list: list[ast.AST | t.CPtr] | t.CPtr, + attr_name: str) -> int: + """检查装饰器列表是否含 t.attr.(@c.Attribute(t.attr.xxx) 或 @t.attr.xxx)。 + + 覆盖 packed/aligned/naked/section 等所有 t.attr 属性(B5)。 + """ + if decorator_list is None: + return 0 + dn: t.CSizeT = len(decorator_list) + if dn == 0: + return 0 + for di in range(dn): + deco: ast.AST | t.CPtr = decorator_list.get(di) + if deco is None: + continue + k: int = deco.kind() + if k == ast.ASTKind.Call: + cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco) + # @c.Attribute(t.attr.xxx) 形式:func 是 Attribute(c, "Attribute") + if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: + continue + at0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: + continue + if cl.args is None: + continue + an: t.CSizeT = len(cl.args) + for ai in range(an): + ag: ast.AST | t.CPtr = cl.args.get(ai) + if ag is not None and self._tattr_name(ag, attr_name) != 0: + return 1 + else: + # @t.attr.xxx 直接形式 + if self._tattr_name(deco, attr_name) != 0: + return 1 + return 0 + def _tattr_aligned_int(self, node: ast.AST | t.CPtr) -> int: + """t.attr.aligned(N) → N(整数参数),非 aligned 或无参返回 0""" + if node is None: + return 0 + if node.kind() == ast.ASTKind.Call: + cl2: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl2.func is None or cl2.func.kind() != ast.ASTKind.Attribute: + return 0 + at2: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl2.func) + if at2.attr is None or string.strcmp(at2.attr, "aligned") != 0: + return 0 + if cl2.args is None or len(cl2.args) == 0: + return 0 + a0: ast.AST | t.CPtr = cl2.args.get(0) + if a0 is None or a0.kind() != ast.ASTKind.Constant: + return 0 + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(a0) + if cn.const_kind != ast.CONST_INT: + return 0 + return int(cn.int_val) + return 0 + def _class_aligned_value(self, decorator_list: list[ast.AST | t.CPtr] | t.CPtr) -> int: + """取 @c.Attribute(t.attr.aligned(N)) 的对齐字节数,无 aligned 返回 0""" + if decorator_list is None: + return 0 + dn: t.CSizeT = len(decorator_list) + if dn == 0: + return 0 + for di in range(dn): + deco: ast.AST | t.CPtr = decorator_list.get(di) + if deco is None: + continue + if deco.kind() == ast.ASTKind.Call: + cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco) + if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: + continue + at0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: + continue + if cl.args is None: + continue + an: t.CSizeT = len(cl.args) + for ai in range(an): + ag: ast.AST | t.CPtr = cl.args.get(ai) + if ag is not None: + av: int = self._tattr_aligned_int(ag) + if av > 0: + return av + else: + av2: int = self._tattr_aligned_int(deco) + if av2 > 0: + return av2 + return 0 def _extract_func_meta(self, decorator_list: list[ast.AST | t.CPtr] | t.CPtr) -> int: """从装饰器列表提取元数据位掩码,返回 0=无装饰器元数据""" meta: int = 0 diff --git a/App/lib/core/Gen/Class/ClassGenDef.vp b/App/lib/core/Gen/Class/ClassGenDef.vp index 4272a7a..f959328 100644 --- a/App/lib/core/Gen/Class/ClassGenDef.vp +++ b/App/lib/core/Gen/Class/ClassGenDef.vp @@ -5,6 +5,7 @@ import ast import llvmlite import memhub import string +import stdio import viperlib import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT @@ -120,6 +121,18 @@ class ClassGenDef(ClassGenBase.ClassGenBase): on_copy)) # Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行) llvmlite.module_add_named_type(trans.Module, pool, existing.Ty) + # B5:existing 路径(Phase B 已注册类)同样需设置 packed/aligned—— + # Phase A 注册时虽已设置,但 Phase B 重新 add_named_type 后节点 + # IsPacked=0,打印输出 `{...}`(idt_entry packed 未生效根因)。 + if self.Gen.ClassGenBase._class_has_tattr(cd.decorator_list, "packed") != 0: + llvmlite.module_set_named_type_packed(trans.Module, pool, existing.Ty, 1) + # StructTable 每 Phase 可能重建(existing 是 Phase B 新 entry), + # 必须补设 IsPacked——否则跨模块 stub 注入(ensure_struct_def + # _in_module)按 entry.IsPacked 判定 packed 时读 0 → stub 非 packed。 + existing.IsPacked = 1 + align_ex: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list) + if align_ex > 0 and existing.Align == 0: + existing.Align = align_ex # Phase A-pre(declare_only=1)多遍扫描时,类已注册但不应翻译方法体 # Phase B(declare_only=0)时全量翻译方法体 if trans._declare_only == 1: @@ -228,6 +241,15 @@ class ClassGenDef(ClassGenBase.ClassGenBase): # L422-426 / FuncGenMixin L482-483),不致命,本遍文件不写盘。 if trans is not None: trans._deps_pending = 1 + # [DEPDBG] 临时诊断:父类未注册即 pending(用后移除) + dbg_p: str = trans.ModuleSha1 if trans.ModuleSha1 is not None else "?" + stdio.printf("[DEPDBG] pending(class_parent): mod=%s cls=%s parent=%s\n", + dbg_p, class_name, pname if pname is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "class_parent", class_name, + "-", "-", pname if pname is not None else "-") return 0 pfc: int = parent_entry.FieldCount for pfi in range(pfc): @@ -345,7 +367,29 @@ class ClassGenDef(ClassGenBase.ClassGenBase): # 供 translate_attribute 做后续 bitcast。 is_generic_sub: int = 0 if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript: - is_generic_sub = 1 + # t.CArray[elem, N](含裸 CArray[elem, N])是定长值数组, + # 非泛型堆对象——元素类型未就绪时若回退 Ptr(i8) 会注册成退化 + # 字段布局,Phase B 复用该 entry 后 GEP 元素成 i8,字段访问/ + # 赋值全部失效(zhuff_tree.codes: t.CArray[zhuff_code, 288] + # 在 1a 因 zhuff_code 未注册(code: UINT 查表失败,类注册整体 + # 中止)退化为 Ptr(i8) 并注册,1b 复用退化 entry → + # self.codes[i].bits 字段查找失败 → pending(attr_read/write) → + # "if 条件表达式翻译失败"根因)。仅真正的泛型类(list[T]/ + # Vector[T] 等堆对象)未特化才回退 Ptr(i8)(AnnotClassName + # 携带 mangled name 供后续 bitcast 恢复)。 + sub_g: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(aa.annotation) + is_carray_sub_g: int = 0 + if sub_g is not None and sub_g.value is not None: + if sub_g.value.kind() == ast.ASTKind.Attribute: + svg_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub_g.value) + if svg_at.attr is not None and string.strcmp(svg_at.attr, "CArray") == 0: + is_carray_sub_g = 1 + elif sub_g.value.kind() == ast.ASTKind.Name: + svg_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_g.value) + if svg_nm.id is not None and (string.strcmp(svg_nm.id, "CArray") == 0 or string.strcmp(svg_nm.id, "rawarray") == 0): + is_carray_sub_g = 1 + if is_carray_sub_g == 0: + is_generic_sub = 1 if is_generic_sub != 0: # 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复 field_ty = llvmlite.Ptr(pool, llvmlite.Int8(pool)) @@ -362,6 +406,15 @@ class ClassGenDef(ClassGenBase.ClassGenBase): # 翻译: ,请检查依赖模块是否已编译/注册"),不静默掩盖。 if trans is not None: trans._deps_pending = 1 + # [DEPDBG] 临时诊断:字段注解类型未注册即 pending(用后移除) + dbg_f: str = trans.ModuleSha1 if trans.ModuleSha1 is not None else "?" + stdio.printf("[DEPDBG] pending(class_field_ty): mod=%s cls=%s field=%s\n", + dbg_f, class_name, tgt.id if tgt.id is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "class_field_ty", class_name, + tgt.id if tgt.id is not None else "-", "-", "-") return 0 # 旧行为:fast-fail NameError(已移除——Phase A 依赖未就绪时 # 误报真实错误,中断多遍收敛;真实错误交给收敛检查兜底) @@ -482,6 +535,14 @@ class ClassGenDef(ClassGenBase.ClassGenBase): # 注册命名结构体到模块(输出 %"name" = type { ... } 定义行) llvmlite.module_add_named_type(trans.Module, pool, struct_ty) + # B5:@c.Attribute(t.attr.packed) 打包结构体 → 类型定义输出 <{...}> + # (LLVM packed struct,无字段填充,GEP 按 packed 布局计算偏移)。 + # idt/gdt/elf/fat32 等二进制格式结构体依赖它读写硬件/磁盘布局。 + is_packed_cd: int = self.Gen.ClassGenBase._class_has_tattr( + cd.decorator_list, "packed") + if is_packed_cd != 0: + llvmlite.module_set_named_type_packed(trans.Module, pool, struct_ty, 1) + # 扫描字段类型,为跨模块结构体引用添加 opaque 声明 # 例如 list[int].__pool__ 引用 memhub.MemManager,需要在当前模块添加 %"sha1.MemManager" = type opaque for fi in range(field_count): @@ -500,6 +561,15 @@ class ClassGenDef(ClassGenBase.ClassGenBase): VLogger.error(f"failed to register {class_name}", "CLASS") return 0 + # B5:记录 packed 标记到 StructEntry(供 __sizeof__/字段布局等派生查询) + if is_packed_cd != 0: + entry.IsPacked = 1 + # B5:记录 aligned 对齐值(@c.Attribute(t.attr.aligned(N)),如 fpu_context 16 对齐), + # 供全局变量/局部 alloca 生成时施加对齐。 + align_cd: int = self.Gen.ClassGenBase._class_aligned_value(cd.decorator_list) + if align_cd > 0: + entry.Align = align_cd + # 标记 VTable 状态(直接用 entry,规避跨模块同名 find_struct 找错) if has_vtable == 1: entry.HasVTable = 1 diff --git a/App/lib/core/Gen/Class/ClassGenVTable.vp b/App/lib/core/Gen/Class/ClassGenVTable.vp index 755e946..479f6dd 100644 --- a/App/lib/core/Gen/Class/ClassGenVTable.vp +++ b/App/lib/core/Gen/Class/ClassGenVTable.vp @@ -5,6 +5,7 @@ import ast import llvmlite import memhub import string +import stdio import viperlib import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT @@ -618,6 +619,12 @@ class ClassGenVTable(ClassGenBase.ClassGenBase): oop_has_vt_pre = oop_entry.HasVTable if oop_has_vt_pre == 1: self._collect_vtable_methods(trans, cd, class_name) + # [PF-DBG] vtable 方法收集后 pending 检测(用后移除) + if trans is not None and trans._deps_pending != 0 and trans._pf_dbg_pend_flagged == 0: + trans._pf_dbg_pend_flagged = 1 + stdio.printf("[PF-DBG] pending-after-vtcollect: cls=%s sha1=%s\n", + class_name, trans.ModuleSha1 if trans.ModuleSha1 is not None else "?") + stdio.fflush(None) for ci in range(cn): stmt: ast.AST | t.CPtr = children.get(ci) if stmt is None: @@ -626,6 +633,19 @@ class ClassGenVTable(ClassGenBase.ClassGenBase): fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(stmt) if fd is not None and fd.name is not None: self._translate_method(trans, fd, struct_ty, class_name) + # [PF-DBG] 单个方法翻译后 pending 检测(用后移除) + if trans is not None and trans._deps_pending != 0 and trans._pf_dbg_pend_flagged == 0: + trans._pf_dbg_pend_flagged = 1 + stdio.printf("[PF-DBG] pending-after-method: cls=%s method=%s sha1=%s\n", + class_name, fd.name, + trans.ModuleSha1 if trans.ModuleSha1 is not None else "?") + stdio.fflush(None) + # [PF-DBG] 方法翻译后 pending 检测(用后移除) + if trans is not None and trans._deps_pending != 0 and trans._pf_dbg_pend_flagged == 0: + trans._pf_dbg_pend_flagged = 1 + stdio.printf("[PF-DBG] pending-after-methods: cls=%s sha1=%s\n", + class_name, trans.ModuleSha1 if trans.ModuleSha1 is not None else "?") + stdio.fflush(None) # 如果有虚表,先生成 VTable 全局变量(设置 VTableMethodCount) # 必须在 __before_init__ 之前,否则 __before_init__ 读不到 VTableMethodCount @@ -635,9 +655,21 @@ class ClassGenVTable(ClassGenBase.ClassGenBase): has_vt_flag = oop_entry.HasVTable if has_vt_flag == 1: self._generate_vtable(trans, cd, class_name) + # [PF-DBG] vtable 生成后 pending 检测(用后移除) + if trans is not None and trans._deps_pending != 0 and trans._pf_dbg_pend_flagged == 0: + trans._pf_dbg_pend_flagged = 1 + stdio.printf("[PF-DBG] pending-after-vtable: cls=%s sha1=%s\n", + class_name, trans.ModuleSha1 if trans.ModuleSha1 is not None else "?") + stdio.fflush(None) # 生成 __before_init__ 函数(零值填充 + 默认值赋值 + vtable 指针设置) self._generate_before_init(trans, struct_ty, class_name) + # [PF-DBG] before_init 生成后 pending 检测(用后移除) + if trans is not None and trans._deps_pending != 0 and trans._pf_dbg_pend_flagged == 0: + trans._pf_dbg_pend_flagged = 1 + stdio.printf("[PF-DBG] pending-after-before_init: cls=%s sha1=%s\n", + class_name, trans.ModuleSha1 if trans.ModuleSha1 is not None else "?") + stdio.fflush(None) return 0 # ============================================================ diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 500568d..b285460 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -824,6 +824,26 @@ class ExprGenAttr(ExprGenBase.ExprGenBase): alias_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx) if alias_ptr is not None: + # [FIX 2026-08-31] REnum 变体字段按声明类型读取 + # (对齐 match 绑定 _lookup_renum_member_ty 机制): + # 槽位是 i32/i64(布局按最大字段大小),直接 load + # 得整数位模式 → 赋给 X|t.CPtr 指针变量触发 int→ptr + # 禁令误报(AssignGenMixin.vp L608 + # arr_ty = array_alloca.Ty.Pointee 根因)。 + # 用别名表存的成员声明类型(自引用退化为 i8*)把 + # 槽位地址 bitcast 后按声明类型 load——同宽无信息 + # 丢失,指针字段读出真正的指针值。 + decl_ty_rn: llvmlite.LLVMType | t.CPtr = \ + self.Gen.ClassGenEnum._lookup_renum_field_alias_ty( + renum_se.Name, at.attr) + if decl_ty_rn is not None: + decl_ptr_rn: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, decl_ty_rn) + if decl_ptr_rn is not None: + cast_ptr_rn: llvmlite.Value | t.CPtr = \ + llvmlite.build_bitcast(builder, alias_ptr, decl_ptr_rn) + if cast_ptr_rn is not None: + return llvmlite.build_load(builder, decl_ty_rn, cast_ptr_rn) return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr) # property getter 回退: obj.attr 是 @property/@x.getter 方法时生成调用 # (字段未找到时先尝试,避免报误导性的"字段未找到"错误) @@ -902,6 +922,25 @@ class ExprGenAttr(ExprGenBase.ExprGenBase): field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( builder, struct_ty, field_ty, obj_ptr, field_idx) if field_ptr is not None: + # [FIX 2026-08-31] REnum 变体字段按声明类型读取(与主路径 + # 变体别名回退一致):field_info 是 _pN 槽位(i32/i64), + # 直接 load 得整数位模式 → 赋给 X|t.CPtr 指针变量触发 int→ptr + # 误报。bitcast 槽位地址后按声明类型 load(同宽无信息丢失)。 + rn_rd_se: GenTypes.StructEntry | t.CPtr = \ + self.Gen.StructGen.find_struct_by_type(struct_ty) + if rn_rd_se is not None and rn_rd_se.Name is not None \ + and self.Gen.ClassGenEnum._is_renum_class_name(rn_rd_se.Name) != 0: + decl_ty_rd: llvmlite.LLVMType | t.CPtr = \ + self.Gen.ClassGenEnum._lookup_renum_field_alias_ty( + rn_rd_se.Name, at.attr) + if decl_ty_rd is not None and self.is_array_type(decl_ty_rd) == 0: + decl_ptr_rd: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr( + pool, decl_ty_rd) + if decl_ptr_rd is not None: + cast_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, field_ptr, decl_ptr_rd) + if cast_ptr_rd is not None: + return llvmlite.build_load(builder, decl_ty_rd, cast_ptr_rd) # 数组类型字段不能 load 为 SSA value,直接返回字段指针 # 用于后续下标访问: self.state[0] → GEP state 字段 → GEP 数组元素 if self.is_array_type(field_ty) != 0: @@ -935,6 +974,15 @@ class ExprGenAttr(ExprGenBase.ExprGenBase): # 空 stub 返回 None 而非 fatal)。真实错误由 Phase A 收敛检查暴露。 if trans is not None: trans._deps_pending = 1 + # [DEPDBG] 临时诊断:读属性字段缺失即 pending(用后移除) + dbg_ar: str = trans.ModuleSha1 if trans.ModuleSha1 is not None else "?" + stdio.printf("[DEPDBG] pending(attr_read): mod=%s field=%s\n", + dbg_ar, at.attr if at.attr is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "attr_read", "-", + at.attr if at.attr is not None else "-", "-", "-") # Phase A 依赖未就绪(_deps_pending,父类/依赖类未注册致字段缺失): # 跳过本属性访问交给下遍重译(对齐 get_attribute_ptr L5541-5547), # 避免 fail-fast 中断多遍收敛;依赖就绪后仍缺失则走下方真实报错。 @@ -1218,6 +1266,15 @@ class ExprGenAttr(ExprGenBase.ExprGenBase): # 暴露("Phase A 无法完成翻译"),不在此 fail-fast 中断多遍收敛。 if trans is not None: trans._deps_pending = 1 + # [DEPDBG] 临时诊断:写属性字段缺失即 pending(用后移除) + dbg_aw: str = trans.ModuleSha1 if trans.ModuleSha1 is not None else "?" + stdio.printf("[DEPDBG] pending(attr_write): mod=%s field=%s\n", + dbg_aw, at.attr if at.attr is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "attr_write", "-", + at.attr if at.attr is not None else "-", "-", "-") # Phase A 依赖未就绪(_deps_pending,父类/依赖类未注册致字段缺失, # 如 Generator.vp 先于 TypeGenMixin.vp 编译时 self.TraceFrames): # 跳过本属性写入交给下遍重译(对齐 HandlesAssign L69-78), diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index bb76d1c..545774e 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -370,7 +370,8 @@ class ExprGenBase: def coerce_to_type(self, builder: llvmlite.IRBuilder | t.CPtr, val: llvmlite.Value | t.CPtr, target_ty: llvmlite.LLVMType | t.CPtr, - node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr: + node: ast.AST | t.CPtr = None, + trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: """将 val 强制转换为 target_ty 整数: sext(扩展)/ trunc(截断) @@ -394,10 +395,35 @@ class ExprGenBase: # 整数转换 val_bits: int = self.get_llvm_type_bits(val.Ty) target_bits: int = self.get_llvm_type_bits(target_ty) - # 整数 → 指针: inttoptr(必须在纯整数转换之前检查,因为 ptr 的 bits 非零会误入 sext/trunc 路径) - # 适用于: 跨模块方法返回 i64/i32(默认推断),目标变量是指针类型 + # 整数 → 指针: 禁止隐式转换(FIX 2026-08-30),唯一豁免是 C 空指针常量 0 + # (NULL: t.CDefine = 0 / 字面量 0)——C 语义中 0 赋指针即 null pointer + # constant,inttoptr 0 生成合法空指针,不属于"整数当指针"的类型信息缺失。 + # 原为 inttoptr 兜底(跨模块方法返回 i64/i32 默认推断时静默转指针), + # 掩盖类型信息缺失——如 `BootInfo = saved_rdi`(整数 → bootinfo*)。 + # 按"按需过滤/不兜底/fail-fast"原则:非常量整数必须显式强转 + # (t.CPtr(x) 无类型指针右值可隐式转为左侧类型化指针,走指针→指针 + # bitcast 路径)。 if val_bits != 0 and self.is_ptr_type(target_ty) != 0: - return llvmlite.build_inttoptr(builder, val, target_ty) + # [FIX 2026-08-31] C 空指针常量豁免:编译期常量 0 赋给指针 → 空指针。 + # NULL 是 t.CDefine = 0(如 self.handle = NULL / CreateThread(NULL,...)), + # 属标准 C 空指针用法,须放行而非报 CastError。先拓宽到 i64 + # (inttoptr 要求同宽,对齐 MatchGenMixin 模式),再 inttoptr 成空指针。 + if val.IsConst == 1 and val.IntVal == 0: + return llvmlite.build_inttoptr( + builder, llvmlite.const_int64(builder.Pool, 0), target_ty) + # [FIX 2026-08-30] Phase A 依赖未就绪(_deps_pending):跨模块调用 + # 返回类型退化为默认 i32,属退化指针重建(如 pool.alloc 实返 i8* + # 被 pending 退化为 i32 赋给同层指针字段/变量——同级指针本可转换) + # 而非隐式转换错误。跳过 fail-fast,文件保持 pending 不写盘,下遍 + # 依赖就绪后多遍收敛精确解析(否则硬中断收敛,memhub↔_list 环 + # 永不求解)。 + if trans is not None and trans._deps_pending != 0: + return val + self.Gen.TypeGenErr.fatal_error( + node, + "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型", + "CastError") + return val # 指针 → 整数: ptrtoint(当目标明确是整数而非指针时) # 注意: target_bits == 8 时跳过 ptrtoint,走后面的 build_load 解引用首字符 # 修复: buf[idx] = '\0' 中 '\0' 是 CONST_STR → i8* 指针, diff --git a/App/lib/core/Gen/Expr/ExprGenMixin.vp b/App/lib/core/Gen/Expr/ExprGenMixin.vp index e920e7d..59e4c7a 100644 --- a/App/lib/core/Gen/Expr/ExprGenMixin.vp +++ b/App/lib/core/Gen/Expr/ExprGenMixin.vp @@ -147,4 +147,16 @@ class ExprGenMixin(ExprGenBase.ExprGenBase): return self.Gen.AsyncGen._emit_await( builder, pool, mod, trans, aw, aw_nm.id) return None + elif k == ast.ASTKind.Starred: + # *x 在值上下文(如 printf("%d", *k) 实参):解析器把调用参数位的 + # '*' 按 Python *args 解包语义归类为 Starred,但 Viper 未实现解包, + # 全代码库无调用点使用;按 C 语义视为单目解引用(*ptr → load pointee, + # 对齐 UnaryOp(Deref))。构造 Deref UnaryOp 复用既有翻译/错误路径。 + st: ast.Starred | t.CPtr = (ast.Starred | t.CPtr)(node) + if st is None or st.value is None: + return None + deref_node: ast.AST | t.CPtr = ast.UnaryOp(pool, ast.OpKind.Deref, st.value) + if deref_node is None: + return None + return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, deref_node, trans) return None diff --git a/App/lib/core/Gen/Expr/ExprGenName.vp b/App/lib/core/Gen/Expr/ExprGenName.vp index 939a87b..0adb948 100644 --- a/App/lib/core/Gen/Expr/ExprGenName.vp +++ b/App/lib/core/Gen/Expr/ExprGenName.vp @@ -71,21 +71,28 @@ class ExprGenName(ExprGenBase.ExprGenBase): # NAME: t.CDefine = value 形式定义的常量在编译期已注册到本地表 # 浮点 CDefine(如 vipermath 的 U_M_PI=3.14...)必须返回浮点常量, # 否则在 double 函数里 return 生成 ret i32 0(llc 报错)。 + # 注意:同名变量优先——当前作用域已存在变量时,不得被 CDefine 表条目 + # 遮蔽。vpi stub 注入会把依赖模块的 CDefine 注册进导入方 CDefine 表 + # (numpy 的 e: t.CDefine = 2.718...),测试模块局部变量 e 被误解析为 + # 浮点常量 → numpy.np_sign(e) 参数传 double → coerce Ptr 触发 + # "float <-> ptr" CastError(NumpyTest np_sign 根因)。守卫与下方跨模块 + # CDefine 查找的 lookup_var 检查(L102)保持一致。 cdef_val_d: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id) if self.Gen.TypeGenCDef.is_cdefine_found() != 0: - if self.Gen.TypeGenCDef.is_cdefine_str() != 0: - # 字符串 CDefine(如 ZLIB_VERSION: t.CDefine = "1.3.2"): - # 返回 global string(i8*),供 return ZLIB_VERSION 等使用。 - cdef_sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id) - if cdef_sv is not None: - return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv, trans) - self.Gen.TypeGenErr.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError") - if self.Gen.TypeGenCDef.is_cdefine_double() != 0: - return llvmlite.ConstFloat(pool, llvmlite.Double(pool), cdef_val_d) - # 整数 CDefine:重新查表取 64 位全精度值(double 往返会丢精度, - # 9223372036854775807 → 9.223372036854776e18 舍入错位,生成错常量) - cdef_val_c: t.CInt64T = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm_id) - return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val_c) + if trans is None or self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None: + if self.Gen.TypeGenCDef.is_cdefine_str() != 0: + # 字符串 CDefine(如 ZLIB_VERSION: t.CDefine = "1.3.2"): + # 返回 global string(i8*),供 return ZLIB_VERSION 等使用。 + cdef_sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id) + if cdef_sv is not None: + return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv, trans) + self.Gen.TypeGenErr.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError") + if self.Gen.TypeGenCDef.is_cdefine_double() != 0: + return llvmlite.ConstFloat(pool, llvmlite.Double(pool), cdef_val_d) + # 整数 CDefine:重新查表取 64 位全精度值(double 往返会丢精度, + # 9223372036854775807 → 9.223372036854776e18 舍入错位,生成错常量) + cdef_val_c: t.CInt64T = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm_id) + return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val_c) # 本地表未找到:尝试跨模块 .vpi 查找(保证多模块查表存表一致) # 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找, @@ -341,6 +348,13 @@ class ExprGenName(ExprGenBase.ExprGenBase): stdio.printf("[DEPDBG] pending(global_stub): mod=%s name=%s mod_src=%s sha1=%s\n", dbg_ms9, nm_id, fi_mod_cut2, StubMergerStore.LookupSha1ByModName(fi_mod_cut2) if fi_mod_cut2 is not None else "none") + # 持久化(用后移除):供 Phase2 收敛失败最终诊断按调用方过滤 + if trans is not None and trans.ModuleSha1 is not None: + self.Gen.CallGenBase._AppendPhaseAPending( + trans.ModuleSha1, "global_stub", "-", nm_id, + StubMergerStore.LookupSha1ByModName(fi_mod_cut2) + if fi_mod_cut2 is not None else None, + fi_mod_cut2) return None if ref_fi is not None and ref_fi.Ty is not None: lty_fi: llvmlite.LLVMType | t.CPtr = ref_fi.Ty.Pointee @@ -918,6 +932,39 @@ class ExprGenName(ExprGenBase.ExprGenBase): return None case _: pass + # [FIX 2026-08-31] 64 位标量 | t.CPtr 联合字段下标读回 + # (如 RenumClassNames: t.CSizeT | t.CPtr,联合解析为 i64* 8 字节 + # 槽数组):槽位按 Int64 load 得位模式,赋给 str(i8*)触发 int→ptr + # 禁令误报(ClassGenEnum.vp existing: str = self.RenumClassNames[i] + # 根因)。槽里实际存的是 perm_pool 字符串指针——联合声明即"槽位可能 + # 存指针",合法 C 联合读回应按指针读回。统一按 i8* load(同宽 8 字节, + # 位模式不变):指针目标直接 bitcast;整数目标隐式 ptrtoint 同样 + # 保持位模式(对齐 MatchGenMixin 的 REnum 槽位按声明类型读机制)。 + if trans is not None and sub.value is not None \ + and sub.value.kind() == ast.ASTKind.Attribute: + fa_u64: str = self.Gen.ExprGenAttr._resolve_attr_field_class( + pool, trans, sub.value) + if fa_u64 is not None and self._annot_is_64bit_scalar(fa_u64) != 0: + match elem_ty2: + case llvmlite.LLVMType.Int(eb_u64): + if eb_u64 == 64: + cell_p_u64: llvmlite.Value | t.CPtr = \ + llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val) + if cell_p_u64 is not None: + i8_ty_u64: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Int8(pool) + i8p_ty_u64: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, i8_ty_u64) + i8pp_ty_u64: llvmlite.LLVMType | t.CPtr = \ + llvmlite.Ptr(pool, i8p_ty_u64) + cast_cell_u64: llvmlite.Value | t.CPtr = \ + llvmlite.build_bitcast( + builder, cell_p_u64, i8pp_ty_u64) + if cast_cell_u64 is not None: + return llvmlite.build_load( + builder, i8p_ty_u64, cast_cell_u64) + case _: + pass # 自定义结构体 (如 self._ht[key]): 仅当类定义了 __getitem__ 才转发方法调用; # 否则普通结构体指针按下标做 GEP 数组访问 cls_nm_rd_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2) @@ -949,6 +996,20 @@ class ExprGenName(ExprGenBase.ExprGenBase): # ============================================================ # 获取下标表达式的元素指针(不加载值,用于赋值) # ============================================================ + def _annot_is_64bit_scalar(self, annot: str) -> int: + """注解是否为 64 位标量 | t.CPtr 联合(AnnotClassName 为提取出的 + 非 CPtr 侧类型名,如 t.CInt64T|t.CPtr → "CInt64T")。此类联合的 + 8 字节单元格存 i64 标量,下标元素类型应为 Int64 而非指针。""" + if annot is None: + return 0 + if string.strstr(annot, "CInt64T") is not None: + return 1 + if string.strstr(annot, "CUInt64T") is not None: + return 1 + if string.strstr(annot, "CSizeT") is not None: + return 1 + return 0 + def get_subscript_ptr(self, builder: llvmlite.IRBuilder | t.CPtr, pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -1046,6 +1107,16 @@ class ExprGenName(ExprGenBase.ExprGenBase): ptr_val_pe: llvmlite.Value | t.CPtr = llvmlite.build_load( builder, pointee, alloca) if ptr_val_pe is not None: + # [FIX 2026-08-30] 64 位标量联合(t.CInt64T|t.CPtr / + # t.CUInt64T|t.CPtr / t.CSizeT|t.CPtr):8 字节单元格存 + # i64 标量(viperlib %n 的 np[0]=write_count 是解引用写入, + # 语义 *np=(i64)x)。GEP 元素必须用 Int64(标量)而非指针, + # 否则元素类型为指针 → i64 存储触发整数隐式转指针 + # fail-fast(把解引用写入误判为 int→ptr)。 + if ve_pe.AnnotClassName is not None \ + and self._annot_is_64bit_scalar(ve_pe.AnnotClassName) != 0: + return llvmlite.build_gep( + builder, llvmlite.Int64(pool), ptr_val_pe, idx_val) match pointee: case llvmlite.LLVMType.Int(bits_pe): if bits_pe == 8: diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index 1f0fb85..5412928 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -193,6 +193,21 @@ class ExprGenOps(ExprGenBase.ExprGenBase): if narrow_node is not None: if self._node_type_unsigned(pool, narrow_node) != 0: return 1 + # 窄侧是高位比特置位的 CDefine 常量(0x80000000..0xFFFFFFFF)→ zext。 + # CDefine 常量是编译期值而非变量,_node_type_unsigned 查不到类型注解; + # 值落在有符号负数区表示无符号哨兵(如 INVALID_FILE_ATTRIBUTES = + # 0xFFFFFFFF = 4294967295)。与宽侧(如 ULONG=CUnsignedLong→i64 的 + # Win32 属性值)比较时须 zext 到 4294967295,否则 sext 成 -1 与运行时 + # 零扩展的 32 位 API 返回值恒不等(SysLibTest exists/rmdir/rename/rmtree + # 全 FAIL 根因)。负值 CDefine(如 -1)不在此区间(-1 > 0x7FFFFFFF 为假), + # 保持 sext 语义不变。 + if narrow_node is not None and narrow_node.kind() == ast.ASTKind.Name: + cdef_nm_zw: ast.Name | t.CPtr = (ast.Name | t.CPtr)(narrow_node) + if cdef_nm_zw.id is not None: + cdv_zw: t.CInt64T = self.Gen.TypeGenCDef.lookup_cdefine_constant(cdef_nm_zw.id) + if self.Gen.TypeGenCDef.is_cdefine_found() != 0: + if cdv_zw > 0x7FFFFFFF: + return 1 return 0 def _node_type_unsigned(self, pool: memhub.MemBuddy | t.CPtr, node: ast.AST | t.CPtr, diff --git a/App/lib/core/Gen/Func/FuncGenBase.vp b/App/lib/core/Gen/Func/FuncGenBase.vp index ad58987..6e3ebae 100644 --- a/App/lib/core/Gen/Func/FuncGenBase.vp +++ b/App/lib/core/Gen/Func/FuncGenBase.vp @@ -93,6 +93,11 @@ class FuncGenBase(CallGenBase.CallGenBase): attr_name: str = self._get_attr_name_from_node(arg_node) if attr_name is None: continue + # B5:section 字符串属性(t.attr.section(".text.startup"))不能内联 + # 在函数签名行(llc: expected string constant),必须走属性组 + # #N(function_set_attr_group)→ 由 FuncGenDef 单独提取处理。 + if string.strcmp(attr_name, "section") == 0: + continue # 映射到 LLVM 属性名并追加 ar: int = self._append_llvm_attr(attrs_buf, attr_name) if ar != 0: @@ -101,6 +106,50 @@ class FuncGenBase(CallGenBase.CallGenBase): if found_any == 0: return None return attrs_buf + def extract_func_section(self, pool: memhub.MemBuddy | t.CPtr, + decorator_list: list[ast.AST | t.CPtr] | t.CPtr, + imported_modules: str) -> str: + """提取 @c.Attribute(t.attr.section("xxx")) 为属性组文本 '"section"="xxx"'。 + + LLVM 字符串属性(带值)必须用属性组 #N(函数行输出 #N + 函数尾 + attributes #N = { ... }),不能内联(llc: expected string constant)。 + main.vp boot 函数 t.attr.section(".text.startup") 供链接器按段放置。 + """ + if pool is None or decorator_list is None: + return None + dn: t.CSizeT = len(decorator_list) + if dn == 0: + return None + for di in range(dn): + deco: ast.AST | t.CPtr = decorator_list.get(di) + if deco is None or deco.kind() != ast.ASTKind.Call: + continue + call_node: ast.Call | t.CPtr = (ast.Call | t.CPtr)(deco) + if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: + continue + func_attr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(call_node.func) + if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: + continue + if call_node.args is None: + continue + args_list: list[ast.AST | t.CPtr] | t.CPtr = call_node.args + an: t.CSizeT = len(args_list) + for ai in range(an): + arg_node: ast.AST | t.CPtr = args_list.get(ai) + if arg_node is None: + continue + attr_name: str = self._get_attr_name_from_node(arg_node) + if attr_name is None or string.strcmp(attr_name, "section") != 0: + continue + sec_val: str = self._get_attr_call_arg_str(arg_node) + if sec_val is None: + continue + sec_buf: str = pool.alloc(128) + if sec_buf is None: + return None + viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val) + return sec_buf + return None def _get_attr_name_from_node(self, node: ast.AST | t.CPtr) -> str: """从 AST 节点提取属性名 @@ -127,6 +176,20 @@ class FuncGenBase(CallGenBase.CallGenBase): return at2.attr return None + def _get_attr_call_arg_str(self, node: ast.AST | t.CPtr) -> str: + """取 t.attr.xxx("val") 调用的首参字符串常量(如 section(".text.startup"))""" + if node is None or node.kind() != ast.ASTKind.Call: + return None + cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl.args is None or len(cl.args) == 0: + return None + a0: ast.AST | t.CPtr = cl.args.get(0) + if a0 is None or a0.kind() != ast.ASTKind.Constant: + return None + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(a0) + if cn.const_kind != ast.CONST_STR: + return None + return cn.str_val def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int: """将属性名映射到 LLVM 属性名并追加到缓冲区 diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index 60473e5..d70a221 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -12,9 +12,9 @@ import stdlib import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT import lib.core.Gen.Call.CallGenBase as CallGenBase -import lib.core.Gen.GenApi as GenApi - - +import lib.core.Gen.GenApi as GenApi + + # ============================================================ # FuncGenDef - FuncGenMixin 拆分(阶段 D):函数声明 / translate_function_def / decorator wrapper # @@ -75,6 +75,33 @@ class FuncGenDef(CallGenBase.CallGenBase): trans._declare_only = 1 cn_count: t.CSizeT = len(ch) + # ============================================================ + # 预注册当前模块 ClassDef(declare_only 模式,仅 struct+字段+方法声明, + # 不译方法体)。背景:1a 阶段(declare_only=1)不注入 vpi、不处理 + # import,依赖模块的 CTypedef(stdint 的 u16/u32/UINT 等)查表失败, + # 含此类字段的类(socket.vp SocketAddr: u16/u32/u64)1a 注册整体中止; + # 1b 此处 vpi 注入已完成、typedef 可解析,若不预注册,forward_declare + # 解析返回/参数注解(如 _MakeAddr -> SocketAddr)时类未注册 → + # "函数返回类型解析失败:注解不可解析"(WebTest socket.vp 根因)。 + # 按 AST 序预注册保证同文件类间依赖(class A 引用 class B,B 在后) + # 也能先注册。已完整注册的类(1a 成功 / 依赖方 stub 注入含字段)跳过, + # 交由 translate_children 走 existing 路径,无重复处理副作用。 + pre_i: t.CSizeT + for pre_i in range(cn_count): + pre_child: ast.AST | t.CPtr = ch.get(pre_i) + if pre_child is None: + continue + if pre_child.kind() != ast.ASTKind.ClassDef: + continue + pre_cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(pre_child) + if pre_cd is None or pre_cd.name is None: + continue + pre_ex: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module( + pre_cd.name, trans.ModuleSha1) + if pre_ex is not None and pre_ex.FieldCount > 0: + continue + self.Gen.ClassGenDef.translate_class_def(trans, pre_child) + for ci in range(cn_count): child: ast.AST | t.CPtr = ch.get(ci) if child is None: @@ -85,6 +112,23 @@ class FuncGenDef(CallGenBase.CallGenBase): if fd is None or fd.name is None: continue + # PEP 695 泛型函数(def f[T](...) -> T)按普通函数编译(TPV 无泛型 + # 函数特化机制,与 translate_function_def L678-691 一致):此处先设置 + # 类型参数上下文(T→"int"),使返回/参数注解中的 T 可解析,否则 + # forward_declare 阶段 "函数返回类型解析失败:注解不可解析" + # (ExpandKeywordsTest fn ident[T] 根因)。所有退出路径(continue / + # 循环末尾)必须 clear_generic_context,避免污染后续函数注解解析。 + tp_names_fd: list[str] | t.CPtr = None + if fd.type_params is not None: + tp_names_fd = fd.type_params + tp_args_fd: list[str] | t.CPtr = list[str](pool, 8) + if tp_args_fd is not None: + tpn_fd: t.CSizeT = len(fd.type_params) + tpi_fd: t.CSizeT = 0 + for tpi_fd in range(tpn_fd): + tp_args_fd.append("int") + self.Gen.TypeGenBase.set_generic_context(tp_names_fd, tp_args_fd) + # 推断返回类型 ret_ty: llvmlite.LLVMType | t.CPtr = None if fd.returns is not None: @@ -155,12 +199,16 @@ class FuncGenDef(CallGenBase.CallGenBase): # 如果函数已存在(如重复定义),跳过 existing: llvmlite.Function | t.CPtr = self.Gen.CallGenBase.find_func_in_module(mod, mangled_name) if existing is not None: + if tp_names_fd is not None: + self.Gen.TypeGenBase.clear_generic_context() continue # 创建 declare func: llvmlite.Function | t.CPtr = llvmlite.create_declare( pool, mod, mangled_name, ret_ty) if func is None: + if tp_names_fd is not None: + self.Gen.TypeGenBase.clear_generic_context() continue # 提取默认参数信息 @@ -233,6 +281,10 @@ class FuncGenDef(CallGenBase.CallGenBase): # 参数添加完成,补全全局函数表的 TypeStr(供 vtable 在 func 被 reset 后使用) self.Gen.CallGenLeafKit.update_global_func_typestr(trans.Pool, fd.name, func) + # 泛型函数上下文用完即清(防泄漏到后续函数的注解解析) + if tp_names_fd is not None: + self.Gen.TypeGenBase.clear_generic_context() + # 恢复原始声明模式 trans._declare_only = saved_declare_only return 0 @@ -299,6 +351,24 @@ class FuncGenDef(CallGenBase.CallGenBase): # 如果仍然为 None,扫描 return 语句推断(至少返回 i32) if ret_ty is None: + # fail-fast:无返回类型注解(fd.returns is None)但函数体返回了 + # 非 None 值 → 报错,不静默推断。返回具体值必须有显式 -> 注解, + # 编译器不替用户推断返回类型(推断位宽会掩盖返回契约,且让 + # __doc__ 等依赖精确返回类型的检查静默出错)。无注解函数只允许 + # 过程形态:无 return / 裸 return / return None(掉尾默认零值返回)。 + if fd.returns is None: + if self.Gen.TypeGenErr.body_has_value_return(pool, fd.children) != 0: + rtn_err: str = pool.alloc(256) + if rtn_err is not None: + viperlib.snprintf( + rtn_err, 256, + "函数 '%s' 无返回类型注解却返回了非 None 值," + "请添加显式返回类型注解(如 -> int)", fd.name) + self.Gen.TypeGenErr.fatal_error(fd, rtn_err, "TypeError") + return 0 + self.Gen.TypeGenErr.fatal_error( + fd, "函数无返回类型注解却返回了非 None 值", "TypeError") + return 0 param_types_str: str = self.Gen.TypeGenErr.build_param_types_str(pool, fd.args) ret_ty = self.Gen.TypeGenErr.infer_return_type( pool, fd.children, param_types_str) @@ -470,6 +540,11 @@ class FuncGenDef(CallGenBase.CallGenBase): func_attrs_e: str = self.Gen.FuncGenBase.extract_func_attrs(pool, fd.decorator_list, imported_modules) if func_attrs_e is not None: llvmlite.function_set_attrs(func, func_attrs_e) + # B5:section 字符串属性走属性组 #N + func_section_e: str = self.Gen.FuncGenBase.extract_func_section( + pool, fd.decorator_list, imported_modules) + if func_section_e is not None: + llvmlite.function_set_attr_group(func, func_section_e) # 参数添加完成,补全全局函数表的 TypeStr self.Gen.CallGenLeafKit.update_global_func_typestr(pool, fd.name, func) @@ -488,6 +563,11 @@ class FuncGenDef(CallGenBase.CallGenBase): func_attrs_reuse: str = self.Gen.FuncGenBase.extract_func_attrs(pool, fd.decorator_list, imported_modules) if func_attrs_reuse is not None: llvmlite.function_set_attrs(func, func_attrs_reuse) + # B5:section 字符串属性走属性组 #N + func_section_reuse: str = self.Gen.FuncGenBase.extract_func_section( + pool, fd.decorator_list, imported_modules) + if func_section_reuse is not None: + llvmlite.function_set_attr_group(func, func_section_reuse) else: # 创建新的 LLVM 函数(使用 SHA1 混淆名) func = llvmlite.create_function(pool, mod, mangled_name, ret_ty) @@ -499,6 +579,11 @@ class FuncGenDef(CallGenBase.CallGenBase): func_attrs: str = self.Gen.FuncGenBase.extract_func_attrs(pool, fd.decorator_list, imported_modules) if func_attrs is not None: llvmlite.function_set_attrs(func, func_attrs) + # B5:section 字符串属性走属性组 #N(LLVM 不接受内联 section="...") + func_section: str = self.Gen.FuncGenBase.extract_func_section( + pool, fd.decorator_list, imported_modules) + if func_section is not None: + llvmlite.function_set_attr_group(func, func_section) # 提取默认参数信息 tfd_defaults: t.CVoid | t.CPtr = None diff --git a/App/lib/core/Gen/GenTypes.vp b/App/lib/core/Gen/GenTypes.vp index 9f64eed..b5c98a1 100644 --- a/App/lib/core/Gen/GenTypes.vp +++ b/App/lib/core/Gen/GenTypes.vp @@ -110,6 +110,8 @@ class StructEntry: FieldCount: int # 字段数量 Fields: FieldEntry | t.CPtr # 字段数组(FIELD_MAX 个槽位) IsUnion: int # 1=联合体 / 0=普通结构体 + IsPacked: int # 1=@c.Attribute(t.attr.packed) 打包结构体(无字段填充) + Align: int # @c.Attribute(t.attr.aligned(N)) 对齐字节数(0=默认,如 fpu_context=16) IsOOP: int # 1=OOP结构体(有方法) / 0=纯内存结构体 HasInit: int # 1=有__init__方法 / 0=无 HasNew: int # 1=有__new__方法 / 0=无 diff --git a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp index 1c87c96..ca86f8f 100644 --- a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp @@ -621,7 +621,20 @@ class StmtGenCifMixin: ret_is_ptr: int = self.Gen.ExprGenBase.is_ptr_type(ret_ty_rt) # ptr 类型不一致性:val 是整数但 ret 是 ptr(或反之),必须转换 if val_is_ptr != ret_is_ptr: - val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node) + # [FIX 2026-08-30] __new__ 返回类型是类指针(T | t.CPtr 语义): + # 返回的实例存储指针来自跨模块调用(如 pool.alloc),Phase A + # 依赖未就绪时其返回类型退化为默认 i32。此时 int→ptr 是退化 + # 指针重建而非隐式转换错误——允许 inttoptr,文件保持 pending + # 不写盘,下遍依赖就绪后多遍收敛精确解析(否则 fail-fast 硬 + # 中断多遍收敛,memhub↔_list 环永不求解)。 + fn_nm_ret: str = None + if cur_func_rt is not None: + fn_nm_ret = llvmlite.function_get_name(cur_func_rt) + if fn_nm_ret is not None and string.strstr(fn_nm_ret, "__new__") is not None \ + and val_is_ptr == 0 and ret_is_ptr != 0: + val = llvmlite.build_inttoptr(builder, val, ret_ty_rt) + else: + val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node) elif val_is_ptr != 0 and ret_is_ptr != 0: # 指针 → 指针且元素类型不同(如 t.CVoid(x, CPtr) 得到 i8*, # 函数返回类型是类指针 Circle* 时)需 bitcast 回, @@ -1932,7 +1945,11 @@ class StmtGenCifMixin: self.Gen.TypeGenErr.fatal_error(for_node, "数组迭代: 无法解析数组类型") # 数组值 → 临时 alloca(数组值不是指针,需拷贝后 GEP 迭代) - arr_tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, arr_ty) + # 必须在 entry 块创建:若在 for 语句所在块(非 entry)alloca, + # 旧版 LLVM 会将其作为动态 alloca → 运行时 sub rsp 大数组, + # 调用栈深时 rsp 骤降踩未映射页 → 概率性 #PF + # (devfs_directory.find 的 for i in result: result=[256 x str] 实测崩溃 rip 0x103002)。 + arr_tmp: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, arr_ty) if arr_tmp is None: self.Gen.TypeGenErr.fatal_error(for_node, "数组迭代: 临时存储分配失败") llvmlite.build_store(builder, array_val, arr_tmp) @@ -2081,8 +2098,9 @@ class StmtGenCifMixin: elem_ty: llvmlite.LLVMType | t.CPtr = first_val.Ty # 构建 [N x elem_ty] 数组值:临时 alloca 逐元素写入后 load + # 同样必须在 entry 块创建,避免非 entry alloca 被降为动态 alloca(见 _handle_array_iter) arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, elem_ty, elts_count) - tmp_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, arr_ty) + tmp_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, arr_ty) if tmp_alloca is None: self.Gen.TypeGenErr.fatal_error(for_node, "列表字面量迭代: 临时数组分配失败") first_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( diff --git a/App/lib/core/Gen/Stmt/StmtGenMixin.vp b/App/lib/core/Gen/Stmt/StmtGenMixin.vp index 8f20a25..1028915 100644 --- a/App/lib/core/Gen/Stmt/StmtGenMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenMixin.vp @@ -48,6 +48,13 @@ class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin): mret: int = self._translate_stmt_impl(trans, node) if trans is not None: trans.TranslateDepth -= 1 + # [PF-DBG] 语句翻译后 pending 检测(用后移除):定位 gfx.vp 收敛失败首条语句 + if trans is not None and trans._deps_pending != 0 and trans._pf_dbg_pend_flagged == 0: + trans._pf_dbg_pend_flagged = 1 + stdio.printf("[PF-DBG] pending-after-stmt: k=%d sha1=%s\n", + node.kind() if node is not None else -1, + trans.ModuleSha1 if trans.ModuleSha1 is not None else "?") + stdio.fflush(None) return mret def _translate_stmt_impl(self, trans: HT.Translator | t.CPtr, diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index bfda2d8..2b6ffce 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -557,7 +557,9 @@ class StructGenMixin: # 模块限定名(module.Class):模块可解析时按模块归属精确匹配, # 防止裸名回退抓跨模块同名类。模块未命中时若裸名类唯一(无冲突) # 仍回退,避免误杀未登记模块的既有解析。 - dot_pos_q: str = string.strrchr(name, '.') + # 取点跳过泛型下标(numpy.ndarray[t.CDouble] → numpy + ndarray[t.CDouble]), + # 否则 strrchr 落到下标内部截错模块名(同 lookup_field_by_class 根因)。 + dot_pos_q: str = self._last_dot_outside_brackets(name) if dot_pos_q is not None and dot_pos_q[1] != '\0': bare_f: str = dot_pos_q + 1 mod_len_f: t.CSizeT = string.strlen(name) - string.strlen(bare_f) - 1 @@ -610,8 +612,8 @@ class StructGenMixin: return None stdlib.free(mod_buf_f) # 第二遍:类名回退(处理模块限定名 "HT.Translator" → "Translator", - # 仅模块名无法解析时走到这里) - dot_pos: str = string.strrchr(name, '.') + # 仅模块名无法解析时走到这里;取点同样跳过泛型下标) + dot_pos: str = self._last_dot_outside_brackets(name) if dot_pos is not None: class_name: str = dot_pos + 1 if class_name is not None and class_name[0] != '\0': @@ -800,6 +802,33 @@ class StructGenMixin: # 所有条目都未匹配 return None + def _last_dot_outside_brackets(self, s: str) -> str: + """返回字符串中最后一个位于 [...] 括号外的 '.' 指针,无则 None + + 泛型特化类名含下标(如 'numpy.ndarray[t.CDouble]'、 + 'list[f467978339422f32.AST|t.CPtr]'),下标内部也含 '.'(t.CDouble + 的模块限定 / sha1.AST)。strrchr 直接找最后一个 '.' 会落到下标内部, + 把模块名截错('numpy.ndarray[t' → sha1 映射表无法解析,NumpyTest + lookup_field_by_class FAIL 根因)。从末尾扫描并跟踪括号深度, + 只认深度为 0 的 '.'。 + """ + if s is None: + return None + n: t.CSizeT = string.strlen(s) + depth: int = 0 + i: t.CSizeT = n + while i > 0: + i -= 1 + ch: t.CChar = s[i] + if ch == ']': + depth += 1 + elif ch == '[': + if depth > 0: + depth -= 1 + elif ch == '.' and depth == 0: + return s + i + return None + def lookup_field_by_class(self, class_name: str, field_name: str, sha1: str = None, @@ -821,9 +850,10 @@ class StructGenMixin: # 先精确匹配,失败后用 strrchr 提取类名部分回退匹配 if sha1 is not None: sha1_matched: int = 0 - # 计算类名回退部分("HT.Translator" → "Translator") + # 计算类名回退部分("HT.Translator" → "Translator";含下标的 + # "numpy.ndarray[t.CDouble]" → "ndarray[t.CDouble]") bare_name: str = class_name - dot_pos_lfbc: str = string.strrchr(class_name, '.') + dot_pos_lfbc: str = self._last_dot_outside_brackets(class_name) if dot_pos_lfbc is not None: bare_name = dot_pos_lfbc + 1 if bare_name is None or bare_name[0] == '\0': @@ -854,7 +884,10 @@ class StructGenMixin: # vthreading.vp 同名 Thread 顶掉 → 'tid' 字段缺失)。 # 模块名经 StubMergerStore sha1 映射表解析为模块 sha1,再精确匹配 # name+sha1(vpi stub 注入 / 定义侧注册的裸名 entry)。 - dot_pos_q: str = string.strrchr(class_name, '.') + # 注意:泛型特化名含下标(numpy.ndarray[t.CDouble]),取点必须跳过 + # 下标内部(_last_dot_outside_brackets),否则模块名截成 + # 'numpy.ndarray[t' → sha1 无法解析(NumpyTest FAIL 根因)。 + dot_pos_q: str = self._last_dot_outside_brackets(class_name) if dot_pos_q is not None and dot_pos_q[1] != '\0': bare_q: str = dot_pos_q + 1 mod_len_q: t.CSizeT = string.strlen(class_name) - string.strlen(bare_q) - 1 @@ -1316,7 +1349,7 @@ class StructGenMixin: if dup is not None: string.strcpy(dup, src) arr[i] = t.CSizeT(dup) - entry.ParentNames = t.CSizeT(arr) + entry.ParentNames = arr entry.ParentCount = count return 1 @@ -1536,6 +1569,11 @@ class StructGenMixin: return # 将完整定义添加到当前模块(会替换已存在的 opaque 声明) llvmlite.module_add_named_type(mod, pool, entry.Ty) + # B5:跨模块注入的 packed 类必须同步 packed 标记(<{...}>),否则引用 + # 模块按非 packed 布局 GEP,与定义侧 packed 布局错位(gdt/elf/fat32 + # 二进制格式字段错读;合并 IR 中 idt_entry/gdt_entry 退化 {…} 根因)。 + if entry.IsPacked != 0: + llvmlite.module_set_named_type_packed(mod, pool, entry.Ty, 1) # 递归扫描字段类型,确保值类型子结构体(如 WIN32_FIND_DATAA 中的 FILETIME) # 也有完整定义。指针类型字段只需 opaque 声明(已由 module_ensure_opaque_for_type 处理)。 self._ensure_field_types_in_module(pool, mod, entry.Ty) diff --git a/App/lib/core/Gen/Translator.vp b/App/lib/core/Gen/Translator.vp index 2941751..c93b6bc 100644 --- a/App/lib/core/Gen/Translator.vp +++ b/App/lib/core/Gen/Translator.vp @@ -12,6 +12,7 @@ import viperlib import lib.core.VLogger as VLogger import lib.Projectrans.Config as Config import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild +import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.core.Gen.Generator as Generator # ============================================================ @@ -251,6 +252,9 @@ class Translator: # 跨模块函数返回类型无法精确解析。Phase A 多遍循环据此下遍重试。 _deps_pending: t.CInt + # [PF-DBG] 用后移除:语句级 pending 探针"已打第一次"标志 + _pf_dbg_pend_flagged: t.CInt + # 嵌套作用域符号表 SymTab: GenTypes.SymbolTable | t.CPtr @@ -293,6 +297,7 @@ class Translator: self.GenericTypeArgs = None self._declare_only = 0 self._deps_pending = 0 + self._pf_dbg_pend_flagged = 0 self.SymTab = None self._async_funcs = None self._async_func_count = 0 @@ -616,17 +621,84 @@ class Translator: GenApi.add_visible_struct(pool, "dict") if self._imported_modules is None or \ GenApi.is_module_imported(self._imported_modules, "_dict") == 0: - self._imported_modules = GenApi.add_imported_module( - pool, self._imported_modules, "_dict") + # [FIX 2026-08-31] 破环:_dict 自身依赖含当前模块则跳过注入。 + # 对齐 Phase1Main._InjectedCreatesCycle,覆盖所有 translate 路径 + # (Phase1 1b / Phase2 pre-inc / PhaseA)——否则 _list/_dict 显式 + # import memhub 且 memhub 用容器泛型时成 2-环,Phase A 永不收敛。 + if self._ContainerInjectionCreatesCycle("_dict") == 0: + self._imported_modules = GenApi.add_imported_module( + pool, self._imported_modules, "_dict") if need_list_box[0] != 0: GenApi.add_visible_struct(pool, "list") if self._imported_modules is None or \ GenApi.is_module_imported(self._imported_modules, "_list") == 0: - self._imported_modules = GenApi.add_imported_module( - pool, self._imported_modules, "_list") + if self._ContainerInjectionCreatesCycle("_list") == 0: + self._imported_modules = GenApi.add_imported_module( + pool, self._imported_modules, "_list") stdlib.free(need_list_box) stdlib.free(need_dict_box) + def _ContainerInjectionCreatesCycle(self, mod_name: str) -> int: + """注入 mod_name(_list/_dict)前检查是否成环,返回 1=成环需跳过注入。 + + 判断被注入模块自身的 deps 是否已含当前模块名。若命中(如 _list 显式 + import memhub,而 memhub 因 `__provides__: list[str]` 注解要注入 _list) + → memhub↔_list 循环依赖 → Phase A 多遍收敛死锁(gfx.vp "无法完成翻译" + 根因)。对齐 Phase1Main._InjectedCreatesCycle,但本方法走 self.ModuleSha1 + 查 RelPath,覆盖所有 translate 路径(Phase1 1b 全量翻译、Phase2 pre-inc/ + PhaseA 都会执行 _inject_auto_container_visibility)。命中则跳过注入: + 去掉环边后,_list 已因当前模块被其依赖而处于闭包,list 类仍由 + add_visible_struct 可见、泛型模板已由 Phase1 注册。 + """ + if mod_name is None or self.ModuleSha1 is None: + return 0 + inj_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name) + if inj_sha1 is None: + return 0 + inj_deps: str = StubMergerStore.LookupIncludesDeps(inj_sha1) + if inj_deps is None: + return 0 + # 当前模块名 = RelPath 基名(最后 '/' 或 '\\' 之后)去扩展名 + rel_path: str = StubMergerStore.LookupSha1RelPath(self.ModuleSha1) + if rel_path is None: + return 0 + base: str = string.strrchr(rel_path, '/') + if base is None: + base = string.strrchr(rel_path, '\\') + if base is not None: + base = base + 1 + else: + base = rel_path + dot: str = string.strchr(base, '.') + nm_buf: bytes = stdlib.malloc(128) + if nm_buf is None: + return 0 + if dot is not None: + nm_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot) - t.CUInt64T(base)) + if nm_len > 127: + nm_len = 127 + string.strncpy(nm_buf, base, nm_len) + nm_buf[nm_len] = '\0' + else: + string.strncpy(nm_buf, base, 127) + nm_buf[127] = '\0' + # 词边界精确匹配:inj_deps 中是否有 token 与当前模块名相同 + nm_len2: t.CSizeT = string.strlen(nm_buf) + d_len: t.CSizeT = string.strlen(inj_deps) + dp: t.CSizeT = 0 + while dp < d_len: + ts: t.CSizeT = dp + while dp < d_len and inj_deps[dp] != ' ': + dp += 1 + tl: t.CSizeT = dp - ts + if dp < d_len: + dp += 1 + if tl > 0 and tl == nm_len2 and string.strncmp(inj_deps + ts, nm_buf, tl) == 0: + stdlib.free(nm_buf) + return 1 + stdlib.free(nm_buf) + return 0 + def _scan_container_usage(self, node: ast.AST | t.CPtr, need_list_box: t.CPtr, need_dict_box: t.CPtr): @@ -646,12 +718,13 @@ class Translator: sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) if sub_s.value is not None: self._check_container_type_node(sub_s.value, need_list_box, need_dict_box) - elif k == ast.ASTKind.List: - # [] 字面量:容器模板仍需注册 - need_list_box[0] = 1 - elif k == ast.ASTKind.Dict: - # {} 字面量:容器模板仍需注册 - need_dict_box[0] = 1 + # [FIX 2026-08-30] `[...]`/`{}` 字面量不再无条件触发 _list/_dict 导入: + # 字面量广泛用于编译期常量列表(c.Asm 的 op/inp/out CLOBBER 参数、 + # 数组数据初始化),并非 list 容器使用。list/dict 容器的真实使用由 + # 类型位置检测覆盖:`x: list` / `list[...]` 注解、`list(...)`/`dict(...)` + # 构造(_check_container_type_node 的 AnnAssign.annotation / Call.func / + # Subscript.value 分支)。无注解 `x = [...]` 若意图 list,编译器在 + # translate_list_literal 阶段会 fail-fast 提示 _list 未注册(需显式注解)。 # 递归语句块子节点 if node.children is not None: ch_s: list[ast.AST | t.CPtr] | t.CPtr = node.children @@ -678,6 +751,29 @@ class Translator: if sub_s.value is not None: self._check_container_type_node(sub_s.value, need_list_box, need_dict_box) + def _annotation_is_container(self, ann: ast.AST | t.CPtr) -> int: + """注解是否为 list/dict 容器(Name 裸名或 Subscript list[...]/dict[...]) + + 数组初始化(t.CArray[...] / 裸数组注解)返回 0,避免 _scan_container_usage + 把数组字面量误判为 list 容器使用(FIX 2026-08-30)。 + """ + while ann is not None: + ak: int = ann.kind() + if ak == ast.ASTKind.Name: + nm_c: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ann) + if nm_c.id is not None: + if string.strcmp(nm_c.id, "list") == 0: + return 1 + if string.strcmp(nm_c.id, "dict") == 0: + return 1 + return 0 + elif ak == ast.ASTKind.Subscript: + sub_c: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(ann) + ann = sub_c.value + else: + return 0 + return 0 + # ============================================================ # IR 输出 diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 21ef982..19ec381 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -5,6 +5,7 @@ import ast import llvmlite import memhub import string +import stdio import viperlib import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT @@ -142,6 +143,21 @@ class TranslatorGenMixin: # 无 builder 的模块级 Assign → 创建全局变量(仅全量翻译模式) added = self.handle_module_level_var(trans, child) added_total += added + # [PF-DBG] 子语句翻译后 pending 检测(用后移除):定位 gfx.vp 类收敛失败 + if trans is not None and trans._deps_pending != 0: + nm_dbg: str = None + if kd == ast.ASTKind.ClassDef: + cd_dbg: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) + if cd_dbg is not None: + nm_dbg = cd_dbg.name + elif kd == ast.ASTKind.FunctionDef: + fd_dbg: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child) + if fd_dbg is not None: + nm_dbg = fd_dbg.name + stdio.printf("[PF-DBG] pending-after-child: kind=%d name=%s sha1=%s\n", + kd, nm_dbg if nm_dbg is not None else "-", + trans.ModuleSha1 if trans.ModuleSha1 is not None else "?") + stdio.fflush(None) return added_total @@ -523,6 +539,12 @@ class TranslatorGenMixin: init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str init_str_val: str = None init_list_node: ast.List | t.CPtr = None + # = None 显式初始化(如 tokens.vp `_kw_head: KwEntry | t.CPtr = None`): + # 对象类全局的懒初始化指针语义——必须保持 NULL,不能创建 BSS storage + # (storage 会让 @g = &g.stg 恒非 NULL,破坏 `if _kw_head != None` 懒初始化 + # 守卫 → _init_keywords 永不执行 → _kw_lookup 遍历零存储 strcmp(NULL) 崩溃, + # 二代自举 1.log ASan 根因;_op_head/_kw_pool 同款模式一并修复)。 + is_none_init: int = 0 annot_node: ast.AST | t.CPtr = None # 类型注解节点(检测 t.CExport 标记) if k == ast.ASTKind.AnnAssign: @@ -575,7 +597,9 @@ class TranslatorGenMixin: val_kind: int = aa.value.kind() if val_kind == ast.ASTKind.Constant: cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value) - if cn.const_kind == ast.CONST_INT: + if cn.const_kind == ast.CONST_NONE: + is_none_init = 1 + elif cn.const_kind == ast.CONST_INT: init_val = cn.int_val init_kind = 1 elif cn.const_kind == ast.CONST_STR: @@ -607,7 +631,9 @@ class TranslatorGenMixin: val_kind2: int = asgn.value.kind() if val_kind2 == ast.ASTKind.Constant: cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value) - if cn2.const_kind == ast.CONST_INT: + if cn2.const_kind == ast.CONST_NONE: + is_none_init = 1 + elif cn2.const_kind == ast.CONST_INT: init_val = cn2.int_val init_kind = 1 elif cn2.const_kind == ast.CONST_STR: @@ -664,6 +690,20 @@ class TranslatorGenMixin: return 0 llvmlite.module_add_global(mod, gv) + # B5:@c.Attribute(t.attr.aligned(N)) 类的全局实例施加对齐(如 fpu_context 16 对齐, + # FXSAVE 区域要求 16 字节对齐,否则 fxsave/fxrstor #GP)。 + al_ty: llvmlite.LLVMType | t.CPtr = var_ty + match var_ty: + case llvmlite.LLVMType.Ptr(pointee_al): + al_ty = pointee_al + case _: + pass + al_entry: GenTypes.StructEntry | t.CPtr = None + if al_ty is not None and al_ty.kind() == llvmlite.LLVMType.Struct: + al_entry = self.Gen.StructGen.find_struct_by_type(al_ty) + if al_entry is not None and al_entry.Align > 0: + gv.Align = al_entry.Align + # 设置初始值(有初始值时清除 external linkage,因为 LLVM 22+ 不允许 external global 带初始值) gv.Linkage = None if init_kind == 1: @@ -712,14 +752,18 @@ class TranslatorGenMixin: # 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer, # 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type" # 对象类全局(有 storage)的 Initializer 由下方 storage 逻辑设为 &@storage, - # 此处不覆盖。 - if resolved_stg_ty is None: + # 此处不覆盖;但 = None 懒初始化全局(is_none_init)既无 storage 也无其他 + # Initializer → 必须显式 zeroinitializer(LLVM 对指针类型输出为 null), + # 否则 gv 无初始化器 → 全局声明残缺。 + if resolved_stg_ty is None or is_none_init != 0: gv.Initializer = "zeroinitializer" # 对象类全局:创建静态 storage 全局(BSS)承载对象实例,@g 编译期引用它。 # 函数内 `g = T()` 构造时把对象复制到 storage(见 AssignGenMixin 全局分支), # 不再依赖栈 alloca(悬垂);c.Addr(g) load @g 得到 &storage,均非 NULL。 - if resolved_stg_ty is not None: + # 豁免:显式 `= None` 初始化(is_none_init)的懒初始化指针全局 + # (如 _kw_head/_op_head)不建 storage,保持 NULL 语义。 + if resolved_stg_ty is not None and is_none_init == 0: # 值类型 storage 需要完整 struct 定义(跨模块类默认 opaque → # @g.stg = global %T zeroinitializer 报 "use of undefined type") self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, resolved_stg_ty) @@ -733,7 +777,22 @@ class TranslatorGenMixin: vt_name_buf: str = pool.alloc(128) if vt_name_buf is not None: viperlib.snprintf(vt_name_buf, 128, "%s.vtable", resolved_stg_entry.Name) - vt_mangled: str = self.Gen.FuncGenBase._mangle_name(trans, vt_name_buf) + # [FIX 2026-08-31] vtable 符号前缀必须用类定义模块的 sha1 + # (对齐 CallGenLeafKit._make_vtable_ref_i8)。此前用当前模块 + # _mangle_name → 跨模块对象类全局(如 Translator 模块的 + # _mbuddy: memhub.MemBuddy)storage 初始化引用 + # "@当前sha1.MemBuddy.vtable",而 vtable 实为定义模块 + # 的 "@7e4672b992208969.MemBuddy.vtable" → llc 报 + # "use of undefined value"(eba110fc7acac3e6.ll:7999 根因)。 + # resolved_stg_entry.ModuleSha1 即定义模块,比 get_struct_sha1 + # 裸名反查更精确(避免跨模块同名类歧义)。 + vt_sha1: str = None + if resolved_stg_entry.ModuleSha1 is not None: + vt_sha1 = resolved_stg_entry.ModuleSha1 + if vt_sha1 is None: + vt_sha1 = trans.ModuleSha1 + vt_mangled: str = self.Gen.FuncGenBase._mangle_name_with_sha1( + vt_sha1, vt_name_buf) if vt_mangled is not None: # 完整 struct 初始化器(LLVM 拒绝部分初始化): # 字段 0 = __vtable__(i8* @vtable),其余用户字段按类型零值。 @@ -774,6 +833,9 @@ class TranslatorGenMixin: llvmlite.module_add_global(mod, stg_gv) stg_gv.Linkage = None stg_gv.Initializer = stg_init_text + # B5:storage(BSS 对象实例)同样施加 aligned 对齐 + if resolved_stg_entry is not None and resolved_stg_entry.Align > 0: + stg_gv.Align = resolved_stg_entry.Align # @g 初始值 = &@storage(对象引用指向 BSS 实例,非 NULL) stg_init_len: t.CSizeT = t.CSizeT(string.strlen(mangled_name)) + 8 stg_init_buf: str = pool.alloc(stg_init_len) diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index d248ed3..06fc587 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -69,6 +69,21 @@ class TypeGenBase: CTypedefFound: int CTypedefResolveDepth: int CTypedefVisiting: list[str] | t.CPtr + # builtins 源扫描标记:注解解析"最后匹配"兜底(_lookup_builtins_typedef) + # 每文件最多读一次 includes/builtins.vp;随 clear_ctypedefs 复位 + BuiltinsTypedefScanned: int + # builtins 最后匹配诊断("变量类型注解解析失败"详细报错用): + # LastBuiltinsTypedefName = 最后一次请求的名称;LastBuiltinsTypedefResult = + # 展开后的类型表达式(未命中 = None)。随 clear_ctypedefs 复位。 + LastBuiltinsTypedefName: str + LastBuiltinsTypedefResult: str + # builtins import 别名缓冲(stdlib.malloc,格式 "alias\0target\0" 连续段): + # _lookup_builtins_typedef 扫描 builtins.vp import 行填充;库 typedef 模块 + # 限定引用按别名还原真实模块名(__stdint.size_t → stdint.size_t)。 + # 用 malloc 而非 pool list——pool 每文件 reset_to 后 list 悬空(clear_ctypedefs + # 未覆盖的路径读到已释放内存 → strcmp 崩溃,1.log 根因);malloc 跨 pool + # 生命周期存活,clear_ctypedefs 释放置空。 + ImportAliasBuf: bytes # --- 常量整数表达式计算成功标志(原 _g_const_eval_ok) --- ConstEvalOk: int @@ -102,6 +117,10 @@ class TypeGenBase: self.CTypedefFound = 0 self.CTypedefResolveDepth = 0 self.CTypedefVisiting = None + self.BuiltinsTypedefScanned = 0 + self.LastBuiltinsTypedefName = None + self.LastBuiltinsTypedefResult = None + self.ImportAliasBuf = None self.ConstEvalOk = 0 self.CurrentFile = None return 0 diff --git a/App/lib/core/Gen/Type/TypeGenCType.vp b/App/lib/core/Gen/Type/TypeGenCType.vp index 4f1950b..7807106 100644 --- a/App/lib/core/Gen/Type/TypeGenCType.vp +++ b/App/lib/core/Gen/Type/TypeGenCType.vp @@ -37,6 +37,11 @@ class TypeGenCType(TypeGenBase.TypeGenBase): """ if name is None or type_str is None or pool is None: return + # rowptr 是编译器内置语法(TPC/TPV 双端),不可重定义: + # 忽略任何对 rowptr 的 typedef 注册,防止覆盖内置裸指针语义 + # (rowptr[T] 固定 = 指向原始类型 T 的裸指针)。 + if string.strcmp(name, "rowptr") == 0: + return if self.CTypedefNames is None: self.CTypedefNames = list[str](pool, 64) self.CTypedefTypeStrs = list[str](pool, 64) @@ -81,6 +86,15 @@ class TypeGenCType(TypeGenBase.TypeGenBase): self.CTypedefTypeStrs = None self.CTypedefResolveDepth = 0 self.CTypedefVisiting = None + # builtins 源扫描标记随表清空复位:每文件最多读一次 includes/builtins.vp + # (_lookup_builtins_typedef 的"最后匹配"兜底,见 TypeGenResolve) + self.BuiltinsTypedefScanned = 0 + self.LastBuiltinsTypedefName = None + self.LastBuiltinsTypedefResult = None + # 别名缓冲 stdlib.malloc 分配,跨 pool 生命周期;此处释放防泄漏 + if self.ImportAliasBuf is not None: + stdlib.free(self.ImportAliasBuf) + self.ImportAliasBuf = None def _ctypedef_push_visiting(self, pool: memhub.MemBuddy | t.CPtr, name: str) -> int: """将 CTypedef 名加入正在解析列表(循环检测) @@ -254,21 +268,25 @@ class TypeGenCType(TypeGenBase.TypeGenBase): # 提取 "=" 后的类型表达式(trim 空格) eq_ptr: str = string.strstr(rest, "=") if eq_ptr is not None: - ty_start: str = eq_ptr + 1 - while ty_start[0] == ' ': - ty_start += 1 - ty_end: t.CSizeT = string.strlen(ty_start) - while ty_end > 0 and (ty_start[ty_end - 1] == ' ' - or ty_start[ty_end - 1] == '\r'): - ty_end -= 1 - if ty_end > 0: + # '=' 相对行首偏移;值段严格限定在行尾 + # (line_len)内——防止跨行吞换行+后续行内容 + eq_off: t.CSizeT = t.CSizeT(t.CUInt64T(eq_ptr) - t.CUInt64T(src)) - line_start + vs: t.CSizeT = eq_off + 1 + while vs < line_len and src[line_start + vs] == ' ': + vs += 1 + ve: t.CSizeT = line_len + while ve > vs and (src[line_start + ve - 1] == ' ' + or src[line_start + ve - 1] == '\r'): + ve -= 1 + if ve > vs: + vlen: t.CSizeT = ve - vs name_buf: str = pool.alloc(name_len + 1) - ty_buf: str = pool.alloc(ty_end + 1) + ty_buf: str = pool.alloc(vlen + 1) if name_buf is not None and ty_buf is not None: string.strncpy(name_buf, src + line_start, name_len) name_buf[name_len] = '\0' - string.strncpy(ty_buf, ty_start, ty_end) - ty_buf[ty_end] = '\0' + string.strncpy(ty_buf, src + line_start + vs, vlen) + ty_buf[vlen] = '\0' self.register_ctypedef(pool, name_buf, ty_buf) count += 1 line_start = i + 1 diff --git a/App/lib/core/Gen/Type/TypeGenErr.vp b/App/lib/core/Gen/Type/TypeGenErr.vp index faa5d67..250affe 100644 --- a/App/lib/core/Gen/Type/TypeGenErr.vp +++ b/App/lib/core/Gen/Type/TypeGenErr.vp @@ -335,6 +335,44 @@ class TypeGenErr(TypeGenBase.TypeGenBase): if new_bits > cur_bits: ret_ty = expr_ty return ret_ty + def body_has_value_return(self, pool: memhub.MemBuddy | t.CPtr, + body: list[ast.AST | t.CPtr] | t.CPtr) -> int: + """扫描函数体,判断是否存在返回非 None 值的 return + + 递归检查控制流语句(if/while/for/try/with/match)内层 return,但跳过 + 嵌套函数/类定义(FunctionDef/ClassDef 由自身翻译时独立检查,避免外层 + 检查误伤嵌套作用域)。裸 return / return None(CONST_NONE)/ 无 return + 均不算返回非 None 值。 + 供无返回类型注解函数的 fail-fast 检查使用:返回具体值必须有显式 -> + 注解,编译器不替用户推断返回类型(推断位宽会掩盖返回契约)。 + """ + if body is None: + return 0 + bn: t.CSizeT = len(body) + for bi in range(bn): + stmt: ast.AST | t.CPtr = body.get(bi) + if stmt is None: + continue + k: int = stmt.kind() + if k == ast.ASTKind.Return: + rt: ast.Return | t.CPtr = (ast.Return | t.CPtr)(stmt) + if rt.value is not None: + # return None(CONST_NONE)不算返回非 None 值 + if rt.value.kind() == ast.ASTKind.Constant: + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rt.value) + if cn.const_kind == ast.CONST_NONE: + continue + return 1 + elif k == ast.ASTKind.FunctionDef or k == ast.ASTKind.ClassDef: + continue + elif k == ast.ASTKind.If or k == ast.ASTKind.While or \ + k == ast.ASTKind.For or k == ast.ASTKind.Try or \ + k == ast.ASTKind.With or k == ast.ASTKind.Match: + sub: list[ast.AST | t.CPtr] | t.CPtr = stmt.children + if sub is not None and len(sub) > 0: + if self.body_has_value_return(pool, sub) != 0: + return 1 + return 0 def build_param_types_str(self, pool: memhub.MemBuddy | t.CPtr, args_node: ast.AST | t.CPtr) -> str: """构建参数类型编码字符串 "name:bits,name:bits,..." """ diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 6c921e5..385c32d 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -9,15 +9,18 @@ import llvmlite import memhub import string import viperlib +import stdio import stdlib import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT import lib.Stub.StubMerger as StubMerger import lib.Stub.StubMerger.StubMergerStore as StubMergerStore import lib.core.CompilerFactory as CompilerFactory +import lib.Projectrans.Config as Config -import lib.core.Gen.Type.TypeGenBase as TypeGenBase +import lib.core.Gen.Type.TypeGenBase as TypeGenBase +import w32.fileio as fileio # ============================================================ # TypeGenResolve - TypeGenMixin 拆分(阶段 D):类型映射/注解解析(map_t_type/resolve_annotation_type 等) @@ -331,13 +334,13 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): elif inner_buf_g[ci_g] == ',' and cur_dep_g == 0: arg_cnt_g += 1 ci_g += 1 - # [FIX] 第二参数是 elem_size(元素字节大小),不是参数个数! - # str 是 i8*(8 字节)。此前误传 arg_cnt_g(参数个数,如 Mat4 - # 的 2)→ __elem_size__=2 → append 只写指针低 2 字节(高 48 位 - # 残留)→ get 读回垃圾指针(低 16 位=pool 偏移、高 48 位=相邻 - # 数据)→ _norm_type_arg_name 递归 strlen 崩(ViperOS Mat4.m - # t.CArray[t.CArray[float, 4], 4] 崩溃根因:strlen 参数三次 - # 均为低 16 位 0x2850、高 48 位漂移)。 + # [FIX] 第二参数是 elem_size(元素字节大小),不是参数个数! + # str 是 i8*(8 字节)。此前误传 arg_cnt_g(参数个数,如 Mat4 + # 的 2)→ __elem_size__=2 → append 只写指针低 2 字节(高 48 位 + # 残留)→ get 读回垃圾指针(低 16 位=pool 偏移、高 48 位=相邻 + # 数据)→ _norm_type_arg_name 递归 strlen 崩(ViperOS Mat4.m + # t.CArray[t.CArray[float, 4], 4] 崩溃根因:strlen 参数三次 + # 均为低 16 位 0x2850、高 48 位漂移)。 arg_list_g: list[str] | t.CPtr = list[str](pool, 8) if arg_list_g is not None: arg_start_g: t.CSizeT = 0 @@ -630,6 +633,21 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): self.Gen.TypeGenCType._ctypedef_pop_visiting() if ity_rt is not None: return ity_rt + # [builtins 最后匹配] 常规 typedef 表 + map_t_type 均 miss 后查 + # includes/builtins.vp(如 rowptr: t.CTypedef = t.CPtr),命中展开递归解析。 + # 与 resolve_annotation_type 的 Name/Subscript 兜底同一口径,保证 + # __sizeof__/REnum 等旁路查表(get_type_size_bytes 等)也能命中 builtins。 + btd_r: str = self._lookup_builtins_typedef(pool, name) + if btd_r is not None: + btd_node_r: ast.AST | t.CPtr = self._parse_type_string(pool, btd_r, ast.ASTCtx.Load) + if btd_node_r is not None: + if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH: + self.Gen.TypeGenCType.CTypedefResolveDepth += 1 + btd_ty_r: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, btd_node_r, imported_modules, from_imports, trans, 1) + self.Gen.TypeGenCType.CTypedefResolveDepth -= 1 + if btd_ty_r is not None: + return btd_ty_r return self.map_t_type(pool, name) def get_type_size_bytes(self, pool: memhub.MemBuddy | t.CPtr, name: str, @@ -649,6 +667,440 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if bits_sz <= 0: return 0 return bits_sz // 8 + def _build_lib_source_path(self, mod: str, sha1: str) -> str: + """定位库模块源文件路径(stdlib 分配,调用方负责 free) + + builtins 最后匹配需读库源扫描 typedef:优先 SHA1 映射 + (_build_source_path_from_sha1,内含存在性验证),miss 时回退直接拼 + {Config.IncludesDir}/{mod 的 '.'→'/'}.vp——覆盖 sha1 映射表缺失或 + incdir 未登记导致路径不可用的场景(stdint.size_t 展开根因排查)。 + """ + if mod is None: + return None + if sha1 is not None: + p_t: str = self.Gen.ExprGenCdef._build_source_path_from_sha1(sha1) + if p_t is not None: + return p_t + if Config.IncludesDir is None: + return None + m_len: t.CSizeT = string.strlen(mod) + p_len: t.CSizeT = m_len + 4 + full_len: t.CSizeT = string.strlen(Config.IncludesDir) + p_len + 1 + p_buf: str = stdlib.malloc(p_len) + if p_buf is None: + return None + mi: t.CSizeT + for mi in range(m_len): + if mod[mi] == '.': + p_buf[mi] = '/' + else: + p_buf[mi] = mod[mi] + p_buf[m_len] = '\0' + full_buf: str = stdlib.malloc(full_len) + if full_buf is None: + stdlib.free(p_buf) + return None + viperlib.snprintf(full_buf, full_len, "%s/%s.vp", Config.IncludesDir, p_buf) + stdlib.free(p_buf) + return full_buf + def _register_import_alias(self, alias: str, target: str) -> None: + """注册 builtins import 别名(import stdint as __stdint → __stdint:stdint) + + 用 stdlib.malloc 缓冲(非 pool list)——pool 每文件 reset_to 后 + list 对象/元素悬空,clear_ctypedefs 未覆盖的路径读到已释放内存 → + strcmp 崩溃(1.log ASan 0xffffffffffffffff 根因)。malloc 缓冲跨 + pool 生命周期存活,clear_ctypedefs 负责释放置空。 + 格式:连续 "alias\0target\0" 段。 + """ + if alias is None or target is None: + return + # 去重:别名已注册过则跳过 + if self._resolve_import_alias(alias) is not None: + return + if self.Gen.TypeGenCType.ImportAliasBuf is None: + self.Gen.TypeGenCType.ImportAliasBuf = stdlib.malloc(256) + if self.Gen.TypeGenCType.ImportAliasBuf is None: + return + self.Gen.TypeGenCType.ImportAliasBuf[0] = '\0' + buf: bytes = self.Gen.TypeGenCType.ImportAliasBuf + al_len: t.CSizeT = string.strlen(alias) + tg_len: t.CSizeT = string.strlen(target) + # 找缓冲末尾(跳过已有 "alias\0target\0" 对,遇首字节 '\0' 即结尾) + cur: t.CSizeT = 0 + while True: + if buf[cur] == '\0': + break + while buf[cur] != '\0': + cur += 1 + cur += 1 # 跳过 alias 段的 '\0' + while buf[cur] != '\0': + cur += 1 + cur += 1 # 跳过 target 段的 '\0' + if cur + al_len + tg_len + 2 > 256: + return + string.strcpy(buf + cur, alias) + cur += al_len + buf[cur] = '\0' + cur += 1 + string.strcpy(buf + cur, target) + buf[cur + tg_len] = '\0' + def _resolve_import_alias(self, mod: str) -> str: + """按 builtins import 别名还原真实模块名(__stdint → stdint) + + Returns: + 命中返回目标模块名(指向 ImportAliasBuf 内部,无需 free), + miss 返回 None。 + """ + if mod is None: + return None + buf: bytes = self.Gen.TypeGenCType.ImportAliasBuf + if buf is None: + return None + mod_len: t.CSizeT = string.strlen(mod) + cur: t.CSizeT = 0 + while True: + if buf[cur] == '\0': + break + al_start: t.CSizeT = cur + while buf[cur] != '\0': + cur += 1 + al_len: t.CSizeT = cur - al_start + cur += 1 + tg_start: t.CSizeT = cur + while buf[cur] != '\0': + cur += 1 + cur += 1 + if al_len == mod_len: + if string.strncmp(buf + al_start, mod, mod_len) == 0: + return buf + tg_start + return None + def _lookup_builtins_typedef(self, pool: memhub.MemBuddy | t.CPtr, + name: str) -> str: + """builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明 + + 所有常规解析路径 miss 后("没有匹配到前面模式的注解")的最终兜底: + builtins 始终纳入编译链(StubMerger 预加入),但其 typedef 不随当前 + 文件 from_imports 注入(builtins 不在 import 闭包),如 + `rowptr: t.CTypedef = t.CPtr`(main2.vp `k: rowptr[int] = &i` 根因)。 + 定位 builtins 源文件按 "NAME: t.CTypedef = TYPE" 逐行扫描,命中即 + 注册到本地 CTypedef 表并返回类型表达式;表内已注册直接返回。 + """ + if name is None: + return None + # 详细报错诊断:记录本次 builtins 最后匹配请求与结果(未命中 = None) + self.Gen.TypeGenCType.LastBuiltinsTypedefName = name + self.Gen.TypeGenCType.LastBuiltinsTypedefResult = None + # rowptr 已内置于编译器(TPC/TPV 双端语法保留字,不可重定义): + # 优先于一切外部声明(builtins.vp 已不再声明,用户自行 typedef 也会被 + # register_ctypedef 拒绝),始终返回内置类型表达式 t.CPtr。 + if string.strcmp(name, "rowptr") == 0: + self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr" + return "t.CPtr" + ts_b: str = self.Gen.TypeGenCType.lookup_ctypedef(name) + if ts_b is not None and self.Gen.TypeGenCType.CTypedefFound != 0: + # 表内值可能仍是模块限定引用(如 size_t → stdint.size_t),展开后返回 + b_hit_r: str = self._expand_typedef_ref(pool, ts_b) + self.Gen.TypeGenCType.LastBuiltinsTypedefResult = b_hit_r + return b_hit_r + # 本文件已扫过 builtins 源(表内无 = 非 builtins typedef):不再重复读文件 + if self.Gen.TypeGenCType.BuiltinsTypedefScanned != 0: + return None + # builtins 模块 SHA1 → 源文件路径(SHA1 存储器查不到时 _build_lib_source_path + # 回退 {IncludesDir}/builtins.vp;仍找不到则静默返回 None——builtins 缺失 + # 属上游配置问题,不在此 fail-fast) + b_sha1: str = StubMergerStore.LookupSha1ByModName("builtins") + b_path: str = self._build_lib_source_path("builtins", b_sha1) + if b_path is None: + return None + BUF_B: t.CSizeT = 65536 + b_buf: bytes = stdlib.malloc(BUF_B) + if b_buf is None: + stdlib.free(b_path) + return None + bf: fileio.File | t.CPtr = fileio.File(b_path, fileio.MODE.R) + stdlib.free(b_path) + if bf.closed: + stdlib.free(b_buf) + return None + b_read: LONG = bf.read_all(b_buf, ULONG(BUF_B)) + bf.close() + if b_read <= 0: + stdlib.free(b_buf) + return None + if b_read < LONG(BUF_B): + b_buf[t.CSizeT(b_read)] = 0 + else: + b_buf[BUF_B - 1] = 0 + # 逐行扫描并注册 builtins 全部 "NAME: t.CTypedef = TYPE"(一次读文件 + # 注册全部,置 scanned 标记后同文件后续查表即中;对齐 + # register_ctypedef_from_source 的文本匹配策略)。已存在的表条目 + # 不覆盖——模块自身/import 注入的 typedef 优先于 builtins 默认。 + b_total: t.CSizeT = string.strlen(b_buf) + # 预扫描 import 行注册别名(import stdint as __stdint → __stdint:stdint), + # 供库 typedef 模块限定引用展开按别名还原真实模块名(__stdint.size_t → + # stdint.size_t)。imports 位于文件顶部,typedef 扫描时别名已就绪。 + b_imp_i: t.CSizeT = 0 + b_imp_ls: t.CSizeT = 0 + while b_imp_i <= b_total: + if b_imp_i == b_total or b_buf[b_imp_i] == '\n': + b_imp_len: t.CSizeT = b_imp_i - b_imp_ls + if b_imp_len > 0: + # 跳过行首空白 + b_imp_s: t.CSizeT = 0 + while b_imp_s < b_imp_len and b_buf[b_imp_ls + b_imp_s] == ' ': + b_imp_s += 1 + # "import" 前缀 + if b_imp_len - b_imp_s >= 6: + if b_buf[b_imp_ls + b_imp_s] == 'i' and b_buf[b_imp_ls + b_imp_s + 1] == 'm' \ + and b_buf[b_imp_ls + b_imp_s + 2] == 'p' and b_buf[b_imp_ls + b_imp_s + 3] == 'o' \ + and b_buf[b_imp_ls + b_imp_s + 4] == 'r' and b_buf[b_imp_ls + b_imp_s + 5] == 't': + # 找 " as " 分隔 + b_as_i: t.CSizeT = b_imp_s + 6 + while b_as_i < b_imp_len - 3: + if b_buf[b_imp_ls + b_as_i] == ' ' and b_buf[b_imp_ls + b_as_i + 1] == 'a' \ + and b_buf[b_imp_ls + b_as_i + 2] == 's' and b_buf[b_imp_ls + b_as_i + 3] == ' ': + break + b_as_i += 1 + if b_as_i < b_imp_len - 3: + # target = "import" 之后跳过空格到 " as " 前 + # (b_imp_s+6 是 "import" 后首个字符,可能是空格, + # 不跳过会带上前导空格 → " stdint" 查 SHA1 失败) + b_tgt_s: t.CSizeT = b_imp_s + 6 + while b_tgt_s < b_imp_len and b_buf[b_imp_ls + b_tgt_s] == ' ': + b_tgt_s += 1 + b_tgt_e: t.CSizeT = b_as_i + while b_tgt_e > b_tgt_s and b_buf[b_imp_ls + b_tgt_e - 1] == ' ': + b_tgt_e -= 1 + # alias = " as " 之后到行尾(trim 尾随空格/\r) + b_al_s: t.CSizeT = b_as_i + 4 + while b_al_s < b_imp_len and b_buf[b_imp_ls + b_al_s] == ' ': + b_al_s += 1 + b_al_e: t.CSizeT = b_imp_len + while b_al_e > b_al_s and (b_buf[b_imp_ls + b_al_e - 1] == ' ' + or b_buf[b_imp_ls + b_al_e - 1] == '\r'): + b_al_e -= 1 + if b_tgt_e > b_tgt_s and b_al_e > b_al_s: + b_tgt_buf: str = pool.alloc(b_tgt_e - b_tgt_s + 1) + b_al_buf: str = pool.alloc(b_al_e - b_al_s + 1) + if b_tgt_buf is not None and b_al_buf is not None: + string.strncpy(b_tgt_buf, b_buf + b_imp_ls + b_tgt_s, b_tgt_e - b_tgt_s) + b_tgt_buf[b_tgt_e - b_tgt_s] = '\0' + string.strncpy(b_al_buf, b_buf + b_imp_ls + b_al_s, b_al_e - b_al_s) + b_al_buf[b_al_e - b_al_s] = '\0' + self._register_import_alias(b_al_buf, b_tgt_buf) + b_imp_ls = b_imp_i + 1 + b_imp_i += 1 + b_i: t.CSizeT = 0 + b_line_start: t.CSizeT = 0 + b_res: str = None + while b_i <= b_total: + if b_i == b_total or b_buf[b_i] == '\n': + b_line_len: t.CSizeT = b_i - b_line_start + if b_line_len > 0: + # 找行内第一个 ':'(跳过注释行) + b_j: t.CSizeT = 0 + b_found_colon: int = 0 + while b_j < b_line_len: + b_ch: t.CChar = b_buf[b_line_start + b_j] + if b_ch == ':': + b_found_colon = 1 + break + if b_ch == '#' or b_ch == '\r': + break + b_j += 1 + if b_found_colon != 0: + # 左段 NAME(trim 尾随空格) + b_name_len: t.CSizeT = b_j + while b_name_len > 0 and b_buf[b_line_start + b_name_len - 1] == ' ': + b_name_len -= 1 + if b_name_len > 0 and b_name_len < 128: + # 右段必须含 "t.CTypedef",提取 "=" 后的类型表达式 + b_rest2: str = b_buf + b_line_start + b_j + 1 + if string.strstr(b_rest2, "t.CTypedef") is not None: + b_eq2: str = string.strstr(b_rest2, "=") + if b_eq2 is not None: + # '=' 相对行首偏移;值段严格限定在行尾 + # (b_line_len)内——此前 strlen 跨行吞掉 + # 换行+下一行注释(size_t: t.CTypedef = + # stdint.size_t 后跟 "# size_t: t.CTypedef = + # t.CSizeT" → 值含 "\n# ..." → 展开取到注释 + # 里的 '.' → 模块名垃圾,main2.vp size_t 根因) + b_eq_off: t.CSizeT = t.CSizeT(t.CUInt64T(b_eq2) - t.CUInt64T(b_buf)) - b_line_start + b_vs: t.CSizeT = b_eq_off + 1 + while b_vs < b_line_len and b_buf[b_line_start + b_vs] == ' ': + b_vs += 1 + b_ve: t.CSizeT = b_line_len + while b_ve > b_vs and (b_buf[b_line_start + b_ve - 1] == ' ' + or b_buf[b_line_start + b_ve - 1] == '\r'): + b_ve -= 1 + if b_ve > b_vs: + b_vlen: t.CSizeT = b_ve - b_vs + b_nb: str = pool.alloc(b_name_len + 1) + b_tb: str = pool.alloc(b_vlen + 1) + if b_nb is not None and b_tb is not None: + string.strncpy(b_nb, b_buf + b_line_start, b_name_len) + b_nb[b_name_len] = '\0' + string.strncpy(b_tb, b_buf + b_line_start + b_vs, b_vlen) + b_tb[b_vlen] = '\0' + # 表内已存在(模块自身/import 注入)不覆盖 + b_exist: str = self.Gen.TypeGenCType.lookup_ctypedef(b_nb) + if b_exist is None or self.Gen.TypeGenCType.CTypedefFound == 0: + # 急切展开模块限定引用再注册(size_t: + # t.CTypedef = stdint.size_t → 注册 t.CSizeT), + # 使表内直接持有最终类型表达式,下游查表即中 + b_tb_x: str = self._expand_typedef_ref(pool, b_tb) + if b_tb_x is not None: + self.Gen.TypeGenCType.register_ctypedef(pool, b_nb, b_tb_x) + if string.strcmp(b_nb, name) == 0: + b_res = b_tb + b_line_start = b_i + 1 + b_i += 1 + # 置扫描完成标记:同文件内后续 miss 直接查表,不再重复读 builtins 源 + self.Gen.TypeGenCType.BuiltinsTypedefScanned = 1 + stdlib.free(b_buf) + if b_res is not None: + b_final_r: str = self._expand_typedef_ref(pool, b_res) + self.Gen.TypeGenCType.LastBuiltinsTypedefResult = b_final_r + return b_final_r + return None + def _lookup_library_typedef(self, pool: memhub.MemBuddy | t.CPtr, + mod: str, attr: str) -> str: + """查库模块源文件中 "attr: t.CTypedef = TYPE" 并注册,返回 TYPE 或 None + + 仅用于 builtins 最后匹配的模块限定引用展开(如 + `size_t: t.CTypedef = stdint.size_t` 的 stdint.size_t 部分): + 直接读模块源扫描 typedef(权威定义),miss 不查表——表内可能是 + builtins 的间接引用(stdint.size_t → 自身)会自引用死循环。 + """ + if mod is None or attr is None: + return None + # builtins import 别名还原真实模块名(__stdint → stdint):builtins.vp + # 用 `import stdint as __stdint`,typedef 值 `__stdint.size_t` 等价 + # `stdint.size_t`;不还原则 SHA1 查找/路径定位全 miss。 + m_real: str = self._resolve_import_alias(mod) + if m_real is None: + m_real = mod + m_sha1: str = StubMergerStore.LookupSha1ByModName(m_real) + m_path: str = self._build_lib_source_path(m_real, m_sha1) + if m_path is None: + return None + BUF_L: t.CSizeT = 65536 + m_buf: bytes = stdlib.malloc(BUF_L) + if m_buf is None: + stdlib.free(m_path) + return None + mf: fileio.File | t.CPtr = fileio.File(m_path, fileio.MODE.R) + stdlib.free(m_path) + if mf.closed: + stdlib.free(m_buf) + return None + m_read: LONG = mf.read_all(m_buf, ULONG(BUF_L)) + mf.close() + if m_read <= 0: + stdlib.free(m_buf) + return None + if m_read < LONG(BUF_L): + m_buf[t.CSizeT(m_read)] = 0 + else: + m_buf[BUF_L - 1] = 0 + attr_len: t.CSizeT = string.strlen(attr) + m_total: t.CSizeT = string.strlen(m_buf) + m_i: t.CSizeT = 0 + m_ls: t.CSizeT = 0 + m_res: str = None + while m_i <= m_total and m_res is None: + if m_i == m_total or m_buf[m_i] == '\n': + m_line_len: t.CSizeT = m_i - m_ls + if m_line_len > 0: + m_j: t.CSizeT = 0 + m_fc: int = 0 + while m_j < m_line_len: + m_ch: t.CChar = m_buf[m_ls + m_j] + if m_ch == ':': + m_fc = 1 + break + if m_ch == '#' or m_ch == '\r': + break + m_j += 1 + if m_fc != 0: + m_nl: t.CSizeT = m_j + while m_nl > 0 and m_buf[m_ls + m_nl - 1] == ' ': + m_nl -= 1 + if m_nl == attr_len: + m_nm_ok: int = 1 + m_ki: t.CSizeT = 0 + while m_ki < attr_len: + if m_buf[m_ls + m_ki] != attr[m_ki]: + m_nm_ok = 0 + break + m_ki += 1 + if m_nm_ok == 1: + m_rest: str = m_buf + m_ls + m_j + 1 + if string.strstr(m_rest, "t.CTypedef") is not None: + m_eq: str = string.strstr(m_rest, "=") + if m_eq is not None: + # '=' 相对行首偏移;值段严格限定在行尾 + # (m_line_len)内,防止跨行吞换行+后续内容 + m_eq_off: t.CSizeT = t.CSizeT(t.CUInt64T(m_eq) - t.CUInt64T(m_buf)) - m_ls + m_vs: t.CSizeT = m_eq_off + 1 + while m_vs < m_line_len and m_buf[m_ls + m_vs] == ' ': + m_vs += 1 + m_ve: t.CSizeT = m_line_len + while m_ve > m_vs and (m_buf[m_ls + m_ve - 1] == ' ' + or m_buf[m_ls + m_ve - 1] == '\r'): + m_ve -= 1 + if m_ve > m_vs: + m_vlen: t.CSizeT = m_ve - m_vs + m_tb: str = pool.alloc(m_vlen + 1) + if m_tb is not None: + string.strncpy(m_tb, m_buf + m_ls + m_vs, m_vlen) + m_tb[m_vlen] = '\0' + self.Gen.TypeGenCType.register_ctypedef(pool, attr, m_tb) + m_res = m_tb + m_ls = m_i + 1 + m_i += 1 + stdlib.free(m_buf) + return m_res + def _expand_typedef_ref(self, pool: memhub.MemBuddy | t.CPtr, + type_str: str, depth: int = 0) -> str: + """展开类型表达式中的模块限定 typedef 引用(stdint.size_t → t.CSizeT) + + builtins 最后匹配的 typedef 值可能引用库模块 typedef(size_t: + t.CTypedef = stdint.size_t)。当前文件未 import stdint 时 Attribute + 分支的 is_module_imported 检查会拦截,故在此把 "mod.attr" 直接展开为 + attr 在 mod 源中的底层类型表达式。含 '[' / '|' 的复合表达式原样返回 + (由常规解析路径处理);t/c 内置命名空间原样返回;递归限深防环。 + """ + if type_str is None or depth > 16: + return type_str + # 复合表达式(联合/泛型)不在此展开 + if string.strchr(type_str, '[') is not None \ + or string.strchr(type_str, '|') is not None: + return type_str + # 拆分 "mod.attr"(取最后一个 '.',支持 w32.win32base.HANDLE) + dot_pos: str = string.strrchr(type_str, '.') + if dot_pos is None: + return type_str + mod_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_pos) - t.CUInt64T(type_str)) + attr: str = dot_pos + 1 + if mod_len == 0 or attr is None or attr[0] == '\0': + return type_str + if mod_len == 1 and (type_str[0] == 't' or type_str[0] == 'c'): + return type_str + mod_buf: str = pool.alloc(mod_len + 1) + if mod_buf is None: + return type_str + string.strncpy(mod_buf, type_str, mod_len) + mod_buf[mod_len] = '\0' + # 库模块权威 typedef(源扫描 + 注册);miss 回退表内 attr(闭包注入) + lib_ty: str = self._lookup_library_typedef(pool, mod_buf, attr) + if lib_ty is not None: + return self._expand_typedef_ref(pool, lib_ty, depth + 1) + ts_x: str = self.Gen.TypeGenCType.lookup_ctypedef(attr) + if ts_x is not None and self.Gen.TypeGenCType.CTypedefFound != 0 \ + and string.strcmp(ts_x, type_str) != 0: + return self._expand_typedef_ref(pool, ts_x, depth + 1) + return type_str def map_t_type(self, pool: memhub.MemBuddy | t.CPtr, type_name: str) -> llvmlite.LLVMType | t.CPtr: """将 t.CXxx / c.CXxx 类型名映射到 LLVMType @@ -708,6 +1160,13 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): return llvmlite.Ptr(pool, llvmlite.Int8(pool)) elif string.strcmp(type_name, "CVoid") == 0: return llvmlite.Void(pool) + # CDefine: 编译期常量定义标记(X: t.CDefine = 64),底层为 32 位有符号整数 + # (对齐 TPC t.CDefine Size=32 IsSigned=True;超大值由 DefineValue 路径升 i64)。 + # 用作函数返回注解时表示宏函数(def MAKE_FLAG(bit) -> t.CDefine: return 1 << bit), + # 返回类型即 i32。全局/字段的 CDefine 注解已由 is_cdefine_annotation 先行短路 + # (不创建 LLVM 全局/字段,仅注册编译期常量),不会走到此处。 + elif string.strcmp(type_name, "CDefine") == 0: + return llvmlite.Int32(pool) # CNeedPtr/CAutoPtr: 参数自动取地址标记,LLVM 层均为通用指针 i8*。 # (TPC 中二者继承 CPtr;TPV 未实现调用点自动取地址,非指针实参应在调用点报错, # 而非静默降级为 i32 导致指针截断。) @@ -750,7 +1209,11 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): # 此处不再硬编码(对齐 INTPTR/UINTPTR 已删范例 L804-807),消除双事实源; # map_t_type 仅保留 t/c 库核心原语 + 内建小写 float/double。 # ============================================================ - # 小写裸名浮点类型(float = t.CFloat = f32, double = t.CDouble = f64) + # 内建小写裸名类型(float = t.CFloat = f32, double = t.CDouble = f64, + # char = t.CChar = i8;int/bool 由 resolve_annotation_type Name 分支直通, + # 小写 stdint 别名由 stdint.vp CTypedef 声明提供) + elif string.strcmp(type_name, "char") == 0: + return llvmlite.Int8(pool) elif string.strcmp(type_name, "float") == 0: return llvmlite.Float(pool) elif string.strcmp(type_name, "double") == 0: @@ -932,6 +1395,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if string.strchr(cn.str_val, 46) is None and string.strchr(cn.str_val, 124) is None: if string.strcmp(cn.str_val, "str") == 0: return None + if string.strcmp(cn.str_val, "char") == 0: + return None if string.strcmp(cn.str_val, "int") == 0: return None if string.strcmp(cn.str_val, "bytes") == 0: @@ -960,6 +1425,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): # 跳过内置类型名 if string.strcmp(resolved_id, "str") == 0: return None + if string.strcmp(resolved_id, "char") == 0: + return None if string.strcmp(resolved_id, "bytes") == 0: return None if string.strcmp(resolved_id, "bool") == 0: @@ -1326,6 +1793,9 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): return llvmlite.Void(pool) elif string.strcmp(nm.id, "int") == 0: return llvmlite.Int32(pool) + elif string.strcmp(nm.id, "char") == 0: + # char = t.CChar = i8(与 map_t_type 中 CChar/char 映射一致) + return llvmlite.Int8(pool) elif string.strcmp(nm.id, "bytes") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) elif string.strcmp(nm.id, "bool") == 0: @@ -1420,6 +1890,17 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): pool, orig_node_a, imported_modules, from_imports, trans, depth + 1) if alias_ty is not None: return alias_ty + # [builtins 最后匹配] 常规路径全部 miss 后的最终兜底:查 + # includes/builtins.vp 的 CTypedef 声明(如 rowptr: t.CTypedef = + # t.CPtr)。命中展开类型表达式后递归解析,仍失败才由调用方报错。 + btd_n: str = self._lookup_builtins_typedef(pool, nm.id) + if btd_n is not None: + btd_node_n: ast.AST | t.CPtr = self._parse_type_string(pool, btd_n, nm.ctx) + if btd_node_n is not None: + btd_ty_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, btd_node_n, imported_modules, from_imports, trans, depth + 1) + if btd_ty_n is not None: + return btd_ty_n return None # Attribute 节点: t.CInt, t.CUInt64T, namespace_defs.PlainStruct 等 @@ -1455,7 +1936,24 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if sha1_attr == 0: mod_imp: int = self.Gen.ImportsGen.is_module_imported(imported_modules, val_name.id) if mod_imp == 0: - return None + # [builtins 最后匹配] 库模块限定 typedef 豁免:builtins + # 声明的 size_t: t.CTypedef = stdint.size_t 无需当前文件 + # 显式 import stdint 也能解析(main2.vp 根因)。权威判定: + # 模块在 Sha1Store 已登记(编译链可达)且 attr 在该模块 + # 源中确有 t.CTypedef 声明(_lookup_library_typedef 读源 + # 确认并注册到表)。仅豁免"库模块 typedef":结构体/类名 + # 仍保持严格导入检查(命名空间隔离)。 + td_known: int = 0 + # 模块名先按 builtins import 别名还原(__stdint → stdint) + v_real: str = self._resolve_import_alias(val_name.id) + if v_real is None: + v_real = val_name.id + if StubMergerStore.LookupSha1ByModName(v_real) is not None: + lib_td_v: str = self._lookup_library_typedef(pool, val_name.id, at.attr) + if lib_td_v is not None: + td_known = 1 + if td_known == 0: + return None # [阶段1b] 库 typedef 动态表优先(正常库分析):attr 是库声明 # 的 typedef 名(如 stdint.INT/BYTE)且表已注册 → 递归展开走库 # 定义(单一真源);miss 再 map_t_type(t/c 库内部特判,如 t.CInt)。 @@ -1654,6 +2152,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): other_nm_b: ast.Name | t.CPtr = (ast.Name | t.CPtr)(other_node) if other_nm_b.id is not None: if string.strcmp(other_nm_b.id, "int") == 0 or \ + string.strcmp(other_nm_b.id, "char") == 0 or \ string.strcmp(other_nm_b.id, "str") == 0 or \ string.strcmp(other_nm_b.id, "bytes") == 0 or \ string.strcmp(other_nm_b.id, "bool") == 0 or \ @@ -1763,7 +2262,7 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): # 字段类型退化 Ptr(i8) → __before_init__ 默认值 List 字面量误走 # 动态 list 构造(list[str].__new__/append 符号未生成 → 链接 # undefined reference,Termination4K3 链接失败根因)。 - if string.strcmp(gen_nm.id, "CArray") == 0: + if string.strcmp(gen_nm.id, "CArray") == 0 or string.strcmp(gen_nm.id, "rawarray") == 0: # 单参数: CArray[elem_ty] → Ptr(elem_ty)(静态推导长度数组) if sub.slice.kind() != ast.ASTKind.Tuple: elem_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( @@ -1793,8 +2292,37 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if self.Gen.TypeGenCType.ConstEvalOk == 0 or count_n <= 0: return None return llvmlite.Array(pool, elem_ty_n, count_n) - return self._resolve_generic_subscript_ty(pool, sub, gen_nm.id, trans, - imported_modules, from_imports, depth + 1) + gen_ty_nm: llvmlite.LLVMType | t.CPtr = self._resolve_generic_subscript_ty( + pool, sub, gen_nm.id, trans, + imported_modules, from_imports, depth + 1) + if gen_ty_nm is not None: + return gen_ty_nm + # 泛型特化 miss 时不能 return None 直接退出——否则下面的 + # [builtins 最后匹配] 兜底永远不可达(rowptr[int] 根因)。 + # 下方 builtins 兜底若也未命中,仍按旧行为返回 None(多遍 + # 收敛/上层报错),不落到"仅支持 t.CArray/t.Callable"fatal。 + if sub.value.kind() == ast.ASTKind.Name: + gen_nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value) + if gen_nm_fb.id is not None: + fb_td: str = self._lookup_builtins_typedef(pool, gen_nm_fb.id) + if fb_td is not None: + fb_slice: str = self._get_type_name_from_annotation(pool, sub.slice) + if fb_slice is not None: + fb_total: t.CSizeT = string.strlen(fb_td) + 1 + string.strlen(fb_slice) + 2 + fb_str: str = pool.alloc(fb_total) + if fb_str is not None: + fb_str[0] = '\0' + string.strcat(fb_str, fb_td) + string.strcat(fb_str, "[") + string.strcat(fb_str, fb_slice) + string.strcat(fb_str, "]") + fb_node: ast.AST | t.CPtr = self._parse_type_string(pool, fb_str, ast.ASTCtx.Load) + if fb_node is not None: + fb_ty: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, fb_node, imported_modules, from_imports, trans, depth + 1) + if fb_ty is not None: + return fb_ty + return None # 检查 value 是否是 Attribute(t.CArray / t.Callable / 模块限定泛型类如 numpy.ndarray[T]) if sub.value.kind() != ast.ASTKind.Attribute: self.Gen.TypeGenErr.fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)") diff --git a/App/lib/core/IncludesScanner.vp b/App/lib/core/IncludesScanner.vp index 354e057..758ae10 100644 --- a/App/lib/core/IncludesScanner.vp +++ b/App/lib/core/IncludesScanner.vp @@ -218,10 +218,13 @@ def scan_directory_recursive(pool: memhub.MemBuddy | t.CPtr, viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir) # 使用 FindFirstFileA 开始搜索 - find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(win32file.WIN32_FIND_DATAA.__sizeof__()) + # find_data 缓冲 +16 余量:TPV 的 WIN32_FIND_DATAA.__sizeof__() 可能小于 + # Windows 实际写入(对齐差异),越界写会破坏相邻堆块(对齐 Phase2 的 +16)。 + fd_size_s: t.CSizeT = win32file.WIN32_FIND_DATAA.__sizeof__() + find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(fd_size_s + 16) if find_data is None: return 1 - string.memset(find_data, 0, win32file.WIN32_FIND_DATAA.__sizeof__()) + string.memset(find_data, 0, fd_size_s + 16) handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) if handle == win32base.INVALID_HANDLE_VALUE: @@ -366,3 +369,176 @@ def find_entry_by_module(result: ScanResult | t.CPtr, if string.strcmp(entry.ModuleName, module_name) == 0: return entry return None + + +# ============================================================ +# _PathExistsA - 路径存在性检查(GetFileAttributesA) +# ============================================================ +def _PathExistsA(path: str) -> int: + """路径是否存在(1=存在,0=不存在/参数错误) + + 用 GetFileAttributesA(无输出缓冲)替代 FindFirstFileA:后者写 + WIN32_FIND_DATAA 结构,而 TPV 该结构 sizeof 小于 Windows 实际写入 + (约 592 字节,差距 >270 字节),会越界写坏相邻堆块(BFS 输出槽 + 0x2e 崩溃根因)。GetFileAttributesA 只返回属性值,无越界风险。 + """ + if path is None: + return 0 + attr: ULONG = w32.win32file.GetFileAttributesA(path) + if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES + return 0 + return 1 + + +# ============================================================ +# _TryResolveModuleFile - 尝试单个候选路径并写输出 +# +# Args: +# inc_dir: includes 根目录 +# rel_seg: 模块名转出的相对路径段(ast/parser) +# rel_len: rel_seg 长度 +# suffix: 候选后缀(".vp" 或 "/__init__.vp") +# out_abs_path/out_rel_path/out_inc_dir: 输出槽(t.CPtr 指针) +# +# Returns: +# 1 命中(已写输出),0 未命中 +# ============================================================ +def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: t.CSizeT, + suffix: str, + out_abs_path: t.CPtr, out_rel_path: t.CPtr, + out_inc_dir: t.CPtr) -> int: + if inc_dir is None or rel_seg is None or suffix is None: + return 0 + inc_len: t.CSizeT = string.strlen(inc_dir) + suf_len: t.CSizeT = string.strlen(suffix) + # 候选绝对路径: {inc}/{rel}{suffix}(手写拼接,避免 %. *s 精度修饰符) + cand_len: t.CSizeT = inc_len + 1 + rel_len + suf_len + cand: bytes = stdlib.malloc(cand_len + 1) + if cand is None: + return 0 + string.strcpy(cand, inc_dir) + cand[inc_len] = '/' + string.strncpy(cand + inc_len + 1, rel_seg, rel_len) + string.strcpy(cand + inc_len + 1 + rel_len, suffix) + if _PathExistsA(cand) == 0: + stdlib.free(cand) + return 0 + # 相对 includes 根目录的路径: {rel}{suffix} + rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1) + if rel_path is None: + stdlib.free(cand) + return 0 + string.strncpy(rel_path, rel_seg, rel_len) + rel_path[rel_len] = '\0' + string.strcat(rel_path, suffix) + # 所属 includes 根目录深拷贝:先全部分配就绪再统一写槽, + # 避免 inc 分配失败时 abs/rel 已转移所有权造成的半写状态 + inc_buf: bytes = None + if out_inc_dir is not None: + inc_buf = stdlib.malloc(inc_len + 1) + if inc_buf is None: + stdlib.free(cand) + stdlib.free(rel_path) + return 0 + string.strcpy(inc_buf, inc_dir) + # 写输出槽(stdlib 深拷贝,调用方负责 free) + # 槽联合必须用 (t.CSizeT | t.CPtr):t.CPtr|t.CPtr 会被折叠成 t.CPtr, + # [0] 变成 1 字节访问 → 指针被截断(0x2e 崩溃根因)。CSizeT 成员为 + # 8 字节,写入用 t.CSizeT(ptr) 直接转换;读取用 t.CVoid(u64, t.CPtr) + # 转回指针(t.CVoid 仅配合 t.CPtr 表示 void* 强转)。 + if out_abs_path is not None: + abs_slot: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(out_abs_path, t.CPtr)) + abs_slot[0] = t.CSizeT(cand) + else: + stdlib.free(cand) + if out_rel_path is not None: + rel_slot: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(out_rel_path, t.CPtr)) + rel_slot[0] = t.CSizeT(rel_path) + else: + stdlib.free(rel_path) + if out_inc_dir is not None and inc_buf is not None: + inc_slot: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(out_inc_dir, t.CPtr)) + inc_slot[0] = t.CSizeT(inc_buf) + return 1 + + +# ============================================================ +# ResolveModuleToPath - 模块名→文件路径(按文件系统按需解析) +# +# 依赖图按需扫描:BFS 遇到模块名(如 ast.parser)时,直接在 +# includes 目录下解析对应文件路径,不做全量目录遍历(未引用的 +# includes 根本不扫描)。相对导入(from . import X)已在解析阶段 +# resolve 成绝对模块名后再调用本函数。 +# +# 解析规则(多 includes 目录逐个尝试,模块优先于包): +# {inc_dir}/{mod.replace('.','/')}.vp (文件模块) +# {inc_dir}/{mod.replace('.','/')}/__init__.vp (包模块) +# +# Args: +# includes_dirs: 空格分隔的多个 includes 根目录 +# mod_name: 绝对模块名 +# out_abs_path: 输出绝对路径(stdlib.malloc,调用方负责 free) +# out_rel_path: 输出相对 includes 根目录的路径(同上,如 ast/parser.vp) +# out_inc_dir: 输出所属 includes 根目录(同上) +# +# Returns: +# 0 找到;1 未找到/参数错误 +# ============================================================ +def ResolveModuleToPath(includes_dirs: str, mod_name: str, + out_abs_path: t.CPtr, out_rel_path: t.CPtr, + out_inc_dir: t.CPtr) -> int: + if includes_dirs is None or mod_name is None: + return 1 + mn_len: t.CSizeT = string.strlen(mod_name) + if mn_len == 0: + return 1 + # mod_name → 相对路径段(. → /):ast.parser → ast/parser + rel_seg: bytes = stdlib.malloc(mn_len + 1) + if rel_seg is None: + return 1 + rel_len: t.CSizeT = 0 + for mi in range(mn_len): + ch_m: t.CChar = mod_name[mi] + if ch_m == '.': + rel_seg[rel_len] = '/' + else: + rel_seg[rel_len] = ch_m + rel_len += 1 + rel_seg[rel_len] = '\0' + + found: int = 0 + d_total: t.CSizeT = string.strlen(includes_dirs) + d_i: t.CSizeT = 0 + while d_i < d_total: + while d_i < d_total and includes_dirs[d_i] == ' ': + d_i += 1 + if d_i >= d_total: + break + d_start: t.CSizeT = d_i + while d_i < d_total and includes_dirs[d_i] != ' ': + d_i += 1 + d_len: t.CSizeT = d_i - d_start + if d_len == 0: + continue + inc_dir_buf: bytes = stdlib.malloc(d_len + 1) + if inc_dir_buf is None: + continue + string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len) + inc_dir_buf[d_len] = '\0' + # 候选 1: {inc}/{rel}.vp(文件模块) + if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp", + out_abs_path, out_rel_path, out_inc_dir) != 0: + found = 1 + stdlib.free(inc_dir_buf) + break + # 候选 2: {inc}/{rel}/__init__.vp(包模块) + if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp", + out_abs_path, out_rel_path, out_inc_dir) != 0: + found = 1 + stdlib.free(inc_dir_buf) + if found != 0: + break + stdlib.free(rel_seg) + if found != 0: + return 0 + return 1 diff --git a/App/main.vp b/App/main.vp index 2041ab9..37dab71 100644 --- a/App/main.vp +++ b/App/main.vp @@ -72,12 +72,13 @@ def main() -> int: stdio.printf("FAIL: malloc for perm_pool arena failed\n") return 1 - mb: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(arena, POOL_SIZE, detect_flag) + mb: rowptr[memhub.MemBuddy] = memhub.MemBuddy(arena, POOL_SIZE, detect_flag) if mb is None: stdio.printf("FAIL: MemBuddy init failed\n") return 1 - perm_pool: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(perm_arena, PERM_POOL_SIZE, detect_flag) + perm_pool: rowptr[memhub.MemBuddy] = memhub.MemBuddy(perm_arena, PERM_POOL_SIZE, detect_flag) + if perm_pool is None: stdio.printf("FAIL: perm_pool init failed\n") return 1 @@ -103,7 +104,7 @@ def main() -> int: lib._mbuddy = mb # P2: 创建项目级 Generator 单例(Phase1/Phase2/BuildPipeline 全程复用; - gen: GenGenerator.Generator | t.CPtr = mb.alloc(GenGenerator.Generator.__sizeof__()) + gen: rowptr[GenGenerator.Generator] = mb.alloc(GenGenerator.Generator.__sizeof__()) if gen is None: stdio.printf("FAIL: Generator alloc failed\n") return 1 @@ -495,7 +496,7 @@ Target: %s cc_flags: str = "-filetype=obj -relocation-model=pic" linker_cmd: str = Config.LinkerCmd if Config.LinkerCmd is not None else "clang++" linker_flags: str = "-lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -lgcc -Wl,--allow-multiple-definition" - linker_static_libs: str = Config.LinkerStaticLibs + linker_static_libs: str = Config.LinkerStaticLibs linker_dynamic_libs: str = Config.LinkerDynamicLibs # --project 模式且未显式指定 --src 时:使用 config 中的 linker_output(如 TransPyV.exe) # 显式指定 --src 时:使用从文件名推导的 output_name(如 test.exe),避免覆盖正在运行的 exe