From 15b416b527747d526a76b24e404d79926111b99f Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 2 Oct 2026 17:29:38 +0800 Subject: [PATCH] =?UTF-8?q?fix(type):=20=E7=BB=9F=E4=B8=80=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E6=9C=9F=E7=B1=BB=E5=BD=92=E5=B1=9E=E4=B8=8E=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E4=B8=8A=E4=B8=8B=E6=96=87=E8=AE=B0=E8=B4=A6=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=8D=A0=E4=BD=8D=E8=A1=A8=E7=88=86=E4=B8=8E=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E6=A0=87=E8=AE=B0=E8=AF=AF=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StubMergerStore 新增共享导入上下文记账(AppendImportedModule / AppendFromImport / ImportCtx / AllocImportCtx / NotePlainImport),ImportsGenMixin 与 VpiReader 共用同一份 口径;顺带移除 add_from_import 的 256 缓冲静默截断,VpiReader 删除两份私有副本 - 注入窗口按「名字归属」判据解析(is_module_class + CurDefModuleSha1): _forward_declare_ptr_from_node 的 Name 分支、resolve_annotation_type 的 Name/Attribute 分支均补齐,使同一 stub 内先声明/先引用的类产出**具名占位**而不是 Ptr(i8),并新增 _is_tattr_marker 让 t.attr.* 属性标记不参与类型联合竞争 - VpiReader._inject_class_struct_declare:命中前向声明占位时不再早退,改为「占位 Ty 原地写入」(对齐 ClassGenDef.fill_fwd),修 StructTable 爆表(table full, cannot register Translator) - 清除全部一次性探针:[DBG-GEN](VpiReader / StructGenMixin / ClassGenDef)、 [DBG-ATTR](ExprGenAttr)、[MODSHA1](CallGenMixin) 根因记录:vpi 注入期漏记 `import X as Y -> from_imports "Y:X"`,使 GenTypes.VarEntry.Spec (TSpec.TypeSpec | t.CPtr)取不到模块 SHA1、退化为 i8* 并被永久冻结 (CallGenCtor.vp:897 .PtrLevel 属性访问失败)。 --- App/lib/Stub/StubGen/VpiReader.vp | 173 +++++++------------ App/lib/Stub/StubMerger/StubMergerStore.vp | 135 +++++++++++++++ App/lib/core/Gen/Call/CallGenMixin.vp | 26 +-- App/lib/core/Gen/Class/ClassGenDef.vp | 16 -- App/lib/core/Gen/Expr/ExprGenAttr.vp | 23 --- App/lib/core/Gen/GenApi.vp | 57 ++++--- App/lib/core/Gen/ImportsGenMixin.vp | 122 ++++++------- App/lib/core/Gen/StructGenMixin.vp | 188 ++++++--------------- App/lib/core/Gen/Type/TypeGenResolve.vp | 130 +++++++++++++- 9 files changed, 460 insertions(+), 410 deletions(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index dfe1a50..c161362 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -1723,76 +1723,15 @@ def _extract_sha1_from_path(vpi_path: str) -> str: # ============================================================ -# _stub_add_imported - 追加模块名到空格分隔列表(stub 上下文收集用) +# stub 导入上下文记账:一律走 StubMergerStore(单一真源) # -# 与 HandlesImports.add_imported_module 逻辑一致,但内联在本模块, -# 避免 VpiReader → HandlesImports → StubMerger → VpiReader 循环依赖。 +# 原 _stub_add_imported / _stub_add_from 是本模块内的两份私有副本,与翻译期 +# ImportsGenMixin.add_imported_module / add_from_import 重复;其中 Import 分支 +# 只抄了 imported_modules 的追加,漏掉 from_imports 的别名映射,导致 vpi 注解 +# 前向声明解析不到模块 SHA1(GenTypes.VarEntry.Spec.PtrLevel 失败根因)。 +# 记账已统一到 StubMergerStore.AppendImportedModule / AppendFromImport / +# NotePlainImport(纯记账、无索引副作用,正合 stub 上下文所需)。 # ============================================================ -def _stub_add_imported(pool: memhub.MemBuddy*, - cur: str, name: str) -> str: - """追加模块名到空格分隔列表(pool 分配),返回新列表""" - if name is None: - return cur - if cur is None: - nlen: sizet = string.strlen(name) - buf: str = pool.alloc(nlen + 1) - if buf is not None: - string.strcpy(buf, name) - return buf - old_len: sizet = string.strlen(cur) - name_len: sizet = string.strlen(name) - buf2: str = pool.alloc(old_len + 1 + name_len + 1) - if buf2 is not None: - string.strcpy(buf2, cur) - buf2[old_len] = ' ' - string.strcpy(buf2 + old_len + 1, name) - return buf2 - - -# ============================================================ -# _stub_add_from - 追加 from-import 映射("local:mod[:orig]" 空格分隔) -# ============================================================ -def _stub_add_from(pool: memhub.MemBuddy*, - cur: str, local: str, mod: str, orig: str) -> str: - """追加 from-import 映射(pool 分配),返回新列表 - - [D7·截断纪律] 旧实现固定 256 缓冲 + `snprintf("%s:%s[:%s]")`:三者合计超长即被 - **静默截断**,该条目在 ImportsGen 侧仍被当作合法条目解析出**错误的 - local/mod/orig** ⇒ 跨模块类名/别名还原取到错名字(D16 之后即污染 - Spec.SourceName 的来源名)。改为按精确长度分配,不存在上限。 - """ - if local is None or mod is None: - return cur - llen: sizet = string.strlen(local) - mlen: sizet = string.strlen(mod) - use_orig: int = 0 - olen: sizet = 0 - if orig is not None and string.strcmp(orig, local) != 0: - use_orig = 1 - olen = string.strlen(orig) - # local ':' mod [':' orig] '\0' - need: sizet = llen + 1 + mlen + 1 - if use_orig != 0: - need += 1 + olen - entry: str = pool.alloc(need) - if entry is None: - return cur - string.strcpy(entry, local) - entry[llen] = ':' - string.strcpy(entry + llen + 1, mod) - if use_orig != 0: - entry[llen + 1 + mlen] = ':' - string.strcpy(entry + llen + 1 + mlen + 1, orig) - if cur is None: - return entry - old_len: sizet = string.strlen(cur) - entry_len: sizet = string.strlen(entry) - buf: str = pool.alloc(old_len + 1 + entry_len + 1) - if buf is not None: - string.strcpy(buf, cur) - buf[old_len] = ' ' - string.strcpy(buf + old_len + 1, entry) - return buf # ============================================================ @@ -2137,6 +2076,10 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, # 解析注解——根治跨上下文误报(如 t.CArray[str] 需要 't' 已导入)。 stub_imported_modules: str = None stub_from_imports: str = None + # 记账统一走 StubMergerStore(单一真源,与翻译期 ImportsGenMixin.translate_import + # 同口径):含 `import X as Y → from_imports "Y:X"`(别名反查模块真名)与 + # `import a.b.c → imported_modules "a"`(顶层包名)。 + stub_ctx: StubMergerStore.ImportCtx* = StubMergerStore.AllocImportCtx(pool, None, None) for si in range(n): schild: ast.AST* = ch.get(si) if schild is None: @@ -2149,9 +2092,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, for sni in range(sn_n): sal: ast.Alias* = ast.Alias*(simp.names.get(sni)) if sal is not None and sal.name is not None: - stub_imported_modules = _stub_add_imported(pool, stub_imported_modules, sal.name) - if sal.asname is not None: - stub_imported_modules = _stub_add_imported(pool, stub_imported_modules, sal.asname) + StubMergerStore.NotePlainImport(pool, stub_ctx, sal.name, sal.asname) elif sk == ast.ASTKind.ImportFrom: sifr: ast.ImportFrom* = ast.ImportFrom*(schild) sifr_mod: str = sifr.module @@ -2219,7 +2160,9 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, string.strcpy(qbuf_r + pkg_len_r + 1, sifr_mod) sifr_mod_q = qbuf_r if sifr_mod is not None: - stub_imported_modules = _stub_add_imported(pool, stub_imported_modules, sifr_mod_q) + if stub_ctx is not None: + stub_ctx.ImportedModules = StubMergerStore.AppendImportedModule( + pool, stub_ctx.ImportedModules, sifr_mod_q) # 方案A:包 __init__.vpi 的重导出类以包 sha1 注册别名 entry # (ast.Attribute 定义在 ast.exprs 但归属 ast 包;查找端按包 # sha1 匹配,注册端按定义文件 sha1 → name+sha1 错位 fail-fast。 @@ -2283,22 +2226,31 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, for sifri in range(sifr_nn): sial: ast.Alias* = ast.Alias*(sifr.names.get(sifri)) if sial is not None and sial.name is not None: - # '*' star import 不建 from 映射(lookup 有 star 回退) + slocal: str = sial.name + sorig: str = None + if sial.asname is not None: + slocal = sial.asname + sorig = sial.name + # `from X import *` 亦写 "*:mod"(与翻译期 + # ImportsGenMixin.translate_from_import_names 同口径): + # lookup_from_import 的 star 回退正是靠这条 entry,旧实现 + # 在此跳过 ⇒ stub 上下文里 star 回退恒失效(同一份约定 + # 的两处 loop 又一处不一致)。 + if stub_ctx is not None: + stub_ctx.FromImports = StubMergerStore.AppendFromImport( + pool, stub_ctx.FromImports, slocal, sifr_mod_q, sorig) + # 重导出类以包 sha1 注册(asname 时按别名注册);star 无 + # 具体类名,不参与重导出(仅记账,见上)。 if string.strcmp(sial.name, "*") != 0: - slocal: str = sial.name - sorig: str = None - if sial.asname is not None: - slocal = sial.asname - sorig = sial.name - stub_from_imports = _stub_add_from( - pool, stub_from_imports, slocal, sifr_mod_q, sorig) - # 重导出类以包 sha1 注册(asname 时按别名注册) if is_pkg_init_rt != 0 and sub_sha1_rt is not None: _inject_reexport_class_declare( pool, mod, sial.name, slocal, sub_sha1_rt, src_sha1, trans) # 用 stub 自己的 imports 替代当前模块的(stub 注解解析上下文正确性) + if stub_ctx is not None: + stub_imported_modules = stub_ctx.ImportedModules + stub_from_imports = stub_ctx.FromImports imported_modules: str = trans._imported_modules from_imports: str = trans._from_imports if stub_imported_modules is not None: @@ -2328,7 +2280,8 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, else: src_mod_buf[sri_src] = ch_src src_mod_buf[sr_len_src] = '\0' - imported_modules = _stub_add_imported(pool, imported_modules, src_mod_buf) + imported_modules = StubMergerStore.AppendImportedModule( + pool, imported_modules, src_mod_buf) stdlib.free(src_mod_buf) # 递归预注册 stub import 闭包的 CTypedef/CDefine:注解(如 codepage: @@ -2780,14 +2733,6 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, flat_buf_c[p_len] = '_' string.strcpy(flat_buf_c + p_len + 1, cls_name) real_name = flat_buf_c - # [DBG-GEN] 一次性取证(用完删除) - _dg_v_isgen: int = 0 - if string.strcmp(real_name, "Generator") == 0: - _dg_v_isgen = 1 - _dg_v_s: str = "" - if src_sha1 is not None: - _dg_v_s = src_sha1 - stdio.printf("[DBG-GEN] vpi inject_class IN name=Generator src_sha1=%s\n", _dg_v_s) # REPL(ReplStubLayoutMode,见文件末"REPL:includes 类完整布局与虚表恢复"): # 本类若已由 .stub.ll 驱动的路径接管 ⇒ 这里整类让位——已登记(或本遍就地 # 登记成功)只标可见;父类未就绪 ⇒ 本遍不登记(收尾遍补齐);布局不可信 ⇒ @@ -2802,23 +2747,26 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, if se.State == _REPL_SS_OK: GenApi.add_visible_struct(pool, real_name) return 1 - # 1. 已注册检查(含源模块编译时注册的 entry):仅标记可见,避免重复注册 + # 1. 已注册检查(含源模块编译时注册的 entry):仅标记可见,避免重复注册。 + # 例外:**前向声明占位**(IsForwardDecl != 0)不早退——必须走下方注册路径, + # 并用「占位 Ty 原地写入」把字段体填进**同一个 Ty 对象**(见下方 fill_fwd_c 段)。 + # existing_c/fill_fwd_c 声明在 if 外:下方填充段要跨块读取。 + existing_c: GenTypes.StructEntry* = None + fill_fwd_c: int = 0 if src_sha1 is not None and src_sha1[0] != '\0': - existing_c: GenTypes.StructEntry* = GenApi.find_struct_by_module(real_name, src_sha1) + existing_c = GenApi.find_struct_by_module(real_name, src_sha1) if existing_c is not None and existing_c.Ty is not None: - GenApi.add_visible_struct(pool, real_name) - if _dg_v_isgen != 0: - stdio.printf("[DBG-GEN] vpi inject_class EXISTING name=Generator fcount=%d\n", - existing_c.FieldCount) - return 1 + if existing_c.IsForwardDecl != 0: + fill_fwd_c = 1 + else: + GenApi.add_visible_struct(pool, real_name) + return 1 # REnum 类(class X(t.REnum))跳过:布局 {i32 __tag, } 由 # ClassGenEnum._translate_renum_def 按变体计算;vpi 侧按普通 struct 注册 # 会得到错误布局(变体字段都在嵌套类里,直接 AnnAssign 扫描得 0 字段)。 # 留给源模块正式翻译注册。LLVMType 本就因 field_count==0 早退,此守卫把 # 该行为显式化,避免将来 REnum 带直接字段时被误注册为普通 struct。 if _vpi_class_is_renum(class_cd) != 0: - if _dg_v_isgen != 0: - stdio.printf("[DBG-GEN] vpi inject_class RENUM-SKIP name=Generator\n") return 0 # 规则 5.5 的证据前置(**必须早于**下方字段注解解析): # 本函数在**注入期**解析字段注解(GenApi.resolve_annotation_type → @@ -2875,12 +2823,6 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, pool, m_aa_c.annotation, imported_modules, from_imports, trans, 0) if field_ty_c is None: # 字段类型解析失败:不注册该类(留给源模块编译注册),不 fatal - if _dg_v_isgen != 0: - _dg_v_fn: str = "" - if m_tgt_c.id is not None: - _dg_v_fn = m_tgt_c.id - stdio.printf("[DBG-GEN] vpi inject_class FIELD-FAIL name=Generator field=%s\n", - _dg_v_fn) return 0 if field_count < GenTypes.FIELD_MAX: field_names_buf[field_count] = sizet(m_tgt_c.id) @@ -2889,8 +2831,6 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, # 3. vpi 存根不含类字段(只序列化 class 名 + def 签名)→ field_count 恒为 0;注册空类会让源模块编译时 find_struct_by_module 命中空类 # → "结构体已注册但字段数为0" InternalCompilerError。故改为不注册:依赖模块已编译时命中真实类(上方已处理),未编译时由 _deps_pending 多遍收敛。 if field_count == 0: - if _dg_v_isgen != 0: - stdio.printf("[DBG-GEN] vpi inject_class EMPTY name=Generator\n") return 0 # 4. 构建 ParamNode 链表 first_node_c: rawptr[llvmlite.ParamNode] = None @@ -2935,6 +2875,22 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, CompilerFactory.factory.PermPool, struct_ty_c) if st_copy_c is not None: struct_ty_c = st_copy_c + # 5.5 前向声明占位命中 ⇒ **占位 Ty 原地写入**(与 ClassGenDef.translate_class_def + # 的 fill_fwd 段逐句同款):给已存在的类型对象 `*existing.Ty` 赋入刚构建的 + # 字段体,并让 struct_ty_c 指向**同一个 Ty**。 + # 这是"不新建条目"的唯一正确做法:若此处让 struct_ty_c 保持新对象,下方 + # register_struct 按 **Ty 指针**去重就会 miss ⇒ 每一遍注入都追加一个新条目 + # ⇒ StructTable 爆表(现场:table full, cannot register Translator)。 + # 必须早于 module_add_named_type:避免同一名字先以占位名注册、又以新对象重复注册。 + if fill_fwd_c != 0 and existing_c is not None and existing_c.Ty is not None: + fwd_name_c: str = llvmlite.get_struct_name(existing_c.Ty) + fwd_fields_c: rawptr[llvmlite.ParamNode] = llvmlite.get_struct_fields(struct_ty_c) + fwd_fc_c: int = llvmlite.get_struct_fcount(struct_ty_c) + *existing_c.Ty = llvmlite.LLVMType.Struct(fwd_fields_c, fwd_fc_c, fwd_name_c) + struct_ty_c = existing_c.Ty + type_name_c = fwd_name_c + existing_c.IsForwardDecl = 0 + existing_c.FieldCount = 0 # 6. 注册到当前模块(输出类型定义行)+ 全局 StructTable + 可见性 llvmlite.module_add_named_type(mod, pool, struct_ty_c) # B5:vpi 保留 @c.Attribute(t.attr.packed) 装饰器,stub 注入的跨模块 @@ -2962,9 +2918,6 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, af_ty_c: llvmlite.LLVMType* = (llvmlite.LLVMType*)(field_types_buf[afi_c]) GenApi.add_field(pool, entry_c, af_name_c, af_ty_c) GenApi.add_visible_struct(pool, real_name) - if _dg_v_isgen != 0: - stdio.printf("[DBG-GEN] vpi inject_class REGISTERED name=Generator fcount=%d ty=%s\n", - field_count, type_name_c) return 1 diff --git a/App/lib/Stub/StubMerger/StubMergerStore.vp b/App/lib/Stub/StubMerger/StubMergerStore.vp index 56254f5..dea67d6 100644 --- a/App/lib/Stub/StubMerger/StubMergerStore.vp +++ b/App/lib/Stub/StubMerger/StubMergerStore.vp @@ -29,6 +29,141 @@ import lib.Stub.StubMerger as StubMerger # StubMergerStore - 全局 SHA1 映射存储器(sha1→模块名/相对路径)读写 # ============================================================ +# ============================================================ +# 导入上下文记账(单一真源) +# +# 「AST 导入语句 → imported_modules / from_imports」的记账口径必须唯一。 +# 翻译期(ImportsGenMixin.translate_import)与 vpi 注入期(VpiReader 顶层 +# 遍历)此前各写一份:注入期漏掉 `import X as Y → from_imports "Y:X"`(别名 +# 反查模块真名的唯一入口,见 LookupModNameByAlias)以及 `import a.b.c → +# imported_modules "a"`(顶层包名放行 is_module_imported)。前者使注解里的 +# `TSpec.TypeSpec*` 在 _forward_declare_ptr_from_node 4a'/4b 全落空 → 退化 +# 为 Ptr(i8)(现场:GenTypes.VarEntry.Spec.PtrLevel 属性访问失败);后者使 +# 包限定解析在 stub 上下文里被误判"未导入"。 +# +# 现统一到本模块,两侧只保留各自的特有副作用(SHA1 校验 / reexport 注册 / +# add_visible_struct),字符串记账一律走下面这组函数。ImportCtx 只承载两个 +# 上下文串(Viper 无多返回值,用 pool 分配的小 struct 传出)。 +# ============================================================ + +class ImportCtx: + """导入上下文容器:两个空格分隔串(ImportedModules / FromImports)""" + ImportedModules: str + FromImports: str + + +# ============================================================ +# AppendImportedModule - 追加模块名到空格分隔列表(纯记账,无索引副作用) +# ============================================================ +def AppendImportedModule(pool: memhub.MemBuddy*, cur: str, name: str) -> str: + """追加模块名到空格分隔列表(pool 分配),返回新列表""" + if name is None: + return cur + if cur is None: + nlen: sizet = string.strlen(name) + buf: str = pool.alloc(nlen + 1) + if buf is not None: + string.strcpy(buf, name) + return buf + old_len: sizet = string.strlen(cur) + name_len: sizet = string.strlen(name) + buf2: str = pool.alloc(old_len + 1 + name_len + 1) + if buf2 is not None: + string.strcpy(buf2, cur) + buf2[old_len] = ' ' + string.strcpy(buf2 + old_len + 1, name) + return buf2 + + +# ============================================================ +# AppendFromImport - 追加 from-import 映射("local:mod[:orig]" 空格分隔) +# +# [D7·截断纪律] 旧实现固定 256 缓冲 + snprintf:三者合计超长即被静默截断, +# 该条目在查找侧仍被当作合法条目解析出错误的 local/mod/orig ⇒ 跨模块类名/ +# 别名还原取到错名字(污染 Spec.SourceName)。此处按精确长度分配,无上限。 +# ============================================================ +def AppendFromImport(pool: memhub.MemBuddy*, cur: str, + local: str, mod: str, orig: str) -> str: + """追加 from-import 映射(pool 分配),返回新列表""" + if local is None or mod is None: + return cur + llen: sizet = string.strlen(local) + mlen: sizet = string.strlen(mod) + use_orig: int = 0 + olen: sizet = 0 + if orig is not None and string.strcmp(orig, local) != 0: + use_orig = 1 + olen = string.strlen(orig) + # local ':' mod [':' orig] '\0' + need: sizet = llen + 1 + mlen + 1 + if use_orig != 0: + need += 1 + olen + entry: str = pool.alloc(need) + if entry is None: + return cur + string.strcpy(entry, local) + entry[llen] = ':' + string.strcpy(entry + llen + 1, mod) + if use_orig != 0: + entry[llen + 1 + mlen] = ':' + string.strcpy(entry + llen + 1 + mlen + 1, orig) + if cur is None: + return entry + old_len: sizet = string.strlen(cur) + entry_len: sizet = string.strlen(entry) + buf: str = pool.alloc(old_len + 1 + entry_len + 1) + if buf is not None: + string.strcpy(buf, cur) + buf[old_len] = ' ' + string.strcpy(buf + old_len + 1, entry) + return buf + + +# ============================================================ +# AllocImportCtx - 用两个既有上下文串构造记账容器 +# ============================================================ +def AllocImportCtx(pool: memhub.MemBuddy*, + imported_modules: str, from_imports: str) -> ImportCtx*: + """构造导入上下文容器(pool 分配);两串可为 None""" + ctx: ImportCtx* = pool.alloc(ImportCtx.__sizeof__()) + if ctx is None: + return None + ctx.ImportedModules = imported_modules + ctx.FromImports = from_imports + return ctx + + +# ============================================================ +# NotePlainImport - 记账 `import X` / `import X as Y`(就地更新 ctx) +# +# 有别名:追加别名 `Y`,并写 from-import 映射 "Y:X"——这就是 `LookupModNameByAlias` +# 与 `_forward_declare_ptr_from_node` 4a' 反查模块真名的唯一来源,绝不可省。 +# 无别名:`import a.b.c` 时把顶层包名 `a` 也追加(使 a.b.c 作为属性访问 base +# 时 is_module_imported("a") 放行)。 +# ============================================================ +def NotePlainImport(pool: memhub.MemBuddy*, ctx: ImportCtx*, + name: str, asname: str) -> int: + """记账单条 import(就地更新 ctx),返回 0""" + if ctx is None or name is None: + return 1 + ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, name) + if asname is not None: + ctx.ImportedModules = AppendImportedModule(pool, ctx.ImportedModules, asname) + ctx.FromImports = AppendFromImport(pool, ctx.FromImports, asname, name, None) + else: + dot_ptr: str = string.strstr(name, ".") + if dot_ptr is not None: + pkg_len: sizet = sizet(uint64(dot_ptr) - uint64(name)) + if pkg_len > 0 and pkg_len < 256: + pkg_buf: str = pool.alloc(pkg_len + 1) + if pkg_buf is not None: + string.strncpy(pkg_buf, name, pkg_len) + pkg_buf[pkg_len] = '\0' + ctx.ImportedModules = AppendImportedModule( + pool, ctx.ImportedModules, pkg_buf) + return 0 + + # ============================================================ # PopulateSha1MapStore - 遍历 ScanResult.Entries 构建 sha1→module_name 映射,填充全局存储器 # diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index 1cc5d78..372144a 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -811,8 +811,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase): # _LookupMethodRetTyFromTextLL 的 strlen(0x1) 零页崩溃 # (llvmlite/__init__.vp CreateGlobalInt 翻译时 0xC0000005 根因)。 mod_ret_sha1: str = None - # [MODSHA1] 诊断用:模块名(cl.func 为 Attribute 时赋值,否则保持 None)。 - # 声明在 if 外,使下方裸名兜底分支也能安全读取(读未声明的局部 = 栈垃圾)。 + # 模块路径(cl.func 为 Attribute 时赋值,否则保持 None)。声明在 if 外, + # 使下方裸名兜底分支也能安全读取(读未声明的局部 = 栈垃圾)。 mod_path_ret: str = None if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: at_ret: ast.Attribute* = ast.Attribute*(cl.func) @@ -836,20 +836,6 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase): if trans is not None: fi_from_rt = trans._from_imports mod_ret_sha1 = self.Gen.StructGen._resolve_module_sha1(mod_path_ret, fi_from_rt) - # [MODSHA1] 临时诊断(定位后删除):模块限定调用的模块名 → SHA1 - # 解析失败时,打印本模块名与对照模块名(stdint,必在 includes) - # 的精确/后缀查询结果,区分「store 整体为空」与「store 有内容但 - # 缺该模块」。devfs.vp 的 string.split 报错即经此分支。 - if mod_ret_sha1 is None: - dg_ex: str = self.Gen.CallGenBase._lookup_module_sha1(mod_path_ret) - dg_sf: str = self.Gen.CallGenBase._lookup_module_sha1_suffix(mod_path_ret) - dg_ctrl: str = self.Gen.CallGenBase._lookup_module_sha1("stdint") - if dg_ex is None: dg_ex = "-" - if dg_sf is None: dg_sf = "-" - if dg_ctrl is None: dg_ctrl = "-" - stdio.printf("[MODSHA1] mod='%s' fn='%s' exact=%s suffix=%s ctrl_stdint=%s line=%d\n", - mod_path_ret, func_name, dg_ex, dg_sf, dg_ctrl, cl.lineno) - stdio.fflush(None) if mod_ret_sha1 is not None: # 函数内懒 import:VpiReader imports HandlesExprCall, # 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致) @@ -904,14 +890,6 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase): # 本遍仅临时占位。 call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 0) else: - # [MODSHA1] 临时诊断(定位后删除):走到裸名兜底 ⇒ mod_ret_sha1 为 - # None。仅当 cl.func 仍是模块限定形态(Attribute)时打印——这正是 - # "模块限定却解析不出模块"的异常组合(此时 mod_path_ret 为 None - # 说明 value 段不是 Name/Attribute;非 None 说明模块名查不到 SHA1)。 - if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: - stdio.printf("[MODSHA1-D] mod='%s' fn='%s' line=%d\n", - mod_path_ret, func_name, cl.lineno) - stdio.fflush(None) # 裸名调用(cl.func 是 Name,mod_ret_sha1=None)两类来源:from-import 的函数(后续解析模块归属构造 mangled call_name 并经 text.ll/vpi 精确解析返回类型,可收敛)→ fatal_if_unknown=0 仅临时占位,不 fail-fast;纯外部 C 函数(无登记、无模块归属,白名单未命中)→ fail-fast(fatal_if_unknown=1)。闭包变量与持有函数指针/闭包的局部变量同样按可解析占位——返回类型由 translate_closure_call/lookup_var 路径固定处理,不依赖外部返回类型推断,否则被 fail-fast 误杀 bare_resolvable: int = 0 if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0: diff --git a/App/lib/core/Gen/Class/ClassGenDef.vp b/App/lib/core/Gen/Class/ClassGenDef.vp index 0a0b7c3..1739d3d 100644 --- a/App/lib/core/Gen/Class/ClassGenDef.vp +++ b/App/lib/core/Gen/Class/ClassGenDef.vp @@ -94,16 +94,6 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): # 检查是否已注册(用 SHA1 区分跨模块同名类) existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1) - # [DBG-GEN] 一次性取证(用完删除) - _dg_cd_isgen: int = 0 - if class_name is not None and string.strcmp(class_name, "Generator") == 0: - _dg_cd_isgen = 1 - if existing is not None: - stdio.printf("[DBG-GEN] classdef Generator EXISTING fcount=%d declare_only=%d\n", - existing.FieldCount, trans._declare_only) - else: - stdio.printf("[DBG-GEN] classdef Generator EXISTING none declare_only=%d\n", - trans._declare_only) # 空类注册不完整检测:existing FieldCount==0 但源类体声明了字段(vpi 注入/注册缺陷)。 # 标记 _deps_pending 延迟会让空 entry 持久存在、多遍死锁不收敛 ⇒ 改为中和旧 entry # (置空 Name + Ty=None,使 find_struct_by_module 不再命中),走下方完整注册路径重建。 @@ -609,9 +599,6 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): if entry is None: VLogger.error(f"failed to register {class_name}", "CLASS") return 0 - if _dg_cd_isgen != 0: - stdio.printf("[DBG-GEN] classdef Generator REGISTER field_count=%d declare_only=%d\n", - field_count, trans._declare_only) # B5:记录 packed 标记到 StructEntry(供 __sizeof__/字段布局等派生查询) if is_packed_cd != 0: @@ -690,9 +677,6 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase): if fe_c is not None: fe_c.IsConst = 1 - if _dg_cd_isgen != 0: - stdio.printf("[DBG-GEN] classdef Generator DONE fcount=%d\n", entry.FieldCount) - # Phase 1a 声明模式:只注册 struct + 设置 OOP 标志,不翻译方法体 if trans._declare_only == 1: self.Gen.ClassGenVTable._translate_oop_methods(trans, cd, struct_ty, class_name, 1) diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index ab5016f..e445c2e 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -933,29 +933,6 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): # stub 空类 FieldCount=0,如 LARGE_INTEGER)→ 走下方真实报错 # (lookup_field_by_class 已对空 stub 返回 None 而非 fatal)。 if trans is not None: - # [DBG-ATTR] 一次性取证(用完删除):任何属性访问失败都打印 - _db_pt: str = "" - if obj_ptr is not None and obj_ptr.Ty is not None: - _db_b: str = pool.alloc(256) - if _db_b is not None: - _db_b[0] = '\0' - llvmlite.TypePrint(_db_b, 256, obj_ptr.Ty, pool) - _db_pt = _db_b - _db_sn: str = "" - _db_snm: str = llvmlite.get_struct_name(struct_ty) - if _db_snm is not None: - _db_sn = _db_snm - _db_sc: int = -1 - _db_se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty) - if _db_se is not None: - _db_sc = _db_se.FieldCount - stdio.printf("[DBG-ATTR] attr=%s obj_ptr.Ty=%s struct=%s fcount=%d\n", - at.attr, _db_pt, _db_sn, _db_sc) - _db_bc: str = "" - _db_bcv: str = self._resolve_attr_field_class(pool, trans, at.value, 1) - if _db_bcv is not None: - _db_bc = _db_bcv - stdio.printf("[DBG-ATTR] base_class=%s\n", _db_bc) diag_annot: str = None diag_var_nm: str = None if at.value is not None and at.value.kind() == ast.ASTKind.Name: diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index 4481e87..af6aaf8 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -348,34 +348,34 @@ def mark_module_class(pool: memhub.MemBuddy*, sha1: str, name: str) -> None: _g_gen.StructGen.mark_module_class(pool, sha1, name) -# ---------------- register_file_classes(委托 StructGenMixin.register_file_classes) ---------------- -def register_file_classes(pool: memhub.MemBuddy*, sha1: str, tree: ast.AST*) -> None: - """文件级预登记本文件顶层 ClassDef 的类名 + 本文件模块 SHA1(规则 5.5 第五类证据)。 - - 由 Translator.translate 的 prologue 调用(在翻译模块级变量/前向声明**之前**), - 使"模块级语句早于类定义"这一窗口里裸类名也能判出归属。""" - if _g_gen is not None: - _g_gen.StructGen.register_file_classes(pool, sha1, tree) - - -# ---------------- register_file_funcs / is_file_func(委托 StructGenMixin) ---------------- -def register_file_funcs(pool: memhub.MemBuddy*, tree: ast.AST*) -> None: - """文件级预登记本文件顶层 FunctionDef 的函数名。 - - 与 register_file_classes 同址调用(Translator 文件入口 prologue,早于依赖 vpi - 注入):注入侧据此跳过"本文件已声明的裸名",避免依赖的 extern 声明顶掉本文件 - 自己的 extern 声明(现场 includes/socket.vp connect 被 w32.winsock2 顶掉)。""" - if _g_gen is not None: - _g_gen.StructGen.register_file_funcs(pool, tree) - - -def is_file_func(name: str) -> int: - """裸名是否为**本文件**顶层声明的函数(1=是 / 0=否)""" - if _g_gen is not None: - return _g_gen.StructGen.is_file_func(name) - return 0 - - +# ---------------- register_file_classes(委托 StructGenMixin.register_file_classes) ---------------- +def register_file_classes(pool: memhub.MemBuddy*, sha1: str, tree: ast.AST*) -> None: + """文件级预登记本文件顶层 ClassDef 的类名 + 本文件模块 SHA1(规则 5.5 第五类证据)。 + + 由 Translator.translate 的 prologue 调用(在翻译模块级变量/前向声明**之前**), + 使"模块级语句早于类定义"这一窗口里裸类名也能判出归属。""" + if _g_gen is not None: + _g_gen.StructGen.register_file_classes(pool, sha1, tree) + + +# ---------------- register_file_funcs / is_file_func(委托 StructGenMixin) ---------------- +def register_file_funcs(pool: memhub.MemBuddy*, tree: ast.AST*) -> None: + """文件级预登记本文件顶层 FunctionDef 的函数名。 + + 与 register_file_classes 同址调用(Translator 文件入口 prologue,早于依赖 vpi + 注入):注入侧据此跳过"本文件已声明的裸名",避免依赖的 extern 声明顶掉本文件 + 自己的 extern 声明(现场 includes/socket.vp connect 被 w32.winsock2 顶掉)。""" + if _g_gen is not None: + _g_gen.StructGen.register_file_funcs(pool, tree) + + +def is_file_func(name: str) -> int: + """裸名是否为**本文件**顶层声明的函数(1=是 / 0=否)""" + if _g_gen is not None: + return _g_gen.StructGen.is_file_func(name) + return 0 + + # ---------------- find_struct_by_type(委托 StructGenMixin.find_struct_by_type) ---------------- def find_struct_by_type(struct_ty: llvmlite.LLVMType*) -> GenTypes.StructEntry*: """委托到 gen.find_struct_by_type(原 Handles 壳)""" @@ -544,6 +544,7 @@ def forward_declare_functions(trans: HT.Translator*, tree: ast.AST*) -> int: """委托到 gen.forward_declare_functions(原 Handles 壳)""" if _g_gen is not None: return _g_gen.FuncGenDef.forward_declare_functions(trans, tree) + return 0 # ============================================================ diff --git a/App/lib/core/Gen/ImportsGenMixin.vp b/App/lib/core/Gen/ImportsGenMixin.vp index 74e3a10..e4e7264 100644 --- a/App/lib/core/Gen/ImportsGenMixin.vp +++ b/App/lib/core/Gen/ImportsGenMixin.vp @@ -170,31 +170,26 @@ novirtual class ImportsGenMixin: def add_imported_module(self, pool: memhub.MemBuddy*, imported_modules: str, name: str) -> str: - """记录已导入的模块名,返回新的 imported_modules 字符串指针""" + """记录已导入的模块名,返回新的 imported_modules 字符串指针 + + 字符串记账走 StubMergerStore.AppendImportedModule(单一真源,与 vpi + 注入期同源);本方法只额外维护查询索引。 + """ if name is None: return imported_modules - if imported_modules is None: - nlen: sizet = string.strlen(name) - buf: str = pool.alloc(nlen + 1) - if buf is not None: - string.strcpy(buf, name) - # 索引:首个模块名(key 指向 buf 内部) - self._index_imported_module(None, buf, buf) - return buf - return None - else: - old_len: sizet = string.strlen(imported_modules) - name_len: sizet = string.strlen(name) - new_len: sizet = old_len + 1 + name_len - buf2: str = pool.alloc(new_len + 1) - if buf2 is not None: - string.strcpy(buf2, imported_modules) - buf2[old_len] = ' ' - string.strcpy(buf2 + old_len + 1, name) - # 索引:复用旧串索引并追加新名字(key 指向 buf2 内的 name 段) - self._index_imported_module(imported_modules, buf2, buf2 + old_len + 1) - return buf2 + old_len: sizet = 0 + if imported_modules is not None: + old_len = string.strlen(imported_modules) + buf: str = StubMergerStore.AppendImportedModule(pool, imported_modules, name) + if buf is None: return imported_modules + if imported_modules is None: + # 索引:首个模块名(key 指向 buf 内部) + self._index_imported_module(None, buf, buf) + else: + # 索引:复用旧串索引并追加新名字(key 指向 buf 内的 name 段) + self._index_imported_module(imported_modules, buf, buf + old_len + 1) + return buf # ============================================================ # 检查模块是否已导入 @@ -287,34 +282,26 @@ novirtual class ImportsGenMixin: 无别名: "local_name:module_name" 有别名: "local_name:module_name:original_name" (original_name 为源模块中的真实函数名,用于跨模块 SHA1 修饰) + + 字符串记账走 StubMergerStore.AppendFromImport(单一真源,与 vpi 注入期 + 同源;按精确长度分配,无旧的 256 缓冲静默截断);本方法只额外维护索引。 """ if local_name is None or module_name is None: return from_imports - entry: str = pool.alloc(256) - if entry is None: - return from_imports - if original_name is not None and string.strcmp(original_name, local_name) != 0: - viperlib.snprintf(entry, 256, "%s:%s:%s", local_name, module_name, original_name) - else: - viperlib.snprintf(entry, 256, "%s:%s", local_name, module_name) - if from_imports is None: - # 索引:首条 entry(local 从 entry 首字节开始,entry_off=0) - self._index_from_import(None, entry, 0, local_name, module_name, original_name) - return entry - else: - old_len: sizet = string.strlen(from_imports) - entry_len: sizet = string.strlen(entry) - new_len: sizet = old_len + 1 + entry_len - buf: str = pool.alloc(new_len + 1) - if buf is not None: - string.strcpy(buf, from_imports) - buf[old_len] = ' ' - string.strcpy(buf + old_len + 1, entry) - # 索引:复用旧串索引并追加(entry 从 buf[old_len+1] 开始) - self._index_from_import(from_imports, buf, old_len + 1, - local_name, module_name, original_name) - return buf + old_len: sizet = 0 + if from_imports is not None: + old_len = string.strlen(from_imports) + buf: str = StubMergerStore.AppendFromImport( + pool, from_imports, local_name, module_name, original_name) + if buf is None: return from_imports + # 索引:首条 entry_off=0;追加时 entry 从 buf[old_len+1] 开始 + entry_off: sizet = 0 + if from_imports is not None: + entry_off = old_len + 1 + self._index_from_import(from_imports, buf, entry_off, + local_name, module_name, original_name) + return buf # ============================================================ # 查找 from-import 名称 → 返回原始函数名(别名场景) @@ -816,7 +803,18 @@ novirtual class ImportsGenMixin: # ============================================================ def translate_import(self, trans: HT.Translator*, node: ast.AST*) -> int: - """处理 import 语句,返回 0""" + """处理 import 语句,返回 0 + + 记账走 StubMergerStore.NotePlainImport(**该约定的唯一实现**):与 vpi + 注入期(VpiReader 顶层遍历)同口径,包含 + · `import X as Y` → from_imports 追加 "Y:X"(别名反查模块真名的唯一 + 入口,见 StubMergerStore.LookupModNameByAlias); + · `import a.b.c` → imported_modules 追加顶层包名 "a"。 + 两侧各自只保留特有副作用(此处:导入 SHA1 校验)。 + 注:NotePlainImport 为纯记账,不再续接 imported/from 两个查询索引 ⇒ 这两 + 个串上的查找走 is_module_imported / lookup_from_import 的逐字符扫描回退 + (与 vpi 注入期上下文同款,正确性不受影响)。 + """ imp: ast.Import* = ast.Import*(node) if imp is None: return 0 @@ -824,6 +822,11 @@ novirtual class ImportsGenMixin: if names is None: return 0 nn: sizet = len(names) + pool_val: memhub.MemBuddy* = trans.Pool + ctx: StubMergerStore.ImportCtx* = StubMergerStore.AllocImportCtx( + pool_val, trans._imported_modules, trans._from_imports) + if ctx is None: + return 0 for ni in range(nn): alias: ast.Alias* = ast.Alias*(names.get(ni)) if alias is not None and alias.name is not None: @@ -832,28 +835,9 @@ novirtual class ImportsGenMixin: # 尚未包含后续才追加的模块(如 App 源文件),检查会造成误报。 if trans._declare_only == 0: self._check_import_sha1(node, alias.name) - pool_val: memhub.MemBuddy* = trans.Pool - im_val: str = trans._imported_modules - trans._imported_modules = self.add_imported_module(pool_val, im_val, alias.name) - # 别名也加入导入模块列表(用于模块限定构造器检查 Module.Class()) - if alias.asname is not None: - trans._imported_modules = self.add_imported_module(pool_val, trans._imported_modules, alias.asname) - # 别名也加入 from_imports(asname:fullname), - # 使 HT.func() 跨模块调用能通过 from_imports 查找别名→完整模块名→SHA1 - # 否则 _lookup_module_sha1("HT") 找不到(mod_arr 只存文件名 HandlesTranslator) - trans._from_imports = self.add_from_import(pool_val, trans._from_imports, alias.asname, alias.name) - else: - # 无别名时(import a.b.c),将顶层包名 "a" 也加入 _imported_modules, - # 使 a.b.c 作为属性访问的 base 被翻译时能通过 is_module_imported("a") 检查 - dot_ptr: str = string.strstr(alias.name, ".") - if dot_ptr is not None: - pkg_len: sizet = sizet(uint64(dot_ptr) - uint64(alias.name)) - if pkg_len > 0 and pkg_len < 256: - pkg_buf: bytes = pool_val.alloc(pkg_len + 1) - if pkg_buf is not None: - string.strncpy(pkg_buf, alias.name, pkg_len) - pkg_buf[pkg_len] = '\0' - trans._imported_modules = self.add_imported_module(pool_val, trans._imported_modules, pkg_buf) + StubMergerStore.NotePlainImport(pool_val, ctx, alias.name, alias.asname) + trans._imported_modules = ctx.ImportedModules + trans._from_imports = ctx.FromImports return 0 # ============================================================ diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 89e8e79..bdd8c2e 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -122,30 +122,30 @@ novirtual class StructGenMixin: # 该窗口内**必然**可判(与 CurDefModuleSha1 同源、同生命周期)。 CurDefClassName: str - # --- 本文件顶层 ClassDef 的类名表(每文件重建;register_file_classes 登记)--- - # 为什么需要(**第二个"裸类名"窗口**):Translator.translate 的 prologue 先翻译 - # 模块级变量与前向声明(Translator.vp:592-601),**早于** translate_children(:602) - # —— 此时"正在定义类"窗口尚未建立(CurDefModuleSha1=None),规则 5.5 的证据 - # ①②③④ 全落空 ⇒ 形参/全局注解里的裸类名(如 includes/ast/base.vp:148 - # `_append_child(..., lst: list[rawptr[AST]])`)以**裸名**形式被首次注册并被后续 - # 复用(首写者胜),与该类自身翻译期解析出的带 SHA1 形式分裂 - # (现场:base.vp:359 报"非基本类型指针不可隐式转换")。 - # 本表与"窗口"无关:判据是**正向事实**"该名字是本文件声明的类"(文件级预扫描 - # 在本文件任何注解解析之前登记),配合 FileModuleSha1 即得归属,不猜。 - FileClassNames: hashtable.HashTable* - - # 本文件(正在翻译的文件)的模块 SHA1:由 Translator 文件入口随预扫描传入。 - # 仅与 FileClassNames 命中**配合**使用 ⇒ 即便窗口里留着"上一文件的旧值"也不会 - # 误归属(旧 sha1 与"本文件声明了该类"两个正向事实不会同时成立)。 - FileModuleSha1: str - - # 本文件顶层 FunctionDef 的函数名表:用途见 register_file_funcs —— - # 依赖 stub 注入的**裸名**声明(extern/state/export,不带 SHA1 前缀)若与 - # 本文件自己的声明同名,会先占用该名字,使本文件声明在 - # forward_declare_functions 里被静默丢弃(调用点改按依赖签名 coerce)。 - # 注入侧(VpiReader._inject_function_declare)据此跳过这些名字。 - FileFuncNames: hashtable.HashTable* - + # --- 本文件顶层 ClassDef 的类名表(每文件重建;register_file_classes 登记)--- + # 为什么需要(**第二个"裸类名"窗口**):Translator.translate 的 prologue 先翻译 + # 模块级变量与前向声明(Translator.vp:592-601),**早于** translate_children(:602) + # —— 此时"正在定义类"窗口尚未建立(CurDefModuleSha1=None),规则 5.5 的证据 + # ①②③④ 全落空 ⇒ 形参/全局注解里的裸类名(如 includes/ast/base.vp:148 + # `_append_child(..., lst: list[rawptr[AST]])`)以**裸名**形式被首次注册并被后续 + # 复用(首写者胜),与该类自身翻译期解析出的带 SHA1 形式分裂 + # (现场:base.vp:359 报"非基本类型指针不可隐式转换")。 + # 本表与"窗口"无关:判据是**正向事实**"该名字是本文件声明的类"(文件级预扫描 + # 在本文件任何注解解析之前登记),配合 FileModuleSha1 即得归属,不猜。 + FileClassNames: hashtable.HashTable* + + # 本文件(正在翻译的文件)的模块 SHA1:由 Translator 文件入口随预扫描传入。 + # 仅与 FileClassNames 命中**配合**使用 ⇒ 即便窗口里留着"上一文件的旧值"也不会 + # 误归属(旧 sha1 与"本文件声明了该类"两个正向事实不会同时成立)。 + FileModuleSha1: str + + # 本文件顶层 FunctionDef 的函数名表:用途见 register_file_funcs —— + # 依赖 stub 注入的**裸名**声明(extern/state/export,不带 SHA1 前缀)若与 + # 本文件自己的声明同名,会先占用该名字,使本文件声明在 + # forward_declare_functions 里被静默丢弃(调用点改按依赖签名 coerce)。 + # 注入侧(VpiReader._inject_function_declare)据此跳过这些名字。 + FileFuncNames: hashtable.HashTable* + # --- 名 | 类型 下标索引(查找快速路径;索引不可信时回退原线性扫描) --- StructNameIdx: sizet* # 名 → 首注册下标(槽 u64:0=空槽,否则 index+1) StructTypeIdx: sizet* # Ty 指针值 → 首注册下标(同布局) @@ -160,12 +160,12 @@ novirtual class StructGenMixin: self.StdlibAlloc = 0 self.VisibleStructs = None self.StrictVisibility = 0 - self.ModuleClasses = None - self.CurDefModuleSha1 = None - self.CurDefClassName = None - self.FileClassNames = None - self.FileModuleSha1 = None - self.FileFuncNames = None + self.ModuleClasses = None + self.CurDefModuleSha1 = None + self.CurDefClassName = None + self.FileClassNames = None + self.FileModuleSha1 = None + self.FileFuncNames = None self.StructNameIdx = None self.StructTypeIdx = None self.StructIdxCap = 0 @@ -594,34 +594,12 @@ novirtual class StructGenMixin: sha1 参数在注册时直接设置 ModuleSha1(避免 set_struct_sha1 找错 entry)。 """ if self.init_struct_table(pool) == 0: return None - # [DBG-GEN] 一次性取证(用完删除) - _dg_isgen: int = 0 - if name is not None and string.strcmp(name, "Generator") == 0: - _dg_isgen = 1 - _dg_sh0: str = "" - if sha1 is not None: - _dg_sh0 = sha1 - _dg_tn0: str = "" - _dg_g0: str = llvmlite.get_struct_name(struct_ty) - if _dg_g0 is not None: - _dg_tn0 = _dg_g0 - stdio.printf("[DBG-GEN] register_struct IN name=Generator sha1=%s ty=%s count_before=%d\n", - _dg_sh0, _dg_tn0, self.StructCount) # 用类型指针去重(同一类型不重复注册,支持跨模块同名类) # 用 uint64 指针值比较(is 比较会触发类型安全检查解引用悬空 Ty) # 走 find_struct_by_type(其 Ty 索引判定已整体停用、恢复原线性扫描), # 去重语义 = 原去重扫描"首个 Ty 指针相等条目"(首注册优先),逐字节一致。 existing: GenTypes.StructEntry* = self.find_struct_by_type(struct_ty) if existing is not None: - if _dg_isgen != 0: - _dg_en: str = "" - if existing.Name is not None: - _dg_en = existing.Name - _dg_es: str = "" - if existing.ModuleSha1 is not None: - _dg_es = existing.ModuleSha1 - stdio.printf("[DBG-GEN] register_struct DEDUP name=Generator -> existing name=%s sha1=%s fcount=%d\n", - _dg_en, _dg_es, existing.FieldCount) return existing if self.StructCount >= STRUCT_MAX: VLogger.error(f"table full, cannot register {name}", "STRUCT") @@ -823,10 +801,6 @@ novirtual class StructGenMixin: if struct_entry.FieldCount >= FIELD_MAX: VLogger.error(f"field table full for {struct_entry.Name}", "STRUCT") return -1 - # [DBG-GEN] 一次性取证(用完删除) - if struct_entry.Name is not None and string.strcmp(struct_entry.Name, "Generator") == 0: - stdio.printf("[DBG-GEN] add_field Generator[%d] name=%s\n", - struct_entry.FieldCount, field_name) idx: int = struct_entry.FieldCount fe: GenTypes.FieldEntry* = self._get_field_entry(struct_entry, idx) if fe is None: return -1 @@ -1053,32 +1027,32 @@ novirtual class StructGenMixin: s_rm = StubMergerStore.LookupSha1ByModNameSuffix(mod_name) return s_rm - def find_struct_entry_with_ty(self, name: str, alt_name: str) -> GenTypes.StructEntry*: - """按名字(含备选名)查**带有效 Ty** 的结构体条目;找不到返回 None。 - - 为什么需要它:`find_struct` 的语义是"首注册命中"。D30 的 REnum 重注册把旧 - entry **就地中和**(`Name[0]='\\0'` / `Ty=None` / `FieldCount=0`)后再追加新 - entry;若名索引/线性扫描先命中那条已中和的旧条目,调用方拿到的 `Ty` 是 None —— - 依赖 `Ty` 的代码(如 match 的 REnum 变体字段绑定)会**静默跳过**,症状表现为 - case 绑定名未定义(NameError,真因被藏到下游)。本函数跳过 `Ty` 无效的同名条目, - 返回首个 `Ty` 有效的同名(或备选名)条目;都没有则返回 None,由调用方决定报错。 - - name / alt_name:主名与备选名(如模块限定名与裸类名),任一命中即可。 - """ - if self.StructTable is None: - return None - for i in range(self.StructCount): - entry: GenTypes.StructEntry* = self._get_struct_entry(i) - if entry is None or entry.Name is None or entry.Ty is None: - continue - if entry.Name[0] == '\0': - continue - if string.strcmp(entry.Name, name) == 0: - return entry - if alt_name is not None and string.strcmp(entry.Name, alt_name) == 0: - return entry - return None - + def find_struct_entry_with_ty(self, name: str, alt_name: str) -> GenTypes.StructEntry*: + """按名字(含备选名)查**带有效 Ty** 的结构体条目;找不到返回 None。 + + 为什么需要它:`find_struct` 的语义是"首注册命中"。D30 的 REnum 重注册把旧 + entry **就地中和**(`Name[0]='\\0'` / `Ty=None` / `FieldCount=0`)后再追加新 + entry;若名索引/线性扫描先命中那条已中和的旧条目,调用方拿到的 `Ty` 是 None —— + 依赖 `Ty` 的代码(如 match 的 REnum 变体字段绑定)会**静默跳过**,症状表现为 + case 绑定名未定义(NameError,真因被藏到下游)。本函数跳过 `Ty` 无效的同名条目, + 返回首个 `Ty` 有效的同名(或备选名)条目;都没有则返回 None,由调用方决定报错。 + + name / alt_name:主名与备选名(如模块限定名与裸类名),任一命中即可。 + """ + if self.StructTable is None: + return None + for i in range(self.StructCount): + entry: GenTypes.StructEntry* = self._get_struct_entry(i) + if entry is None or entry.Name is None or entry.Ty is None: + continue + if entry.Name[0] == '\0': + continue + if string.strcmp(entry.Name, name) == 0: + return entry + if alt_name is not None and string.strcmp(entry.Name, alt_name) == 0: + return entry + return None + def find_struct(self, name: str) -> GenTypes.StructEntry*: """按类名查找结构体,返回 StructEntry 或 None @@ -1197,14 +1171,6 @@ novirtual class StructGenMixin: if self.StructCount < 0 or self.StructCount > 200000: VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_module)", "STRUCT") stdlib.exit(1) - # [DBG-GEN] 一次性取证(用完删除) - _dg_fbm_isgen: int = 0 - if string.strcmp(name, "Generator") == 0: - _dg_fbm_isgen = 1 - _dg_fbm_s: str = "" - if sha1 is not None: - _dg_fbm_s = sha1 - stdio.printf("[DBG-GEN] find_struct_by_module IN name=Generator sha1=%s\n", _dg_fbm_s) if sha1 is not None: # 名索引快速路径:同名条目都位于 >= 首个同名下标处,故可从该下标起扫描 # name+sha1(等价原全表扫描"首个 name 且 sha1 命中")。 @@ -1221,14 +1187,9 @@ novirtual class StructGenMixin: if entry.ModuleSha1 is not None: # 同上:sha1 判等蕴含首字符相等,先比首字符再 strcmp。 if entry.ModuleSha1[0] == sha1[0] and string.strcmp(entry.ModuleSha1, sha1) == 0: - if _dg_fbm_isgen != 0: - stdio.printf("[DBG-GEN] find_struct_by_module HIT name=Generator sha1=%s fcount=%d\n", - entry.ModuleSha1, entry.FieldCount) return entry # SHA1 查找失败:不回退到 find_struct(会返回跨模块同名错误 entry) # 返回 None 让调用方创建新类型,避免 Phase B 用错误类型短路 - if _dg_fbm_isgen != 0: - stdio.printf("[DBG-GEN] find_struct_by_module MISS name=Generator\n") return None # 无 SHA1:按类名查找第一个 return self.find_struct(name) @@ -1396,43 +1357,6 @@ novirtual class StructGenMixin: if struct_ty is None or field_name is None or self.StructTable is None: return None - # [DBG-GEN] 一次性取证(用完删除) - if string.strcmp(field_name, "FuncGenBase") == 0: - _dg_ty: str = self._extract_struct_name(struct_ty) - _dg_ty_t: str = "" - if _dg_ty is not None: - _dg_ty_t = _dg_ty - stdio.printf("[DBG-GEN] lookup_field 'FuncGenBase' struct_ty=%s\n", _dg_ty_t) - _dg_pm: GenTypes.StructEntry* = self.find_struct_by_type(struct_ty) - if _dg_pm is not None: - _dg_pn: str = "" - if _dg_pm.Name is not None: - _dg_pn = _dg_pm.Name - _dg_ps: str = "" - if _dg_pm.ModuleSha1 is not None: - _dg_ps = _dg_pm.ModuleSha1 - stdio.printf("[DBG-GEN] ptr-match name=%s sha1=%s fcount=%d\n", - _dg_pn, _dg_ps, _dg_pm.FieldCount) - _dg_fi: int = 0 - while _dg_fi < _dg_pm.FieldCount: - _dg_fe: GenTypes.FieldEntry* = self._get_field_entry(_dg_pm, _dg_fi) - if _dg_fe is not None and _dg_fe.Name is not None: - stdio.printf("[DBG-GEN] field[%d]=%s\n", _dg_fi, _dg_fe.Name) - _dg_fi += 1 - else: - stdio.printf("[DBG-GEN] ptr-match NONE\n") - _dg_i: int = 0 - while _dg_i < self.StructCount: - _dg_e: GenTypes.StructEntry* = self._get_struct_entry(_dg_i) - if _dg_e is not None and _dg_e.Name is not None: - if string.strstr(_dg_e.Name, "Generator") is not None: - _dg_sh: str = "" - if _dg_e.ModuleSha1 is not None: - _dg_sh = _dg_e.ModuleSha1 - stdio.printf("[DBG-GEN] tbl[%d] name=%s fcount=%d sha1=%s\n", - _dg_i, _dg_e.Name, _dg_e.FieldCount, _dg_sh) - _dg_i += 1 - # ============================================================ # 两遍查找策略(跨模块同名类冲突的根本修复): # diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index b1dcbd0..45ea401 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -1997,6 +1997,23 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): # 4b. mod 本身是完整模块路径(无别名 import) if sha1 is None: sha1 = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_buf) + # 4c. vpi 注入期:模块段 = 本 stub 自己的模块名(或其末段)⇒ 归属即 + # CurDefModuleSha1(inject_class 入口 mark_module_class 建立), + # 与 Name 分支的注入窗口证据同口径:同一 stub 内写成 + # `自身模块名.类名`(无需 self-import)的指针注解据此前向声明为具名占位。 + if sha1 is None: + inj_mod_a2: str = self.Gen.StructGen.CurDefModuleSha1 + if inj_mod_a2 is not None: + inj_nm_a2: str = StubMergerStore.LookupModNameBySha1(inj_mod_a2) + if inj_nm_a2 is not None: + inj_seg_a2: str = inj_nm_a2 + dot_a2: str = string.strrchr(inj_nm_a2, '.') + if dot_a2 is not None: + inj_seg_a2 = dot_a2 + 1 + nm_eq_a2: int = string.strcmp(inj_nm_a2, mod_buf) + seg_eq_a2: int = string.strcmp(inj_seg_a2, mod_buf) + if nm_eq_a2 == 0 or seg_eq_a2 == 0: + sha1 = inj_mod_a2 elif k == ast.ASTKind.Name: nm: ast.Name* = ast.Name*(node) if nm.id is None: @@ -2010,6 +2027,23 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): if sha1 is None: if self.Gen.StructGen.is_file_class(pool, cls_nm) != 0: sha1 = self.Gen.StructGen.get_file_module_sha1() + # vpi 注入期的"本模块"归属:inject_class 入口已 mark_module_class(src_sha1, + # real_name) 登记"{src_sha1}.{类名}"(**名字归属**,与 struct 是否已登记 + # 无关),并把 CurDefModuleSha1 指向本次注入的 stub 源模块。注入期 + # trans.ModuleSha1 是"正在翻译的文件"(≠ 被注入 stub 的模块),故上面 + # is_file_class 必然落空;而同一 stub 里**先声明**的类若本遍字段尚未解析 + # 成功(还没 register_struct),它的归属同样可判——据此前向声明为具名占位 + # "{src_sha1}.{类名}",而不是退化成 Ptr(i8)。 + # 现场:Translator.vp 的 `class Translator`(:84) 与 `class Mixin`(:1011), + # 字段 `Mixin.Trans: Translator | t.CPtr` 在 Translator.vpi 注入到 + # CallGenLeafKit.vp 时解析(self.Trans.Pool 属性访问失败根因):若本遍 + # Translator 尚未登记就退化成 i8*,随后 Mixin 因 field_count>0 被判"注册 + # 成功"、i8* 被永久冻结(inject_class 的已注册检查直接早退)。 + if sha1 is None: + inj_mod_n: str = self.Gen.StructGen.CurDefModuleSha1 + if inj_mod_n is not None: + if self.Gen.StructGen.is_module_class(pool, inj_mod_n, cls_nm) != 0: + sha1 = inj_mod_n else: return None if cls_nm is None or sha1 is None or sha1[0] == '\0': @@ -2229,6 +2263,43 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): if nm.id is None or string.strcmp(nm.id, "t") != 0: return 0 return 1 + def _is_tattr_marker(self, node: ast.AST*) -> int: + """检测节点是否为 `t.attr.<名>` 属性标记(含调用形 `t.attr.<名>(...)`)。 + + 解析层把**类型位置**的前置 `__attribute__(...)` 经 `_MakeTAttrMarker` 脱糖为 + `t.attr.<名>` / `t.attr.<名>(...)`,再由 `_PrependDescriptorMarkers` 贴到类型 + 联合的**最左操作数**位置。它**不是类型**:联合分支若把它当类型解析,会把 + `[]char`(= `t.CArray[char, None]` = i8*)的数组外壳吞掉退化成 i8——现场 + main.vp:44 `V_TPV_VERSION: __attribute__(section(".PRVERSN")) []char` + 生成 `@g = global i8 ` 非法 IR(opt: constant expression + type mismatch: got type 'ptr' but expected 'i8')。 + """ + if node is None: + return 0 + inner: ast.AST* = node + if inner.kind() == ast.ASTKind.Call: + cl: ast.Call* = ast.Call*(inner) + if cl.func is None: + return 0 + inner = cl.func + if inner.kind() != ast.ASTKind.Attribute: + return 0 + at: ast.Attribute* = ast.Attribute*(inner) + if at.attr is None or at.value is None: + return 0 + if at.value.kind() != ast.ASTKind.Attribute: + return 0 + root: ast.Attribute* = ast.Attribute*(at.value) + if root.attr is None or root.value is None: + return 0 + if root.value.kind() != ast.ASTKind.Name: + return 0 + rn: ast.Name* = ast.Name*(root.value) + if rn.id is None or string.strcmp(rn.id, "t") != 0: + return 0 + if string.strcmp(root.attr, "attr") != 0: + return 0 + return 1 def _is_cvoid_marker(self, node: ast.AST*, imported_modules: str) -> int: """检测节点是否为 CVoid 指针标记,返回 1=是 / 0=否""" @@ -2921,7 +2992,22 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): # 为兼容 `d: dict = dict(bd)` 这类"裸名 + 构造"写法而设, # 代价是同一类型出现"裸名=指针 / 联合=再加一层"的分裂 # (现场 includes/json/__init__.vp:90 CastError)。 - return own_entry.Ty + return own_entry.Ty + # vpi 注入期:CurDefModuleSha1 = **本次注入的 stub 源模块**(inject_class 入口 + # 的 mark_module_class 建立)。注入期 trans.ModuleSha1 是"正在翻译的文件", + # 故上面的本模块查找必然落空。此处按同一口径认"被注入 stub 自己声明的类": + # 已登记条目、或先前由指针前向声明建立的具名占位,都返回原型(裸类名恒为原型, + # 见上),使同一 stub 内"先声明/先被引用"的类在后继字段注解里口径一致,不再 + # 绕经"联合回退 → 前向声明 → 退化为 Ptr(i8)"那条链。 + # 注:此处**不新建**占位——值语义位置凭空建不透明原型会得到不可用布局; + # 占位创建仍归 _forward_declare_ptr_from_node(指针语境)。 + if trans is not None and trans.ModuleSha1 is not None: + inj_mod_rn: str = self.Gen.StructGen.CurDefModuleSha1 + if inj_mod_rn is not None: + if string.strcmp(inj_mod_rn, trans.ModuleSha1) != 0: + inj_e_rn: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(nm.id, inj_mod_rn) + if inj_e_rn is not None and inj_e_rn.Ty is not None: + return inj_e_rn.Ty # from-import 归属查找:注解里的裸名(或 import 别名)经 from_imports 定位到 # 定义模块,再按 (name, sha1) 精确取类——这是 stub 注入场景的正解:此刻 # trans.ModuleSha1 是"正在翻译的文件",而注解里的类归属 stub 源模块 @@ -3143,6 +3229,24 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): # 模块名 → sha1(精确/后缀/import 别名还原,统一经 # StructGenMixin._resolve_module_sha1 逐级尝试) mod_sha1_v: str = self.Gen.StructGen._resolve_module_sha1(val_name.id, from_imports) + # [vpi 注入窗口] 模块段恰为**本 stub 自己的模块名**(或其末段)时, + # 归属即 CurDefModuleSha1(inject_class 入口 mark_module_class 建立) + # ——与 Name 分支的注入窗口证据同口径:同一 stub 内写成 + # `自身模块名.类名`(无需 self-import)的注解不因"模块名未在 + # Sha1Store 登记"被判不可解析。 + if mod_sha1_v is None: + inj_mod_a: str = self.Gen.StructGen.CurDefModuleSha1 + if inj_mod_a is not None: + inj_nm_a: str = StubMergerStore.LookupModNameBySha1(inj_mod_a) + if inj_nm_a is not None: + inj_seg_a: str = inj_nm_a + dot_a: str = string.strrchr(inj_nm_a, '.') + if dot_a is not None: + inj_seg_a = dot_a + 1 + nm_eq_a: int = string.strcmp(inj_nm_a, val_name.id) + seg_eq_a: int = string.strcmp(inj_seg_a, val_name.id) + if nm_eq_a == 0 or seg_eq_a == 0: + mod_sha1_v = inj_mod_a mod_sha1_rv = mod_sha1_v if mod_sha1_v is not None: full_q_v: str = self._concat_sha1_class(pool, mod_sha1_v, at.attr) @@ -3458,13 +3562,23 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase): li2 += 1 return res_ty - # 无 CPtr 标记:返回第一个非 None(装饰器标记如 CExport/CExtern 被忽略) - left_ty: llvmlite.LLVMType* = self.resolve_annotation_type(pool, bop.left, imported_modules, from_imports, trans, depth + 1) - if left_ty is not None: - return left_ty - right_ty: llvmlite.LLVMType* = self.resolve_annotation_type(pool, bop.right, imported_modules, from_imports, trans, depth + 1) - if right_ty is not None: - return right_ty + # 无 CPtr 标记:返回第一个**非属性标记**的类型侧。 + # 属性标记(t.attr.<名> / t.attr.<名>(...))不是类型,且解析层会把 + # 它们贴在联合的**最左操作数**位置(_PrependDescriptorMarkers)⇒ 若 + # 照旧先解析左边,会把这个标记当类型硬解,把真正的类型侧吞掉: + # 现场 main.vp:44 `V_TPV_VERSION: __attribute__(section(".PRVERSN")) []char` + # ⇒ 左边是 Call(t.attr.section),右边是 t.CArray[char, None](= i8*), + # 结果丢掉数组外壳得到 i8,而初始化器按"指针模式"生成 getelementptr + # → `@g = global i8 ` 非法 IR(opt: got 'ptr' but expected 'i8')。 + # 故标记侧**跳过**(既不参与竞争,也不进入解析)。 + if self._is_tattr_marker(bop.left) == 0: + left_ty: llvmlite.LLVMType* = self.resolve_annotation_type(pool, bop.left, imported_modules, from_imports, trans, depth + 1) + if left_ty is not None: + return left_ty + if self._is_tattr_marker(bop.right) == 0: + right_ty: llvmlite.LLVMType* = self.resolve_annotation_type(pool, bop.right, imported_modules, from_imports, trans, depth + 1) + if right_ty is not None: + return right_ty return None # Subscript 节点: t.CArray[elem_ty, count] → [count x elem_ty]