TermiNexus
|
15b416b527
|
fix(type): 统一注入期类归属与导入上下文记账,修占位表爆与属性标记误解析
- 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 属性访问失败)。
|
2026-10-02 17:29:38 +08:00 |
|
TermiNexus
|
2c33effee1
|
fix(type): 前向声明补 import-as 别名反查(LookupModNameByAlias)
_forward_declare_ptr_from_node 的 Attribute 分支此前仅走
ImportsGen.lookup_from_import 反查别名;对 `import X as Y`(from_imports 里存
"Y:X")补一道 StubMergerStore.LookupModNameByAlias 专用反查,避免别名解析不到
→ 拿不到模块 sha1 → 无法前向声明而退回 Ptr(i8)。现场候选:GenTypes.vp 的
`import lib.core.Gen.Type.TypeSpec as TSpec`,字段 `Spec: TSpec.TypeSpec*`
(ve_i.Spec.PtrLevel 属性访问失败)。
|
2026-10-02 11:48:31 +08:00 |
|
TermiNexus
|
4958fd2c14
|
debug(attr): [DBG-ATTR] 泛化到任意属性访问失败,打印基座/结构体/字段数(用完删除)
Generator.FuncGenBase 已由前向声明修复;新报错 ve_i.Spec.PtrLevel 属同类症状
(某字段指针的 pointee 不是可查字段的结构体)。去掉 attr=="FuncGenBase" 守卫,
任何属性访问失败都打印 obj_ptr.Ty / struct 名 / 条目 FieldCount / base_class。
仅打印,不改行为。
|
2026-10-02 02:50:01 +08:00 |
|
TermiNexus
|
87e2356eea
|
feat(type): rawptr[T]/t.CPtr[T] 下标形也走前向声明,并覆盖同文件前向引用
承接 c0d3231(X | t.CPtr 已改前向声明):
- t.CPtr[T] 分支:inner 解析为 None 时先尝试前向声明具名占位并返回
Ptr(占位.Ty),保住类身份;无法确定类名/模块时才回 None 交多遍收敛。
rawptr[T] 经 builtins typedef(rawptr = t.CPtr)兜底归一到本分支,故一并覆盖。
- _forward_declare_ptr_from_node 的 Name 分支补"本文件顶层类"回退:用
StructGen.register_file_classes 登记的"{名字 → 本文件模块 SHA1}"前向声明,
覆盖同文件前向引用 / 自我引用(如 JsonValue | t.CPtr 自引用字段)。
无编译验证。
|
2026-10-02 02:48:26 +08:00 |
|
TermiNexus
|
c0d3231e02
|
fix(type): 未注册类指针改前向声明(具名不透明占位)+ 定义侧原位填充
对齐 TPC 语义(未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充)。
此前 TPV 对"指向尚未注册类的指针"回退匿名 Ptr(i8),丢类身份且被冻结后
无法修复:现场 CallGenBase.Gen: Generator.Generator* 与 Generator 构成
循环依赖(Gen→Generator,Generator.CallGenBase→CallGenBase),Gen 先于
Generator 注册 ⇒ 冻结成 i8* ⇒ self.Gen.FuncGenBase 属性访问失败
(1.log 取证:Generator 条目本身正确 fcount=47,但 lookup_field 收到
struct_ty=<non-struct>)。
- GenTypes.StructEntry 新增 IsForwardDecl 标记(占位条目)。
- StructGenMixin.ensure_forward_class:按"类名 + 定义模块 SHA1"登记具名
不透明结构体 {sha}.Class 占位(名称与定义侧 ClassGenDef 同口径,持久池
分配),幂等;定义侧据此按名定位原位填充。
- TypeGenResolve._forward_declare_ptr_from_node(Attribute/Name 两形,经
from_imports 与模块名表求 sha1,再 _class_true_def_sha1 校正)+ BinOp
(X|t.CPtr) 未注册回退:优先返回 Ptr(占位.Ty);仅在无法确定类名/模块时
才退回 Ptr(i8) 由 AnnotClassName 恢复。
- ClassGenDef:命中前向声明占位时不早退,把新构建的字段体**原位写入同一
Ty 对象**(*existing.Ty = Struct(...),指针身份不变),登记条目沿用占位
条目;declare_only 1/0 均适用。所有已发出的 Ptr(占位.Ty) 引用在填充后
自动指向完整定义。
TPC 侧核对:TPC 本就是"具名占位 + set_body"模型(TypeAnnotationResolver
.CreateFromTypeName / BaseGen._get_or_create_struct / StructGen op 占位),
并有 fail-fast 拒绝类引用降级为 void**,无需改动。
|
2026-10-02 02:40:05 +08:00 |
|
TermiNexus
|
d1c0f6754c
|
debug(gen): 补 [DBG-ATTR] 探针——FuncGenBase 处 obj_ptr 类型与注解类名兜底(用完删除)
已知(1.log 取证):Generator 结构体登记正确(tbl[337] fcount=47 含 FuncGenBase),
真正问题是 `self.Gen` 字段类型退化成 i8*(lookup_field 收到 struct_ty=<non-struct>)。
本探针在报错点补打印:
- obj_ptr.Ty(确认是否为 i8*)
- base_class(_resolve_attr_field_class 取到的 Gen 字段注解类名)
- 用 base_class 走 lookup_field_by_class 找 FuncGenBase 的字段下标
用于判定"注解类名未记录"还是"按类名查询也失败"。仅打印,不改行为。
|
2026-10-02 02:31:09 +08:00 |
|
TermiNexus
|
01e95b51cf
|
debug(gen): 一次性取证插桩——Generator 结构体登记/查找全链路(用完删除)
覆盖点(全部以 [DBG-GEN] 为前缀,仅打印不改行为):
- StructGenMixin.register_struct:IN(name/sha1/ty/StructCount)、DEDUP(命中条目)
- StructGenMixin.add_field:逐字段打印写入 Generator 条目的字段名与下标
- StructGenMixin.find_struct_by_module:IN / HIT / MISS
- StructGenMixin.lookup_field(field=FuncGenBase):struct_ty 名、按 Ty 指针命中条目的
全字段名、以及结构体表内所有含 "Generator" 的条目
- VpiReader._inject_class_struct_declare:IN / EXISTING / RENUM-SKIP / EMPTY /
FIELD-FAIL / REGISTERED
- ClassGenDef.translate_class_def:EXISTING / REGISTER / DONE
目的:一次重建即可判定 Generator.FuncGenBase 查不到是"命中空/旧条目""Ty 错配"
还是"字段名悬空",并看清是哪条登记路径产出的条目。
|
2026-10-02 02:18:55 +08:00 |
|
TermiNexus
|
1ad7abf33d
|
refactor(TransPyV): 统一自举写法(novirtual / define / rawptr→* / 去续行反斜杠)
- @t.NoVTable class X → novirtual class X
- t.CDefine = N → define N
- rawptr[X]、X | t.CPtr → X*
- t.CPtr → rawptr(裸指针标记)
- t.Callable[...] → callable[...]
- 去掉 if/elif 条件的行续反斜杠(改单行写法)
- w32.win32file.X → win32file.X(已 import as)
- core/__init__.vp 去掉未使用的 import t, c / from stdint import *
涉及:CompilerFactory、IncludesScanner、Repl、core/__init__、
Gen/Call/{CallGenBase,CallGenCBuiltin,CallGenCall,CallGenDispatch,CallGenLeafKit}、
Gen/Class/ClassGenGeneric。
|
2026-10-02 02:16:37 +08:00 |
|
TermiNexus
|
7e82ed6940
|
chore(debug): 移除 [DBG-GEN] 一次性插桩(改走静态分析)
按用户要求撤掉 lookup_field 里的临时打印,恢复原函数体。
|
2026-10-02 02:14:54 +08:00 |
|
TermiNexus
|
1d1efc6288
|
debug(gen): 一次性插桩打印 Generator.FuncGenBase 字段查找上下文(用完删除)
AsyncGenMixin.vp:58 `self.Gen.FuncGenBase` 报「属性访问失败」,需确认
lookup_field 收到的 struct_ty 是谁、结构体表里 Generator 条目的 FieldCount/sha1,
以判断是字段缺失、Ty 身份不一致还是按到了空/错误条目。仅打印,不改行为。
|
2026-10-02 02:10:58 +08:00 |
|
TermiNexus
|
f865986675
|
fix(stub): App 预注册注入依赖 vpi 时跳过函数/方法 declare,避免打断多遍收敛
承接上一提交(App 预注册期开始注入依赖 vpi)。注入的依赖 vpi 里,函数/方法
declare 的形参/返回注解可能引用**本遍尚未注册**的类(跨文件注册顺序:被依赖模块
的类可能排在更后,需靠下一遍收敛),resolve 落空会在 declare 注入里 fail-fast,
直接打断 Phase A-pre 的多遍收敛。现场:App 预注册 AssignGenMixin.vp 注入
Translator.vpi 时,InjectTracebackInstallCtor 形参 rawptr[Translator] 解析失败
(Translator 结构体因字段引用尚未注册的 GenTypes 类而本遍未登记)。
改动:VpiReader._ParseVpiIntoModuleImpl 新增 pre_skip_fn_declares(仅当
trans._pre_allow_dep_inject != 0 即 App 预注册时置 1),在「第一遍 B 方法 declare」
与「模块级函数 declare」两处跳过注入。预注册的唯一目的是注册 struct/enum/union
(第一遍 A)与包重导出别名(ImportFrom 段),两者均保留;方法/函数 declare 写在
预注册的瞬时模块里、随即随 mb.reset_to 丢弃,无跨遍作用。全量翻译
(_declare_only==0)不跳过,仍 fail-fast。
|
2026-10-02 01:47:18 +08:00 |
|
TermiNexus
|
981af07d5c
|
fix(phase): App 预注册期注入依赖 vpi,修 list[ast.ClassDef*] 票名前缀分裂
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。
|
2026-10-02 01:03:31 +08:00 |
|
TermiNexus
|
57f8c655c8
|
chore(debug): 移除 [DBG-P7] 一次性插桩(根因已定位)
定位结论见 Debugger/debug-nested-class-struct.md 的 P7:不是别名登记失败,而是
Phase A-pre(预注册 struct/enum/union,declare_only)解析类字段注解时,被 import
模块的 vpi 一概尚未注入 ⇒ 包重导出别名 entry 不存在 ⇒ 字段侧票名保留包前缀并被
冻结进结构体布局,与阶段 A 构造函数侧的真定义前缀分裂。
删除 96c1102 / 3b8a2b2 引入的全部临时打印(VpiReader.vp / TypeGenResolve.vp /
CallGenCtor.vp 三处),代码回到插桩前状态(-108/+0)。
|
2026-10-02 00:59:44 +08:00 |
|
TermiNexus
|
acd6c9a615
|
docs(expr): 去掉指向已删除的 wiki/14-oo-refactor.md 的注释引用
该文档(对象化重构计划)已完成并从 wiki 删除,注释只保留「对象化 P3」的来源标记,
避免留下悬空文档引用。
|
2026-10-02 00:47:17 +08:00 |
|
TermiNexus
|
3b8a2b225f
|
debug(stub): 补全 [DBG-P7] 插桩覆盖所有票名产生路径(定位后删除)
在 96c1102 基础上把"可能产出该票名"的路径一次打全,保证一次重建即可定死根因:
- TypeGenResolve._class_true_def_sha1:MISS 时额外 dump 结构体表里所有名字含
ClassDef 的条目(下标/名字/归属 sha1/Ty 的 IR 名前缀)——直接看出别名 entry
到底有没有、挂在哪个 sha1 上。
- TypeGenResolve.extract_class_name_from_annotation:AnnotClassName 通道算出的
泛型票名(含 ClassDef 才打)。
- TypeGenResolve._resolve_generic_subscript_ty:注解/字段侧算出的泛型特化名
gen_mangled(含 ClassDef 才打)。
- CallGenCtor._extract_type_args_from_slice:构造函数侧实参串的 raw/norm 两态。
- VpiReader._inject_class_struct_declare:进入 + 各出口
(already / renum_skip / field_unresolved / zero_fields / registered)。
- VpiReader._inject_reexport_class_declare:调用现场与四个出口
(already / no_src_entry / alias_reg_fail / alias_ok)。
|
2026-10-02 00:34:38 +08:00 |
|
TermiNexus
|
96c110242e
|
debug(stub): 一次性插桩定位 ast.ClassDef 票名前缀分裂(定位后删除)
9994d2e 改判据后现象不变,需要在一次重建里同时看到三处事实,避免反复猜:
- TypeGenResolve._class_true_def_sha1:cls 含 "ClassDef" 时打印 HIT/MISS 与候选
sha1、命中条目的 IR 名前缀(用 strstr 以便同时捕获 "ClassDef*" 形态);
- VpiReader._inject_reexport_class_declare:src/local 为 ClassDef 时打印调用现场
的「源 entry 是否已注册 / 别名是否已存在」(src_entry=0 ⇒ 别名必然建不起来);
- VpiReader 的 ImportFrom 分支:判定为包 __init__ 时各打印一行(模块名、包前缀、
子模块名与解析出的 sha1),确认包判定与子模块解析是否成立。
全部带 [DBG-P7] 前缀,便于 grep 与事后清除。
|
2026-10-02 00:32:05 +08:00 |
|
TermiNexus
|
9994d2e983
|
fix(stub): 包 __init__ 判定改按模块名,修 ast.ClassDef 票名前缀分裂
存量缺口:VpiReader 判定"本 stub 是否包 __init__"依赖 sha1→rel_path 反查
(strstr(rel_path, "__init__.vp"))。该反查在部分运行形态下取不到条目 ⇒ 判定恒
false ⇒ 包重导出类的别名 entry 静默不登记 ⇒ `ast.ClassDef` 这类限定名在泛型实参
里保留包前缀(幽灵名 `list[1abcd5277fa442de.ClassDef*]` 全产物恒 opaque),与构造
函数侧的真定义模块前缀(`list[e70bd9b4487d921d.ClassDef*]`)分裂成两个类型,报
「指针隐式转指针已禁止」(现场 ClassGenGeneric.vp:92 的 CastError)。
改法(不再依赖 rel_path 字段,均为模块名表的既有事实):
- StubMergerStore 新增 LookupModNameBySha1:按 SHA1 反查模块名(只读指针)。
- VpiReader 的 ImportFrom 分支:取 level>0 的兄弟模块 S,用其模块名去掉末段得到包
前缀 P;又因 Utils.PathToModuleName 把 pkg/__init__.vp 记成 "pkg",故
「本 stub 的模块名 == P」即证明本 stub 就是包 P 的 __init__ ⇒ is_pkg_init_rt=1,
同时直接用 P 拼出子模块全名(不再从 rel_path 反推目录名)。
- 模块名证据不足时仍回退原 rel_path 路线,行为不变。
|
2026-10-02 00:07:23 +08:00 |
|
TermiNexus
|
b91dfef777
|
feat(stub): vpi 注入侧递归登记嵌套类为扁平名 {外类}_{内类}
VpiReader 此前只登记顶层 ClassDef,跨模块引用 `外类.内类` 拿不到类型
(TPC 侧 stub.ll 已递归声明,双端不对齐)。现按定义端
ClassGenDef._translate_nested_classdefs 的命名口径补上:
- _inject_class_struct_declare 增加 name_prefix 形参,非空时算出扁平名
real_name;REPL 分支、已注册检查、命名结构体类型名、register_struct、
add_visible_struct 一律改用 real_name。
- 新增 _inject_nested_class_declares:遍历类体内缩进 ClassDef 子节点,
以 real_name 为前缀递归调用(多层嵌套自然展开为 A_B_C)。
- 嵌套登记后再次调用 mark_module_class 复原"正在定义类"窗口:否则内层
类名会顶掉 CurDefClassName,使外层字段注解的规则 5.5 自引用证据判错、
且 `外类.内类` 的守卫①失效。
|
2026-10-01 23:39:11 +08:00 |
|
TermiNexus
|
c3f554f1f5
|
feat(struct): 嵌套 class/struct 发射为独立结构体 {外类}_{内类}
用户 2026-10-01 定:struct 内允许叠加 struct(与 class 内写 class 同口径)。
此前类体里的嵌套 ClassDef 被**静默丢弃**(字段循环只认 AnnAssign、
_translate_oop_methods 只认 FunctionDef),现按 TPC 侧 union/renum 的
`{ClassName}_{Nested}` 命名先例发射为独立结构体,与 TPC 提交 5eef845e 同源。
- ClassGenDef.vp:新增 _translate_nested_classdefs —— 在 enum/union/renum/泛型/
异常分派之后、existing 检查之前(所有提前 return 之前,故 declare_only=1/0 都递归)
遍历类体 ClassDef 子节点,把子节点 name 临时改写为扁平名后递归
translate_class_def(本模块所有名字派生点都读 cd.name ⇒ 改名即全链路一致)。
两个护栏:扁平名分配在 PermPool(跨 Phase 存活);递归后还原 CurDefModuleSha1/
CurDefClassName(内层 mark_module_class 会覆盖,否则外层字段解析误判当前定义类)。
- TypeGenResolve.vp:新增 _resolve_nested_class_type 并在 Attribute 分支的
"base 不是模块" 出口前接上 —— 限定名 `外类.内类` 按扁平名查结构体表即可命中
(裸名 `外类_内类` 走既有 Name 分支天然命中)。判据两道:base 是已知类
(find_struct 命中,或正处于该类的定义窗口内);扁平名条目存在且 Ty 非空。
引用两种写法都可用:`外类.内类`、`外类_内类`;多层嵌套递归为 A_B_C。
|
2026-10-01 23:30:15 +08:00 |
|
TermiNexus
|
3ccc37ca3f
|
fix(type): 泛型实参归一先剥尾随星号,消除包前缀票名分裂
_norm_type_arg_name 规则 4 用 strchr('.') 切出 attr 时把尾随 '*' 一起带进了类名
('ast.ClassDef*' → attr='ClassDef*'),于是 _class_true_def_sha1 按名查不到
entry、真定义模块校正失效,留下包前缀:
字段侧 ClassGenBase.vp:123 → list[1abcd5277fa442de.ClassDef*](无定义的幽灵名,
全部产物里只有 = type opaque,从无 = type {...})
构造侧 ClassGenGeneric.vp:92 → list[e70bd9b4487d921d.ClassDef*]
⇒ ClassGenGeneric.vp:92 报「指针隐式转指针已禁止」。
TPC 侧名称域(SymbolUtils.ResolveTypeArgStr 的 BinOp 分支)本就是「先解析基类型 X,
再 AddPointerLayers 追加 '*'」→ {真定义sha}.X*,TPV 与之对齐。
新增规则 1.6:剥掉全部尾随 '*' → 递归归一基名 → 回贴同样多的 '*'。
i8** / int* / T* / 已带 SHA1 前缀的文本逐字不变。
|
2026-10-01 22:40:54 +08:00 |
|
TermiNexus
|
02b9429363
|
fix(type): void* 归并分支的 match 必须守 None,修 ASan 空指针崩溃
BinOp 的 CPtr 分支里,上一笔为 void* 归并新增的 match other_ty 未判空。
other_ty = resolve_annotation_type(另一边) 在 Phase 1a prescan 时**可以是 None**
(本分支下方既有代码就写着 if other_ty is None: ... 回退 Ptr(i8),正说明 None 是
期望值);对 None 做 REnum case 匹配会按其 tag 解引用空指针。
现场:TPV(ASan) 自举 Phase1 报 access-violation on address 0x0(READ),
栈 _prescan_funcdef_signatures → resolve_annotation_type;触发源是用户 WIP 里
新增的 X* 返回注解(parser 把 X* 展开成 BinOp X | t.CPtr,正好走本分支)。
改为 if other_ty is not None: 包住,None 继续走下方既有回退路径。
|
2026-10-01 22:07:41 +08:00 |
|
TermiNexus
|
be14fa5f94
|
fix(ctor): REnum 变体构造识别取错限定名,导致构造物化豁免失效
_ctor_call_agg_ty 从被调方沿属性链一路走到链根裸名,三段式
llvmlite.LLVMType.Struct(...) 于是被判成限定名 'llvmlite'(非 REnum 类)⇒
返回 None ⇒ ClassGenDef.vp:128 的 *existing.Ty = LLVMType.Struct(...) 被
「指针 → 非基本类型值」禁令拦下。改为取「变体名左边那一段」:两段式取裸名
(EnumType.Variant)、三段式取属性名(llvmlite.LLVMType.Struct → LLVMType)。
|
2026-10-01 21:44:55 +08:00 |
|
TermiNexus
|
2f6db41b11
|
fix(void): 形参与字段注解禁止 void,改为编译期 fail-fast
void 只允许用于 void* 与 typedef 不透明结构体别名(用户 2026-10-01 定)。
此前 def f(p: void) / class C: x: void 不走 translate_annassign,会按 LLVM void
直接建形参/字段槽,生成非法 IR 拖到 llc 阶段才暴露。现补 6 处形参落点
(FuncGenDef 前向声明/外部声明/函数体/alloca 各一、ClassGenVTable 签名/alloca 各一)
与 1 处字段落点(ClassGenDef),统一编译期 TypeError。
void* 已在上游归并为 i8*,不受影响。
|
2026-10-01 21:43:11 +08:00 |
|
TermiNexus
|
59865b2a3a
|
feat(TPV): void 值操作 fail-fast(声明 void 变量 / 把 void 值当值用一律报 TypeError)
承上一笔(裸名 void = t.CVoid 别名,仅 `void*` 与 `typedef 不透明结构体 = void` 可用):
- ExprGenBase.coerce_to_type 顶部:void 值被 coerce 到**非 void** 目标(赋值 / 传参 /
返回 / 二元运算对齐)直接 fatal_error(TypeError),不再按 LLVM void 硬拼出非法 IR
("load void" / "bitcast ... to void");void → void 属无值传递,放行。
- AssignGenMixin.translate_annassign:注解解析结果为 void 时禁止声明该变量
(否则会按 LLVM void 建槽)。
|
2026-10-01 21:39:48 +08:00 |
|
TermiNexus
|
e0bcf4bcef
|
feat(TPV): 裸名 void 作为 t.CVoid 的别名(void* = i8*)
用户 2026-10-01 需求:`void` 即 t.CVoid 的别名,只允许两种用法——`a: void*` 与
`typedef 不透明结构体 = void`(后者 `X*` 亦为 i8*)。
- TypeGenResolve 内置名表:新增 "void"(与 "CVoid" 同义 → llvmlite.Void)
- TypeGenResolve._is_cvoid_marker:裸名 "void" 与 "CVoid" 同判
- TypeGenResolve BinOp 联合合并:裸名 "void" 直接置 other_is_cvoid(无需 import t);
解析结果本身是 Void 时亦按 void* = i8* 归并,覆盖 `typedef X = void` 的 `X*`
(否则会拼出非法的 Ptr(void))
- VpiReader:注解文本里的裸 "void" 段与 "CVoid" 同判(has_cvoid → i8*);
has_cptr 分支对解析结果为 Void 的 first_ty 同样归并为 i8*(跨模块侧同口径)
TPC 侧无需改动:HandlesBase 的内置名表已有 'void': (t.CVoid, 0),
`void*` 经既有 CVoid+CPtr 合并即 i8*。
|
2026-10-01 21:37:40 +08:00 |
|
TermiNexus
|
21d5cd1cd1
|
fix(ExprGenBase): REnum 变体构造纳入「构造物化」豁免;错误报文只报问题本身
1) _ctor_call_agg_ty 增加 REnum 变体构造识别:`EnumType.Variant(...)` 的变体名不是结构体名
(find_struct 必 miss)⇒ 取属性链最右裸名,若判为基础枚举则返回该枚举的聚合类型。
否则 `*槽 = <boxed 变体>` 会被「指针 → 非基本类型值」禁令拦下——
现场 ClassGenDef.vp:128 `*existing.Ty = llvmlite.LLVMType.Struct(...)`。
2) 按用户要求简化 Error 输出:CastError 只报问题(原因 + 源/目标类型),去掉
";出错表达式: <形态>(行/列)源码: ..." 后缀(文件与行号已由 TypeGenErr 的 Traceback
帧给出,属重复展示),并删除随之失去调用点的 _cast_fail_where / _source_snippet。
|
2026-10-01 21:17:49 +08:00 |
|
TermiNexus
|
a714ec9d37
|
fix(VpiReader): 返回/形参注解切分只在顶层 | 处断段(括号感知)
_resolve_vpi_ret_annot_str 原按裸 `|` 切段,`list[ast.AST | t.CPtr] | t.CPtr` 会被切成
`list[ast.AST` / `t.CPtr]` / `t.CPtr`;首段再经 strrchr('.') 落到 `AST` ⇒ 定义侧类型解析成
`AST*`(元素指针),而正确答案是 list 专化结构体的指针。现场:跨模块调用 coerce 时报
"源类型 list[AST*]* → 目标类型 AST*"(CallGenLib._coerce_args_to_def_params 的 cargs 形参)。
现切分按 `[`/`]` 深度,只在顶层 `|` 处断段(与 _cache_vpi_param_annots_text 的顶层逗号
切分同口径)。改后该段解析不出 ⇒ 落 i8* 通配,与 `list[str] | t.CPtr` 等同形注解的既有
行为一致;配合 CallGen 侧的 bitcast 对齐(见上一笔)即可安全使用。
|
2026-10-01 20:45:21 +08:00 |
|
TermiNexus
|
8e3811c490
|
fix(CallGen): 跨模块调用的 declare 形参与实参统一以定义侧为准
问题一(declare 形参被首个调用点冻结):CallGenBase._ensure_method_declare 新建跨模块方法
declare 时形参类型取"首个调用点的实参类型",而这条 declare 被同模块所有调用点共用 ⇒ 后续
调用传入另一种具体指针类型时,被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换"。
现场:VarGenMixin.repl_ensure_entry 的 node: ast.AST*,先被 translate_annassign 以 ast.AnnAssign*
建 declare,后 translate_assign 传 ast.AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*"。
⇒ 指针形参与整数同口径,一律采用定义侧(.vpi 注解段)类型;解析不出指针时保留实参类型。
问题二(实参未对齐 declare):declare 改用定义侧类型后实参也必须对齐,否则首个调用点
(由它建立 declare、不走 coerce_to_type)原样传具体指针会与 declare 类型不一致(llc 报
argument type mismatch)。⇒ CallGenLib._coerce_arg_value_to_ty 的同层指针分支改为"类型内容
不一致即 bitcast 到定义侧类型"(原判据只覆盖一侧为 i8*、或模板注解来源两种情形)。
|
2026-10-01 20:29:15 +08:00 |
|
TermiNexus
|
93e983f74e
|
fix(TPV): 删除残留的"| t.CPtr struct 引用语义吸收"(T* 被降一层的真正根因)
现场(0a81e67 之后仍复现):Test/SlotsBugTest 编译 includes/_list.vp:54
TypeError: 下标赋值的左值元素是对象 'd1ed020479454541.AST'(48 字节),右值却是指向
'd1ed020479454541.AST' 的指针
elem_ptr[0] = item
关键事实:`T*` **不是** `rawptr[T]` 下标形——parser 把尾随 '*' 解析成
BinOp(`T | t.CPtr`)(includes/ast/parser.vp:1528-1556 `_make_ptr_type`、:2700-2707)。
所以它走 resolve_annotation_type 的 **BinOp 联合分支**,0a81e67 只修了
rawptr[T] / t.CPtr[T] 两条**下标**分支,覆盖不到这里。
链路(逐段在源码上核对过):
1. 泛型实参 `rawptr[AST]` 经 _norm_type_arg_name 归一为文本 `{sha}.AST*`;
2. `T` 走 Name 分支的泛型替换 → _parse_type_string 的 `T*` 反解析(:995-1018,
按尾随 '*' 层数包 rawptr[...])⇒ other_ty = Ptr(AST),本身正确;
3. BinOp 分支尾部的"struct 引用语义吸收":other_ty 已是 Ptr(已注册 struct)
⇒ 直接 `return other_ty`,**吞掉 CPtr 这一层** ⇒ `T*` 变成 AST*(应 AST**)。
改动:
- TypeGenResolve.vp BinOp 分支:删除该吸收,恒 `return Ptr(other_ty)`
(与 rawptr[T] / t.CPtr[T] 下标形、_parse_type_string 的 `T*` 反解析三处同口径)。
- VpiReader.vp:1614-1621 注入侧的同源吸收一并删除(维持"定义侧 / 注入侧 / 调用侧
三处指针层数口径同步")。
- 两处口径注释同步改为"`| t.CPtr` 恒追加恰好 1 层,无例外"。
安全性(该吸收已是死补偿):它是"裸类名自动升指针"时代的产物——当时裸类名解析成
Ptr(struct),所以要吸收;自 2026-10-01 起裸类名恒为原型 struct,`X | t.CPtr` 由
Ptr(struct) 天然得到 X*。json/__init__.vp:90 的引号注解 `'JsonValue' | t.CPtr`
现在 other_ty 就是 struct ⇒ 吸收本不触发,删除后行为不变(JsonTest 已实测翻译通过并
进入 llc 阶段)。`list[int]`(i32*)、`list[ast.AST]`(struct*)、裸类名 `X | t.CPtr`
(struct*)均不受影响。
待验证:重编译 TransPyV(自举两遍)→ SlotsBugTest / JsonTest / WebTest。
|
2026-10-01 17:46:53 +08:00 |
|
TermiNexus
|
0a81e67e89
|
fix(TPV/TypeGenResolve): rawptr[T]/t.CPtr[T] 恒追加 1 层,删除"裸类引用吸收"旧补偿
现场:Test/SlotsBugTest 编译 includes/_list.vp:54 报
TypeError: 下标赋值的左值元素是对象 'd1ed020479454541.AST'(48 字节),右值却是指向
'd1ed020479454541.AST' 的指针
源码: elem_ptr[0] = item
用户判定正确:`elem_ptr: T*` 与 `item: T` 同为 T,赋值本身合法;错在编译器把 `T*`
少算了一层——T 实例化为 rawptr[AST](= AST*)时,`T*` 应为 AST**。
根因:rawptr[T] / t.CPtr[T] 分支里有"切片是裸类引用(解析结果 Ptr(已注册 struct))
就吸收这 1 层"的旧补偿(_ptr_subscript_absorbs_class)。那是"裸类名自动升指针"时代的
产物——当时裸类名解析成 Ptr(struct),于是 rawptr[X] 会多包一层,需要吸收;
自 875a9be 起裸类名恒为**原型(struct 值)**,Ptr(struct) 天然就是 X*,补偿已无对象;
而**泛型形参实例化为指针类型**时(T = rawptr[AST]),切片的解析结果同样是
Ptr(已注册 struct) ⇒ 被误吸收:`elem_ptr: T*` 变成 AST*(应为 AST**)⇒ 左值元素被判成
48 字节的 AST 对象、右值是指针 ⇒ fail-fast。
改动:
- 删除 _ptr_subscript_absorbs_class,以及 rawptr[T] / t.CPtr[T] 两处调用,改为**恒追加
1 层**:`rawptr[T]` ≡ `t.CPtr[T]` ≡ `T*` ≡ `T | t.CPtr` 四种写法同型(用户口径),
不再有"裸类引用吸收"例外。
- 同步修正四处引用该函数的口径注释(:407 / :433 / :567 / :1000)。
- **保留** BinOp `| t.CPtr` 分支内的同名吸收:它只对**非裸名**侧可达(Constant/Attribute;
裸名侧在更早的"Name 必须已注册 struct"守卫处就改走其它分支),故
includes/json/__init__.vp 的引号注解 `'JsonValue' | t.CPtr` 行为不变。
仓库内也不存在 `rawptr['X']` / `CPtr['X']` 切片写法(仅注释提及),删除吸收无副作用。
已知遗留(本次未改,待你定夺):`T | t.CPtr` 这一写法在 T 实例化为指针类型或类类型时
仍会退化——Name 侧解析出的 Ptr(struct) 被丢弃,随后按名字 'T' 查 struct 失败 ⇒
返回 Ptr(i8)(位置:本文件 BinOp 分支的 keep_other_ty 判定,约 :3166-3194)。
当前仓库没有使用该写法的现场,故未一并改动。
待验证:重编译 TransPyV(自举两遍)→ SlotsBugTest / JsonTest / NoVTableCrossMixinTest。
|
2026-10-01 16:51:31 +08:00 |
|
TermiNexus
|
f748cae562
|
fix(TPV/CallGenCall): 判据化重加兄弟 Mixin 方法解析(修 NoVTableCrossMixinTest 未定义符号)
现场:Test/NoVTableCrossMixinTest 链接期报
ld.lld: error: undefined symbol: 1e19037e6cfdce36.BaseB.helper
>>> referenced by ...(1e19037e6cfdce36.BaseB.caller)
ld.lld: error: undefined symbol: 1e19037e6cfdce36.MixinC2.mid2
>>> referenced by ...(1e19037e6cfdce36.MixinC2.outer2)
这是 455c760 删除"兄弟 Mixin 兜底"的必然结果:NoVTable 多继承下,Mixin 内的
self.<兄弟方法>()(Combined(BaseA, BaseB) 中 BaseB.caller 调 BaseA.helper)本类
MRO 查不到、跨模块 stub 也没有 ⇒ 调用名退化成 {sha1}.BaseB.helper(该符号不存在)。
改动(不是恢复原兜底,而是**判据化解析**,不做"同名即绑"的猜测):
- CallGenCall._translate_method_call:在自身 MRO 查找失败后新增兄弟 Mixin 解析,
两条**正向判据**缺一不可:
① 接收类必须是某个已注册类的**直接父类**(它确实被组合使用,是真 Mixin)——
挡掉 includes/w32/fileio.vp 的 FileW.__init__ → self.seek() 误绑 File.seek
(FileW 无人继承,不是任何人的 Mixin);
② 候选类与接收类必须同处某个已注册**组合类的 MRO**(真·兄弟)。
命中后 found_func / mc_anc 都指向定义类 ⇒ 调用名用定义类 SHA1 前缀(符号存在),
this 由既有的 mc_anc this-adjust 分支按组合布局前移(该分支未改动)。
只取**本模块内已有函数**的候选(跨模块定义类需经其 text.ll 取签名,本轮不启用);
接收类为跨模块类时不启用(沿用原 sib_recv_local 守卫,避免误绑跨模块同名方法)。
- 新增两个判据辅助方法:_recv_is_mixin_base / _mixin_sibling_same_composite。
待验证:重编译 TransPyV(自举两遍)→ NoVTableCrossMixinTest / WebTest / FileIoTest。
|
2026-10-01 15:00:55 +08:00 |
|
TermiNexus
|
dc4e06d88b
|
fix(TPV): 本文件自己声明的 extern 裸名优先于依赖 vpi 注入的同名声明
现场:Test/WebTest 编译 includes/socket.vp:236 报
CastError: 指针隐式转指针已禁止:源 %"56d0ee99ecf0a0b5.SocketAddr"* →
目标 %"99c88d4cbd933ac6.WSASocketAddr"*
源码: result = connect(self.fd, c.Addr(addr), 16)
机制(**链接期无冲突**——两边都不是 export,@connect 是同一个 C 符号,最终只有
ws2_32 里一个定义;问题在"编译 socket.vp 时的同一个模块内"):
1. extern/state/export 声明的符号**不带 SHA1 前缀**(FuncGenDef.vp:172-173
`mangled_name = fd.name`)⇒ socket.vp 与 w32.winsock2 都产出裸名 @connect
(前者形参 SocketAddr|t.CPtr,后者 WSASocketAddr|t.CPtr)。
2. Translator 的 prologue 先注入依赖 w32.winsock2 的 vpi
(StubMergerBuild.LoadDepVpiIntoModule → VpiReader.ParseVpiIntoModule →
_inject_function_declare:create_declare + module_add_function),当前模块里
先出现了 winsock2 版的 @connect。
3. 随后 forward_declare_functions 处理 socket.vp 自己的 `extern def connect` 时,
find_func_in_module 命中已存在 ⇒ `continue`(FuncGenDef.vp:178-182)
⇒ **本文件自己的声明被静默丢弃**,调用点改按 winsock2 的形参 coerce
(报错里的目标类型 WSASocketAddr* 正是 winsock2 版的指纹)。
(同文件 send/recv/socket/listen 亦被顶掉,只是形参是 i8*/u64,不报错——
即 socket.vp 的 POSIX 分支本来就在调用 winsock2 的声明。)
改动(注入期跳过"本文件已声明的裸名",让本文件自己的声明优先):
- StructGenMixin:新增 FileFuncNames 表 + register_file_funcs(pool, tree) /
is_file_func(name),与既有的 FileClassNames / register_file_classes /
is_file_class 同规矩(键与表同池、每文件随 reset_visible_structs 重建)。
- GenApi:新增 register_file_funcs / is_file_func 委托包装。
- Translator._translate_module_level:在 register_file_classes 之后调用
register_file_funcs(三种模式共用、早于依赖 vpi 注入)。
- VpiReader._inject_function_declare:算出 mangled name 后,若 `use_bare_name != 0`
且 `is_file_func(fd.name) != 0` 直接返回(不注入)。判据只用**裸名**:带 SHA1
前缀的依赖函数名与本文件同名声明不会互相顶替,避免误跳过跨模块调用。
待验证:重编译 TransPyV(自举两遍)→ WebTest / JsonTest / FileIoTest。
|
2026-10-01 14:46:47 +08:00 |
|
TermiNexus
|
d979cc8c1f
|
fix(TPV/ExprGenName): 结构体值局部变量在使用处要求同型结构体值时 load 出值
现场:Test/WebTest 编译 includes/socket.vp:121 报
CastError: 非基本类型的值/指针隐式转换已禁止(构造物化除外):
不能把指针类型隐式转为非基本类型值(Foo* → Foo)
源类型 %"56d0ee99ecf0a0b5.SocketAddr"* → 目标值类型 %"56d0ee99ecf0a0b5.SocketAddr"
现场代码:
class SocketAddr: family: u16 / port: u16 / addr: u32 / zero: u64 # 纯结构体,无 __new__、无方法
def _MakeAddr(host: str, port: INT) -> SocketAddr:
addr: SocketAddr # 结构体值局部变量
string.memset(c.Addr(addr), 0, 16)
...
return addr # ← 第 121 行报错
根因:ExprGenName.translate_name_value 对"结构体值局部变量"的右值**恒返回 alloca 地址**
(原实现,注释解释为"对象即指针"语义,避免 ptrtoint %Struct 非法指令);而 _MakeAddr
的返回类型经 resolve_annotation_type 解析为**结构体值**。translate_return 会把函数返回
类型作为 target_ty 下传给 translate_value(StmtGenCifMixin.vp:557-558),于是 `addr`
求值出 %SocketAddr* → coerce_to_type 的"指针 → 非基本类型值"分支
(ExprGenBase.vp:954-970)只对"构造物化"豁免 → 报错。
标量局部变量无此问题(同函数内非 Struct 分支会 build_load 出值),属结构体值局部变量
独有的表示差异。
改动:ExprGenName.translate_name_value 的 Struct 分支增加 target_ty 判据——使用处
**明确要求同一非基本类型(非指针)**时(_same_agg_ty 判据:与"构造物化豁免"同一类型
匹配真源 AssignGen._stg_ty_equal),像标量一样 build_load 出值;否则维持既有"返回地址"。
- target_ty 为指针时不命中(裸类形参 lowering 为 Ptr(struct),见 FuncGenDef)⇒
Test/StructTest 的 point_sum(p) 这类实参传址行为不变。
- 字段访问基座(ExprGenAttr.vp:743-744,直接 lookup_var 取地址)、c.Addr 取址
(CallGenCBuiltin.vp:293)、方法接收者均不经本函数取地址,不受影响。
- 同时修正该处已失真的注释(原文举例 `x: Cls | t.CPtr` 实际解析为 Ptr(struct),
并非 Struct 值局部变量)。
待验证:重编译 TransPyV(自举两遍)→ WebTest / StructTest / ClassArrayTest / JsonTest。
|
2026-10-01 14:26:18 +08:00 |
|
TermiNexus
|
25c1c9875b
|
fix(TPV/VpiReader): 阻断 vpi 自注入(循环 import)+ __init__ 声明端与定义端同取 void
现场:Test/JsonTest 编译 includes/json/__parser.vp 后 llc 报
06cb4f5b8fd08841.ll:259:7: error: value doesn't match function result type 'i32'
ret void
(该函数是 define i32 @"06cb4f5b8fd08841.ParseState.__init__",函数体末尾却是 ret void)
根因两层,都在 vpi 注入侧:
1) 自注入:__parser.vp 与 json/__init__.vp 循环 import(__parser 顶部
`from . import (JsonValue, ...)`,__init__.vp 底部 `from .__parser import parse`)。
__parser 编译时,其**自身** vpi(06cb4f5b8fd08841.vpi 里有
`class ParseState: def __init__(self, src: str, pool: rawptr[...]): pass`)经依赖链
(deps: t c stdint memhub string json → json 的依赖含 json.__parser)被反向注入
⇒ 模块 IR 里先出现 `declare i32 @"06cb....ParseState.__init__"`(IsDeclared=1)。
该方法随后走 _translate_method 的"复用前向声明"路径,函数体按**定义端**语义发射
(__init__ → 末尾 ret void),而函数对象自身的 RetTy 仍是注入时的 i32。
2) 类型口径分裂:VpiReader._inject_method_declare 对"方法无返回注解"一律退 i32 默认,
而定义端 ClassGenVTable._translate_method 对 __init__ 恒 Void ⇒ declare i32 /
define void。此前只在跨模块出现(不同 .obj,链接器不校验类型,静默 ABI 不一致);
自注入把两者塞进同一个模块,才被 llc 拦下。
改动:
- VpiReader._inject_method_declare / _inject_function_declare:新增自注入守卫——
仅全量翻译阶段(trans._declare_only == 0)且 src_sha1 == trans.ModuleSha1 时直接返回。
本模块自己的 define 由 ClassGenVTable._translate_oop_methods / FuncGenDef 负责发射,
不需要也不允许经依赖 vpi 反向注入(注入侧类型可能与定义端不一致)。
- VpiReader._inject_method_declare:__init__ 无返回注解 → Void(与定义端一致),
消除 declare/define 返回类型分裂。__init__ 的返回值在三处调用点
(CallGenCtor.vp:227 / :589 / :1275)均未被使用,改 void 不影响调用方。
待验证:重编译 TransPyV(自举两遍)→ JsonTest / FileIoTest。
|
2026-10-01 14:11:45 +08:00 |
|
TermiNexus
|
2bfcb6ef6e
|
fix(TPV): 修 455c760 遗漏——args_node 必须声明在 decl_reuse 分支之外
现场:Test/FakeDuckTest 编译 includes/viperio.vp 第 12 行
self.data = data → NameError: 未定义名称 'data'
(Buf.__init__ 的第一个形参名在方法体里找不到。)
原因:455c760 把整段"参数创建块"置于 if decl_reuse == 0: 之下时,把
args_node: ast.Arguments* = fd.args 一并圈了进去;但函数体段的
"为其他参数创建 alloca 并 store"(ClassGenVTable.vp:904 起)仍要遍历
args_node。复用前向声明(decl_reuse==1,现已是常规路径)时 args_node
未定义 → 形参不进符号表 → 方法体引用形参名即报 NameError。
修法:把 args_node 的声明提到 decl_reuse 分支之前。该块内其余变量
(md_defaults/md_default_count/md_param_count/md_args_node/md_ags/
md_alist/md_dlist/max_funcs/first_pname/first_pname_buf/fd_ags/
fd_first_arg/ags/alist/an/start_idx/ai/arg/param_ty/resolved/pname/
i1_ty_si/i1_ptr_ty_si)已逐一核对,全部只在块内使用,无需外提。
待验证:重编译 TransPyV(自举两遍)→ FileIoTest / FakeDuckTest。
|
2026-10-01 13:57:04 +08:00 |
|
TermiNexus
|
455c7608b1
|
fix(TPV): 方法先声明后定义(消除方法查找的源顺序依赖),删除兄弟 Mixin 兜底
现场:Test/FileIoTest 编译 includes/w32/fileio.vp 报
CastError: 源 %"b0721da228be3d55.FileW"* → 目标 %"b0721da228be3d55.File"*
(指针隐式转指针已禁止:被指类型是非基本类型)
根因(有旧 IR 佐证,非本轮改动引起):FileW.__init__(源 304 行)里的
self.seek(0, SEEK_END) 被解析成 File.seek,而不是 FileW.seek:
1) _translate_method 只在方法体翻译时才 create_function,于是方法查找
(find_func_in_module)依赖方法在类体内的书写顺序:FileW.seek 在源 403 行,
晚于 __init__ → 查不到 → None;
2) MRO 无父类 → None;
3) 兄弟 Mixin 兜底随即遍历全部已注册 struct,取"本模块存在同名方法"的
第一个候选类 → 命中 File → 绑成 File.seek,self 再被 this-adjust
bitcast 成 File*。
旧 IR(TransPyV\.tpv_build\temp\b0\b0721da228be3d55.text.ll,mtime 9/27,
早于本轮全部改动)里就是:
%97 = bitcast %"...FileW"* %96 to %"...File"*
%98 = call i32 @"...File.seek"(%"...File"* %97, i32 0, i32 2)
即靠一层隐式跨类 bitcast 静默通过;指针规则收紧后以 CastError 报出。
(File.__init__ 的 self.seek 同样查不到,只是兜底恰好扫到 File.seek 而"碰巧正确"。)
改动:
1. ClassGenVTable._translate_oop_methods:翻译方法体之前,先对本类**全部方法**
跑一遍 sig_only=1 建前向声明(declare + 参数 + 函数表登记)——与
FuncGenDef.forward_declare_functions 对模块级函数做的事完全同构,
使方法查找不再依赖类体内的书写顺序。
2. ClassGenVTable._translate_method 新增 sig_only 形参:
· 签名部分改用 create_declare(IsDeclared=1),已存在同名前向声明则复用;
· 发射函数体前把 IsDeclared 清零转 define(对齐 FuncGenDef.vp:544-545),
否则打印端按 IsDeclared 输出 declare、函数体整体丢失;
· 复用声明时跳过重复的 add_param 与 add_func_to_table;
· 命中同名函数且 IsDeclared==0(函数体已发射)时直接返回,杜绝重复发射
(llvmlite.create_function 不做同名去重,旧写法会造 duplicate symbol)。
3. CallGenCall._translate_method_call:按"不兜底、fail-fast"决定,整块删除
兄弟 Mixin 兜底(遍历 struct 表猜候选类)及其死分支
(mixin_hit / mixin_ret_ty / mixin_anc_name / mixin_anc_sha1 的声明与消费点)。
方法未命中即为真未定义,交下方 text.ll / vpi / 本模块豁免路径
(CallGenCall.vp:962 起的 is_local_mod 分支,从已声明的本模块函数直接取
返回类型)处理,不再猜候选类。
4. 同步修正 ClassGenVTable 顶部 _translate_method 的注释块与函数文档串。
TPC 侧无需同步:TPC 的声明阶段把类成员全部登记进 SymbolTable,本就不存在
"方法查找依赖源顺序",也没有这段兄弟 Mixin 扫描(HandlesExprCall.py:3877-3914
的兜底带"调用类自身未声明该方法"守卫,与本次删除的 TPV 版不同)。
待验证(不自行执行编译命令):重编译 TransPyV(自举需编两遍),再跑
FileIoTest / NoVTableCrossMixinTest / DictTest / 泛型相关测试。
|
2026-10-01 13:45:10 +08:00 |
|
TermiNexus
|
875a9beb08
|
refactor(TypeGenResolve): 裸类名恒为原型(struct 本身),删除 5 处 HasNew 自动升指针
口径(用户 2026-10-01 定):**裸类名注解 = 原型(struct 本身,类型/布局语义)**,
堆对象引用必须显式写 `T*` / `T | t.CPtr` / `rawptr[T]`。旧行为"有 __new__ 的类
(堆对象)裸名 ⇒ Ptr(struct)"是为兼容 `d: dict = dict(bd)` 这类"裸名 + 构造"写法
而设,代价是同一类型两种写法分裂:
includes/json/__init__.vp:79 `def __getitem__(...) -> 'JsonValue' | t.CPtr`
左边(引号字符串注解)先被解析成 Ptr(struct),联合分支再包一层 ⇒ JsonValue**
而 `cur: JsonValue*` 是 JsonValue* ⇒ :90 `return cur` 报
「指针层数不同(源 1 级 → 目标 2 级)」(660427f 已在联合分支做过吸收,
本次从源头统一:裸名不再自动升指针,吸收那条规则对裸名侧自然不再触发)。
改动(resolve_annotation_type 的裸名/限定名分支,共 5 处,全部去掉 HasNew 分支):
1. `{16hex}.Class` 已归一前缀名分支(原 :2768-2770)
2. 本模块自有类分支(原 :2806-2814,删掉那段"有 __new__ ⇒ Ptr"的注释与分支)
3. from-import 归属分支(原 :2832-2834)
4. 可见结构体裸名分支(原 :2842-2844)
5. `mod.Class` 限定名分支(原 :3040-3044)
影响面盘点(静态扫描 474 个 .vp):本文件内"裸堆类名注解"16 处、跨文件候选 63 处
(含同名跨模块误报),典型:
includes/hashtable.vp:45 ht: HashTable = HashTable(mb)
includes/_dict.vp:29 d: dict = dict(mb)
Test/BuiltinDecoratorTest/App/main.vp:33/36/68/98/137/180/184/194/199/234/241/248
TransPyV/Test/App/new_test.vp:81/93/105
Test/{ClassArrayTest,StructTest,ClosureTest2,StringIterTest}/App/*.vp 若干
这些站点需按新口径显式补 `*`(或改用 `T | t.CPtr`);真实集合以编译器 fail-fast
报错为准(同名跨模块无法静态判定归属)。
TPC 侧核查结论:**无需改动,TPC 本就同口径**。证据:
1. 全仓 `HasNew|has_new` 仅 8 处,全在**构造路径**——HandlesAnnAssign.py:401
(`list[T] = [...]` 字面量构造)、HandlesExprCall.py:5105/5161/5170/5198/5235/5263
(`ClassName(...)` 构造,`_HandleClassNewLlvm`);**注解解析链上一处都没有**。
2. 规范注解解析 `TypeAnnotationResolver._from_node_name`(裸 Name)、
`_from_node_attribute`(mod.Class)、`_from_node_constant`('X' 引号注解)
全程不查 __new__,裸类名一律 PtrCount=0(原型)。
3. 局部变量路径 HandlesAnnAssign.py:678-683 显式把裸类名 PtrCount 由 1 改回 0
("纯结构体名注解应为值类型"),仅保留 vtable 类为指针;模块级全局
HandlesAssign.py:1840-1856 亦按 struct 值类型建 GlobalVariable。
4. 符号表类条目 PtrCount 默认 0(SymbolTable._InsertClassSymbol:489-518 从不设
PtrCount;TypeSpec.py:139 默认 0)。
因此 `d: dict = dict(mb)`(dict 有 __new__、非 vtable 类)在 TPC 侧本来就是
struct 值语义,与本次 TPV 改动后的口径一致。json/__init__.vp 的
`'JsonValue' | t.CPtr`(定义端 0+1 层、声明端裸类引用吸收 1 层)两侧都已是
`%"…JsonValue"*`(1 层),本次 TPV 改动后亦对齐。
待验证(不自行执行编译命令):先重编译 TransPyV,再跑
JsonTest / DictTest / StructTest / BuiltinDecoratorTest / ClassArrayTest
预期先出现一批"注解需补 `*`"的报错——那正是本次口径变更要求的显式化。
|
2026-10-01 12:38:24 +08:00 |
|
TermiNexus
|
660427f759
|
fix(TypeGenResolve): X | t.CPtr 的 struct 引用语义吸收这层指针,修 json __getitem__ 报 JsonValue** vs JsonValue* 层数不同
现象(TransPyV 编译 includes/json/__init__.vp):
line 90 return cur
CastError: 指针隐式转指针已禁止:指针层数不同(源 1 级 → 目标 2 级)
源 %"e966d554e91364bc.JsonValue"* → 目标 %"e966d554e91364bc.JsonValue"**
根因链(逐环取证):
1. includes/json/__init__.vp:79
def __getitem__(self, key: str) -> 'JsonValue' | t.CPtr:
注意左边是**引号字符串注解**(Constant),不是裸 Name。
2. JsonValue 是带 __new__ 的**堆类**(该文件 :34),按引用语义,裸类名注解在
resolve_annotation_type 的 Name/Attribute 分支直接给 Ptr(struct)
(:2806-2813 注释「有 __new__ 的类(堆对象)注解 → Ptr(struct)(引用语义,对齐 TPC)」,
Attribute/sha1 前缀/from-import 各分支同口径)。
3. 于是 BinOp(`| t.CPtr`) 分支里 other_ty 已经是 Ptr(JsonValue);而 :3176 那段
「Name 且未注册 struct 才丢弃」的守卫只认 Name —— Constant 绕过了它,
带着 Ptr(struct) 走到 :3264 `return llvmlite.Ptr(pool, other_ty)` 再包一层
⇒ JsonValue**。
4. 裸名写法(不带引号)不会暴露:它会在 :3222-3239 被判 td_res_is_ptr=1 →
other_ty 置 None → 走 :3254 回退取 struct 再包一层,恰好抵消。
⇒ 同一类型两种写法产出两个层数(`JsonValue*` vs `JsonValue**`),与用户
2026-09-30 定的统一口径冲突:T* ≡ rawptr[T] ≡ t.CPtr[T] ≡ T | t.CPtr。
改动(TypeGenResolve.resolve_annotation_type 的 BinOp(|t.CPtr) 分支):
- 在 :3264 包装前落实本文件 :3219-3221 注释里早已写明、但此前**未实现**的那条
规则:「| t.CPtr 恒为追加 1 层,唯一例外是 struct 引用语义时吸收这 1 层
(判据 = Ptr 的 pointee 是已注册 struct,find_struct_by_type 命中即吸收)」。
- 即:other_ty 已是 Ptr(已注册 struct) 时直接返回 other_ty,不再加一层。
- 副作用(同步修正同族写法):引号字符串写法、以及 Attribute 分支在
find_struct_by_module 命中 HasNew 时返回 Ptr(struct) 的那条路径
(:3040-3044),都从「多一层」收敛为 T*。
- 不影响的既有例外:VOIDPTR/CVoid|CPtr 等「指针别名」侧解析出的是 Ptr(i8)
(i8 非 struct,命中不到 find_struct_by_type)⇒ `VOIDPTR | t.CPtr` 仍是
i8**(追加一层),与 VpiReader 侧口径一致。
仅 TPV 侧(TPC 侧走 SymbolUtils.AddPointerLayers 的另一套口径,本次不动)。
待验证(不自行执行编译命令):先重编译 TransPyV,再跑
TransPyV.exe --project ./Test/JsonTest/project.json --clean --clear-cache --rebuild-includes --traceback
预期 :90 不再报层数不同;若还有同族报错请把栈贴回。
|
2026-10-01 00:51:51 +08:00 |
|
TermiNexus
|
3329c9beae
|
fix(TPV/CallGenBase): text.ll 返回类型的命名指针不再坍缩成通用 i8*,修 FakeDuck parts[0] 报整数隐式转指针
现象(TransPyV 编译 Test/FakeDuckTest):
main.vp:115 p0: str = parts[0]
CastError: 整数隐式转指针已禁止…出错表达式: 下标表达式 Subscript
(parts = s1.split(','),s1: str,split 由 includes/_fakeduck.vp 的 _str 提供)
根因链(逐环取证,均落在产物文件上):
1. _fakeduck.vp:273 `def split(self, delimiter: str) -> list[str]*`。
2. 定义侧产物正确:temp/88/884eaabef9bd161c.text.ll:1775
define %"b0798adca340002b.list[str]"* @"884eaabef9bd161c._str.split"(...)
(884eaabef9bd161c = includes/_fakeduck.vp,b0798adca340002b = includes/_list.vp)
3. 调用侧 declare 走了 vpi 注解:temp/88/884eaabef9bd161c.vpi:27
def split(self, delimiter: str) -> list[str] | t.CPtr: pass
该联合在 stub 侧解析不出 `list[str]` 这个泛型特化名 ⇒ 坍缩成 i8*(既有设计已知的
「跨模块 declare 的 list[T] 返回类型被简化」),故 _translate_fakeduck_call 里那条
「从定义模块 text.ll 读精确返回类型」的兜底(CallGenFd.vp:134-142)必须生效。
4. 但该兜底读到的 `%"b0798adca340002b.list[str]"*` 又被本函数坍缩回 i8*:
CallGenBase._ParseLLVMRetTyStr 的「含 * 者一律返回通用 Ptr(Int8)」分支
(原注释:"包括 i8*, i32*, %"name"*, { i32, i8 }* 等")。
5. 于是 parts 的 alloca 退化为 i8* ⇒ parts[0] 走字节下标取到整数 ⇒ 赋给 str 报
「整数隐式转指针已禁止」(报文指不到真因,正是本问题最难查的地方)。
改动(CallGenBase._ParseLLVMRetTyStr):
- 新增「命名类型 `%"name"*` / `%name*`」分支:剥引号/空格与尾随 `*`(支持多级),
先 GenApi.get_struct_type(name)、未命中再 GenApi.get_enum_base_type(name),命中即
按星号层数包 Ptr 返回真实结构体/枚举指针;查不到登记项时才保持原来的通用 Ptr(Int8)
行为(名字可能来自尚未登记的模块,不引入新的静默降级面)。
- 同步更新该函数顶部说明(原写「含 * 者返回通用 Ptr(Int8)」,现区分字面量结构体 /
命名类型 / 其余)。
- 收益面不限 FakeDuck:所有经 text.ll 取返回类型的方法调用(CallGenCall / CallGenDispatch
/ CallGenProp / CallGenFd 等)都从「指针一律 i8*」变为「命名类指针精确」,
调用方 alloca 与下标/字段语义不再退化。
待验证(不自行执行编译命令):用 TransPyV.exe 重跑
--project ./Test/FakeDuckTest/project.json --clean --clear-cache --rebuild-includes --traceback
预期 115 行不再报错;若仍在 parts2.get(0)(131 行)等处冒新错,请把栈贴回来。
|
2026-09-30 23:53:18 +08:00 |
|
TermiNexus
|
f2f3e5b7bf
|
fix(TypeGenResolve): 裸名内建类型统一为 t.X/c.X,消 CPtr 与 t.CPtr 的分裂与误报 ERROR
内建伪模块 t/c 没有模块 SHA1(规则 5 的模块名→sha1 表查不到),于是 from t import CPtr 之后的裸名 CPtr 被原样保留,而规则 3 保留的是 t.CPtr ⇒ 同一类型两种票名(list[CPtr] 与 list[t.CPtr] 分裂),并额外刷 ERROR [类型实参归属] 裸类名 CPtr 保留裸名(现场 Test/NewAstTest/App/main.vp,log 里 from_imports 明写 CPtr:t)。新增规则 5.2:from_imports 命中模块 t/c 且 map_t_type 认该名 ⇒ 返回 t.X/c.X(规则 3 已定的规范形)。只对"本名就是 t/c 已知内建类型名"生效,别名写法不猜真实符号名、维持原样。
|
2026-09-30 22:59:01 +08:00 |
|
TermiNexus
|
79f044bc47
|
fix(TypeGenResolve): _class_true_def_sha1 只取 hex 段,修双点前缀 d1ed…461..AST*
GenApi.sha1_prefix_len 返回含末尾 "." 的 17(其文档写明),前一版按 17 拷贝 hex 段后再交 _concat_sha1_class 拼 ".",得到双点前缀 %"….d1ed020479454541..AST*" ⇒ parser.vp:3355 return scratch.children 处 coerce 报 CastError。改为按 17 判形、比对与拷贝都用 16(hex 段本身)。
|
2026-09-30 22:45:26 +08:00 |
|
TermiNexus
|
7b0c05b70b
|
fix(TypeGenResolve): 类名前缀校正为真定义模块,消 ast.AST(包) 与裸 AST 的前缀分裂
规则 4b 对 ast.AST(模块限定属名)走"模块名→SHA1"表,对包名回退到 ast/__init__.vp 的 sha1 1abcd5277fa442de(re-export 模块),而裸名 AST(stmts.vp 字段注解)经规则 5 后缀匹配得到真定义模块 d1ed020479454541(ast/base.vp)⇒ list[1abcd….AST*] 与 list[d1ed….AST*] 两个特化体并存,main.vp:63 classify_list(ifn.orelse,…) 报"指针隐式转指针已禁止"。新增 _class_true_def_sha1:在 cand 名下精确找登记项(find_struct_by_module,不按名回落,避免同名类跨模块误改),取其结构体 IR 名 %"<sha>.{类名}" 的前缀作真定义模块(不能取 entry.ModuleSha1——包 re-export 的别名 entry 记的是包 sha1、Ty 复用源 entry);无证据或前缀相同则原样返回 cand(已一致情形逐字不变)。接线:规则 4a/4b/5 各一行。
|
2026-09-30 22:16:15 +08:00 |
|
TermiNexus
|
0f6e102e02
|
fix(TypeGenResolve): rawptr[T] 下标形漏折叠致两个 list 特化体(修 _list.vp:83 return self 的 CastError)
前一版把 rawptr 从 _is_ptr_marker_text 移除(避免裸 rawptr 参与联合时名说 T*、类型说 T),但下标容器分支复用了同一判据 ⇒ rawptr[T] 完全没折叠,只有 T|t.CPtr 折叠:现场同时存在 list[<sha>.AST*] 与 list[rawptr[<sha>.AST]] 两个结构体,_list.vp:83 return self 的 self(前者)被 coerce 到返回注解(后者)时报 CastError。新增专用判据 _is_ptr_subscript_container_text(rawptr/t.rawptr/c.rawptr/CPtr 一族),下标分支改用它;联合分支仍用 _is_ptr_marker_text 保持与类型域 _is_cptr_marker 同口径。
|
2026-09-30 21:39:21 +08:00 |
|
TermiNexus
|
b289e93650
|
fix(泛型实参 T* 形): 补齐 T* 消费侧(VpiReader 段解析 / AnnotClassName 提取 / 构造推断)
唯一规范形 T*(rawptr[T] ≡ t.CPtr[T] ≡ T|t.CPtr,见 83baf5a/2b2b70e)落地后,
所有"读**已归一实参文本**或**结构体名**并做文本解析/嗅探"的点都必须接受 T* 形,
否则会**静默**走错分支(不是报错,更难发现):
- VpiReader._resolve_single_vpi_type_str:尾随 * 段按层重写回 rawptr[...] 文本形
再走原分支(与既有 rawptr[ 分支逐字等价)。必须补的原因:_subst_template_arg
用特化类名 [...] 内的**已归一实参**替换模板注解里的 T,喂进来的就是 {sha}.AST*;
旧代码只认 rawptr[ / t.CPtr[ / 裸 t.CPtr ⇒ 落到 strrchr('.') 取末段 "AST*"、
查 struct miss ⇒ 返回 None/i8*,跨模块参数/返回宽度被悄悄收窄。
- ExprGenCls / AssignGenMixin 的 _extract_annot_class_name(各 2 处):段尾剔除集
补 '*',剥后再 find_struct(旧形态 {sha}.AST|t.CPtr 按 '|' 分段能命中,新形态
不剥则 miss ⇒ 返回 None ⇒ 链式 .get(0).kind() 分派退化)。
- CallGenCtor._infer_generic_ctor_type_arg:新增 _ptr_layer_type_arg —— 先经
_norm_type_arg_name 归一(保证类名带定义模块 SHA1,否则裸 "AST*" 无法再归一、
反而新分裂),再按 VarEntry/FieldEntry 的 Spec.PtrLevel 补 '*',使构造侧推出的
类型实参与注解侧同票名(否则 Box(x) 这类隐式特化分裂成两个结构体)。
- CallGenDispatch.__sizeof__:'*' 判据由 strrchr 改为**尾随**判定 —— 原写法会把
list[{sha}.AST*](元素为指针的容器)误判成指针并返回 8。
现场:Test/AstTest/App/main.vp:21 的 list[ast.AST | t.CPtr] | t.CPtr 特化链
(→ includes/_list.vp:36 T.__sizeof__())。
|
2026-09-30 21:16:48 +08:00 |
|
TermiNexus
|
2b2b70e636
|
fix(CallGenDispatch): __sizeof__ 认归一后的指针规范形 T*(尾随 * 一律按 8)
泛型实参归一为 T* 后,T.__sizeof__() 的实参串如 {sha}.AST* 既不命中 | 也不命中 rawptr[ 前缀,落回把接收者 Name(T) 当实例翻译 → NameError: 未定义名称 T(现场 Test/AstTest/App/main.vp:21 特化 list[ast.AST|t.CPtr] → includes/_list.vp:36)。在既有两条指针判据旁补尾随 * 判据(同一处、同一顺序,均在 dot-strip 之前)。
|
2026-09-30 20:58:02 +08:00 |
|
TermiNexus
|
83baf5a4b6
|
fix(TypeGenResolve/ExprGenName): 指针类型实参统一为 T*,消泛型特化名分裂(假报错 CastError)
用户定口径:t.CPtr[T] / rawptr[T] / T|t.CPtr ≡ T*(与 TPC 侧
SymbolUtils.ResolveTypeArgStr+AddPointerLayers 逐字同形,两侧必须同票名)。
- _norm_type_arg_name:联合一侧为裸 CPtr 标记 ⇒ 折成 <另一侧>*;单实参的
指针标记容器 rawptr[T]/t.CPtr[T] ⇒ 折成 <实参>*(新增 _is_ptr_marker_text /
_ptr_star_text;刻意只认 CPtr 一族,与类型域 _is_cptr_marker 同判据,
避免名字说 T*、类型说 T 的不一致)
- _parse_type_string:能反解析尾随 '*'(按层包回 rawptr[...],供 ClassGenGeneric
克隆注解时替换实参文本)
- ExprGenName._list_elem_obj_ty_from_struct_name:元素段剥尾随 '*' 后再查结构体
(否则对象元素还原失败、静默退回 Ptr(i8) 丢元素类名)
现场:Test/AstTest/App/main.vp:63 classify_list(ifn.orelse, ...) 源
list[rawptr[<sha>.AST]] vs 目标 list[<sha>.AST|t.CPtr] ⇒ 严格指针规则按
「不同非基本类型」拒绝(假报错);改后同票名 ⇒ 同一结构体。
注意:特化名进结构体名与方法符号名 ⇒ 必须全量重编 includes
(--clean --rebuild-includes --clear-cache)。
|
2026-09-30 20:19:42 +08:00 |
|
TermiNexus
|
f3c708613f
|
refactor(TransPyV): 统一自举写法(字段默认值/novirtual/define/enum/指针注解)
- CallGenBase:字段声明处直接给默认值,InitCallGen 不再逐字段赋值
- CallGenFd/CallGenDispatch:@t.NoVTable 改 novirtual,清理过时注释
- VLogger:LogLevel 改 enum、t.CDefine 改 define 关键字、Logger|t.CPtr 改 Logger*、longjmp 用 rawptr
- Generator:_g_gen 由 Generator|t.CPtr 改为 Generator*
- CallGenProp:合并续行
|
2026-09-30 19:09:22 +08:00 |
|
TermiNexus
|
849001ae9e
|
fix(AssignGen): 禁止注解声明类型被右值继承上转窄化
TPV 侧「注解退化修复」只挡了裸泛型基类降级,漏挡「注解写明基类、右值是派生类」的上转窄化:node: rawptr[AST] = Name(...) 会把 alloca 重建成 Ptr(Name),随后 node = <返回 rawptr[AST]> 变成被禁止的父→子下转(includes/ast/parser.vp:1919 报 指针隐式转指针已禁止:%AST* → %Name*)。补上与 TPC HandlesAnnAssign._rb_is_upcast 同判据的守卫(复用 ExprGenBase._is_inheritance_upcast)。
|
2026-09-30 19:08:58 +08:00 |
|
TermiNexus
|
a92b38e826
|
fix(Translator): 文件级类名预登记覆盖三种 _declare_only 模式(修 Phase 1a 的类型实参裸名分裂)
类别:a(编译器缺陷)
现象:编译 includes/ast/base.vp 报
ERROR [TypeGen]: [类型实参归属] 裸类名 'AST' 本次保留裸名(可能与带前缀形式分裂):
entry_present=0 entry.Ty=None cur_def_mod=d1ed020479454541 cur_def_cls=ASTFlag
is_module_class=0 find_struct_by_module=0 from_imports=*:stdint TokOp:ast.tokens
file=./includes/ast/base.vp
根因链(逐条对应报文):
报文来自 TypeGenResolve._norm_type_arg_name 规则 5.5:五类证据全落空即返回裸名。
- ④ entry_present=0 / ① find_struct_by_module=0:'AST' 尚未 register_struct
(AST 在 base.vp:313;本次归一化发生在更早的模块级函数签名处)。
- ② is_module_class=0:'AST' 的 mark_module_class 尚未执行(类定义入口按 AST 顺序走)。
- ③ cur_def_cls=ASTFlag:CurDefClassName 停在更早的 enum ASTFlag(类结束后不复位),
与裸名 'AST' 逐字不同 ⇒ ③ 不适用(该值同时证明本次不是"泛型特化平移窗口")。
- ⑤ is_file_class + get_file_module_sha1 本应兜住"本文件自己声明的类名",但
register_file_classes 的调用被 `if self._declare_only == 0:` 整块门住;而本次是
Phase 1a(_declare_only == 1)——TranslatorGenMixin 在 1a 会调用
_prescan_funcdef_signatures **预解析模块级函数签名**(含注解归一化)⇒ 证据⑤ 恒落空。
⇒ 该实参特化名被冻结成**裸名**版本,与 1b 全量翻译期解析出的带 SHA1 形式分裂成两个
特化结构体名,后续以"非基本类型指针不可隐式转换"报出(同类旧现场 base.vp:359)。
修复:把 GenApi.register_file_classes(pool, self.ModuleSha1, tree) 从 `_declare_only == 0`
块内提到 _translate_module_level 开头,三种模式(1a=1 / 1a-pre=2 / 全量=0)共用。
该登记只依赖 tree + 本文件模块 SHA1,与导入、类定义窗口、类定义顺序都无关;
reset_visible_structs 在每个模式的 translate 之前调用(BuildPipeline:226 /
Phase1Main:643),FileClassNames 与键同池创建,无悬空风险。未放宽任何 fail-fast。
|
2026-09-30 17:43:15 +08:00 |
|
TermiNexus
|
1cce6c4a47
|
fix(TypeGenResolve): 「保留裸名」误报的第二入口——逐实参归一时跳过定长数组的元素个数位 + CDefine 名不再算类名
类别:a(编译器缺陷)。`ef4421d` 只补了 `_norm_type_arg_name` 规则 1.5 的"跳过第 2 实参",
但那条规则**拿不到容器上下文**的入口不止一个,实测 still 报:
├ ERROR [TypeGen] [类型实参归属] 裸类名 'TRACEBACK_FRAME_BUF' 本次保留裸名 …
entry_present=0 entry.Ty=None cur_def_mod=ba7cdbd0dec2aef8 cur_def_cls=TracebackState
… file=./includes/traceback.vp
两处修(互为补充,判据都指向同一条事实:定长数组的第 2 实参是**常量**,不是类名):
1) `extract_class_name_from_annotation` 的 **Tuple 分支**(:2156/:2167,本函数自己按
`,` 把实参拆开逐个归一,规则 1.5 在这里根本不生效):
`TracebackState.FrameAddr: t.CArray[uint64, TRACEBACK_FRAME_BUF]`
⇒ 先按 `_is_count_arg_container(base_name)` 判容器是 t.CArray/list,是则第 2 实参
(`ti_sn == 1`)**原样保留、不调用归一**(长度计算与拼接两个循环同步改)。
2) `_is_non_class_type_name` 增 **⑤ CDefine 名**:`NAME: t.CDefine = 128` 是编译期常量,
名域就是它自身,永远不是类名 ⇒ 命中 `TypeGenCDef.lookup_cdefine_constant` +
`is_cdefine_found()`(-1 也是合法值,必须看标志)即豁免"保留裸名"取证。
这条判据落在**所有入口共用**的函数上,彻底覆盖其余"逐实参归一"的点
(如 CallGenCtor:768 的泛型实参提取)。
静态核验:同形语句片(`self.Gen.TypeGenCDef.lookup_cdefine_constant(...)` 丢弃返回值、
`elt_name: str = raw` 后条件重赋值、`range`/`len` 循环)过 `lib.vast` 解析。
生效需重新自举 TransPyV.exe。
|
2026-09-30 16:05:29 +08:00 |
|
TermiNexus
|
2a798dd643
|
fix(类型转换): int→i1 目标改为真值归一化 != 0,不再 trunc 取最低位
类别:a(编译器缺陷)。`b: bool = f()`(f 运行时返回偶数非零,如 2)此前经
coerce_to_type 的窄化分支走 trunc ⇒ 最低位 0 ⇒ false,而同一值在 `if 2:` /
`bool(2)`(icmp ne 0)为真 —— 两处判据相反(静默语义分裂)。
- ExprGenBase.coerce_to_type:整数窄化分支前先判目标位宽 == 1 ⇒ `icmp ne x, 0`
(C 6.3.1.2 的 _Bool 转换语义)。归一化不丢位,故对 i1 目标不再适用下方
「窄化截断溢出」常量诊断(先返回)——`b: bool = 2` 由编译期 OverflowError
变为正确的 true。
- CallGenCtor._translate_t_type_cast:显式 `t.CBool(x)` 与隐式落存储同一判据。
判据与 bool(x)(CallGenDispatch.vp:255-281)、条件归一化(AsyncGenMixin.vp:1611
_normalize_cond)、and/or 操作数三处统一。App/ 与 includes/ 全量检索未见
`: bool = …(& | >> | // | |)` 位提取写法,既有代码无依赖 trunc 取最低位的点。
生效需重新自举 TransPyV.exe(并清缓存)。
|
2026-09-30 15:14:32 +08:00 |
|
TermiNexus
|
ef4421db20
|
fix(TypeGenResolve): 定长数组容器的「元素个数」实参不参与类名归属归一(消「保留裸名」误报)
【b 机制漏豁免】`_norm_type_arg_name` 规则 1.5 把 `Name[...]` 的**每个**顶层实参都递归
归一(目的是把类名统一成 `{定义模块SHA1}.类`)。但定长数组容器 `t.CArray[T, N]` /
`list[T, N]` 的**第 2 实参是元素个数**(编译期整数:CDefine 名 / 字面量 / 常量表达式,
依据是 TPC 侧 t.CArray 的定义原文"第一个参数为元素类型,第二个参数为数组大小(整数或
常量名)",lib/includes/t.py),对它做类名归属归一既无意义,又会落进规则 5.5 的
"保留裸名"取证 ⇒ 误报"类名归属缺失"。
现场:编译 includes/traceback.vp 时 TracebackState 的字段注解
`t.CArray[uint64, TRACEBACK_FRAME_BUF]`(traceback.vp:532),第 2 实参
'TRACEBACK_FRAME_BUF' 是 traceback.vp:482 的 `TRACEBACK_FRAME_BUF: t.CDefine = 128`
⇒ 每次编译刷
"[类型实参归属] 裸类名 'TRACEBACK_FRAME_BUF' 本次保留裸名…"ERROR。
修法:新增 `_is_count_arg_container(container)`(按**容器短名**判定,兼容 `tt.CArray`
这类别名;只认 CArray / list,泛型类 GSList[T]/Box[T] 的实参全是类型名不受影响),
规则 1.5 的实参拆分循环按序号跳过第 2 实参 —— 原样保留、不递归。
归一结果逐字不变:常量名与字面量既非类名也非 CTypedef,归一恒返回原串;TPC 侧
`ResolveTypeArgStr` 对同位置同样原样保留(Name→NormalizeGenericArgName / Constant→
unparse),两侧仍一致。改变的只是不再触发该取证日志。
配套:上一提交 d63787f 的 `_is_non_class_type_name` 覆盖同注解的**第 1 实参**
(`uint64` = builtins.vp 的 typedef)——两条判据互补:类型位放行非类名,个数位不归一。
未验证:需重新自举 TransPyV.exe 才生效。vast 解析通过。
|
2026-09-30 14:05:03 +08:00 |
|
TermiNexus
|
d63787f8fc
|
fix(TypeGenResolve): 「保留裸名」取证对 CTypedef/内置标量/泛型形参不再报 ERROR
【b 机制漏豁免】取证日志(df1244d 加入)的前提是"这是个**类名**":只有类名存在
"{定义模块SHA1}.类" 这第二个 mangling 形式,留裸名才可能与它分裂。CTypedef /
语言内置标量 / 泛型形参的名域就是它自身,保留裸名即规范形式(TPC 侧
SymbolUtils.NormalizeGenericArgName 同口径),对它报 ERROR 是纯噪声。
现场:编译 includes/traceback.vp 时,TracebackState.FrameAddr 的注解
`t.CArray[uint64, TRACEBACK_FRAME_BUF]`(traceback.vp:532)经规则 1.5 递归规范化
实参 'uint64' ⇒ 规则 5.5 取不到 SHA1 ⇒ 每次编译刷一条
"[类型实参归属] 裸类名 'uint64' 本次保留裸名…"。'uint64' 是 includes/builtins.vp 的
`typedef uint64 = t.CUInt64T`,不进类归属是预期行为,不是缺陷。
修法:新增 `_is_non_class_type_name(pool, name)`(正向白名单,不猜):
① CTypedef 动态表命中(Phase1 1b register_ctypedef_from_source 文本扫描注册);
② builtins 兜底 `_lookup_builtins_typedef`(与 resolve_typedef_or_map 同一口径:
builtins 始终在编译链内,但其 typedef 不随 from_imports 注入 ⇒ 表可能 miss;
命中即幂等注册进表);
③ 语言内置标量/字符串(int/char/bool/float/double/str/bytes/void,与
_resolve_annot_type_name 的跳过清单同口径);
④ 泛型形参名(单字母大写 T/U/V…,特化前出现在实参位属正常)。
门控只对「结构体表里根本没有该名」(_bare_entry is None)豁免:若它确是已注册的类
(.vpi 半条目)只是缺定义模块 SHA1,那是真正的归属缺口,照旧报出,不被同名 typedef 掩盖。
未验证:本改动需重新自举 TransPyV.exe 才生效(TPC 侧无对应逻辑)。vast 解析通过。
|
2026-09-30 13:51:22 +08:00 |
|
TermiNexus
|
0e5ebd07ba
|
fix(TypeGenCType): 撤销在子类类体重复声明父类字段(TypeGenBase 已声明),修指针隐式转整数误报
上一版(6a287b9)为修 Repl.vp:802 在 TypeGenCType 类体补了 5 个字段,但这 5 个
(CTypedefNames / CTypedefTypeStrs / CTypedefNameIdx / CTypedefNameIdxCap /
CTypedefNameIdxBuilt)**本就由父类 TypeGenBase 在类体声明**(TypeGenBase.vp:73-84),
属重复声明(c 源码违规)。
后果(有实证):这 5 个名字进入子类"自有字段"后,HandlesImports._FlattenExt 的
继承前缀前置过滤器(HandlesImports.py:2516-2521,为防重复前置而按名字过滤)
把**同名继承项整条丢弃** ⇒ class_members['TypeGenCType'] 变成 [36 继承项]+[5 自有项]。
被丢掉的 5 项(继承下标 22/23/25/26/27)都排在 LastBuiltinsTypedefResult
(继承下标 32)之前,使其表内下标降为 27;NoVTable 类 base=0 ⇒ 命中 body 槽 27
= CTypedefNameIdxBuilt(i32) ⇒ TypeGenResolve.vp 的
`self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 触发
"指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。
该重复声明还会让父类方法(TypeGenBase.vp InitTypeMixinFields 等)与子类方法
访问同一字段落到不同槽(split-brain)。
撤销后:子类自有字段=0,继承前缀 41 项按原顺序保留,LastBuiltinsTypedefResult
回到表内下标 32 = body 槽 32(i8*,与 TypeGenBase 声明的 str 一致)。
说明:Repl.vp:802 的真实根因(引用模块里继承前缀是否可达)尚未定论,本次只撤销
误修、不掩盖;该类属性链失败现已由 TPC 侧新增记账(a18ca4da)逐条给出失败原因链。
|
2026-09-30 10:46:43 +08:00 |
|
TermiNexus
|
6a287b955e
|
fix(TypeGenCType): 跨模块可见字段改为在类体声明(修 Repl.vp:802 属性链求值为空)
类别:c 源码违规(跨模块读得到的字段必须在类体声明)
现象:TPC 编译 TransPyV/App/lib/core/Repl.vp:802
ct_names: list[str]* = GenApi._g_gen.TypeGenCType.CTypedefNames
报「带注解赋值表达式 …(Attribute 形态)编译期求值为空(表达式未解析出目标、无值可存),
赋值将被跳过、变量保持未初始化」。
判据(.vpi 与定义侧不同源,实证):
· TypeGenCType 类体**一个字段都没声明**:CTypedefNames / CTypedefTypeStrs / CTypedefNameIdx
/ CTypedefNameIdxCap / CTypedefNameIdxBuilt 全部只以方法体里的裸 `self.x = ...` 出现
(:184 `self.CTypedefNames = tn`、:185、:66-73、:118-121)。
· 声明存根(.vpi)只携带**类体 AnnAssign 字段**与 `__init__` 里**带注解**的 self.x;方法体
里的裸赋值只被**定义模块自己**的成员收集(HandlesClassDef 的 self.x 扫描)收录
⇒ 同一字段"定义模块成员表里有、引用模块没有"(实证:temp/05d515186a7c3668.vpi 内不含
CTypedefNames 条目)⇒ 引用模块的属性链在第三级断掉,整条带注解赋值被判"无值可存"而终止。
修法:把这 5 个字段在**类体**显式声明(类型取自各自赋值处的真实类型):
CTypedefNames / CTypedefTypeStrs : list[str] | t.CPtr (:177-178 的 tn / ts 即 list[str]*)
CTypedefNameIdx : sizet* (:64 / :71 的 idx)
CTypedefNameIdxCap / CTypedefNameIdxBuilt : int
声明后定义侧与 .vpi 两侧字段集一致(类体 AnnAssign 先收集,方法体 self.x 扫描按名去重不再
重复登记)⇒ 布局同源。注意:该类的字段顺序/类型与旧构建不同(字段从"仅扫描收集"变为
"类体声明"),需要清缓存重编译。
附带(另一仓 commit):TPC 侧 `_handle_attr_nested` 的 7 处静默 return 已补 `_NoteAttrNone`
记账(含内层失败原因链),下次同类报错会直接给出"读取侧原因=…"。
|
2026-09-30 09:59:54 +08:00 |
|
TermiNexus
|
c1a22411ca
|
fix(TypeGenResolve): 规则 5.5 增「本文件顶层类名」证据,模块级早于类定义的窗口不再留裸名
类别:a 编译器缺陷(TPV 侧;补齐"第二个裸类名窗口")
现象(与上一轮 e1bb42f 同一现场、不同窗口):编译 includes/ast/base.vp 时 :359
`_append_child(self.children, node, self.pool)` 报"指针隐式转指针已禁止(被指类型是
非基本类型…)",源 list[rawptr[AST]]* 与目标 list[rawptr<定义模块SHA1>.AST]* 差在实参的
SHA1 前缀。
判据(第二个窗口 = 模块级语句早于类定义):
· Translator.translate 的 prologue 先跑模块级 AnnAssign/Assign(Translator.vp:592-599)
与 forward_declare_functions(:601),**之后**才 translate_children(:602) 逐个翻译类;
而"正在定义类"窗口(CurDefModuleSha1/CurDefClassName)只在类定义入口
(ClassGenDef.vp:50)或 .vpi 注入(VpiReader.vp:2659)才建立 ⇒ 前者解析注解时窗口为
None ⇒ `_norm_type_arg_name` 规则 5.5 的 ①②③④ 全落空 ⇒ 注解里的裸类名(base.vp:148
`_append_child(..., lst: list[rawptr[AST]])` 的形参即此窗口)以**裸名**形式被首次注册,
并被后续复用(首写者胜:StructGenMixin 按 Ty 去重、ClassGenDef existing 早退不重算)
⇒ 与同一特化在类定义期解析出的带 SHA1 形式分裂。
修法(正向事实,不猜;与窗口/类定义顺序无关):
1. `StructGenMixin` 增文件级登记:字段 `FileClassNames`(本文件顶层 ClassDef 类名表,
表与键同池、每文件随 `reset_visible_structs` 重建)+ `FileModuleSha1`(本文件模块
SHA1),以及 `register_file_classes` / `is_file_class` / `get_file_module_sha1`。
键从 AST 的 name **复制到 pool** 后再登记(AST name 零拷贝指向 src_buf,跨阶段可能
失效,直接当表的键会 strcmp 到悬空内存)。
2. `Translator.translate` prologue:在模块级变量/前向声明**之前**调用
`GenApi.register_file_classes(pool, self.ModuleSha1, tree)`(GenApi 增同名委托)。
3. `TypeGenResolve._norm_type_arg_name` 规则 5.5 增**第五类证据**:①②③④ 全落空时,
若 `is_file_class(name)`(该裸名确由本文件声明)且本文件模块 SHA1 已知 ⇒ 归属本文件。
验证:TPV 侧改动需重新自举 TransPyV.exe 后生效;本轮以代码级证据链
(Translator.vp:592-602 / ClassGenDef.vp:50 / TypeGenResolve.vp 规则 5.5 闸门)自证。
TransPyV 仓另有他人未提交文件,本次只提交本改动的 4 个文件。
|
2026-09-30 00:23:43 +08:00 |
|
TermiNexus
|
e1bb42fade
|
fix(ClassGenGeneric): 泛型特化不再平移"类定义窗口",裸类名实参不再冻结成裸名
类别:a 编译器缺陷(TPV 侧)
现象(反复出现 4 轮):编译 includes/ast/base.vp 时 :359 `_append_child(self.children, node,
self.pool)` 报
「指针隐式转指针已禁止(被指类型是非基本类型…):源类型 %"…list[rawptr[AST]]"* →
目标类型 %"…list[rawptr<定义模块SHA1>.AST]]"*」——同一泛型特化被写成两种拼名(实参一侧
裸类名 `AST`、另一侧带定义模块 SHA1 前缀)。
判据(结构缺陷,代码级可证):
· `_norm_type_arg_name` 规则 5.5 判定"裸类名归属"依赖两个**全局窗口变量**
`StructGen.CurDefModuleSha1` / `CurDefClassName`(证据 ② `is_module_class`、
③ `CurDefClassName` 逐字相同),入口闸门是 `_def_mod is not None`。
· `ClassGenGeneric._specialize_generic_class` 在 :715-720 把 `trans.ModuleSha1` 临时切到
**泛型模板定义模块**,再于 :729 调 `ClassGenDef.translate_class_def`;后者在
`ClassGenDef.vp:50` 会 `mark_module_class(pool, trans.ModuleSha1, class_name)`
⇒ **窗口被覆盖成"模板模块 + 特化类名"**。原实现返回时只恢复 `trans.ModuleSha1` 与
`_imported_modules`(:733-735),**没有恢复这两个窗口变量**。
· 后果:调用方文件在此之后解析任何裸类名时,证据 ②③ 都在对着"模板模块 / 特化类"判 ⇒
全落空 ⇒ 该实参以**裸名**形式被首次注册并被后续复用(首写者胜:`StructGenMixin.vp:455`
按 Ty 指针去重、`ClassGenDef.vp:100-150` existing 早退不重算)⇒ 与同一特化在别处解析出的
带 SHA1 形式分裂成两个名字 ⇒ 被"非基本类型指针不可隐式转换"拦下。
修法:
1. `ClassGenGeneric.vp`:窗口与 `ModuleSha1` **同进同出** —— 在切 ModuleSha1 之前保存
`CurDefModuleSha1`/`CurDefClassName`,:744 之后一并恢复(嵌套特化不再污染调用方窗口)。
2. `TypeGenResolve.vp` 规则 5.5 的取证日志扩展:除原有"命中 struct 条目但 entry.ModuleSha1
为空"外,**新增触发条件"窗口存在却属于另一个类"**(`_def_mod` 有值且
`CurDefClassName` 与裸名不同)——正是"窗口被特化平移/污染"或"前向引用未登记类"的签名;
报文补出 `entry_present` 与 `find_struct_by_module` 两项事实,一轮即可定位来源是否缺失。
验证:TPV 侧改动需重新自举 TransPyV.exe 后生效;本次以静态证据链(上述行号)与取证日志
自证。注:TransPyV 仓另有 7 个他人未提交文件,本次只提交本文件与 TypeGenResolve.vp。
|
2026-09-30 00:20:30 +08:00 |
|
TermiNexus
|
31f341cf6f
|
fix(transpyv): vpi 注入期登记「本模块声明的类」,裸类名实参不再冻结成裸名
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 的入口闸门是
`_def_sha1 is None and _def_mod is not None`(_def_mod = CurDefModuleSha1),而
VpiReader._inject_class_struct_declare 是在**注入期**解析字段注解的——此刻既没有
"类定义窗口"(CurDefModuleSha1=None ⇒ 闸门整条关闭、四条证据一条都不查),被注入的类
也尚未 register_struct ⇒ 注解里的裸类名实参取不到定义模块 SHA1 ⇒ 特化名被冻结成
**裸名版本**,与该类在自身翻译期(类已注册)解析出的带前缀形式分裂成两个特化名,
最终以"非基本类型指针不可隐式转换"报出。现场:includes/ast/base.vp:359
_append_child(self.children, node, self.pool)
源 %"…list[rawptr[AST]]"* vs 目标 %"…list[rawptr<定义模块SHA1>.AST]"*
这也解释了前两轮修复为何无效:③(自引用判据)依赖 CurDefClassName、④(entry.Ty 取
前缀)依赖 struct 条目,而注入期两者都不可用,且此时连日志条件(entry 命中)都不成立。
- GenApi:新增 mark_module_class 委托(与 add_visible_struct / find_struct_by_module 同款)。
- VpiReader._inject_class_struct_declare:在解析字段注解之前调用
GenApi.mark_module_class(pool, src_sha1, cls_name) —— 一次调用同时完成
① 登记"{src_sha1}.{cls_name}"(名字归属,与 struct 注册状态无关);
② 把 CurDefModuleSha1/CurDefClassName 指向本次注入的模块与类(打开规则 5.5 的闸门)。
|
2026-09-29 22:29:48 +08:00 |
|
TermiNexus
|
df1244d6f4
|
fix(transpyv): 裸类名归属补「符号名真源」(entry.Ty 的 {SHA1}.{类} 前缀)+ 取证日志
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 判定裸类名归属只认三条证据——
struct 注册表的 ModuleSha1、ModuleClasses 登记表、以及"正在定义类"。Phase A-pre 没有
"类定义窗口"(CurDefModuleSha1=None),而 .vpi 注入的类条目常常 ModuleSha1 为空 ⇒
三条全落空 ⇒ 实参保留裸名,与后续(类已注册时)解析出的带前缀形式分裂成两个特化名
⇒ 被"非基本类型指针不可隐式转换"拦下。现场:includes/ast/base.vp:359
_append_child(self.children, node, self.pool)
源 %"…list[rawptr[AST]]"* → 目标 %"…list[rawptr<定义模块SHA1>.AST]"*
- 规则 5.5 新增证据 ④(表/窗口都不可用时的**符号名真源**):结构体自身的 IR 名本就是
"{定义模块SHA1}.{类名}"(与本仓全部 mangling 同源:ClassGenDef/ClassGenVTable/
VpiReader 的 Stub 构造)⇒ 打印 entry.Ty 并取回其 sha1 前缀(复用
_is_sha1_prefixed 这一唯一判据,不另写判定)。
- 同一处补取证日志:命中 struct 条目却一条 SHA1 证据都取不到时(可疑子集:
entry.ModuleSha1 为空),把 entry.Ty / cur_def_mod / cur_def_cls /
is_module_class / from_imports / file 一次打出(只加日志,返回值语义不变)。
|
2026-09-29 21:28:18 +08:00 |
|
TermiNexus
|
bbd0cdbf62
|
fix(transpyv): 自引用类名归属加「表无关」判据,字段与形参不再分裂成两个特化名
类别 a(编译器缺陷):_norm_type_arg_name 规则 5.5 判定"正在定义的类"的裸名归属
时只认两条证据——struct 注册表(find_struct_by_module)与 ModuleClasses 登记表
(is_module_class)。自引用字段(includes/ast/base.vp `AST.children:
list[rawptr[AST]]`)在类定义体内解析时本类尚未 register_struct,而 ModuleClasses
的键是 pool 字符串,登记/查询受"表未就绪、键随 pool 回收"影响 ⇒ 两条都可能落空
⇒ 实参留在裸名 `list[rawptr[AST]]`,与同一注解在形参处(类已注册)解析出的
`list[rawptr<定义模块SHA1>.AST]` 分裂成两个特化名 ⇒ 被"非基本类型指针不可隐式
转换"拦下(现场:includes/ast/base.vp:359
`_append_child(self.children, node, self.pool)`,源 %"list[rawptr[AST]]"* →
目标 %"list[rawptr<sha1>.AST]"*)。
- StructGenMixin:新增 CurDefClassName —— 与 CurDefModuleSha1 同源同生命周期,
由 mark_module_class 在类定义入口(ClassGenDef 第 50 行,早于字段注解解析)
一并记录;InitStructGen / reset_visible_structs 同步清空。
- TypeGenResolve._norm_type_arg_name 规则 5.5:新增证据 ③(表无关)——查询名与
正在定义的类名逐字相同即判为本类,用 CurDefModuleSha1 加前缀。类名直接来自类
定义入口、不经任何表 ⇒ 自引用窗口内必然可判;证据 ② 失败时不再退回裸名。
|
2026-09-29 20:16:05 +08:00 |
|
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 |
|