From 981af07d5cc5a94019dd6e19b17bf159da03311c Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 2 Oct 2026 01:03:31 +0800 Subject: [PATCH] =?UTF-8?q?fix(phase):=20App=20=E9=A2=84=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E6=9C=9F=E6=B3=A8=E5=85=A5=E4=BE=9D=E8=B5=96=20vpi=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=20list[ast.ClassDef*]=20=E7=A5=A8=E5=90=8D=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E5=88=86=E8=A3=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase A-pre(App 预注册)此前不做导入处理、不注入依赖 .vpi,导致包重导出 类的字段注解在预注册期只能保留「包前缀」(1abcd…),并被冻结进结构体布局; 阶段 A 构造函数侧却能借包别名 entry 校正为真定义前缀(e70bd…),两处票名 分裂 ⇒「非基本类型指针不可隐式转换」(现场 ClassGenGeneric.vp:92)。 改动(只放开 App 预注册,不动 includes 预注册那条防依赖环自锁的约束): - Translator 新增 _pre_allow_dep_inject 开关,并在导入处理门拆成两道: ①导入处理 + 自动容器可见性 + LoadDepVpiIntoModule —— 全量翻译与 App 预注册共用; ②模块级变量预注册 + 函数前向声明 —— 仍只在全量翻译做。 - BuildPipeline.TranslateFileGetTrans 新增 pre_allow_dep_inject 参数并写入 Translator。 - Phase2 App 预注册调用点传 1;includes 预注册保持默认 0。 --- App/lib/Phase/BuildPipeline.vp | 10 +++- App/lib/Phase/Phase2/__init__.vp | 7 ++- App/lib/core/Gen/Translator.vp | 80 +++++++++++++++++++++----------- 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/App/lib/Phase/BuildPipeline.vp b/App/lib/Phase/BuildPipeline.vp index 77a2b4d..9757226 100644 --- a/App/lib/Phase/BuildPipeline.vp +++ b/App/lib/Phase/BuildPipeline.vp @@ -119,12 +119,16 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, current_package: str = None, declare_only: int = 0, - temp_dir: str = None) -> rawptr[HandlesTranslator.Translator]: + temp_dir: str = None, + pre_allow_dep_inject: int = 0) -> rawptr[HandlesTranslator.Translator]: """翻译文件,返回 Translator 对象(调用者负责 dump_ir),None 失败 current_package: 当前文件所属包名(用于解析相对导入),None 表示顶级模块 declare_only: 0=全量翻译(默认),1=仅注册 struct/enum/union(不翻译方法体) temp_dir: temp 目录路径(非 None 时在翻译前生成 .vpi 存根文件) + pre_allow_dep_inject: 仅 **App 预注册**(Phase A-pre)传 1:声明期也处理导入语句并 + 注入依赖 .vpi,使类字段注解与阶段 A 同上下文(详见 Translator._pre_allow_dep_inject)。 + includes 预注册(Phase A-pre-inc)必须保持 0。 """ if file_path is None: return None f: rawptr[fileio.File] = fileio.File(file_path, fileio.MODE.R) @@ -190,6 +194,10 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, tr.ModuleSha1 = sha1_val tr.CurrentPackage = current_package tr._declare_only = declare_only + # App 预注册(Phase A-pre)专用开关:置 1 时声明期也做导入处理 + 依赖 vpi 注入 + # (详见 Translator._pre_allow_dep_inject)。includes 预注册(Phase A-pre-inc) + # 必须传 0,否则依赖环自锁(见 Translator 内 _declare_only==0 门处注释)。 + tr._pre_allow_dep_inject = pre_allow_dep_inject # P2: 复用全局 gen 单例(App/main.vp 创建;HandlesType 等旧模块函数经 # 延迟 import 委托到 gen 方法)。单例未注册时兜底创建(防御直接调用路径)。 gen: rawptr[GenGenerator.Generator] = GenGenerator._g_gen diff --git a/App/lib/Phase/Phase2/__init__.vp b/App/lib/Phase/Phase2/__init__.vp index 31e19f0..6ac590e 100644 --- a/App/lib/Phase/Phase2/__init__.vp +++ b/App/lib/Phase/Phase2/__init__.vp @@ -946,7 +946,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, if string.strlen(ent_pre.Path) > src_dir_len_pre + 1: rel_path_pre: str = ent_pre.Path + src_dir_len_pre + 1 pkg_pre = GenApi.compute_package_from_relpath(mb, rel_path_pre) - tr_pre: rawptr[HandlesTranslator.Translator] = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1) + # App 预注册传 pre_allow_dep_inject=1:声明期也做导入处理 + 依赖 vpi 注入, + # 使类字段注解在预注册期即与阶段 A 同上下文(否则包重导出类 + # list[ast.ClassDef*] 的票名前缀会在预注册期被冻结成包前缀,与阶段 A + # 解析出的真定义前缀分裂 → 结构体布局不兼容,见 Translator 字段说明)。 + tr_pre: rawptr[HandlesTranslator.Translator] = BuildPipeline.TranslateFileGetTrans( + mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1, None, 1) if tr_pre is None: VLogger.error(f"预注册失败: {ent_pre.Path}", "PhaseA") # OOM 清理:回收本 App 文件预注册翻译的瞬时数据 diff --git a/App/lib/core/Gen/Translator.vp b/App/lib/core/Gen/Translator.vp index 4502adc..a8938b1 100644 --- a/App/lib/core/Gen/Translator.vp +++ b/App/lib/core/Gen/Translator.vp @@ -262,6 +262,18 @@ novirtual class Translator: # Phase 1a 声明模式标志:1=只注册 struct/enum/union 不翻译代码体,0=全量翻译 _declare_only: int + # App 预注册(Phase A-pre)专用开关:置 1 时,即使 _declare_only != 0 也执行 + # 导入语句处理与 LoadDepVpiIntoModule(依赖 stub 注入),使**类字段注解在预注册期 + # 就与阶段 A 处于同一上下文**。 + # 为什么需要:预注册期要注册类的字段类型,而包重导出类的票名前缀校正 + # (`_class_true_def_sha1`)依赖包 __init__ 注入时登记的别名 entry;预注册期不注入 + # ⇒ 只能保留**包前缀**,该票名被冻结进结构体布局,与阶段 A 构造函数侧解析出的 + # 真定义前缀分裂 ⇒「指针隐式转指针已禁止」(现场 ClassGenGeneric.vp:92, + # `list[ast.ClassDef*]`:预注册得 1abcd….ClassDef*、阶段 A 得 e70bd….ClassDef*)。 + # 只对 **App 预注册**置位:includes 预注册(Phase A-pre-inc)必须保持不注入, + # 否则依赖环自锁(见下方 `_declare_only == 0` 门处注释)。 + _pre_allow_dep_inject: int + # 嵌套作用域符号表 SymTab: rawptr[GenTypes.SymbolTable] @@ -308,6 +320,7 @@ novirtual class Translator: self.GenericTypeParamNames = None self.GenericTypeArgs = None self._declare_only = 0 + self._pre_allow_dep_inject = 0 self.SymTab = None # REPL 模式上下文:默认未设置(= 非 REPL,Gen 侧拦截全部旁路) self.ReplCtx = None @@ -563,8 +576,17 @@ novirtual class Translator: # w32.winsock2 的 connect 顶掉 → socket.vp:236 报 # 「指针隐式转指针已禁止:SocketAddr* → WSASocketAddr*」。 GenApi.register_file_funcs(pool, tree) - # 全量翻译模式下,先处理导入语句,再创建前向声明,解决前向引用问题 - if self._declare_only == 0: + # 先处理导入语句,再创建前向声明,解决前向引用问题。 + # 两道门分开: + # ① 导入处理 + 自动容器可见性 + 依赖 vpi 注入 —— 全量翻译**与 App 预注册** + # (_pre_allow_dep_inject,见 Translator 字段说明)都要做,使类字段注解 + # 在预注册期即与阶段 A 同上下文(否则包重导出类的票名前缀会分裂: + # list[ast.ClassDef*] 现场); + # ② 模块级变量预注册 + 函数前向声明 —— 仍只在全量翻译做(声明期跳过, + # 保持 Phase A-pre 的"只注册 struct/enum/union"语义)。 + # includes 预注册(Phase A-pre-inc)两者都不做:_pre_allow_dep_inject 只由 + # App 预注册置位,避免依赖环自锁。 + if self._declare_only == 0 or self._pre_allow_dep_inject != 0: # 预处理导入语句,确保 _imported_modules 和 _from_imports 已填充 # (前向声明需要解析类型注解,如 t.CArray[str] 依赖 t 模块已导入) # 记录最后处理的 import 行号,供 vpi 注入错误上下文帧定位 @@ -595,32 +617,38 @@ novirtual class Translator: # CTypedef 声明由 Phase1 1b 文本扫描注册(RegisterCTypedefFromSource)—— # AST 字符串字段 token 零拷贝指向 src_buf,跨阶段内容不稳定,此处 AST 扫描 # 读 attr 会 0xC0000005,故移除。 - # 跨模块 .vpi 注入:加载依赖模块的 .vpi,把函数 declare 注入当前模块 - # (严格保证 declare_only == 0 时才注入,避免 Phase A-pre-inc 死锁)。 - if self._imported_modules is not None: - # 错误上下文帧:当前模块触发依赖 stub 加载的 import 位置 - GenApi.push_trace(GenApi.get_current_file(), cur_imp_lineno) - StubMergerBuild.LoadDepVpiIntoModule(pool, mod, self._imported_modules, self._from_imports, GenApi.get_temp_dir(), self) - GenApi.pop_trace() + # 跨模块 .vpi 注入:加载依赖模块的 .vpi,把函数 declare 注入当前模块。 + # 注入条件 = 全量翻译,或 **App 预注册**(_pre_allow_dep_inject):后者必须 + # 与阶段 A 同上下文,否则预注册期解析出的类字段票名(包重导出类的 + # 真定义前缀校正依赖包别名 entry)会与阶段 A 分裂。includes 预注册 + # (Phase A-pre-inc)不置位该开关 ⇒ 仍不注入,避免依赖环自锁。 + if self._imported_modules is not None: + # 错误上下文帧:当前模块触发依赖 stub 加载的 import 位置 + GenApi.push_trace(GenApi.get_current_file(), cur_imp_lineno) + StubMergerBuild.LoadDepVpiIntoModule(pool, mod, self._imported_modules, self._from_imports, GenApi.get_temp_dir(), self) + GenApi.pop_trace() # 文件级类名预登记已提前到本函数开头执行(覆盖三种 _declare_only 模式; # 为什么必须早于模块级变量/前向声明,见那里的说明)。 - # 预注册模块级变量(AnnAssign/Assign)到 SymTab - # 解决时序问题:class Logger 的 __init__ 引用 _mbuddy 时, - # _mbuddy 的 AnnAssign 在 AST 中位于 class Logger 之后, - # 按 AST 顺序翻译时 _mbuddy 尚未注册 → lookup_var 返回 None → rhs_val is None - # 预处理先于 translate_children 注册所有模块级变量, - # 后续 translate_children 遍历到同一 AnnAssign 时 handle_module_level_var - # 通过 lookup_module_var 检测已存在并跳过(HandlesMain.py:206-209) - if ch is not None: - for ci in range(cn_count): - child: ast.AST* = ch.get(ci) - if child is None: - continue - kd_mv: int = child.kind() - if kd_mv == ast.ASTKind.AnnAssign or kd_mv == ast.ASTKind.Assign: - GenApi.handle_module_level_var(self, child) - # 创建前向声明 - GenApi.forward_declare_functions(self, tree) + # 模块级变量预注册 + 函数前向声明:仍**只在全量翻译**做(声明期跳过, + # 保持 Phase A-pre"只注册 struct/enum/union"的语义)。 + if self._declare_only == 0: + # 预注册模块级变量(AnnAssign/Assign)到 SymTab + # 解决时序问题:class Logger 的 __init__ 引用 _mbuddy 时, + # _mbuddy 的 AnnAssign 在 AST 中位于 class Logger 之后, + # 按 AST 顺序翻译时 _mbuddy 尚未注册 → lookup_var 返回 None → rhs_val is None + # 预处理先于 translate_children 注册所有模块级变量, + # 后续 translate_children 遍历到同一 AnnAssign 时 handle_module_level_var + # 通过 lookup_module_var 检测已存在并跳过(HandlesMain.py:206-209) + if ch is not None: + for ci in range(cn_count): + child: ast.AST* = ch.get(ci) + if child is None: + continue + kd_mv: int = child.kind() + if kd_mv == ast.ASTKind.AnnAssign or kd_mv == ast.ASTKind.Assign: + GenApi.handle_module_level_var(self, child) + # 创建前向声明 + GenApi.forward_declare_functions(self, tree) added: int = GenApi.translate_children(self, tree) # 容器模块自动可见性注入(对齐 TPC _inject_auto_imports)