Commit Graph
514 Commits
Author SHA1 Message Date
TermiNexus 1b2324edab fix(transpyv): 修正 mark_module_class 的表生命周期(ASan 悬空键崩溃)
类别:a(编译器缺陷 —— 上一提交自引入的跨文件悬空 HashTable)

现场:ASan access-violation(READ @0x1),rcx=0x1
  string.strcmp (includes/string.vp:86)
  → HashTable._find_slot (_resize) → __setitem__ → set_int
  → StructGenMixin.mark_module_class → ClassGenDef.translate_class_def
  → TranslatorGenMixin.translate_children → Translator.translate → RunPhase1 → main

根因:`ModuleClasses` 原先在 `mark_module_class` 里**懒创建**,并长期挂在 Gen 上;
表体与键都取自当次文件的 pool,而 pool 在文件/遍切换时会被 reset ⇒ 下一个文件里
`_resize` 重新散列旧键时 strcmp 读到已回收内存(0x1)⇒ 崩溃。

改动(与既有 `VisibleStructs` 同口径:表与键同池、同文件生命周期):
1. `ModuleClasses` 改由 `reset_visible_structs`(每文件翻译前调用)**无条件新建**
   (不再区分 strict 分支),与键同池 ⇒ 无悬空。
2. `CurDefModuleSha1` 指向 `trans.ModuleSha1`(同一 pool 的串),同样在该函数里
   清空,避免下一次 strcat/strcmp 读已回收内存。
3. `mark_module_class` 不再懒建表:池或表缺失时只记 `CurDefModuleSha1`、跳过登记
   (宁可退化为改前行为,也不冒悬空风险)。

语义不变:规则 5.5 的证据链仍是 ① 结构体表按名(跨文件/跨遍)→ ② 正在定义类所属
模块 + find_struct_by_module → ③ 本文件已声明类归属表(自引用字段窗口 —— 登记与
查询在同一文件同一遍内,正是该表的作用域)。
2026-09-29 19:14:10 +08:00
TermiNexus 2b8cc2a69b fix(transpyv): 特化名规范化下沉到唯一生成器,裸实参路径不再造第二个名字
类别:a(编译器缺陷 —— 特化名生成点不止一处,实参在"裸名/带前缀"两态并存)

背景(命名源审计,两个独立检索子代理收敛到同一清单):`_norm_type_arg_name`
是唯一规范化器,但"拼 Class[a0,a1]"的构造点有 20+ 处,其中 6 处绕过它。
本轮把规范化**下沉到唯一生成器**,让所有调用方自动同源 —— 该函数对已规范化的串
是幂等的(规则 2/3/6 原样返回,嵌套 rawptr[X] 递归同形)。

改动:
1. ClassGenGeneric._mangle_generic_class_name 增 from_imports 参数:逐实参经
   TypeGenResolve._norm_type_arg_name 归一后再拼串(长度计算与拼接同用归一后列表)。
   唯一调用点 _specialize_generic_class 传 trans._from_imports。
2. ClassGenBase._get_base_name 增 from_imports 参数:Subscript 分支的类型实参
   (原为裸 Name.id)同样归一 —— 该函数产出的父类特化名要拿去 find_struct_by_module
   查、并写入 entry.ParentName,而注册名由生成器产出,不归一必然对不上
   (父类 GSListNode[Value] vs 注册的 GSListNode[{sha1}.Value])。
   三处调用点(_get_parent_class / _collect_parents / _trigger_base_specialization)
   传 trans._from_imports;递归调用透传。
3. _trigger_base_specialization 本身无需改:它的裸实参进入 _specialize_generic_class,
   已由第 1 条统一归一。

收敛效果(连带):_specialize_generic_class 的 spec_name 与
TypeGenResolve._resolve_generic_subscript_ty 自拼的 gen_mangled 现已同形(后者本就
归一),源码注释里"gen_mangled 与特化名可能不一致 ⇒ 用 spec_name_chk 重查"那条
隐性双名自然不再触发。

遗留(未动,见审计清单):ExprGenConst.translate_list_literal /
CallGenCtor._infer_generic_ctor_type_arg 传的内建名不受影响(规则 6 原样保留),
但其"类名"实参现由生成器统一归一;CallGenCall/CallGenDispatch 从已注册名回拆
"[...]" 时用 strchr 按首个逗号切分(深度不敏感)另计。

影响面:ABI 可见(结构体名 / 方法符号后缀段);需 TPV 自举重建 + 回归验证,
重点观察 VpiReader 的泛型键匹配(其键以 T 占位符,初判不受影响)。
2026-09-29 18:53:58 +08:00
TermiNexus 868d48ae22 fix(transpyv): 自引用字段的特化名不再留在裸名(新增"本模块声明类"归属表)
类别:a(编译器缺陷 —— 字段类型在类注册前被冻结成裸名版特化名)

现场(上一轮未修掉的那一处):includes/ast/base.vp:357
    if self.children is None: self.children = list[rawptr[AST]](self.pool, 8)
CastError:源 %"7e2b6dda6e423bfb.list[rawptr[d1ed020479454541.AST]]"*
       → 目标 %"7e2b6dda6e423bfb.list[rawptr[AST]]"*

根因:字段 children: list[rawptr[AST]] | t.CPtr 写在**类 AST 自己体内**(自引用),
其类型在 translate_class_def 的字段循环里解析,而本类的 register_struct 在该函数
**末尾**才执行 ⇒ 解析时 struct 表里没有 AST ⇒ 上一轮 5.5 的两条查法
(find_struct 按名 / find_struct_by_module)全部落空 ⇒ 实参仍留裸名 rawptr[AST],
字段类型被冻结成 list[rawptr[AST]](第一遍 A-pre 即发生);而 :357 的构造调用发生在
类注册之后 ⇒ 带 SHA1 ⇒ 两侧分裂。(同一分裂在 stmts.vp:60 表现为形参侧/实参侧,
本处表现为字段侧/构造侧。)

改动:
1. StructGenMixin 新增"本模块声明类"归属表 ModuleClasses(键 "{模块SHA1}.{类名}")
   与 CurDefModuleSha1(正在定义类所属模块 SHA1,登记时同步记录):
   - mark_module_class(pool, sha1, name) / is_module_class(pool, sha1, name)
   - 键含 SHA1 ⇒ 与翻译顺序/模块切换无关、无需每文件重置,也不会把别的模块的类
     错认成本模块。
2. ClassGenDef 类定义入口(add_visible_struct 之后、枚举/联合分派之前)登记
   mark_module_class(pool, trans.ModuleSha1, class_name),覆盖 class/enum/union/
   renum 全部类种类。
3. TypeGenResolve 规则 5.5 补齐证据链:① 按名取 StructEntry.ModuleSha1;
   ② struct 表暂无条目时用 CurDefModuleSha1 走 find_struct_by_module;
   ③ 仍无条目(正是自引用窗口)时查归属表 ⇒ 加 SHA1 前缀。
   CurDefModuleSha1 取代 CurrentModuleSha1 作为"当前定义模块"真源——后者在
   A-pre/StubMerger 路径未设置或留着上一文件旧值(只有 Translator.ModuleSha1 权威)。

影响面:ABI 可见(特化名);需 TPV 自举重建 + 回归验证。
2026-09-29 18:47:35 +08:00
TermiNexus dddaba3254 fix(transpyv): 5.5 步补「类定义中」回退,字段侧特化名不再留在裸名
类别:a(编译器缺陷 —— 同一特化在字段侧/构造侧仍分裂,方向相反)

现场:includes/ast/base.vp:357
    if self.children is None: self.children = list[rawptr[AST]](self.pool, 8)
CastError:非基本类型指针不可隐式转换
  源(构造调用结果) %"7e2b6dda6e423bfb.list[rawptr[d1ed020479454541.AST]]"*
  目标(字段声明类型) %"7e2b6dda6e423bfb.list[rawptr[AST]]"*

上一处(stmts.vp:60)是"形参侧裸名 vs 实参侧带前缀",本处反向:**字段侧**
仍是裸名。字段 children: list[rawptr[AST]] | t.CPtr 写在**类 AST 自己体内**
(自引用),解析该字段类型时类正在定义中:find_struct('AST') 按名查不到(登记项
尚未命名)⇒ 5.5 首条查法落空 ⇒ 仍返回裸名;而 :357 的构造调用发生在类已注册之后
⇒ 带前缀,两侧再度分裂。

改动:5.5 步在 find_struct 未命中 / ModuleSha1 为空时,回退用与
resolve_annotation_type 的 Name 分支**同一处证据**认定本模块类:
find_struct_by_module(name, self.Gen.TypeGenBase.CurrentModuleSha1) 命中即以当前
模块 SHA1 加前缀。对同一模块的类,"按名取定义模块 SHA1"与"当前模块身份"给出同一
SHA1,故不引入新分裂。文档串规则表同步补该回退。
2026-09-29 18:38:52 +08:00
TermiNexus 65d7f7cc31 fix(transpyv): 泛型实参里的「本模块裸类名」也加定义模块 SHA1,特化名不再分裂
类别:a(编译器缺陷 —— 同一类型在本模块与被引用模块得到两个特化名)

现场:includes/ast/stmts.vp:60
    _set_parent_list(targets, self)
CastError:非基本类型指针不可隐式转换
  源   %"7e2b6dda6e423bfb.list[rawptr[d1ed020479454541.AST]]"*
  目标 %"7e2b6dda6e423bfb.list[rawptr[AST]]"*
外层特化前缀相同,只有类型实参里的类名一个带 SHA1、一个裸名 ⇒ 同一个
list[rawptr[AST]] 变成两个结构体 ⇒ 被"非基本类型指针不能隐式转换"拦下。

根因:_norm_type_arg_name 的第 5 步只查 from_imports(可见性表);**本模块自己
声明的类**不在表内 ⇒ 落到第 6 步原样返回裸名,而被引用模块解析同一类型时第 5 步
命中(from-import 归属模块)带 SHA1 ⇒ 两侧对同一类型产出两个名字。

改动(_norm_type_arg_name 新增 5.5 步):
- 简单裸名(不含 '[' / '.')先经结构体表 find_struct(name) 取该类的
  **定义模块 SHA1**(StructEntry.ModuleSha1)加前缀 —— 与"当前模块"无关,
  故本模块内与跨模块得到同一票名;仅当登记项存在且 ModuleSha1 非 None 时生效。
- 文档串规则表同步补 5.5;第 6 步措辞改为"内置类型名等"。

与第 5 步同口径(都只处理简单裸名):泛型特化名 list[...] 与模块限定名
mod.Class 各有自己的分支,不在 5.5 重复加工。

注:TPC 侧同名规则源(SymbolUtils.ResolveTypeArgStr)是否需要同步,待该现场
回归通过后再核。
2026-09-29 18:31:52 +08:00
TermiNexus 8e7d7feb57 fix(transpyv): rawptr[T] / t.CPtr[T] 对裸类引用吸收一层指针(不再多包一层)
类别:a(编译器缺陷 —— TPV 注解解析与「联合写法 / TPC 两侧」口径分裂)

现场:includes/ast/tokens.vp:217
    ptr: rawptr[Token] = pool.alloc(Token.__sizeof__())
CastError(TPV ExprGenBase.vp:746-753):指针层数不同(源 1 级 → 目标 2 级);
源类型 i8* → 目标类型 %"…Token"**。

根因链:
1. 本文件 Name 分支(:2269-2277):有 __new__ 的堆类裸名注解解析为 Ptr(struct)
   (引用语义,注释明写「对齐 TPC」)⇒ Token → %"…Token"*。
2. rawptr[T] 分支(原 :2865-2871)与 t.CPtr[T] 分支(原 :2928-2939)只做
   Ptr(resolve(T)),不含「裸类引用吸收一层」⇒ %"…Token"**(2 级)。
3. 同一语义的其余实现都有该规则,唯此两处缺失:
   - 联合写法 X | t.CPtr 的 BinOp 分支(:2682-2684 的判据注释 + :2703-2720 回退)
     得 1 级;
   - TPC 侧 LLVMTypeMapper._rawptr_subscript_type_str(内层映射成 %…* 即吸收)
     与 HandlesTypeMerge._MergeAllComponentTypes(IsStruct 时吸收一层)。
   规则真源见 TPC SymbolUtils.IsPtrSubscriptNode 文档:「内层 T 为类引用时该层被
   结构体自身吸收('<sha1>.X*'),其余(标量 / 已是指针的 typedef / 泛型容器特化 /
   嵌套指针下标)逐层外包」。

改动:
1. 新增判据 _ptr_subscript_absorbs_class(slice, resolved):切片**非**用户写明的
   指针包装(联合 BinOp / 嵌套 rawptr[...]、t.CPtr[...]、裸名 CPtr[...])且解析
   结果已是 Ptr(已注册 struct)(find_struct_by_type 命中,与 BinOp 分支同一判据)
   ⇒ 返回 1(吸收该层)。纯判据,不发射类型。
2. rawptr[T] 与 t.CPtr[T] 两个分支接入该判据。

逐条核对保持不变的形态:rawptr[int]→i32*、rawptr[str]/rawptr[bytes]→i8**、
t.CPtr[t.CPtr]→i8**、rawptr[rawptr[X]]→X**、t.CPtr[int]→i32*、
rawptr[Point(无 __new__ 的栈类)]→Point*。

影响面:ABI 可见(字段 / 形参 / 局部变量类型),需 TPV 自举重建 + 整轮回归验证。
2026-09-29 18:07:37 +08:00
TermiNexus c2d7480f3d fix(transpyv): 槽视图统一 cast 到声明类型(47 处,含 codegen 自身)
同 includes 的同类修正:把一级指针 i8* 隐式塞进二级注解(i8** / str*)。
TransPyV 侧 coerce(ExprGenBase._ptr_cast_reason)没有 TPC 那样的「值侧是显式
指针 cast ⇒ 按 bitcast 重解释」豁免,故一旦用新构建的 TransPyV 编到这些文件
必然 fail-fast(回归测试首爆点在 includes/llvmlite 侧,本仓同形态站点随后必然
同样触发)。

涉及的两种形态:
  · `slot: str | t.CPtr = (t.CPtr)(addr)`   —— cast 目标是一级 t.CPtr
  · `nm_slot: str* = (t.CPtr)(addr)`        —— 注解 str* 即 i8**
统一改为 cast 到声明类型:`(str | t.CPtr)(addr)` / `str*(addr)`(后者与
includes/sys.vp `argv_arr: str* = str*(...)` 同一写法)。

本仓 47 处 / 6 文件:
  App/lib/core/Gen/VarGenMixin.vp 3
  App/lib/core/Gen/TranslatorGenMixin.vp 8
  App/lib/core/Gen/Func/FuncGenMixin.vp 13
  App/lib/core/Gen/AsyncGenMixin.vp 15
  App/lib/core/Gen/DebugInfoGen.vp 6
  App/lib/Stub/StubGen/Converter.vp 2
Converter.vp:107 的 `_g_pyi_cdef_names: str | t.CPtr = None` 保持原样
(None 即空指针,任何指针类型均可,不属本类问题)。

只提交本 6 个文件;本仓其余未提交改动(CallGenBase/CallGenDispatch/CallGenFd/
CallGenProp/Generator/Translator/VLogger)与本迁移无关,保持不动。
2026-09-29 15:10:33 +08:00
TermiNexus 07f2f2e9fc fix(transpyv): CallGenCBuiltin 的 clobbers 改为单级 t.CPtr,并同步该文件语法迁移
【类别 c 源码违规】clobbers 声明的指针层数与 alloc/memset 不一致。

现象:
  翻译 App/lib/core/Gen/Call/CallGenCBuiltin.vp 报
  「指针隐式转换已禁止(层数不同):不能用指针类型 i8* 初始化 clobbers: i8**」
  出错表达式 'pool.alloc(8 * ASM_MAX_CLOBBERS)'(Call 形态,CallGenCBuiltin.vp:955:33)。

判据(三条,缺一不可):
  1. memhub.MemBuddy.alloc(size: sizet) -> t.CPtr(includes/memhub.vp)即 i8* 单级;
  2. str | t.CPtr 是 i8**(CChar + PtrCount 2),即字符串指针数组视图;
  3. MemoryOps._ClassifyPtrImplicitConvert 判定顺序为**先比层数**,层数不同直接禁,
     通用指针通配(void*)不跨层。
  故 i8* 结果不能隐式初始化 i8** 变量 —— 与是否同名 str 无关。

取证补充:同函数下一行 string.memset(clobbers, 0, ...) 首参同样是 t.CPtr
  (includes/string.vp: memset(ptr: t.CPtr, value: int, num: sizet)),
  即便只给 clobbers 加 cast,memset 一样会挂 —— 必须把声明本身改回单级。

修法:clobbers 声明改回单级裸缓冲 t.CPtr,每槽按 8 字节字符串指针的写入沿用
  下方既有的显式 cast 槽视图写法
  (cl_ptr: str | t.CPtr = (str | t.CPtr)(cl_addr)),
  与 includes/_dict.vp 的「缓冲单级 t.CPtr、槽视图 str | t.CPtr 显式 cast」范式一致。
  此改**不引入新 cast、不绕过检查**,只是把声明修正到与真实分配/清零层数相符。

同类扫描:全 App + includes 内 str | t.CPtr = *.alloc(...) 形态仅此一处;
  includes/sys.vp 两处早已是显式 str*(...) 转换。

同时并入本文件他人已完成的语法迁移(同文件合并提交):
  - 常量声明 t.CDefine: T = 值 → define NAME = 值 语法糖(_CEXPORT_MAX /
    ASM_MAX_OPERANDS / ASM_MAX_CLOBBERS / ASM_TEXT_BUF_SIZE / ASM_CONST_BUF_SIZE /
    ASM_ARGS_BUF_SIZE / ASM_LINE_BUF_SIZE / LLVMIR_MAX_OPS / LLVMIR_TEMPLATE_BUF);
  - @t.NoVTable + class → novirtual class;
  - 删除常量块内已失效的注释与多余空行。

1 file changed, 17 insertions(+), 18 deletions(-)
2026-09-29 14:47:42 +08:00
TermiNexus 1f93550b6d revert(transpyv): 还原 *existing.Ty = <boxed 变体构造>(c.DerefAs 是绕过自家检查)
a9e4aaf 把 `*existing.Ty = llvmlite.LLVMType.Struct(...)` 改写成
c.DerefAs(existing.Ty, ...)。但 DerefAs ≡ *p = a(_HandleCDerefAsLlvm 的
「整对象拷贝」分支就是 load + store),二者语义完全等价 —— 换写法只是绕开了
*槽 = <boxed 变体> 触发的严格规则③,并未解决根因。

根因在编译器侧:「构造物化」豁免漏了 REnum 变体构造这一形态。已在 TPC 侧修好
(fix(tpc): 「构造物化」豁免覆盖 REnum 变体构造),本处源码随之还原为直白的
`*槽 = <boxed 变体>` 写法,不再用内建绕开严格检查。
2026-09-28 23:21:36 +08:00
TermiNexus a9e4aafae1 fix(transpyv): ClassGenDef 用 c.DerefAs 物化 REnum 变体(替代 *ptr = <boxed 构造>)
c 源码违规:`*existing.Ty = llvmlite.LLVMType.Struct(...)` 把 **boxed 变体指针**
赋给了值槽(指针→值,严格规则禁止)。REnum 变体构造返回 boxed 指针,仓库既定
写法是用 c.DerefAs(ptr, <boxed 变体>) 做整对象物化(includes/llvmlite/__types.vp:195
的同款 `c.DerefAs(ptr, LLVMType.Struct(fields, fcount, name))`),
_HandleCDerefAsLlvm 的"整对象拷贝"分支正是为此(HandlesExprCall.py:4718-4740)。

用 DerefAs 而非改成 `existing.Ty = <新指针>`:本段的目的就是修 Phase 1a 遗留的
**悬空 Name**(注释 L97-101),Phase A 已把该指针登记进 named-type 表;换指针会让
旧条目继续指向悬空 Name,in-place 覆盖才保留指针同一性、真正修掉悬空。

现场报错:非基本类型的值/指针隐式转换已禁止(构造物化除外):
  不能把指针类型 %"50741ad4b4b0934d.LLVMType"* 隐式转为值类型
  %"50741ad4b4b0934d.LLVMType"
2026-09-28 23:15:46 +08:00
TermiNexus fddadf9d56 fix(transpyv): 修正 _PyiClass 的 type_params 局部变量注解(list[ast.AST*]* → list[str]*)
c 源码违规(注解写错,被严格指针规则抓出):ClassDef.type_params 的真身是
rawptr[list[str]](**名字串**列表,见 includes/ast/stmts.vp:475-483 与
parser.vp:3619-3663 的 `list[str](ps.pool, 8)`);`list[rawptr[AST]]` 是
decorator_list 的类型。Converter._PyiClass 把局部变量注解从 decorator_list
抄错成 list[ast.AST*]* ⇒ `.get()` 被解析到 list[ast.AST*].get,而实参是
list[str]* ⇒ fail-fast:

  调用 650c1c3366d09e39.list[10f3f43ddd447eec.AST*].get 第 0 个实参
  %"650c1c3366d09e39.list[str]"* 不能隐式转为形参
  %"650c1c3366d09e39.list[10f3f43ddd447eec.AST*]"*

已核对全仓其余 8 处 type_params 用点(VpiReader.vp:2253/2274/4345、
Phase2/__init__.vp:317、Phase1Scan.vp:153/318、ClassGenDef.vp:69、
CallGenMixin.vp:415)均写作 list[str]*,本次与之对齐。
2026-09-28 22:54:50 +08:00
TermiNexus aa52cf157d revert(transpyv): 回滚跨模块链表 Next 改访问器(改由编译器沿继承链解析字段名)
回滚 13080f6 的 110 处访问器调用,恢复裸字段访问 `x.Next`。

理由:该批访问器是把访问移到定义模块内执行,**绕开**了"继承字段名解析",
属于规避而非修复;缺陷本体在 TPC 侧(导入侧继承展平把泛型基类的继承字段
记成 __inherited_N 占位名,名字丢失)。TPC 已按语义修正:
  · e6451a07 继承展平沿继承链取真实字段名(泛型特化回退模板名),
    展平表直接落 `[("Next", <槽位类型>), ...]`,属性访问是普通表命中;
  · 3ca46c01 顺带修掉 _get_or_create_struct 里"同名枚举成员遮蔽类定义"的 i32 出口。
因此源码侧恢复为唯一写法(裸字段访问),由编译器负责解析。

注:本 commit 只回滚访问器;13080f6 里夹带的 CallGenProp.vp 续行合并(非访问器改动)
已在工作区保留(未提交)。
2026-09-28 21:48:53 +08:00
TermiNexus 13080f646f fix(transpyv): 跨模块链表 Next 字段改用访问器(消除 __inherited_N 占位名 fail-fast)
c 源码违规:llvmlite 的 Value/ParamNode/Function/Param/GlobalVariable/PhiIncoming/
SwitchCase 的 Next 继承自泛型基类 GSListNode[T],而其泛型特化体不在任何 .vpi 中
⇒ 导入侧 class_members 只有 __inherited_N 占位名;严格属性解析(回退已移除)下
裸写 `x.Next` fail-fast。现场:CallGenCall.vp `cur_fb = cur_fb.Next` 报
"[fast-fail] 属性 Next 在类 ParamNode 中找不到(class_members=[__inherited_0, Ty])"。

按仓库既有约定(_Type.md:链表遍历用 paramnode_get_next/paramnode_set_next;
VarGenMixin/TypeSpec/StructGenMixin/VpiReader 等已如此书写)统一改为访问器:
  value_get_next/value_set_next、paramnode_get_next/paramnode_set_next、
  function_get_next、param_get_next、globalvariable_get_next、
  phiincoming_set_next、switchcase_set_next
覆盖 24 个文件共 110 处(含 CallGenProp.vp:122 的嵌套读 cur_arg_pc.Next)。

访问器在 includes 侧为定义模块内直通实现(None 守卫 + x.Next),语义等价、零行为
变化。已全量扫描 core\Gen 全 47 文件与 Stub/Phase/core/main:App 侧已无裸继承字段
访问(剩余 .Next 命中全部在注释里)。
2026-09-28 21:20:58 +08:00
TermiNexus 75ea6fb9b2 fix(transpyv): 消除隐式向下转型(ast.AST* 实参 → 派生 AST 形参)
c 源码违规(严格指针规则:基类指针传派生类形参属向下转型,须显式转换):

1) TranslatorGenMixin._prescan_funcdef_signatures:形参由 ast.FunctionDef* 改为
   ast.AST*,函数体内显式 `fd: ast.FunctionDef* = ast.FunctionDef*(node)`。
   理由:模块级调度器只持有 ast.AST* 子节点,同一处调用的
   translate_function_def / translate_class_def / _register_cexport_from_funcdef
   形参一律是 ast.AST*,本函数是唯一例外(报错现场:第 2 个实参 %"..AST"* 不能
   隐式转为形参 %"..FunctionDef"*)。改为与兄弟入口同口径,调用点无需改动。

2) CallGenDispatch len() 内置分支:_is_list_builtin_receiver 第 5 形参是
   ast.Call*(仅用于 D25 fail-fast 定位节点),实参 len_arg_node 声明为基类
   ast.AST* ⇒ 显式窄化 `len_arg_cl: ast.Call* = ast.Call*(len_arg_node)`。

附:对 App\lib 全量静态扫描(core\Gen 全 47 文件 + Stub/Phase/core/main)确认
此类「基类指针实参 → 派生形参」的隐式向下转型仅此两处,其余调用点均为同类型
或向上转型(规则允许)。

同文件另含仓库内既有书写整理(续行合并),无行为变化。
2026-09-28 20:20:56 +08:00
TermiNexus af5fba81c5 fix(transpyv): 严格指针规则下的源码级窄化与跨层指针写法修正
c 源码违规(按新严格指针规则的显式书写):

1) AssignGenMixin.vp 属性赋值:target 是基类 ast.AST*,而
   CallGenProp._try_property_setter 形参是 ast.Attribute*。基类指针传派生类形参属
   向下转型,严格指针规则下禁止 ⇒ 在 ASTKind 判据确定为 Attribute 后显式窄化
   `at_tg: ast.Attribute* = ast.Attribute*(target)`,并复用到 fd_an(不再重复窄化)。

2) TypeGenResolve.vp `return (str | t.CPtr)(v)`:`(str | t.CPtr)` 语义是 i8**(char**),
   而该函数声明返回 i8*,声明/返回层数不符 ⇒ 改为 `t.CPtr(v)`(i8* 显式转换)。

同文件另含仓库内既有书写统一(ast.OpKind.USub 全限定名、rawptr(...) 取代
(str | t.CPtr)(...)、空行/续行缩进整理),无行为变化。
2026-09-28 19:40:42 +08:00
TermiNexus 825fe6d326 fix(TransPyV): 指针 cast 的跨层 idiom 修正:(str | t.CPtr)(x) → rawptr(x)
c 类(源码违规):(str | t.CPtr)(x) 按既定语义 = i8**(char**,指针的指针),

而这些位置要的是 i8*。TPC 的 return/赋值检查现在按声明精确校验(本次即

TypeGenResolve.vp:1221 `return (str | t.CPtr)(v)` 声明返回 i8* 却 return i8** 被抳下),

该 idiom 在旧的无条件 bitcast 下一直不可见。

改法:86 处 (str | t.CPtr)(X) → rawptr(X)(rawptr 是 t.CPtr 的内置名,同为 i8*;

整数操作数走 inttoptr,指针操作数走 bitcast)。不用 str(X):整数操作数会编译成 itoa

(Gen/FuncGenMixin.vp:1159 注释已记录该陷阱),精确类型仍由使用处的 `: str` 标注承担。

保留 4 处本就该是 i8** 的用例(未改):Converter.vp:820(_g_pyi_cdef_names 声明为

str | t.CPtr 的指针数组)、CallGenCBuiltin.vp:1194/1215(声明即 str | t.CPtr)、

1411(先 cast 再 [0] 取首元素)。

另:Gen/Type/TypeGenResolve.vp:1221 手工改为 return t.CPtr(v)(该文件含你的未提交改动,未一并提交)。
2026-09-28 18:43:59 +08:00
TermiNexus d579d70113 fix(TransPyV): 修正形参/字段链节点的注解为 llvmlite.ParamNode*
c 类(源码违规):这些变量由 new_param_node 构造、或取自 get_struct_fields /

param_list_append / function_get_param_head,实际是 llvmlite.ParamNode 链节点,

注解却写成 llvmlite.Function*。旧的无条件指针 bitcast 掩盖了该错误,严格指针规则下暴露。

涉及:VarGenMixin / CallGenVt / VpiReader / ClassGenEnum / ClassGenVTable /

ExprGenName / FuncGenMixin / FuncGenDef / TypeGenResolve / TypeSpec /

StmtGenCifMixin / AssignGenMixin(纯注解替换,零语义变化)。15 行。
2026-09-28 17:54:13 +08:00
TermiNexus 70448f4590 fix(CallGenLeafKit): 闭包/函数指针调用的形参链表误注解 Function* → ParamNode*(Vipeye 600/814/818)
param_node/pn_tail/pn_new(600 的 Func 实参、593 的 pn_tail.Next)与 fp_first/fp_prev/pn2(814 paramnode_set_next、818 Func 实参)均为 ParamNode 链节点,原先误注解 llvmlite.Function*(不相关类)→ 严格指针规则下四处实参/字段赋值被判 struct↔struct 隐式转换禁止。改注解即根治。
2026-09-28 17:50:05 +08:00
TermiNexus 29acf97dbc fix(CallGenCBuiltin): 多输出 asm 返回 struct 的字段链表误注解 Function* → ParamNode*(Vipeye 1268)
out_pn_head/out_pn_last/pn5 与 new_param_node 的返回、Struct(fields: rawptr[ParamNode]) 形参、ParamNode.Next 语义一致,原先误注解 Function*(不相关类)⇒ out_pn_last.Next = pn5 / Struct(pool, out_pn_head, ...) 被判 struct↔struct 隐式转换禁止。改注解即根治。
2026-09-28 17:18:41 +08:00
TermiNexus eb1065f387 fix(CallGen): Function 的 Param 形参链与 llvmlite.Func 的 ParamNode 构造链不可互传(Vipeye 329/401/1065)
① 新增 CallGenBase._FuncTyFromFunction(pool, f_ref):Function.Params 是 Param 链(__function.vp),LLVMType.Func 的类型构造链是 ParamNode 链(__types.vp,new_param_node/paramnode_set_next),两者是不相关 class ⇒ 严格指针规则下 Func(pool, ret, p_head, cnt) 的实参传递直接报错。改为逐个 new_param_node 重建 ParamNode 链(语义等价,只取各参数类型)。CallGenCBuiltin 329/401 + ExprGenOps 878(第 3 处同型缺陷)三处统一走该 helper。

② CallGenCBuiltin 1065 asm-out 全局变量回退:原把 find_global_in_module 的 GlobalVariable* 直接赋给 llvmlite.Value* 变量(不相关类;且 GlobalVariable 字段序为 Name→Ty,Value 为 Ty→Name,旧无条件 bitcast 会让 .Ty 读到 Name,后续 alloca.Ty.Pointee 全是错位数据)⇒ 改走 c.Addr(模块全局) 分支同款写法 make_global_ref(pool, Name, Ty) 构造带引号的 Ptr(Ty) Value 引用,同时修掉该错位读取。
2026-09-28 17:16:57 +08:00
TermiNexus 23428b63a5 fix(CallGenCall): 虚表/函数类型参数链表节点误注解 Function* → ParamNode*(Vipeye 143/146/179/184)
includes/llvmlite/__types.vp: new_param_node -> rawptr[ParamNode]、paramnode_set_next/Struct/Func 的 params 形参均为 rawptr[ParamNode];本处 vt_first/vt_prev/pn/fp_first/fp_prev/fpn 误注解为 llvmlite.Function*(不相关类)⇒ paramnode_set_next / Struct / Func 三处实参被判 struct↔struct 隐式转换禁止。

修法:6 处注解改 llvmlite.ParamNode*(改类型即根治,不用强转)。
2026-09-28 17:04:57 +08:00
TermiNexus 4807012bd8 fix(CallGenBase): struct 字面量字段链表节点误注解 Function* → ParamNode*(Vipeye 922/927)
事实:includes/llvmlite/__types.vp 里 new_param_node -> rawptr[ParamNode]、paramnode_set_next(node: rawptr[ParamNode], nxt: rawptr[ParamNode])、Struct(fields: rawptr[ParamNode], ...),而本处 th_st/tt_st/en_st 被注解成 llvmlite.Function*(Function 与 ParamNode 是不相关类)⇒ 严格指针规则下这两处实参传递被判「struct↔struct 隐式转换已禁止」。

修法:把注解改成真实类型 llvmlite.ParamNode*(不需要、也不应该 t.CPtr 强转——变量本来就是 ParamNode,强转只是把类型错误重新掩盖)。
2026-09-28 17:02:35 +08:00
TermiNexus 9c427aa548 fix(CallGenCall): 拆开 vtable_base 类型推断的变量复用(LLVMType* 与 StructEntry* 不可同一变量)
严格指针规则下 struct↔struct 隐式转换已禁止(仅允许派生→基类上转),原写法把 get_struct_type_from_value 的返回(LLVMType*)赋给 vc_entry: StructEntry* 再转手交给 find_struct_by_type,靠的是旧的无条件 bitcast。

改为各用独立变量(vb_ty / sp_ty),语义与改前逐字一致(仅去掉两次隐式 bitcast)。
2026-09-28 16:54:25 +08:00
TermiNexus 3bed3a70a6 docs(len): len() 两条 fail-fast 报文措辞与 TPC 逐字对齐(行为零改动)
补上「只支持 定长数组/数组指针、i8*(str/bytes)、带 __len__ 的结构体」+「非 i8 裸指针没有长度语义」的正确做法提示,与 TPC HandlesExprBuiltin._LEN_SUPPORTED_HINT 一致,便于用户对照两侧报文。
2026-09-28 15:53:09 +08:00
TermiNexus be1acdb602 feat(cast): 放行数组退让([N]T* → T* 单向),与 TPC 同口径
新增 _is_array_decay(复用 is_array_type + LLVMType.Array 解构 + AssignGen._stg_ty_equal),在 _pointee_convert_reason 的非基本分支里紧接 _is_inheritance_upcast 之后调用

与 TPC 真值表一致:数组→元素指针 ALLOW、反向 DENY、多维只退一层;其余规则(通用指针/层数/完全相同/None/构造物化/标量无损/继承上转)未动
2026-09-28 15:38:42 +08:00
TermiNexus e2c4c7645e fix(cast): 补继承上转豁免(Derived*→Base* 放行),与 TPC 同口径
新增 _class_name_of_ptr_pointee(复用 StructGen.get_class_name_by_type)与 _is_inheritance_upcast(复用 StructGen.get_mro_chain:目标类名命中源类 MRO 链自身之后的祖先即放行,含多级)

在 _pointee_convert_reason 落入“非基本类型”分支前插入上转豁免;其余规则(通用指针通配/层数不同即禁/完全相同放行/None 任意/构造物化豁免/标量无损)未动;Base*→Derived* 与无关结构体仍禁

注:本提交顺带带入 App/main.vp 的既有版本号改动(0.0.5-dev.479,非本次会话产出)
2026-09-28 15:29:42 +08:00
TermiNexus f03aea15c7 fix(cast): 规则③两侧对齐——聚合值↔指针也上闸,仅豁免构造物化(p: Point = Point())
TPV 的 coerce_to_type 两条“聚合值↔指针”路径不再无条件放行:新增 _ctor_call_agg_ty(AST 层识别构造调用,经 _value_node_of 下沉 + find_struct 取聚合类型)与 _same_agg_ty(委托 _stg_ty_equal);非构造物化一律 fatal_error,报文前缀与 TPC 逐字一致

无损口径以本文件 _scalar_convert_reason 为真源(int↔int 全向、float↔float 全向、int→float 需目标浮点位宽≥源整数位宽、float→int 有损),未动

已知未覆盖:泛型构造(Box(42))静态拿不到特化 struct 名 ⇒ 不豁免;部分 coerce 调用点不传 node ⇒ 可能误报(需实机确认)
2026-09-28 15:17:37 +08:00
TermiNexus ee50ce61fe feat(cast): 指针隐式转换按“同级+被指类型无损”判定(通用指针 t.CPtr/i8* 通配),并修诊断错位
_ptr_cast_reason 统一判据:层数不同→禁;完全相同类型→放行;任一侧通用指针(i8*/t.CPtr)→放行;否则按被指类型:非基本类型→禁,基本标量按既有无损规则(int↔int、float↔float、int→float 需目标浮点位宽≥源整数位宽、float→int 有损)

4 处禁令与新增的“指针→指针”处统一走 _value_node_of/_cast_fail_where:外层 Assign(kind=3) 下沉到真正被转换的值节点;列号按有符号打印(-1/未知打 -,不再出现 4294967295);附源码片段

未动“聚合值↔指针”那条路径(构造返回指针 load 回结构体,如 p: Point = Point()):LLVM 层无法与真实 Foo*→Foo 区分,禁止会直接编不过;规则③在该轴上的收紧待 AST 层区分构造调用后另做
2026-09-28 15:10:30 +08:00
TermiNexus 59a9fc5b1f fix(cast): None 允许隐式转换为任意类型(修 p.fd_used[fd] = None 被“指针隐式转整数已禁止”拦下)
coerce_to_type 顶部新增单一短路判据 IsNoneValue(val, node):AST 为 CONST_NONE,或已物化的 null 常量(实机报错里 node 是外层 Assign kind=3、col=-1,拿不到 None 字面量节点,故以值判定为主)

指针目标保持既有 bitcast 形态(与 T* x = None 初始化一致);非指针目标按目标类型物化零值:整数 0 / 浮点 0.0 / 聚合与其余用 ConstZero

覆盖范围:App/** 下真正 fatal 的指针/整数禁令只有 coerce_to_type 内 4 处,均在短路之后;CallGenDispatch/ExprGenAttr/CallGenLib 等命中都是注释或不报错的原样返回,无遗漏

未动指针层级/被指类型的转换规则(待确认后另行实现)
2026-09-28 14:59:31 +08:00
TermiNexus dfae2f090c refactor(return): 移除 @c.CReturn 处理,改为遇到即 fatal_error(只保留 -> tuple[...])
FuncGenBase:整体删除 _resolve_creturn_ret_ty,新增 RejectCReturnDecorator(兼容 @c.CReturn(...) 与裸 @c.CReturn 两形态;命中即 TypeGenErr.fatal_error,报文指向 -> tuple[ty1, ty2, ...])

FuncGenDef:前向声明与定义两处调用点改为调用 RejectCReturnDecorator,删除随之失效的注释;周围类型解析主干(-> 注解、State/CExport、无注解 fail-fast)逐字未动

Converter:删除 _PyiCreturnArgs 与其“无 -> 注解则合成 -> tuple[...]”的调用点,改为 _PyiRejectCReturnDecorator(vpi 中仍带 @c.CReturn 即报错,提示用 --clean 重新生成该 .vpi)

注:TPV.vp.txt 是生成的单文件打包产物,需重新生成(本次未手改)
2026-09-28 13:57:05 +08:00
TermiNexus d06c4d2c0e refactor(main): 旧写法 a: static int 迁移为前置形式 static a: int
static 修饰符改为语句起始前置(TPC 与 includes 同步改动)后,本文件是 TPV 自身源码,必须一并迁移,否则新编译器编不过自己
2026-09-28 13:49:30 +08:00
TermiNexus f2d7b748ea fix(func): 末尾收尾的可达性判据只认 _ctrl_merge_reachable(删掉“穷尽通配臂即不可达”的错误推断)
症状:includes/llvmlite/__types.vp::_TypePrintDepth 末句 match ty:(各臂只 snprintf 后 br 到 merge)被生成 match.end: unreachable;llc 不给可达 unreachable 生成任何指令 ⇒ 执行掉进布局紧随其后的同一 switch 跳转表,用上一条 snprintf 的返回值 3 当 tag 取 table[3]=Array 臂 ⇒ 把 Int 的 Bits(32) 当指针递归解引用 ⇒ AV at 0x20(裸 0xC0000005;ASan 下同一崩溃被转为 0x80000003 = -2147483645)

根因:last_is_ctrl and (_ctrl_merge_reachable == 0 or MatchHasWildcardArm(...)) 的“追加92”通配臂子句——穷尽性只保证匹配总能命中某臂,不保证各臂都终止且不 br 到 merge;而 _ctrl_merge_reachable 恰是逐臂真正 br end_bb 时才置 1 的精确信号(MatchGenMixin:717 写回)

修法:两处收尾判据(FuncGenDef 模块级函数 / ClassGenVTable 方法)都只在 _ctrl_merge_reachable == 0 时 build_unreachable;merge 可达时走既有分支正常收尾(void→ret void;无注解→ConstZero;有非 void 注解→fail-fast,不发明返回值)

删除随之失效的 MatchHasWildcardArm 与其私有 helper _IsWildcardPattern(全仓已无引用)
2026-09-28 12:41:04 +08:00
TermiNexus 5e100e2195 fix(match): REnum 变体字段绑定不再静默跳过(__types.vp bits NameError)
根因:_handle_renum_match step6 用 renum_name 查 struct 且不校验 Ty;拿不到 entry/Ty 时 step7 的 _bind_variant_fields 被整段静默跳过 -> case 绑定名在 body 里未定义,最终只报 NameError(bits),真因藏到下游

1) 名字回退:变体表与 struct 注册都按裸类名,而 renum_name 来自注解 SourceName(Attribute 可带模块限定名)-> 精确查找失配;与 tag 查表同一裸名推导(tag_cls_bare)

2) 新增 StructGenMixin.find_struct_entry_with_ty:跳过 D30 重注册时就地中和的旧 entry(Name[0]=0/Ty=None/FieldCount=0),避免命中它拿到 Ty=None

3) 禁止静默:仍有 REnum 判据为真却无法绑定时 fail-fast,打印 class/查表名/entry/FieldCount/ty_present(对齐 D30/D32 的响亮失败口径)
2026-09-27 23:58:15 +08:00
TermiNexus a79e723e8d fix(dwarf): DIFile 改用绝对路径,栈转储显示完整路径并回读源码行
根因:CurrentFile 是项目相对路径(./Test/LLvmLiteTest/App/main.vp),LLVM 把该目录写成 CU 的 DW_AT_comp_dir,主文件条目用 dir_index: 0 指向 comp_dir;stackmap 写入端只解析 --debug-line(无 comp_dir)⇒ 行记录退化成裸名 main.vp,运行期既显示不全也无法按路径重读源码行

改法:DIFile filename 保留整条绝对路径(相对路径先经 w32file.GetCurrentDirectoryA 前置 CWD,与 Repl.vp 同款低层调用,不经 os._mbuddy);directory 另存目录前缀。无论 LLVM 保留整条 name 还是拆成 dir+basename,写端都得到绝对路径,故无需改写入端
2026-09-27 19:18:16 +08:00
TermiNexus bb864dff6f fix(traceback): stackmap 的文件名全部退化为 ?? —— _tb_parse_dec 不跳前导空格
dwarfdump 的方括号索引右对齐(file_names[  1] / include_directories[ 12]),_tb_bracket_idx 把 '  1]' 交给 _tb_parse_dec,首字符非数字使循环体一次不进 -> 全部 pidx=0;行记录 fidx=1..N 永不匹配 -> 每条记录落到 '??'(实证 9146/9146 条共用同一个 '??' 串,而函数名/行号均正确)

_tb_parse_dec 增跳前导空格与制表符(修复文件名与目录索引两处)

dir_index: 偏移 st+9 指向冒号本身,恒解析为 0 -> 相对文件名拼不出绝对路径;改为 st+10
2026-09-27 19:01:23 +08:00
TermiNexus 31898ab4a5 fix(stubmerger): define→declare 降级时剥掉行尾函数级元数据附加(!dbg !N)
llc 对 declare 不接受元数据附加,报 expected = here(实证 LLvmLiteTest 949685246c8e241c.ll:898:104 的 declare ... !dbg !71)

根因:降级只删尾部 ' {',源 define 头行的 ' !dbg !71' 被原样抄下;declare 无函数体,!dbg 无意义(FunctionPrint 对 declare 从不打印 !dbg)

判据:参数表之后、括号深度 0、不在双引号内的 ' !';连同前导空白一起截断
2026-09-27 18:30:39 +08:00
TermiNexus 66e697e8df feat(codegen): 模块级 extern 全局生成外部声明(@裸名 = external global T)
注解含 t.CExtern/t.State → is_global_extern:裸名(无 SHA1 头)、Linkage=external、不写初始值、不建 storage

与 export 全局(本模块定义 + 裸名)区分;配合 includes f854f7e 的 export/extern 变量语法
2026-09-27 17:56:51 +08:00
TermiNexus d82f24e17a fix(diag): AST 解析失败日志行内联 行/列 + 原因
四处解析失败上报点(Phase2 的 [Phase A-pre-vpi]、Phase1 的 [Phase 1b-pre] 与单文件、main.vp 单文件)此前只打 AST 解析失败,看不到定位与原因;现内联 第 N 行第 M 列: <原因>,与 dump_parse_traceback 的完整 traceback 同源(原因由 includes/ast/parser.vp 的 _record_* 与 parse_tokens 出口兜底补记提供)。另:App/main.vp 同时带上你工作区既有的 define 迁移/版本号改动。
2026-09-27 17:32:34 +08:00
TermiNexus e65f04f16b fix(codegen): 字段表↔槽位一致性护栏豁免联合体
union 的字段表有 N 条(每成员一条)但 LLVM 布局只有 1 个槽位(成员全偏移 0、按最大成员定宽),二者本不相等;字段访问走 bitcast 到字段类型后的 GEP,不依赖槽位下标。误报实证:EdgeCaseTest 的 class Data(t.CUnion)(i_val/f_val 两成员 → 1 槽)被护栏拦下。
2026-09-27 17:26:04 +08:00
TermiNexus 0279965074 fix(codegen): 指针强转基座 T*(x) 的字段读写补按需过滤类名通道
(ast.Call*(vinner)).IsVolatile 报属性写入失败:T*(x) 被 parser 的 _consume_ptr_suffix_call 解析成 (T | t.CPtr)(x) 强转调用,结果类型是联合简化后的 i8*,lookup_field(i8,...) 必失败,而按需过滤的类名提取只认 Name/Attribute。新增 _base_class_from_cast_base:从强转注解(func 是 BitOr 联合)取裸类名,读路径、写路径、属性链递归父节点三处接入。取裸名而非模块限定名——lookup_field_by_class 的模块限定分支对「包名可解析、类注册在子模块」的形态会 fail-fast。
2026-09-27 16:02:04 +08:00
TermiNexus beccb36b66 feat(codegen): 单目 & 支持对构造调用/函数调用结果取址
&ViperKernel() 此前被左值检查拒绝(只在 Name/Attribute/Subscript 放行)。构造调用 _translate_struct_ctor 返回的就是入口 alloca 的栈存储指针,取址即原样返回该指针;其它返回值落到入口临时槽 alloca+store。复用 translate_unaryop 已求值结果,避免构造被求值两遍(__before_init__/__init__ 跑两次)。
2026-09-27 15:15:59 +08:00
TermiNexus 4255ab9d32 fix(fstr): f-string 改回每站点栈缓冲 + 禁止逃逸,逃逸点改 stdlib 拷贝
ExprGenFstr._fstr_finalize:结果缓冲改回每站点栈 alloca(线程安全、零运行时依赖;每站点各建一个,站点之间不再共享),docstring 与文件头写明语义:f-string 结果是当前函数内的临时值。

GenApi 新增唯一判据 fail_if_fstring_escape 与唯一拷贝出口 escape_fstring_copy(stdlib.malloc + string.strcpy);逃逸位置接护栏:translate_return(函数返回值)、translate_assign(字段/元素/全局变量赋值)、handle_module_level_var(模块级 AnnAssign 初始化)。

编译器自身 7 处逃逸点(ExprGenBase._cast_fail_where 6 处、AssignGenMixin.const_assign_msg 1 处)改为 escape_fstring_copy 显式拷贝——正是造成 ASan stack-buffer-underflow in vsnprintf 的那条路径。

残留(已在 GenApi 头注释诚实记录):间接逃逸(把 f-string 传进会保存它的函数、或放进容器后带走容器)静态判不出;类字段默认值路径未接护栏。全仓自查:TransPyV/App、includes、ViperApps 均无 容器/字段/全局 存 f-string 的写法,也无残留 return f-string。
2026-09-27 14:45:28 +08:00
TermiNexus 72a3c483e4 fix(fstr): f-string 结果缓冲改为模块级全局,修 return f-string 的悬空栈指针
现象:--asan 下 ASan stack-buffer-underflow(READ size 1,地址落在栈左红区)in vsnprintf,栈为 ExprGenBase.coerce_to_type:409 的 f-string 报文;非 asan 构建表现为随机乱码或 0xC0000005。

根因:_fstr_finalize 用 _alloca_at_entry 在当前函数开一个 [FSTR_BUF_SIZE x i8] 栈缓冲并返回其指针;而 f-string 结果在全仓被大量 return 带出函数(ExprGenBase._cast_fail_where、AssignGenMixin.const_assign_msg、CallGenLib 报文构造等),返回后再作 %s 喂给 snprintf 即读已弹栈的帧;且同一函数内多个 f-string 共用同一个 entry alloca 会互相覆盖。

修法:结果缓冲改为每个 f-string 站点一个模块级可写全局 internal unnamed_addr global [N x i8] zeroinitializer,经 bitcast 取 i8*(与 ExprGenConst.create_global_string 同一构造路径,符号链接期解析)。被否决的中间方案记录在注释里:编译期池缓冲 + inttoptr 常量地址会把编译进程的宿主地址烘进 .ll,对产出程序无效,绝不可用。

已知取舍(注释中写明,与 C 的 static char buf[N] 惯用法同):同一站点反复求值且同时保留多个结果时后者覆盖前者;多线程避免共享同一站点结果。彻底消除需运行时字符串分配器(当前无)。
2026-09-27 14:33:54 +08:00
TermiNexus ca5473304f fix(gen): 结构体字段表与 LLVM 槽位一致性 fail-fast 护栏
新增 StructGenMixin.verify_field_slot_consistency:字段表条数必须等于结构体槽位数,且每条目 Ty 与 Index 槽位同类(指针性/整数位宽/浮点位宽);不一致即 fatal_error 并指名 类/字段/下标/两侧类型。由 lookup_field 第一遍调用。

背景(真实崩溃):模块 8a96a2885bae09ca(ExprGenOps)把 ast.Name.id 编译成 GEP 下标 9(该槽是 ctx,i32),同一次构建里 320b55907b889d59(AssignGenMixin)用下标 8(i8*),两者共用同一 %struct 布局。后果:nm.id 读成 ctx(ASTCtx.Store=1)- inttoptr i32 1 - strlen(0x1) - ASan access-violation at 0x1。
2026-09-27 14:00:18 +08:00
TermiNexus 12bd7b7a16 fix(gen): 收敛 static 跨模块拦截为独立方法,消除类型推断污染
新增五个独立方法承载 static 检查与登记:_CheckStaticCrossModule(mod.name 取值)、_CheckStaticCallCrossModule(mod.fn 调用)、_CheckStaticFromImport(from-import)、_RegisterStaticModuleVar(模块级变量登记)、_RegisterStaticFunc(函数登记)。调用点只保留一行调用——内联新增分支会改变超大函数的类型推断,曾导致 translate_attribute 后续 match obj_ptr.Ty 报指针隐式转整数已禁止。

删除 _resolve_module_attribute_global 内与入口检查重复的 static 拦截(其两个调用点均在 _CheckStaticCrossModule 之后)。
2026-09-27 13:12:22 +08:00
TermiNexus dd56580855 feat(gen,static): static 符号禁止跨模块使用(定义侧登记 + 引用侧三处拦截)
语义 = C 的 internal linkage:static 符号(函数/变量)只在定义模块内可见,任何跨模块引用一律 fail-fast(NameError)。

- 登记表:CallGenBase 新增 StaticSha1Arr/StaticNameArr/StaticCount(PermPool 定长槽,与 CexportSha1Arr 同款)+ RegisterStaticSymbol/IsStaticSymbol(按 (符号名, 模块 SHA1) 精确配对,避免跨模块同名误判)。

- 定义侧登记:FuncGenDef 识别注解形态(-> t.CVoid | t.CStatic)与装饰器形态(@t.CStatic;裸 static def 已由解析层归一为装饰器)→ 登记函数名;TranslatorGenMixin.handle_module_level_var 识别注解形态(static x: int 已归一为 t.CStatic | int)→ 登记变量名。

- 引用侧拦截(三处,覆盖全部跨模块形态):ExprGenAttr.translate_attribute 入口统一拦截 mod.name(含 import 别名还原);CallGenMixin.translate_call 入口拦截 mod.fn(...) 调用(按 AST 形态一次覆盖 found_func/vpi/裸名兜底各分支);ImportsGenMixin.translate_import_from_names 拦截 from mod import name。

- 性能:三处检查均以 StaticCount > 0 快速短路——项目中无 static 符号时零开销(不触碰逐条线性扫描与 sha1 解析)。

- 同模块自引用(sha1 相同)与非模块基座(self.method / obj.field / Class.static_method)一律放行。
2026-09-27 12:58:51 +08:00
TermiNexus 424dbd7379 chore(gen): 加 [MODSHA1] 临时诊断定位 string.split 模块名解析失败(定位后删除)
在 translate_call 的模块限定分支加两条探针:

- [MODSHA1]:mod_ret_sha1 为 None 时打印 mod_path_ret / func_name / exact / suffix / ctrl_stdint(对照模块,必在 includes)——可区分 store 整体为空 与 store 有内容但缺该模块。

- [MODSHA1-D]:裸名兜底分支里,若 cl.func 仍是 Attribute(模块限定却解析不出模块的异常组合)则打印 mod_path_ret 与 func_name——覆盖 mod_path_ret 为 None 的情形。

同时把 mod_path_ret 的声明从 if 块内移到 if 外(与 mod_ret_sha1 一致),避免下游分支读未声明局部取到栈垃圾。

证据链:报错来自本函数 861 行(全仓唯一 fatal_if_unknown=1 的调用点),条件恒为 _resolve_module_sha1 对 string 返回 None;但 _sha1_map.txt 有 includes/string.vp、其 vpi 有 def split -> int、Phase1Main:390 是唯一写 map 处 ⇒ 静态无法解释,需运行时诊断。
2026-09-27 12:52:03 +08:00
TermiNexus 87f5b1026c chore(gen): 删除 CallGenDispatch 的 [MDISP] 临时探针及其专用 import stdio
该探针(并排取证 __with_ctx.__enter__ 与 pool.alloc 的 H6 判据命中差异)为定位 MpoolOopTest 的「跨模块调用 pool.alloc 找不到模块 SHA1」临时加入,现缺陷已定位,探针与其唯一使用者 import stdio 一并移除;非 local 对象分派逻辑不变。
2026-09-27 12:32:52 +08:00
TermiNexus 9455db0eec feat(gen,type): callable[] / callable[[]] / callable[[A,B],R] 与 t.Callable 逐点等价
- 判据单一真源:_SubscriptAttrIsCallable 扩为 _IsCallableSubscriptValue,同时认 Attribute(attr=Callable) 与裸名 Name(callable | Callable)。

- Subscript 分支在 Name 路径(泛型特化 / CArray / builtins typedef 兜底)之前拦截裸名 callable/Callable → i8*;否则 callable[] 会先撞 slice=None 的 fail-fast、callable[[]] 会被当泛型类名查空。

- TypeGenErr.try_extract_callable_ret 改用同一判据:callable[[A, B], R] 现在也会注册返回类型 R(可间接调用),无签名形态([]、[[]])仍返回 None。
2026-09-27 12:30:47 +08:00
TermiNexus a76cf55a2d feat(gen,type): 注解位置的小写裸名 callable 等价 t.Callable(i8*)
支持 func: callable = 函数名 的写法:Name 分支兜底 map_t_type 命中 callable → Ptr(Int8),与 t.Callable / t.Callable[] / t.Callable[[]] 同一 LLVM 表示(无签名函数指针,只取地址不作间接调用)。

同时明确大写裸名 Callable 同时服务 from t import Callable 与 Attribute 分支的 t.Callable。
2026-09-27 12:28:58 +08:00