From 47489154726c8b359d8bc17b8c5c9b74eadedf50 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 29 Aug 2026 05:46:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpv):=20=E5=A4=96=E9=83=A8=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E8=BF=94=E5=9B=9E=E7=B1=BB=E5=9E=8B=E4=B8=8D=E5=8F=AF?= =?UTF-8?q?=E6=94=B6=E6=95=9B=E6=97=B6=20fail-fast=EF=BC=8C=E8=B1=81?= =?UTF-8?q?=E5=85=8D=20from-import=20=E4=B8=8E=E5=A4=9A=E5=B1=82=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E9=99=90=E5=AE=9A=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _infer_external_func_ret_ty 白名单未命中改为 fatal_error(Python 风格 traceback),移除默认 i32 的 WARN 输出 - 裸名调用 fail-fast 前查 from_imports 可解析性:from-import 函数走 mangled 精确解析收敛(SSAValue 场景),纯外部 C 函数才 fail-fast - mod_ret_sha1 解析支持多层模块路径(w32.win32file.CreatePipe 场景), 避免嵌套模块限定调用被误判为裸名外部函数 - 移除 FuncGenBase/StructGenMixin 遗留调试输出 --- App/lib/core/Gen/Call/CallGenLib.vp | 35 ++++++++++------ App/lib/core/Gen/Call/CallGenMixin.vp | 57 +++++++++++++++++++-------- App/lib/core/Gen/Func/FuncGenBase.vp | 2 +- App/lib/core/Gen/StructGenMixin.vp | 6 --- App/lib/core/VLogger.vp | 6 +-- App/main.vp | 2 +- 6 files changed, 68 insertions(+), 40 deletions(-) diff --git a/App/lib/core/Gen/Call/CallGenLib.vp b/App/lib/core/Gen/Call/CallGenLib.vp index bb4e24c..089da70 100644 --- a/App/lib/core/Gen/Call/CallGenLib.vp +++ b/App/lib/core/Gen/Call/CallGenLib.vp @@ -4,7 +4,6 @@ import ast import llvmlite import memhub import string -import lib.core.VLogger as VLogger import lib.core.Gen.Translator as HT from . import CallGenBase @@ -111,13 +110,21 @@ class CallGenLib(CallGenBase.CallGenBase): llvmlite.add_param(pool, fn, i64_ty, "n") return fn def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy | t.CPtr, - func_name: str) -> llvmlite.LLVMType | t.CPtr: + func_name: str, + node: ast.AST | t.CPtr, + fatal_if_unknown: int) -> llvmlite.LLVMType | t.CPtr: """根据函数名推断外部 includes 函数的返回类型 支持三种函数名形式: 1. 裸名: "parse", "strlen" 2. 别名: "json_parse"(from X import parse as json_parse) 3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name) + + fatal_if_unknown: 白名单全部未命中时的行为。 + =1(纯裸名外部函数,无法收敛)→ fatal_error fail-fast(默认 i32 + 会截断指针/i64,静默生成导致运行时数据损坏,不兜底原则)。 + =0(Phase A 中间遍,_deps_pending 下遍重译可收敛)→ 返回默认 + i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。 """ if pool is None or func_name is None: return llvmlite.Int32(pool) @@ -296,16 +303,16 @@ class CallGenLib(CallGenBase.CallGenBase): string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) - # 默认 i32(整数):与本模块函数定义的默认返回类型一致(HandlesFunctions.py line 324) - # 已知返回指针的函数在上方显式列出(返回 i8*),已知返回 i64 的函数也显式列出 - # 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配 - # x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致 - # WARNING: 未登记的外部函数返回类型默认推断为 i32,若实际返回指针/i64 会被截断 - # --nowarn-register 可抑制此 WARN(自举编译中大量出现,刷屏影响日志收敛) - if VLogger._nowarn_register == 0: - VLogger.warning( - f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}", - "HandlesExprCall") + # 默认 i32:无登记、无模块归属、白名单全部未命中的外部函数。 + # fatal_if_unknown=1(纯裸名外部调用,无法收敛)→ fail-fast 报错: + # 默认 i32 会截断指针/i64,静默生成导致运行时数据损坏(不兜底原则)。 + # fatal_if_unknown=0(Phase A 中间遍,_deps_pending 下遍重译收敛)→ + # 保持默认 i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。 + if fatal_if_unknown != 0: + self.Gen.TypeGenErr.fatal_error( + node, + f"外部函数返回类型未登记且无法收敛推断(默认 i32 会截断指针/i64,需提供 stub 声明或加入白名单): {func_name}", + "TypeError") return llvmlite.Int32(pool) def _get_external_func_param_ty(self, pool: memhub.MemBuddy | t.CPtr, func_name: str, @@ -566,7 +573,9 @@ class CallGenLib(CallGenBase.CallGenBase): # 返回类型(使用访问器) call_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func) if call_ret_ty is None: - call_ret_ty = self._infer_external_func_ret_ty(pool, func_name) + # found_func 已有 declare 却读不到返回类型(访问器异常,罕见防御): + # 白名单未命中也不 fail-fast,保持默认 i32(有声明即有类型来源)。 + call_ret_ty = self._infer_external_func_ret_ty(pool, func_name, None, 0) # memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失) if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3: diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index a00d84b..8c8e4d7 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -729,21 +729,29 @@ class CallGenMixin(CallGenBase.CallGenBase): mod_ret_sha1: str = None if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: at_ret: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + # 解析调用模块路径:单层(stdlib.free,value 是 Name)与多层 + # (w32.win32file.CreatePipe,value 是嵌套 Attribute)都支持。 + # 缺失多层路径 → mod_ret_sha1=None → 误走裸名外部函数路径, + # fail-fast 误杀可收敛的模块限定调用(CreatePipe 根因)。 + mod_path_ret: str = None if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name: mod_ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_ret.value) - if mod_ret_nm.id is not None: - mod_ret_sha1 = self._lookup_module_sha1(mod_ret_nm.id) - if mod_ret_sha1 is not None: - # 函数内懒 import:VpiReader imports HandlesExprCall, - # 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致) - VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir) - ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot( - mod_ret_sha1, None, func_name) - if ret_annot_fc is not None: - vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str( - pool, ret_annot_fc, - llvmlite.Int32(pool), - llvmlite.Ptr(pool, llvmlite.Int8(pool))) + mod_path_ret = mod_ret_nm.id + elif at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Attribute: + mod_path_ret = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_ret.value) + if mod_path_ret is not None: + mod_ret_sha1 = self._lookup_module_sha1(mod_path_ret) + if mod_ret_sha1 is not None: + # 函数内懒 import:VpiReader imports HandlesExprCall, + # 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致) + VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir) + ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot( + mod_ret_sha1, None, func_name) + if ret_annot_fc is not None: + vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str( + pool, ret_annot_fc, + llvmlite.Int32(pool), + llvmlite.Ptr(pool, llvmlite.Int8(pool))) if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty elif mod_ret_sha1 is not None: @@ -766,9 +774,23 @@ class CallGenMixin(CallGenBase.CallGenBase): if trans is not None and trans._declare_only == 0: if self._dep_text_ll_exists(pool, mod_ret_sha1) == 0: trans._deps_pending = 1 - call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name) + # 已标记 _deps_pending,下遍重译会收敛(依赖 text.ll 就绪后 + # 走精确解析)→ fatal_if_unknown=0,本遍仅临时占位。 + call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 0) else: - call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name) + # 裸名调用(cl.func 是 Name,mod_ret_sha1=None)。两类来源: + # 1. from-import 的函数(如 SSAValue from .__values)→ 后续 + # L1193-1251 解析模块归属构造 mangled call_name,L1272 经 + # text.ll/vpi 精确解析返回类型(可收敛)→ fatal_if_unknown=0 + # 仅临时占位,不 fail-fast。 + # 2. 纯外部 C 函数(无登记、无模块归属)→ 白名单未命中无法收敛 + # → fail-fast(fatal_if_unknown=1)。 + bare_resolvable: int = 0 + if trans is not None and trans._from_imports is not None: + if self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0) is not None: + bare_resolvable = 1 + call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty( + pool, func_name, node, 1 - bare_resolvable) if found_func is None: fp_alloca: llvmlite.Value | t.CPtr = None @@ -1255,7 +1277,10 @@ class CallGenMixin(CallGenBase.CallGenBase): # 用 orig_func_name="parse" 重新推断,能正确匹配返回 i8*(JsonValue*) # 避免指针被截断为 i32 后 inttoptr 丢失高 32 位导致崩溃 if orig_func_name is not None and orig_func_name != func_name: - re_inferred_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name) + # 别名附加尝试:仅作为 mangled 精确解析前的临时推断(fatal_if_unknown=0)。 + # 若 call_name 是 mangled,L1272 起的 text.ll/vpi 精确解析会覆盖并 fail-fast; + # 未命中时保持白名单/默认 i32 由后续路径兜底。 + re_inferred_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name, node, 0) if re_inferred_ty is not None: call_ret_ty = re_inferred_ty # ============================================================ diff --git a/App/lib/core/Gen/Func/FuncGenBase.vp b/App/lib/core/Gen/Func/FuncGenBase.vp index 30fe991..676c1e8 100644 --- a/App/lib/core/Gen/Func/FuncGenBase.vp +++ b/App/lib/core/Gen/Func/FuncGenBase.vp @@ -50,7 +50,7 @@ class FuncGenBase(CallGenBase.CallGenBase): cur_file: str = self.Gen.TypeGenResolve.get_current_file() if cur_file is None: cur_file = "?" - stdio.printf("[ATTR] extract_func_attrs: file='%s' dlist_len=%d\n", cur_file, dn) + for di in range(dn): deco: ast.AST | t.CPtr = decorator_list.get(di) if deco is None: diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 0290fdc..c0c1ac1 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -401,12 +401,6 @@ class StructGenMixin: fe.DefaultVal = default_val struct_entry.FieldCount = idx + 1 - # [ADF-DIAG] 确认字段表实际注册内容(定位 Thread.arg 缺失/索引错位) - dbg_adf: str = fname_pool.alloc(192) - if dbg_adf is not None: - viperlib.snprintf(dbg_adf, 192, "[ADF] struct=%s field=%s idx=%d\n", - struct_entry.Name, field_name, idx) - stdio.printf("%s", dbg_adf) return idx def _deep_copy_default_ast(self, pool: memhub.MemBuddy | t.CPtr, diff --git a/App/lib/core/VLogger.vp b/App/lib/core/VLogger.vp index e9f2847..11d4ecb 100644 --- a/App/lib/core/VLogger.vp +++ b/App/lib/core/VLogger.vp @@ -208,9 +208,9 @@ def init_logger(): # ============================================================ # --nowarn-register 开关(追加):抑制 Phase A 依赖类 WARN 输出—— -# ①"外部函数返回类型未登记"(HandlesExprCall)②"Phase A 依赖未就绪, -# 下遍重试"(PhaseA-DBG)。自举编译中大量出现,刷屏影响日志收敛。 -# 仅抑制输出,不影响内部逻辑(默认 i32 推断、标记重试等照常执行)。 +# ①"跨模块函数依赖模块 text.ll 尚未生成"(HandlesExprCall,Phase A 多遍 +# 收敛的中间遍,下遍重译后精确解析)。自举编译中大量出现,刷屏影响日志收敛。 +# 仅抑制输出,不影响内部逻辑(标记重试等照常执行)。 # ============================================================ _nowarn_register: int = 0 diff --git a/App/main.vp b/App/main.vp index e782bf2..a77ac3a 100644 --- a/App/main.vp +++ b/App/main.vp @@ -236,7 +236,7 @@ Target: %s BuildPipeline._phase2_cmd_path = p2c log.info(f"--phase2command: 不执行 llc/链接,命令写入 {p2c}", "phase2cmd") - # --nowarn-register:抑制"外部函数返回类型未登记"类 WARN 输出 + # --nowarn-register:抑制 Phase A 依赖类 WARN 输出(跨模块 text.ll 未生成,下遍收敛) if args.get_bool("nowarn-register"): VLogger._nowarn_register = 1 log.info("--nowarn-register 已启用:抑制注册类 WARN 输出", "vlogger")