TermiNexus
|
64f3d79b64
|
refactor(asan): ASan 运行库目录改为按 PATH 自动探测,去除本机硬编码路径
- _FindAsanRuntimeDir() 重写为 _FindAsanRuntime():遍历 PATH 定位
- clang64/bin 由前置改为追加到 PATH,避免裸名 clang++/llc/opt 被解析到 clang64 的 libc++ 版本
- 缺失 ASan 运行库时的报错文案改用占位符路径
|
2026-10-07 11:25:37 +08:00 |
|
TermiNexus
|
305242aa8a
|
feat(parse): typedef 右值纳入类型位,支持 const T / T*
X: t.CTypedef = const str / = const uint16* 现按类型位解析(含 typedef X = T 语法糖):
- 文本层:_ExpandTypeDescriptors 新增 _IsTypedefAnnotationRhs(= 右侧且语句含 CTypedef);
- 解析层:vast.Parser._TryParseAnnAssign 对 CTypedef 注解的右值走 _ParseTypeAnnotation,vast_c.c 同步并重建 _parser.pyd;
- 文本回退:TypeAnnotationResolver._NormalizeTypedefRhsText 把 const T / T* 归一为 | 联合;
- 值解析:LLVMTypeMapper.resolve_typedef_value_type 把 CConst/CVolatile 限定符标记视为无类型(_TYPEDEF_VALUE_QUALIFIERS),修 const str 被误解析成 CConst * 导致 LPCSTR 登记失败。
|
2026-10-05 20:36:29 +08:00 |
|
TermiNexus
|
59378e6a2a
|
fix(codegen): 内建标量别名双参指针包装 + 解引用赋值按 c.Load 原样拷贝
HandlesExprBuiltin: int/char/bool/float/double(v, t.CPtr) 不再被标量分支吞掉第二参,改由 TSpecialCallHandle._HandleTPtrCastLlvm 处理(等价 t.CInt(v, t.CPtr));此前退化为 ptrtoint+trunc,includes/argparse.vp get_bool 因此恒真,--version/--project 一律打印帮助。
HandlesAssign: 目标与右值同为解引用(*a = *b)时按 c.Load 语义做原样字节拷贝,抽出 CSpecialCallHandle.EmitRawCopyLlvm 复用;此前按 fptosi/trunc 转换,mlirlite ConstFloat 的位双关 *&fv_bits = *&val 会把 double 位模式截断为整数,浮点字面量全部错误。
|
2026-10-05 14:14:25 +08:00 |
|
TermiNexus
|
397bab1360
|
chore(vast_c): 同步 C 解析器二进制产物 _parser.pyd
重新编译 vast_c 源码后的 .pyd 产物(217027 -> 220943 字节)。
|
2026-10-05 09:26:26 +08:00 |
|
TermiNexus
|
9fe88831f3
|
fix(codegen): 修复模块级 str 字面量全局的初始化器缺失
模块级 `X: str = "字面量"` 有两条路径都没写出正确初始化器:
1) 新建全局分支在创建 GlobalVar 之后又查 `VarName not in Gen.module.globals`
(此时刚登记,条件恒假),于是永远走 else 分支、从不写初始化器:全局退化成
无初值的声明,第二遍再被升级成 `common global i8* null`,字符串常量沦为无人
引用的 @NAME_str。只读常量(如 BuildPipeline.MLIR_OPT_CMD)在 mlir 后端
strlen(NULL) 直接崩(ASan 0x0)。
2) 把既有 external 声明升级为本模块定义的分支一律设 common,但 LLVM 要求
common 全局的初始化器必须是零值,字符串字面量会触发 verifier 报错。
新增 _BuildStringPtrInitializer 统一产出「指向 @{Name}_str 常量数组首元素的
GEP」,两处改用它。
验证:TransPyV 自举通过;TPV 顺序回归 77/77 全通过。
|
2026-10-05 09:26:26 +08:00 |
|
TermiNexus
|
07ba7a4fe5
|
fix(codegen): 新规范迁移暴露的 4 类解析/代码生成缺陷
1) HandlesExpr._HandleConstantLlvm 引号判定用 AST lineno 直查磁盘行,漏了 _inject_auto_imports 注入行的偏移 N,取到错行;改用 _DiskLineno 还原,并把 col_offset(UTF-8 字节偏移)转字符偏移后再切片。2) Phase2Translator 新增 _DedupComdatDecls:_dso.ll 由各依赖片段拼接,每片段各带同名 comdat 声明,llc 报 redefinition of comdat;在 _AddWeakComdat 入口与两处落盘点去重。3) LLVMTypeMapper._resolve_name 补存储类/类型限定符标记分支(CExport/CExtern/CConst… 返回空串,由 _resolve_bitor 取另一侧真实类型),与 _resolve_attribute 同口径。4) 短别名(int/int64/sizet/char…)在 typedef 右值与类型转换调用处解析不到:SymbolTypeResolver._resolve_typedef_from_value 与 LLVMTypeMapper.resolve_typedef_value_type 的 ast.Name 分支补 builtins typedef / BuiltinTypeMap 兜底(BaseType 可能是 CType 实例,须取 type(x).__name__),HandlesExprCall 的类型转换求值补 _lookup_builtins_typedef 回退并保留双参数 CPtr 指针包装。验证:TransPyV 自举 includes 52/52 + App 76/76 全通过;TPV 顺序回归 77/77。
|
2026-10-05 09:25:02 +08:00 |
|
TermiNexus
|
65f0e87193
|
feat(dunder): 补齐 __bool__ 条件真值调度与反向运算符/位运算增量重载
- BaseHandle 新增 _CoerceCondToBool:if/while/and/or/not/三元条件统一归一到 i1,类实例定义了 __bool__ 时优先调度(此前仅 if 支持)
- HandlesExprOps:新增反向运算符重载(__radd__/__rsub__/... 共 12 个),左操作数无普通 dunder 时调度右操作数 __rX__
- HandlesAugAssign:_AUGOP_MAP/_AUGOP_FALLBACK 补齐位运算与移位增量(__iand__/__ior__/__ixor__/__ilshift__/__irshift__)
|
2026-10-03 00:39:53 +08:00 |
|
TermiNexus
|
2f70bd2733
|
feat(switch): switch 允许重复 case 标签(match 仍禁止)
- HandlesMatch / MatchGenMixin:值标签去重仅对 match 生效;switch 允许重复 case
标签(按书写顺序,先出现者生效);通配臂多个(default / case _)仍 fail-fast
- wiki/07-control-flow.md 同步该语义
|
2026-10-03 00:18:54 +08:00 |
|
TermiNexus
|
d6eefcbece
|
fix(switch): case 标签支持裸名(按值引用)并给出常量标签 fail-fast
- 裸名在 match 里是捕获模式(MatchAs),switch 里对齐 C 的 `case NAME:` 语义,
三端解析均归一为 MatchValue(Name);`_` 仍作通配(等价 default)
- includes/ast/parser.vp、lib/vast/Parser.py、lib/vast_c/vast_c.c 同步实现
- HandlesMatch:case 标签求值非编译期常量时 fail-fast(原先会因取 .constant 崩成
AttributeError),报文明确“case 标签必须是编译期常量”
|
2026-10-03 00:14:51 +08:00 |
|
TermiNexus
|
61df58334f
|
fix(match): 补齐 TPC 侧 MatchHandle._CaseLabelKey
- 上一轮重名 case 检测引用了 _CaseLabelKey,但该方法未真正落盘,运行期报
AttributeError: type object 'MatchHandle' has no attribute '_CaseLabelKey'
- 新增 _CaseLabelKey / _ValueLabelKey:MatchValue(Constant/Name/Attribute 链)、
MatchSingleton、MatchClass(变体名)、MatchOr(子键拼接);MatchAs/序列返回 None
|
2026-10-02 23:35:58 +08:00 |
|
TermiNexus
|
5de9cb6527
|
fix(vast_c): C 解析器补齐 match 的 is_switch,并实现 switch / fallthrough
- ParseMatch:Match 由 2 字段改为 3 字段(subject, cases, is_switch=False),
修运行期 AttributeError: 'Match' object has no attribute 'is_switch'
- 新增 ParseSwitch:switch 脱糖为 is_switch=True 的 Match,支持 case 常量/枚举
变体/多值或模式与 default:(标签非法或不支持 if 守卫时 fail-fast)
- 新增 ParseFallThrough:fallthrough 脱糖为 c.NoBreak() 标记调用
- 语句分派:match / struct 改为硬关键字,新增 switch / fallthrough 分派
|
2026-10-02 23:34:33 +08:00 |
|
TermiNexus
|
74cfaeb917
|
feat(switch): 新增 switch 语句并禁止 match 内 fallthrough
- vast/Nodes.py:Match 增加 is_switch 字段(含默认值)
- vast/Parser.py:新增 _ParseSwitch(case <常量|枚举变体> / default:,desugar 为 Match(is_switch=True));match / struct / fallthrough 改为硬关键字
- vast/Unparser.py:round-trip 忠实还原 switch 与 default:
- HandlesMatch / HandlesBody:新增 in_switch_case 门控,fallthrough 只允许出现在 switch 的 case 体内
- HandlesFor:补齐嵌套函数的 saved_loop_break/continue_targets 定义(修 NameError),并隔离 in_switch_case
- LLVMCG/BaseGen:新增 in_switch_case 状态
|
2026-10-02 23:15:42 +08:00 |
|
TermiNexus
|
8b6c36d24f
|
feat(match): 重名 case 标签 fail-fast
HandlesMatch._HandleMatchLlvm 新增前置检查(在产出任何 IR 之前):新增
_CaseLabelKey 提取 case 的 switch 标签(MatchValue 值 / MatchSingleton(None) /
MatchClass 变体名 / MatchOr 组合),标签重复或出现多个 `case _` 时报 FailFastError,
并给出重复的 case 序号与标签键(C switch 不允许重复 case 标签)。
|
2026-10-02 22:44:34 +08:00 |
|
TermiNexus
|
4353efd8ff
|
fix(flow): 嵌套/协程函数体独立 break/continue 目标,越界即 fail-fast
- HandlesFor._HandleNestedFunctionLlvm:进入嵌套函数体前清空 loop_break_targets /
loop_continue_targets,退出恢复——原实现会让嵌套函数体内的 break/continue 继承
外层函数的跳转目标(生成跨函数 br,非法 IR)
- HandlesAsyncFunctions:协程体内循环之外的 break/continue 改为 FailFastError,
不再静默不生成跳转
|
2026-10-02 22:40:04 +08:00 |
|
TermiNexus
|
34c4eeff02
|
fix(flow): 循环/match 外的 break/continue 改为 fail-fast
HandlesBody:loop_break_targets / loop_continue_targets 为空时不再静默不生成跳转,
直接 FailFastError 暴露——原实现会让 break/continue 无声无效果(不兜底原则)。
|
2026-10-02 22:35:26 +08:00 |
|
TermiNexus
|
cc0615e519
|
feat(match): fallthrough 软关键字 + case 内 break 跳出 match
- vast/Parser:语句位置识别裸 `fallthrough`(仅整条语句命中),脱糖为 c.NoBreak()
标记调用;作标识符的写法不受影响
- HandlesBody:`c.NoBreak()` 标记语句不产出 IR(语义由 HandlesMatch 消费)
- HandlesMatch:新增 _HandleMatchCaseBody(case 内 break 目标 = match 的 AfterBB,
经 loop_break_targets 压栈/出栈,不影响外层循环)与 _FallThroughTarget;
9 处 fallthrough 由"只留无终结符块"改为显式 br 下一个 case body
|
2026-10-02 22:33:26 +08:00 |
|
TermiNexus
|
9ff301d747
|
refactor(types): 移除 list[T, N] 的定长数组遗留判据,统一走 _list.vp
- SymbolUtils.IsListAnnotation 不再把双参 list[type, count] 判为栈上定长数组:
list 是 includes/_list.vp 的堆上 STL 容器泛型类,list[T] / list[T, N] 都走泛型特化
- ParseListAnnotation / ListAnnotationParseResult / ExtractListFromBinOp 相应收窄为
只处理 t.CArray / rawarray / array,并同步文档串
- HandlesAnnAssign / HandlesBase / HandlesFunctions / HandlesImports / SymbolUtils 的
注释改按 CArray 形态表述
|
2026-10-02 22:16:30 +08:00 |
|
TermiNexus
|
0efdc9e111
|
fix(expr): 整型与 None 比较统一按 == 0 / != 0 处理
- HandlesExprOps.py:is / is not / == / != 遇整型与 None 比较时直接与 0 比较,
避免把 i8 当作空指针 load;检查置于类型对齐与指针分支之前
- README.md:同步引用 wiki 第 12 章(12-compilation-system.md)
|
2026-10-02 22:07:06 +08:00 |
|
TermiNexus
|
5eef845e5b
|
feat(struct): 嵌套 class/struct 发射为独立结构体 {外类}_{内类}
用户 2026-10-01 定:struct 内允许叠加 struct(与 class 内写 class 同口径)。
此前类体里的嵌套 ClassDef 在两个编译器里都被**静默丢弃**(字段遍历只认 AnnAssign、
方法遍历只认 FunctionDef),现按既有 REnum/union 嵌套命名先例发射为独立结构体。
命名与引用口径(双端同源):内层结构体 = {外类}_{内类}(多层递归 A_B_C);
引用两种写法都可用 —— 限定名 `外类.内类`、扁平名 `外类_内类`。
- HandlesClassDef.py:新增 _EmitNestedClassDefs(deepcopy + 改名后递归
_EmitClassLlvm,deepcopy 避免污染源码 AST)+ _RegisterNestedClassName
(SymbolTable 登记裸名/点号名/扁平名,对齐 REnum 变体的三键先例)。
- TypeAnnotationResolver.py:_from_node_attribute 增加嵌套分支 —— 限定名按**扁平名**
取条目并让 Name/BaseType 都是扁平名(否则会拼出 t.CStruct(name='内类'),
与声明端 `%"{sha}.外类_内类"` 不同源 ⇒ 布局分叉)。
- DeclarationGenerator.py:_get_type_str 支持 `外类.内类` → `%"{sha}.外类_内类"`;
_generate_class_decl 递归产出嵌套类型的完整声明(否则跨模块只拿到 opaque,
指针可用但字段偏移全失效)。
- Phase1Generator.py / Phase2Translator.py:跨模块注册表按扁平名登记嵌套类。
Phase2Translator 原先用 ast.walk 把嵌套类按**短名**登记(占住平坦命名空间、
与定义端不同源),改为递归扁平名。
实测(TPC 侧):嵌套类名字登记 {A_B, A_C, A_C_D};`x: A.B` → CTypeInfo(A_B);
声明端 `%"cafe.A" = type { i32, %"cafe.A_B" }` + `%"cafe.A_B" = type { i32 }`
+ 多层 `%"cafe.A_C_D" = type { i32 }`。
已知缺口(下一步):TransPyV 侧跨模块走 .vpi,VpiReader 只登记顶层类 ⇒ 跨模块引用
嵌套类型仍需在 VpiReader 侧补登记(同模块路径已通)。
|
2026-10-01 23:29:54 +08:00 |
|
TermiNexus
|
a8f6ca006d
|
feat(struct): TPC 前端支持 struct 关键字(含 C 加速解析器)
与 TransPyV/includes 侧(c27ebc1)逐点对齐:`struct X:` / `struct X(Base):` 脱糖为
`@t.NoVTable class X(t.CStruct):`,禁止 virtual/novirtual 修饰、禁止类体内 def。
- lib/vast/Parser.py:软关键字分派(语句起始 + 后随标识符;`struct = 5` 不受影响)、
_ParseStructDef(复用 _ParseClassDef 全路径 + 合成 @t.NoVTable + 追加 t.CStruct
标记基类 + 类体禁 def 校验)、_StructSyntaxError 报文、_ParseVirtualModified
显式拦截 `virtual/novirtual struct`;@deco 前缀同样支持。
- lib/vast_c/vast_c.c:同构实现(ParseStatement / ParseDecorated 分派、
ParseStructDef、ParseVirtualModified 拦截)。
- lib/vast_c/_parser.pyd:按 README §4.3 的命令重建(gcc 15.2.0)。
验证(两条路径逐一对照):bases=[t.CStruct] / [Base, t.CStruct]、
decos=[t.NoVTable];`virtual struct` 与 `struct 内 def` 均 fail-fast;
`struct = 5` 仍解析为普通赋值。
|
2026-10-01 23:07:49 +08:00 |
|
TermiNexus
|
85d68e5913
|
fix(type): 支持内建裸名函数指针构造子 callable[[...], R]
TransPyV 侧 TypeGenResolve._IsCallableSubscriptValue 已把 t.Callable[...] 与内建裸名
callable[...] / Callable[...] 视为逐点等价,TPC 侧只有模块限定形态能识别:
- TypeAnnotationResolver._from_node_subscript 的 Callable 分支要求 IsTModule(parts[0]),
裸名落进泛型特化兜底未命中 → 空 CTypeInfo;改为裸名同判并共用同一构造体。
- DeclarationGenerator._get_type_str 的 Subscript 分支缺 callable 判定,落到末尾
兜底 'i32';补判返回 'i8*'(与 Attribute 分支的 t.Callable 一致)。
现场:Repl.vp:307 ReplFragFn: callable[[rawptr], int] 被声明成 i32 全局,
Repl.vp:2727 ReplFragFn = fp_addr(i8*)触发「指针隐式转整数已禁止」。
|
2026-10-01 21:53:43 +08:00 |
|
TermiNexus
|
f0e630457f
|
chore(Phase2Translator): 自动并行度上限 8 worker,并精简并行提示文案
- 新增 _MAX_WORKERS=8(用户定:16 worker 太多,收益不足而内存压力翻倍);
只约束自动取值,显式 TPC_JOBS=N 仍按硬件/文件数上限
- 并行相关的 RuntimeError 与日志精简为一行数字 + 出路,去掉"拒绝降级为串行 /
串行会让编译时间从 ~400s 掉到 ~1180s 档"等长说明
|
2026-10-01 20:58:08 +08:00 |
|
TermiNexus
|
42c13e372b
|
fix(HandlesAssign): 对"指针的指针"右值的自动解引用收窄为取地址形态
原判据只看右值的 LLVM 类型(Ptr(Ptr(...)))就 load 一层,于是显式 cast 的结果
(`entry = str*(uint64(argv) + idx*8)`,右值是 i8**)被多解一次引用 → 静默取到野指针
(includes/argparse.vp 的 parse_args 因此 ASan access-violation 0xffffffffffffffff)。
现限定为 Name/Attribute/Subscript 这类"取地址形态"(求值结果是存储地址,即"对象即指针"
返回 alloca/字段地址)才解引用;Call(显式 cast、构造/普通函数调用)与其它表达式给出的
是值本身,不再多解一层 —— 与 TransPyV 侧行为对齐(其 _deref_if_ptr_ptr 只用于字段访问
与调用分发,不在普通赋值路径)。
|
2026-10-01 18:33:21 +08:00 |
|
TermiNexus
|
2ba38ae842
|
fix(Phase2Translator): 并行 worker 数不再按可用内存降级串行,改为"预分配校验 + 强制预留"(TPC_JOBS 可显式覆盖)
现象:同一条命令的编译时间在 ~400s 与 ~1180s 两档之间随机跳(PROF 目录当日
366s~2239s),用户反馈"耗时不降反增"。
根因(三份 profile 实证):
- 旧 _PickWorkerCount 用 `by_mem = max(1, avail_mb // per_worker_mb)` 按**可用**
物理内存夹 worker 数:可用稍紧(<~2.8GB)就整体降为 1 ⇒ **静默退回串行**。
其文档字符串自己就写着"76 文件逐个翻译 1180s,并行时 ~400s,波动 2-3 倍"。
- PROF/20260930_224857(438.40s):主进程 _translate_file **0 次**、
StructGen/HandlesImports/HandlesClassDef **0 条记录**、总调用 7.28 亿
⇒ 翻译在 worker 进程里做完了(未 profile),主进程只在等 → **并行档**。
- PROF/20260930_231916(1048.55s)与 20260930_235354(1168.15s):
_translate_file **75 次全在主进程**、_parallel_translate_worker/_batch 0 次、
总调用 19.6 亿 ⇒ **串行档**(1180s 档)。
⇒ 拿 438s 当基线比 1168s 是"并行 vs 串行"。本次 A/B(同为串行、总调用
19.616 亿 vs 19.631 亿)里各项优化均为正收益(_RewritePlain −5.0s、
__exit__ −1.5s、re.match+_compile −1.3s、_RewriteQuoted −0.75s…);
+12% 属机器波动(同一批 C 函数在调用次数不变下变慢 25%~73%)。
改动(口径由用户定:不降级 + 预分配内存避免波动 + 强制要求预留内存):
1. **不降级**:删掉"内存紧就退串行"。worker 数 = min(cpu_count, n_files),
不再因可用内存抖动而变化。
2. **预分配校验 + 强制预留**:新增 _MemStatus()(读可用/总量)与
_ProbeReserveMb()——按 base×per_worker_mb(翻译 350 / 编译 400)分块**真正
提交**该预算(峰值=预算),成功后立即整体释放,紧接其后创建 worker 进程
由 OS 复用这批页。等于先向 OS 确认"这笔预算确实拿得到",而不是只看含大量
可回收页的 ullAvailPhys。
3. **fail-fast**:可用内存 < 预算、或预分配校验提交失败 ⇒ RuntimeError 报出
"需要 X MB(N worker × M MB)/ 当前可用 Y MB / 总量 Z MB",由使用者释放内存
或**显式** TPC_JOBS=N 重跑(N=1 即显式接受串行)。不再静默降级。
4. 新增 TPC_JOBS=N 显式覆盖(跳过校验,使用者自担内存风险)。
5. 每次决策都打日志:显式指定 / 读不到内存状态 / 预分配校验耗时 / 最终 workers。
已知取舍:内存确实不足时编译会**直接失败**(这是"不降级"的应有语义),出路是
释放内存或 TPC_JOBS=N;预分配校验会短暂占用峰值=预算的内存(约 1GB/秒级耗时)。
未动(下一步候选):串行档的真热点 StructGen._generate_structs(151s 自身 /
480s 累计,被调 36,389 次≈每类 22 次,O(类数²))。
|
2026-10-01 00:23:16 +08:00 |
|
TermiNexus
|
b18d57fa38
|
perf(全仓库/正则): 字面量正则全部改模块级预编译常量(15 文件 / 115 个调用点)
背景(prof 基线 PROF/20260930_224857,主进程 433.69s):正则这一族合计 ≈45s ——
`re.match`(未预编译入口)被调 1970 万次 / 自身 10.70s、它内部 `Pattern.match` 占 8.06s,
`re._compile`(缓存查找)1975 万次 / 7.19s,`re._make`(真正编译)274 万次 / 7.62s
(说明 re 的 512 条缓存已被打穿,每 7 次调用就有 1 次重新编译)。
每次 `re.match(字面量, s)` 都要做"模式串哈希 → 缓存查找";预编译后这层全省掉。
改动:把 15 个文件里 97 处"字面量当模式"的 `re.match/search/sub/subn/fullmatch/
findall/finditer/split(...)` 提升为模块级 `_RE_*: re.Pattern[str] = re.compile(...)`,
共 115 个调用点(含同字面量跨处共用)。模式串与 flags 一字未改(flags 从调用处
搬进 re.compile,含原先以**位置参数**传入的 flags)。4 处拼接型动态模式按设计不动
(FuncGen 2 处、HandlesImports 2 处)。
核验(不靠肉眼):写了 AST 指纹对拍脚本,对每个文件取 HEAD 版与新工作区版,把
"旧 re.<m>(字面量, 其余实参, 关键字) + flags" 与 "新 _RE_X.<m>(实参, 关键字) +
常量 (模式, flags)" 做多重集比对 —— 15 个文件、115 个调用点全部逐一等价(0 失败),
另查无重名常量、无遗留字面量调用、GetDiagnostics 全空。
对拍过程中查出一处子代理抄写错误:`_RE_GLOBAL_DSOLOCAL_MULTILINE` 丢了行首锚 `^`
(原文注释明确"使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare"),已修正。
同轮经查证后**未做改动**的两项(避免把假热点当真事修):
- VLogger 的 `re.sub` 6.43s:profile 当时的 VLogger 版本里根本没有 `import re`
(`git show 3658ab06:lib/core/VLogger.py` 已确认),属 C 帧错挂,不做。
- tokenize 274 万次:那是 `_generate_tokens_from_c_tokenizer` 的 **token 产出次数**
(≈350 个整文件的词法量),SymbolUtils 的 4 个 tokenize 调用点 nc 仅数百,且已有
`_EXPAND_CACHE` 去重;再加一层 token 缓存无收益,不做。
|
2026-09-30 23:47:36 +08:00 |
|
TermiNexus
|
4641777db1
|
perf(Phase2Translator/_split_ll): 括号计数走 C 级 str.count 快路径 + 调用点预筛
prof 基线(PROF/20260930_224857,主进程 433.69s)实测 _split_ll:
ncalls 53 / tottime 0.263s / cumtime 3.347s,其中
_count_braces_outside_strings 102,397 次 / tottime 1.999s / cumtime 2.769s
(占其 cumtime 的 83%);同函数内 strip 0.078s、startswith 0.084s 可忽略。
- _count_braces_outside_strings:行内不含 c" 时退化为 C 级
str.count('{') - str.count('}'),不再逐字符跑 Python 循环。
语义逐字等价:逐字符版唯一的额外分支就是"跳过 c" 字面量",而该分支
只在出现子串 c" 时进入 ⇒ 无 c" 时等于纯括号计数。
- 两个调用点(in_define 函数体续行 / in_global_def 全局初始化器续行)加预筛
`'{' in line or '}' in line`:无花括号的行必然返回 0,省掉一次函数调用。
- `stripped == '{'` 分支的 3 处 re.sub 字面量改用同模式串的预编译常量
(_RE_DEFINE_PREFIX / _RE_DEFINE_LINKAGE / _RE_ALWAYSINLINE),与上面
define 分支保持同一口径(模式串一字未改)。
顺带实测(供下一步参考):str.strip 179M 次 / 22.8s 是全流程最大项,但
cProfile 归属帧几乎全在 stdlib(re.match 5.25M、threading.wait 4.88M、
futures.as_completed 3.63M、json.load 0.66M、subprocess 各 ~0.4M);
本项目内只有 _extract_llvm_type_name 1.37M、_ParseDwarfLineRows 0.34M、
_split_ll 0.35M。
|
2026-09-30 23:18:29 +08:00 |
|
TermiNexus
|
e292e2390d
|
perf(Phase2Translator): 文本读缓存改按路径直存(零 stat),正确性靠写/删点显式失效
- _ReadTextCached 不再 os.stat:原实现每次调用都要 stat 判新旧,等于把省下的
1.2 万次 open 换成 1.2 万次 stat(同为系统调用热点),命中路径仍有系统开销。
改为 OrderedDict[路径 → 文本],命中零系统调用;newline 非 None 的逐字节读不进缓存。
- _InvalidateTextCache 改 O(1)(按路径直接 pop),并在 14 处文本写点 + 1 处 stale
文件删除(os.remove)处调用 —— 读侧不再 stat 后,写/删点显式失效是唯一正确性保证。
- 失效为何必须显式(时间戳判据在同 tick 内会失效):_compile_one 前的 type 定义
重排(行集合不变仅次序变,长度逐字节相同)、_recombine_ll 的旧 sha1 前缀替换
(16 位 hex 等长替换)。
- 安全性前提:TPC 单次运行内被读文件内容不变。已核对:self.sig_files /
self.stub_files 指向的都是 Phase1 产物(temp_dir / _GLOBAL_CACHE_DIR,Phase2
期间不再改写);Phase2 自产的 .ll/.stub.ll/.deps.json/.vpi 每次写、删都走上面的失效。
基线 PROF/20260930_224857:_io.open 12,740 次/self 28.8s、read 11,902 次/30.4s、
TextIOWrapper.__exit__ 16.6s(128 个模块却上万次读,同一份产物被多阶段反复打开)。
|
2026-09-30 23:15:22 +08:00 |
|
TermiNexus
|
1748940d30
|
perf(Phase2Translator): 文本读全走 LRU 缓存 + 写侧显式失效,削重复文件 I/O
- 新增 _ReadTextCached:键 =(路径, st_mtime_ns, st_size),命中即免 open/read/close;
按总字节预算(64MB)LRU 淘汰,单文件 >32MB 不入缓存;newline 非 None 时退化为直读。
- 21 处文本读点全部改走缓存(.vpi/.pyi 解析、.ll/.stub.ll/.text.ll、deps.json、
doc.json、include 源、pyi 缓存预热等),json.load(f) 统一改 json.loads(_ReadTextCached(...))。
- 新增 _InvalidateTextCache:14 处本进程文本写点(原地改写/新落盘)后显式失效。
Windows 文件时间为系统时钟派生,粒度约 15.6ms,"先读 → 同 tick 内原地改写 → 再读"
且改写前后字节数相同时 (mtime, size) 键完全一致(实测两例:_compile_one 前的 type
定义重排——行集合不变仅次序变;_recombine_ll 的旧 sha1 前缀替换——16 位 hex 等长替换),
只靠 stat 判新旧会读到过期文本,故写侧必须显式失效。
基线 PROF/20260930_224857:_io.open 12,740 次/self 28.8s、read 11,902 次/30.4s、
TextIOWrapper.__exit__ 16.6s(128 个模块却上万次读,同一份产物被多阶段反复打开)。
|
2026-09-30 23:13:01 +08:00 |
|
TermiNexus
|
3658ab0637
|
perf(Phase2Translator/IRMetadataFix): 削逐行热点——跳过无谓 strip、预编译逐行正则、C 级数字扫描
prof 基线(PROF/20260930_224857/main_time.txt=438.40s,7.28 亿次调用)显示全流程最大的 Python 侧热点是逐行扫描产生的内置字符串/正则调用:str.startswith 2.372 亿次(32.1s)、str.strip 1.791 亿次(22.8s)、re.Pattern.match 3130 万次(11.4s)、re.match 模块级 1971 万次(10.7s)+re._compile 1975 万次(7.2s)。四处等价改动:
1) _split_ll 第二遍:in_define/in_global_def 续行(.ll 里占比最高)不再 str.strip——这两支只用行内容与花括号计数,前导/尾随空白不含花括号,_count_braces_outside_strings 结果逐字不变;
2) _split_ll 第一遍:加 "define" not in line 的 C 级快路径,绝大多数行连 strip 都不做;
3) Phase2Translator 逐行正则全部预编译为模块级 _RE_* 常量(11 处 re.match + 4 处 re.sub,模式串一字未改)——消掉每次调用的模式串哈希+缓存查找;
4) IRMetadataFix:_RewritePlain 的前导数字扫描由逐字符 Python 循环改为 str.lstrip 的 C 级扫描(该函数 prof 5.8M 次调用,是项目内 self time 最大者);SanitizeIRMetadata 的逐行 startswith("!") 改为 [:1] 索引切片。
离线自测:数字扫描与旧实现逐例一致;SanitizeIRMetadata 幂等性保持;_split_ll 冒烟输出与预期语义一致(global/多行初始化器/define→declare/constant/metadata 分流正确)。
|
2026-09-30 23:04:25 +08:00 |
|
TermiNexus
|
e2c84daa65
|
fix(Handles): 成员表登记改实时按名判重,消成员表重名条目(AST.children 重复)
类体字段收集循环每轮都可能重入并清空重填同一张成员表(CollectAnnAssignMember → 泛型特化 → _generate_structs → _TryLoadClassMembersFromPyi(force=True) → HandlesImports.py:3415,该 force 分支不受字段收集中保护),而三处只在循环前快照一次名字集合的判据即时失效,会把嵌套帧已登记的同一字段再追加一条:AST 的最后一个字段 children(注解含 list[...] 泛型容器,是全类唯一触发该重入的字段)被登记两次 ⇒ 表 8 项/去重 7 项、重名条目 children@7;该表再被祖先块展平原样拷给 AST 的每个子类 ⇒ StructGen「[结构体布局] 成员表含重名条目」在大量 AST 子类(ExprNode/Stmt/MatchCase/ExceptHandler…)上齐发。三处一律改为实时读表判重(HandlesClassDef 自有字段段与 _PreRegisterClassMembers、HandlesImports 跨模块自有字段段),判据语义与改前逐字相同;自有字段段刻意只跳过本轮循环开始后才出现的同名,以保住字段名遮蔽 fail-fast 与既有布局语义。
|
2026-09-30 19:31:21 +08:00 |
|
TermiNexus
|
a7d8589af6
|
fix(HandlesAnnAssign): 补上指针占位类名判据的定义,修 _PTR_MARKER_CLASS_NAMES 未定义
「注解声明类台账」只引用未定义 _PTR_MARKER_CLASS_NAMES,_DeclCls 为真时即 NameError(翻译 lib/Projectrans/Utils.vp 触发)。定义为 frozenset 常量并改用 _IsPtrMarkerClassName,覆盖裸名 / sha1.rawptr 前缀 / rawptr[X] 下标三种占位形态。
|
2026-09-30 19:10:59 +08:00 |
|
TermiNexus
|
b0aa9f6d76
|
fix(Handles): 注解声明的类是权威类型,赋值不得改掉它
新增 var_annotated_class 台账(AnnAssign 声明完成时登记真实类名,随 var_struct_class 在函数作用域清空),HandlesAssign 三处「右值推出类」登记点命中该台账时不再覆盖声明类型。否则 node: rawptr[AST] = Name(...) 会把 node 记成 Name,后续 node = <AST*> 被判成被禁止的向下转型(includes/ast/parser.vp:1919)。
|
2026-09-30 19:09:37 +08:00 |
|
TermiNexus
|
ecd336771f
|
fix(FuncGen): base/字段偏移一律按"重名条目不占槽位"算,修 MatchCase 偏移 6(应为 7)
类别:a(编译器缺陷)
现象(早前现场,本轮定位):
TypeError: [fast-fail] len(mcc_pre.children) 分派 'list[….AST*].__len__' 失败:
类='MatchCase' 偏移=6 接收者类型=i32
判据(磁盘实证):
· MatchCase 的 body 在 temp 与**全部** output stub 中都是 **10 槽**
(vtable + AST 的 7 字段 + pattern/guard)⇒ `children` 的正确偏移 = base(1) + 表内下标(6) = **7**。
· 观测到 6 ⇒ base 被算成 0 ⇒ `_member_base` 的 `_Delta = 槽数 - 表长` 为 0,
即**表长 = 10**(9 个不同字段 + 1 条重名条目),而不是 9。
· 重名条目是**产物**:源码层面的字段名遮蔽已由 HandlesClassDef 的
"字段名遮蔽 fail-fast"(:1083)拦下 ⇒ 表里的重名只可能来自"展平残留
(半截祖先块,已由 2e7bac18 在插入点剥除)/ 同一字段被两条收集路径各登记一次"。
修复(与 `_get_member_offset` 配对路径已有的"重名条目不占槽位"同口径,全线统一):
· `_member_base` 的长度校准改用**去重名数**(`_Delta` 不再被重名条目冲掉 ⇒ base 正确);
· 新增 `_MemberSlotIndex(Members, FieldName)`:取"首个同名的**去重**下标"
(重名条目不计槽位),并把 `_get_member_offset` 中**全部 12 处** "base + 表内下标"
站点改为调用它(含继承链回退、REnum 变体搜索、stub 重载后、_PreRegisterClassMembers
之后等分支);"最后回退"里的 `_num_members_final` 同步改为去重计数。
· 对无重名的表,取值与改前**逐字相同**(去重下标 == 原始下标)。
核验:py_compile + compileall 全 lib 通过;离线冒烟(真实方法、未绑定实例调用):
含 1 条重复 'children' 的表上 `children -> 6`、`guard -> 8`、缺名 -> -1;
去重名数 9 ⇒ `_Delta = 10 - 9 = 1` ⇒ base=1 ⇒ `children` 偏移 7(与 body 10 槽一致)。
未放宽任何 fail-fast。
|
2026-09-30 18:44:48 +08:00 |
|
TermiNexus
|
2e7bac1816
|
fix(HandlesImports/HandlesClassDef): 剥掉"祖先块前缀的陈旧半截副本",消除成员表重名条目
类别:a(编译器缺陷)
现场(用户构建日志):
WARN[StructGen]: [结构体布局] 类 'ClassGenDef' 的成员表含**重名条目**:
表 50 项 / 去重 42 项,现有 body 42 槽恰等于「去重名数 + vtable」
判据(磁盘实证 + 结构推导,非猜测):
· ClassGenDef 自身 **0** 个字段(全是方法),父类 ClassGenBase 恰 **42** 个字段;
temp / output 两份 stub 的 ClassGenDef body 都是 **42** 槽,且槽序 = ClassGenBase
的字段声明序 ⇒ body 正确、**表多出 8 条**(50 = 42 + 8)。
· ClassGenBase 自己的 Phase-1 stub 就是 42 槽(不是 8)⇒ 多出的 8 条并非"父类 stub 短",
只能来自**内存里父类表尚未收集完就被展平**(写入了半截祖先块 Block[:k],k=8)——
正是 PrependInheritedBlock 文档里写明的"已知残留"。
· 重名条目不是布局槽位(见 FuncGen._get_member_offset 的重名段),却让
_member_base 的 _Delta = 42 - 50 = -8 走进类型校准,并让回退路径的
base + 表内下标 整体偏斜(同族现场:MatchCase / TypeGenResolve 的偏移算错)。
修复(判据全部为正向事实,且与插入方向无关):
· 新增 NormalizeAncestralCopies(Table, Block, InsertAtHead) -> (新表, 是否已含完整块):
① 完整块在**头**、尾部挂前缀副本 ⇒ 剥尾(副本是被后来的整块前置推过去的);
② 完整块在**尾**、头部挂前缀副本 ⇒ 剥头(副本先写入、整块随后追加);
③ 完整块尚未出现 ⇒ 剥插入边(仅当这些名字已另有正本,见下)。
剥掉的每一项在(表里已有的 / 随后插入的)完整块里同名同型 ⇒ **不丢布局信息**,
与 PrependInheritedBlock 性质①"不删块内条目"不冲突(剥的是副本)。
第一版曾"两端都剥",离线冒烟立刻暴露会把**正本**当副本剥掉([A,B,C,D,A,B] →
[C,D,A,B],再前置整块反而多出 C,D 两份)⇒ 改为先定位完整块在哪一端、再剥另一端。
· TrimStalePrefixCopy 判据:插入边连续游程与 Block[:j] 逐项同 name 同 IR 类型,
1 ≤ j < len(Block),且这 j 个名字在表里**各另有一份**(计数 ≥ 2)——最后一条把
"子类自有字段恰好与祖先块前缀同名同型"的误判排除。
· 两个插入点(HandlesImports.PrependInheritedBlock 前置 / HandlesClassDef 展平追加)
统一走该函数,判据对称;真正剥除时各留一条 WARN(含条数与前后表长)。
· PrependInheritedBlock 原有两条幂等提前返回("表头等于块" / InheritedBlockAlreadyPresent)
合并为 _HasFull 一条(语义等价),并更新其"已知残留"段落。
· StructGen 的重名 WARN 追加**逐条明细**(名@表内下标):单看"表 50 / 去重 42"无法区分
"祖先块前缀的陈旧副本"与"两条收集路径各登记一次"(两种成因修法完全不同)。
核验:py_compile + compileall 全 lib 通过;离线冒烟(调用真实函数,8 组用例 × 两个插入方向):
①/② 均 6→4 正确剥除,"无重复 / 带自有字段 / 类型不同 / 无正本 / 空表"均不改动。
未放宽任何 fail-fast。
|
2026-09-30 18:41:24 +08:00 |
|
TermiNexus
|
c2f53852ee
|
fix(Handles/PythonParser/DeclarationGenerator): 补齐 3 处"调用了不存在成员"的死路径 + 1 处被 hasattr 掩盖的静默降级
类别:a(编译器缺陷)
现场:翻译 TransPyV/App/lib/core/Gen/Call/CallGenLeafKit.vp 时终止编译
RuntimeError: 翻译 ... 失败: 'ClassHandle' object has no attribute '_PreRegisterClassMembers'
(FuncGen.py:965 有调用,方法全仓无定义)
按要求做了全仓静态扫描(ast:属性访问 vs 全仓属性定义;symtable:未定义全局名),共修 4 处:
1) HandlesClassDef.ClassHandle._PreRegisterClassMembers(缺失 → 实现)
调用点:FuncGen._get_member_offset 的"struct body 已存在、class_members[类名] 为空"分支
—— 该类的成员表在本次翻译里从未被收集,而按名定位字段槽位必须有表。
实现与既有两处成员收集段同源(CollectAnnAssignMember → 追加 (名, 类型)),但**只做收集**:
· 不展平继承前缀(调用方随后经 _member_base → EnsureClassMembersFlattened 补齐);
· 不调用 _generate_structs(body 已存在,重入 set_body 会把残缺布局永久固化);
· 只追加尚无同名的条目(幂等,重复命中不会把表撑长)。
跳过 __provides__/__requires__/__require_must__(非布局槽位),并登记字段限定符。
2) HandlesExprAttr.ExprAttrHandle._load_bitfield_member(缺失 → 实现)
读路径 7 处调用(位域分支)全部指向不存在的方法 ⇒ 任何位域字段读取都会 AttributeError。
实现与 HandlesAssign._store_bitfield_member 严格对称:位域类 body 只有一个 storage 槽
(elements[0]),字段位置由 class_member_bitoffsets/bitfields/signeds 表达,
读取 = `(storage >> 位偏移) & ((1<<位宽)-1)`,有符号字段再做符号扩展;
缺位偏移 / 位宽 ≤ 0 / struct 未具化 / storage 非整数一律 fail-fast(不返回 i32 兜底)。
3) PythonParserMixin 的 __set_default__ 分支(幽灵调用 → 明确报未支持)
原调用 self._handle_set_default_in_parse(...) 全仓无定义。核实:该注解在 TransPyC/lib 内
无任何消费端(只有 t.py 的定义与 StubGen/Converter 的"剥离该调用保留类型"),
includes 全仓亦无源使用 ⇒ 不静默丢弃源码语义,改为 NotImplementedError 明确报"未支持"。
4) DeclarationGenerator._llvm_type_size_str 的数组分支(静默降级 → 修好)
原写作 `self._ARR_TYPE_RE`(少一个 A)并以 hasattr 守卫 —— 该属性全仓从未定义 ⇒ 守卫恒假
⇒ 数组字段一律落到"未知类型 → 8",与 docstring 声明的"_EmitREnumLlvm 尺寸口径对齐"不符。
改用本类已定义的 _ARRAY_TYPE_RE,并去掉守卫(属性再丢失应当响亮失败)。
核验:compileall 全 lib 通过;属性扫描剩余 26 条全为仓外对象(logging / vast AST /
llvmlite Value·Instruction·IRBuilder / 内置 Exception.args / __class__ / __dict__);
symtable 未定义全局名仅 __file__(运行时内置)。未新增/放宽任何 fail-fast。
|
2026-09-30 18:23:25 +08:00 |
|
TermiNexus
|
52ea21f082
|
fix(LlvmGenerator/Phase2Translator): 本模块声明的类钉死本模块 SHA1 前缀,消除同名类前缀漂移
类别:a(编译器缺陷)
现象(上一版 ab906f2c 之后的下一步):
includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)` 报
TypeError: Type of #1 arg mismatch:
%"fef783da4d24330d.Keyword"* != %"8312ce86a2fd82c0.Keyword"*
(llvmlite 该报文是「形参 != 实参」且下标 1-based ⇒ 实际就是**第 0 个形参 self**)
根因(磁盘实证,非推断):TransPyV/output/8312ce86a2fd82c0.stub.ll 内自相矛盾
- `%"fef783da4d24330d.Keyword" = type {…}` 与 `%"8312ce86a2fd82c0.Keyword" = type {…}`
两份同名类的 struct 同时存在;
- `declare %"fef783da4d24330d.Keyword"* @"8312ce86a2fd82c0.Keyword.__new__"(%"8312ce86a2fd82c0.Keyword"* %self, …)`
- `declare void @"8312ce86a2fd82c0.Keyword.__before_init__(%"fef783da4d24330d.Keyword"* %".1")`
其中 8312ce86a2fd82c0 = includes/ast/astaux.vp、fef783da4d24330d = includes/ast/tokens.vp
(TransPyV/temp/_sha1_map.txt),两者各自定义了一个 `class Keyword`。
机制:`_build_struct_sha1_map` 建的是「**类名** → 定义模块 SHA1」,但去重判据写成
`if sha1_key in struct_sha1_map` —— 用模块 SHA1 去查一张按类名建的表,判据恒假 ⇒
实际是「后扫到的覆盖先扫到的」,同名类归属随 os.listdir 顺序漂移。定义端
`StructGen._generate_structs` 在 `class_sha1_map` 为空时用此表取前缀,于是 astaux
**自己**的 Keyword 被命名成 `fef783da4d24330d.Keyword`(另一个模块的 sha1);而按本
模块前缀另建的 `8312ce86a2fd82c0.Keyword` 被 `__new__`/`__init__` 当作 self,
`HandlesClassDef._EmitNewFunctionLlvm` 生成的 `__before_init__` 却取
`Gen.structs['Keyword']`(错名前缀那份)⇒ 声明与调用点分叉。
修复(两点,均为正向事实判据):
1) LlvmGenerator(Gen 构造处,**先于** setup_from_symbol_table 内部的 _generate_structs):
把本文件顶层 ClassDef 名单钉死为本模块 SHA1,写进 Gen.class_sha1_map(其优先级高于
_struct_sha1_map)。"本文件声明的类"就在本次要翻译的 AST 里;class_sha1_map 是 Gen
实例级、每模块重建 ⇒ 非本模块声明的类不受影响。
2) Phase2Translator._build_struct_sha1_map:去重判据改为**类名**(先扫到的为准),并另按
模块 SHA1 去重"同一模块文件被两处目录重复解析"——归属不再随 listdir 顺序漂移。
未放宽任何 fail-fast;未改动 __new__/__before_init__ 的调用点。
|
2026-09-30 18:12:24 +08:00 |
|
TermiNexus
|
ab906f2cde
|
fix(HandlesExprCall): 构造的 self 按 callee 形参类型解析(同名类消歧),并合并"sha1 全名键"分支
类别:a(编译器缺陷)
现象(用户重跑后仍复现):includes/ast/parser.vp:1774
`kw: rawptr[AST] = KwNode(ps.pool, None, val)` 报
FailFastError: 指针隐式转换已禁止(非基本类型(结构体/类/联合)):调用
8312ce86a2fd82c0.Keyword.__new__ 第 0 个实参 %"fef783da4d24330d.Keyword"*
不能隐式转为形参 %"8312ce86a2fd82c0.Keyword"*
根因:`Gen.structs` 的键恒为**短名**(StructGen._generate_structs 以 ClassName 作键,
IR 名才带 sha1 前缀),别名注册只是**浅拷贝**(HandlesImports
`Gen.structs[asname] = Gen.structs[name]`)。Keyword 同时存在于 ast.tokens
(fef783da4d24330d) 与 ast.astaux(8312ce86a2fd82c0):裸名 'Keyword' 先被 .tokens 占用,
别名 'KwNode' 键又只是浅拷贝 ⇒ 构造的 self 按名字取到 tokens 的实例,而 callee
(_find_function('KwNode.__new__'))命中的是 astaux 的。llvmlite 的类型相等按 **IR 名字**
判定(实测同名不同对象 == 为真,见提交说明)⇒ 两份不同名实例必被
_ClassifyPtrImplicitConvert 拒绝。
上一版修复(ba7e117e)不成立:它按"callee 限定类名 + 与 ClassName 同短名"改判,而
ClassName 是别名 'KwNode'、限定类名是 '...Keyword',同短名判据恒假 ⇒ 从未生效。本版删除。
修复:
1) 新增 _CalleeSelfType():self 的 struct 直接取 callee(__new__ → __before_init__ →
__init__)第 0 个形参的 pointee —— 正是 _adjust_args 的比较对象,无歧义;形参上的
实例未具化时按 IR 名换同名已具化实例(_FindStructByIrName;IR 里类型身份就是名字)。
2) _get_struct_type 统一解析序:callee 形参 → sha1 全名键 → 名字键。
3) 合并"sha1 全名键命中"分支:原实现只解析 StructType、**不产出 alloca / 构造调用**,
控制流随后落到函数末尾 `return i32 0` ⇒ 该路径上的跨模块构造会**静默不构造**
(同为类别 a,一并修掉)。
|
2026-09-30 17:00:50 +08:00 |
|
TermiNexus
|
ba7e117ecb
|
fix(HandlesExprCall): 同名类构造消歧——ctor 的 self 实参按 callee 限定类名解析,不再回退有歧义的短名
类别:a(编译器缺陷)
现象:includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)` 报
FailFastError: 指针隐式转换已禁止(非基本类型(结构体/类/联合)):调用
8312ce86a2fd82c0.Keyword.__new__ 第 0 个实参 %"fef783da4d24330d.Keyword"*
不能隐式转为形参 %"8312ce86a2fd82c0.Keyword"*
根因:Keyword 同时存在于 ast.astaux(8312ce86a2fd82c0) 与 ast.tokens(fef783da4d24330d)。
KwNode 是 `from .astaux import Keyword as KwNode` 的导入别名,_ctor_module_sha1
只从 ModulePath(ast.attr 形态) 解析 ⇒ 裸名/别名调用拿不到 sha1;
_get_struct_type 回退短名 Gen.structs.get('Keyword') ⇒ 撞上 tokens 的同名实例。
修复:从**已解析到的构造函数对象**的 IR 名({sha1}.{Class}.{method})取限定类名,
仅在「同短名 + 已具化」时改判——同短名保证不会把无关类引进来,已具化保证
_allocaEntry 的尺寸正确(opaque 实例算不出大小)。取不到限定名时保持原短名回退,
对无同名冲突的类零变化。
|
2026-09-30 16:38:09 +08:00 |
|
TermiNexus
|
cad70fa8f3
|
chore(诊断): len 分派 fail-fast 追加成员槽来源明细(项数/base/下标/名序/布局槽/实例同源判定)
类别:b(机制漏豁免)/诊断增强,不改变任何成功路径。
现场:编译 StmtGenDeclMixin.vp:242 `len(mcc_pre.children)` 报
[fast-fail] len(mcc_pre.children) 分派 'list[10f3f43ddd447eec.AST*].__len__' 失败:
类='MatchCase' 偏移=6 接收者类型=i32
静态可查到的**正确**布局(temp/8c9751aec486f41c.stub.ll 实测 10 槽):
[0]i8*(vtable) [1]AST*(parent) [2]MemManager*(pool) [3..6]i32(lineno/col_offset/
end_lineno/end_col_offset) [7]list[AST*]*(children) [8]AST*(pattern) [9]AST*(guard)
声明侧(.vpi):`class AST` 7 字段 + `class MatchCase(AST)` 2 字段 ⇒ 表 9 项、
children 表内下标 6 ⇒ 正确 base=1 ⇒ 正确偏移 7。而失败现场是 6 ⇒ **base 被算成 0**
(表 10 项触发 `_Delta==0 ⇒ base=0`,或取值处实例与类键实例不同源)——
只看 `偏移=6` 无法区分,故把两侧读数打进报文。
- `AssignHandle._DescribeMemberSlot` 增可选 `StructHint`:base 与失败现场**同源**
(不传时行为与改前逐字一致)。
- `HandlesExprBuiltin._HandleLenLlvm` 的 len 分派失败路径(仅该路径)追加上述明细:
`class_members[类] 项数/base/下标/声明槽类型`、**完整名序**、
`布局 struct name+元素数+elements[offset]`、`structs[类] 与布局是否同一对象`。
明细生成失败只追加一行说明,绝不掩盖原始 TypeError。
静态核验:py_compile 通过;全仓 symtable 未定义名扫描 0 条。
|
2026-09-30 16:16:38 +08:00 |
|
TermiNexus
|
9afd6a45ed
|
fix(HandlesClassDef/HandlesImports): 展平块幂等——块已整段在表头/尾时不再重复插入(修 41→73 的 body 分叉)
类别:a(编译器缺陷,且已泄漏进 emit 的 IR)。
on-disk 实证:Phase 1 声明(temp/<sha1>.stub.ll)与 Phase 2 emit(output/<sha1>.stub.ll)
对同一类的 body 槽数不一致,共 7 个类,全在 Generator/ClassGen/TypeGen 继承链上:
TypeGenCType 41 → 73(+32) TypeGenCDef 41 → 73 TypeGenErr 41 → 73
ClassGenMixin 42 → 50(+8) ClassGenVTable 42 → 50
ClassGenEnum 42 → 50 ClassGenDef 42 → 50
逐槽比对:output[0:41] == temp,且 **output[41:73] == temp[9:41]** —— 多出的 32 槽
正是"追加前那张表**尾部** 32 项的原样副本"。下游即 Repl.vp:802
`GenApi._g_gen.TypeGenCType.CTypedefNames` 因两份 body(41/73)歧义而
"基座 pointee 未匹配 Gen.structs 中任何 struct"。
成因:两条展平路径判据**不对称**——
- 导入侧 `PrependInheritedBlock`:前置,且自带"表头是否等于块"的幂等判据;
- 定义侧 HandlesClassDef 展平:直接 `class_members + _flat` **追加**,**无任何判据**。
一旦块已整段位于表尾(或表中段),就会被再插一次,表里多出 len(块) 条重复条目,
body 随之多出同名槽。
修法:新增唯一判据源 `HandlesImports.InheritedBlockAlreadyPresent(Table, Block)`——
"块是否整段位于 `Table` 的**头部或尾部**(名字 + IR 类型逐项相同)",
两个插入点都接它(导入侧前置 ⇒ 头部;定义侧追加 ⇒ 尾部)。
刻意**不判表中段**:中段的同序列与"合法的同名并存"(子类在自有段重复声明父类字段,
body 本来就有两个槽)不可区分,判成"已存在"会误删本该有的槽;真出现中段重复时
仍由 StructGen 的重名条目告警兜住。跳过重复时打印一行 WARN(类名 / 块长 / 表长),
只跳过重复、不改其余布局。
冒烟(内联 python,无临时文件):现场形状(块 = 表[9:41])→ True;中段 → False;
仅名字相同而类型不同 → False;空块 / 块比表长 / 非 tuple 条目 → False。
静态核验:compileall 通过;全仓 symtable 未定义名扫描 0 条。
|
2026-09-30 15:44:13 +08:00 |
|
TermiNexus
|
83453c68db
|
fix(HandlesImports): 解冻 struct 加载——缓存键缺失也删、索引陈旧定点再读、opaque 条目不再压住好 body
类别:a(编译器缺陷)。三处都会把"该类本 Gen 内始终 opaque"**永久固化**,
下游表现为 Repl.vp:802 `GenApi._g_gen.TypeGenCType.CTypedefNames` 报
"基座 pointee 未匹配 Gen.structs 中任何 struct(同短名候选=['TypeGenCType'],
全部未命中)"——而磁盘上 temp/<sha1>.stub.ll 明明有 41 槽 body。
1) `_TryLoadStructFromStub` 尾部缓存键删除判据:原只在"该键**有**实例且 opaque"时删;
若索引里**有该类的条目**、而该键此刻根本没有实例(例如只落了 sha1 全名键、短名键
尚未建立),这一轮的成功缓存被永久留下 ⇒ 之后每次同名查询都在入口直接 return。
现改为"无可用 body(缺失或 opaque)且索引有条目"即删键,失败可重试。
2) `_entry_priority`:把 `is_opaque` 提到 `is_temp` 之前。原顺序让**陈旧的 output 占位
条目压住 temp 里已有的好 body**(两侧都有 body 时仍保持 output > temp 原判据)。
3) 新增 `_ReReadStructBodyFromDisk`:`_build_stub_index` 只按"文件名集合"增量,假定
stub `只增不改`;而 Phase 2 会**原地重写** output/<sha1>.stub.ll(译文生成点 :2194、
依赖变化重组点 :1785-1790)⇒ 新 body 永不进索引。现在"选中的索引条目没有 body"
这条失败路径上按磁盘当前内容**定点再读一次**(只读那一个文件,取不到即维持原状)。
静态核验:compileall 通过;全仓 symtable 未定义名扫描 0 条。
|
2026-09-30 15:44:10 +08:00 |
|
TermiNexus
|
393ffefcd5
|
fix(HandlesAssign): self 属性写入的基址收口统一为 _ConcretizeGepBase;收口失败不再静默丢写
类别:a(编译器缺陷)。
1) 收口统一:`self.字段 = v` 的基址此前走**内联**孪生体搜索,缺
`_TryLoadStructFromStub` 一步 —— 类键那份仍 opaque 且 Gen.structs 里尚无已具化
孪生体时(body 只在 stub 文件里待加载),内联版会一路走到 GEP 撞 opaque 守卫
(现场:includes/ast/astaux.vp:165 `self.arg = _copy_str(pool, arg)`,基址 pointee
是 opaque 的 %"8312ce86a2fd82c0.Keyword"*,而同短名 'Keyword' 键那份已具化 10 槽)。
现改为调用读路径的**同一判据源** HandlesExprAttr._ConcretizeGepBase /
_ConcretePointeeFor:① 类键那份 + stub 补 body → ② 同短名已具化孪生体 →
③ 都拿不到则原样返回、由守卫报根因。偏移与 GEP 同源(返回实例同时作为
_get_member_offset 的 StructHint)。
2) 收口失败改 fail-fast:三条路都拿不到 elements 时,此前会落回
`offset >= max_offset` 的「存回临时变量」分支 —— 该临时变量不在对象布局里,
后续 `self.attr` 读路径按 GEP 取值,写入被**静默丢弃**。现直接 FailFastError,
并报出三次尝试的结果(不兜底)。
静态核验:py_compile 通过;全仓 symtable 未定义名扫描 0 条;以 _GuardIrBuilder +
真实 _ConcretizeGepBase 复现现场实例形状(opaque '8312ce86a2fd82c0.Keyword' +
已具化 'fef783da4d24330d.Keyword'(10 槽))→ 解析成功、GEP 正常发出(索引 3);
未收口的裸 opaque 基址仍被守卫拦下。
|
2026-09-30 15:14:31 +08:00 |
|
TermiNexus
|
7ee6ebe6b0
|
fix(类型转换): int→i1 目标改为真值归一化 != 0,与 bool(x)/if 条件同判据
类别:a(编译器缺陷)。TPC 侧此前对 i1 目标一律 trunc(取最低位):运行时值 2 →
最低位 0 → false,而同一值在 `if 2:` / `bool(2)`(icmp ne 0)为真 ⇒ 条件上下文与
落存储/强转两处结论相反(静默语义分裂)。
- MemoryOps._CoerceIntToI1(新增,唯一判据源):非 i1 整数 → `icmp ne x, 0`。
- MemoryOps._coerce_value:窄化且目标位宽 == 1 时走 _CoerceIntToI1。
- HandlesAnnAssign._StoreWithCoerce:同上(self.字段 / obj.字段 落存储)。
- HandlesExprCall._HandleTypeCastLlvm:显式强转目标 i1 时同上。
与 TransPyV 侧同批修复(coerce_to_type / _translate_t_type_cast)对齐。
注:TPC 的 `bool` 注解仍是 i8(t.CBool.Size=8),故 `b: bool = 2` 仍按 i8 保位存储
(int(b)=2);本次只覆盖**真 i1 目标**,双端 bool 位宽差异见
SymbolUtils.py:1218-1223 已记录的全局语义分裂。
|
2026-09-30 15:14:31 +08:00 |
|
TermiNexus
|
3efe83f1ea
|
fix(编译期名称解析): 修 StructGen 缺 _vlog 导入与 HandlesImports 的 node/Node 误用
两处均为 a(编译器缺陷),且都被 HandlesImports.py:1055 的
`except Exception -> WARN 忽略异常` 吞掉,表现为模块声明静默退化
(本次现场:VpiReader.vp 阶段 WARN + 结构体声明路径中断)。非源码违规、
非机制漏豁免。
1) lib/core/LLVMCG/StructGen.py
- `_generate_structs` 的「成员表含重名条目、body 已是权威布局」提示调用
`_vlog().warning(...)`,但本文件从未定义/导入 `_vlog` ⇒ NameError。
该分支一旦命中(astaux.ExceptHandler 一类),`_generate_structs` 整体中断。
- 补 `from lib.core.VLogger import get_logger as _vlog`,与同目录
FuncGen.py / TypeConvert.py 同口径。
2) lib/core/Handles/HandlesImports.py:1317
- `_EmitExternalGlobalDeclLlvm` 形参名为 `Node`,该 elif 分支误写成小写 `node`
⇒ 只要传入的是**非带注解**的跨模块全局量(`ast.Assign`)就恒抛 NameError,
分支整体不可达:外部全局变量从未声明 external,消费侧表现为
unknown global / 类型占位。
- 判据改回 `isinstance(Node, ast.Assign)`。
静态核验:对 TransPyC 全部 .py 做 symtable 未定义名扫描,结果 0 条;
`python -m py_compile` 通过。
|
2026-09-30 14:55:40 +08:00 |
|
TermiNexus
|
e5f044dd03
|
chore(诊断): AnnAssign RHS 为 接收者链.方法(...) 时补接收者基址诊断 + 最近一次属性读取失败原因
【b 机制漏豁免 —— 只加取证,不改任何判定/IR】现场:翻译
TransPyV/App/lib/core/Gen/Class/ClassGenEnum.vp:59
`ty: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr,
imported_modules, from_imports)` 报"(Call 形态)编译期求值为空"。
已按代码**排除**出来的事实(写进注释,供后续不再重复排查):
· 方法声明缺失会在 HandlesExprCall._HandleMethodCallLlvm 内 raise
"Undefined method …",不会走到该 fail-fast;
· 该函数在"方法已找到"之后不会返回 None,唯一的 None 出口是
`ObjVal = HandleExprLlvm(Node.func.value)` 为假(L3335-3337)——
即**接收者链 `self.Gen.TypeGenResolve` 自身求值为空**才是根因;
· 但原报文的"基址诊断"只在 RHS 是 **Attribute** 时才打印(HandlesBase:784),
Call 形态下接收者失败原因被丢弃 ⇒ 报错点与真因相隔数层。
修法(纯取证):HandlesBase 的通用 fail-fast 增 `elif Call(func=Attribute)` 分支,补三行:
① `被调者接收者诊断:` —— 复用 `_DescribeAttrBase(Gen, Node.func.value)`:接收者
(如 `self.Gen`)的基座形态 / var_struct_class / alloca 类型 / 是否在 Gen.structs /
class_members 项数与字段名单 / struct body 元素数 / 该字段计算偏移;
② `最近一次属性读取失败:` —— 回显 `Gen._LastAttrNone` 的 (类, 字段, 原因):Call 形态下
无法用 (类, 字段) 与被报节点比对,故此处**无条件**回显(Attribute 形态仍保持
"必须与被报节点一致"的原判据,避免旧记录误导);
③ `被调方法=… 已声明同名方法键(示前 4 / 共 N):` —— 区分"方法根本没声明"与
"接收者读不出来"(后者才会命中本 fail-fast,但这一行让两者一眼可分)。
未验证:本提交只改诊断文本,行为不变;需再跑一次构建才能看到新增三行。
|
2026-09-30 14:45:55 +08:00 |
|
TermiNexus
|
00edd90c28
|
fix(Handles*): GEP 基址 opaque pointee 收口统一为单一判据源,补齐同族 12 处缺口
【a 编译器缺陷】7aa12263 修好 `_get_attr_ptr` 的 self 分支后,同族其余 GEP 点仍会在
"基址 pointee 是另一份 opaque 同名实例"时抛裸 TypeError(被 IR 边界守卫拦下,报文为
"GEP 的基址指向**未具化**的具名结构体")。根因是同一套收口逻辑被各点各写一份、且
**多数点根本没写**:真正需要的判据只有一条。
新增唯一判据源(HandlesExprAttr):
· `_ConcretePointeeFor(Gen, ClassName, Pointee)`:取该类的**已具化实例** ——
① 类键那份先 `_TryLoadStructFromStub` 补 body;② 否则 `Gen._FindConcreteTwin`
(同短名、槽数最多);③ 都取不到 ⇒ None(**不兜底**,交守卫 fail-fast 报同短名
实例清单 + TPC 发射点)。
· `_ConcretizeGepBase(Gen, Ptr, ClassName, Tag)`:pointee 非 opaque ⇒ 原样返回
(对既有正确路径零变化);opaque ⇒ bitcast 到已具化实例并**同时返回该实例**,
供 `_get_member_offset` 作 StructHint(偏移与 GEP 必须同源,与 a43513c9 同口径)。
接入点(全部改为调用上述判据源,逐点行为只在"原本必崩"的情形下变化):
· HandlesExprAttr._get_attr_ptr — self / 变量 / 属性链 / i8* / 下标 / 强转基座 六条分支
(self 分支已由 7aa12263 修,此处并入统一判据;下标分支此前**完全没有**收口);
· HandlesAnnAssign._HandleAttributeStoreLlvm — self / 变量 / 属性链 三条分支;
· HandlesBase._get_int_ptr — 属性分支;
· HandlesExprBuiltin(len() 的两个接收者 GEP:`obj.len` 与 list 特化字段);
· HandlesMatch — REnum 变体 match 的载荷 GEP(变体 struct 可能未 set_body)。
冒烟(临时脚本已删,鸭子类型 Gen + 真实方法):
· `_get_attr_ptr` self 分支 ⇒ 基址 `%"fef783da4d24330d.Keyword"*`(经 cast_self_Keyword)PASS;
· `_get_attr_ptr` i8* 分支 ⇒ 同上 PASS;
· `HandlesAnnAssign` self 分支 ⇒ GEP 基址为已具化实例 PASS;
· 回归:基址本就具化时必须**原样**(不得多出 bitcast)PASS。
另:`python -m compileall TransPyC/lib` 全绿。
|
2026-09-30 14:42:56 +08:00 |
|
TermiNexus
|
7aa12263f5
|
fix(HandlesExprAttr/BaseGen): _get_attr_ptr 的 self 分支补 opaque 基址收口(孪生体 bitcast)+ GEP 守卫报出 TPC 发射点
【a 编译器缺陷】现场:Phase2 翻译 includes/ast/astaux.vp:165
`self.arg = _copy_str(pool, arg)`,守卫报
"getelementptr(GEP) 的基址指向**未具化**的具名结构体:基址类型=%"8312ce86a2fd82c0.Keyword"*,
GEP 名='arg'",而同短名实例清单显示 'Keyword' 键上那份已具化(10 槽)。
根因:`_get_attr_ptr` 的 **self 分支**是全仓"self 字段取址"家族里唯一**没有**做 opaque
收口的一处 —— 同函数下方各分支(变量分支 / 属性链分支 / i8* 分支 / 强转基座分支)都先
`_TryLoadStructFromStub` 补 body、再 bitcast 到已具化实例;`HandlesAssign` 的 `self.X = v`
写路径与读路径 `_gep_and_Load_member` 也都有同一套孪生体搜索(`_FindConcreteTwin`)。
本分支直接 `gep(SelfPtr, …)`,typed pointer 下 llvmlite 必须在编译期从 pointee 算元素类型
⇒ 撞 opaque 即抛裸 TypeError(被 IR 边界守卫拦下)。
修法(与同函数其余分支同口径,不引入新判据):
· 基址 pointee 为 opaque 时,先在类键(`Gen.structs[ClassName]`)上 `_TryLoadStructFromStub`
补 body;类键那份仍不可用则退回 `Gen._FindConcreteTwin`(同短名、取槽数最多);
· 命中即 bitcast 到该实例,并把该实例作为 `StructHint` 传给 `_get_member_offset`
—— 偏移与 GEP 必须同源(与 a43513c9 的"hint 只接受同类"口径一致);
· 顺带补 `offset is None ⇒ return None`(原实现会把 None 塞进 `ir.Constant(i32, None)`,
属同一函数其余分支已有的判断)。
【a 编译器缺陷·取证】`RequirePointerOperand` 的 opaque 分支新增一行
「TPC 发射点(由内向外)」:取 Python 栈中位于本仓 `lib/`、且不在 BaseGen.py(守卫自身)
的最内 3 帧,报出 `文件:行(函数)`。理由:该守卫的报文只能指到**语句**,而同一语句里常有
多个候选 GEP 点(本例 astaux.vp:165 在读写两侧共有 4 个 self 字段 GEP 点),逐个读代码
无法判定是哪一个 —— 本次即靠该取证能力闭环(先按"唯一漏收口的分支"修正,下一次构建若
仍失败会直接指出真实发射点)。只在失败路径执行,热路径零开销。
冒烟(临时脚本已删):
· `_get_attr_ptr` 复刻现场(self 槽持有 opaque `%8312…Keyword*`、'Keyword' 键那份 10 槽)
⇒ 输出 GEP 基址类型 `%"fef783da4d24330d.Keyword"*`(经 `cast_self_Keyword`),PASS;
· 守卫取证:伪造 lib/ 下的发射帧 ⇒ 报文含
`TPC 发射点(由内向外): core/Handles/_FakeProbe.py:2(_LibEmit)`,PASS。
|
2026-09-30 14:35:50 +08:00 |
|
TermiNexus
|
49930c56c7
|
fix(LLVMCG/HandlesExprAttr): REnum 变体字段按唯一变体键取槽 + 修 _PointeeMatchesStruct 的「同模块前缀」误判
【a 编译器缺陷】两处根因,同一现场(CallGenDispatch.vp:787
`len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee`):
1) `_PointeeMatchesStruct` 只比 sha1 前缀 ⇒ 把**同模块的兄弟类**当成同一个类
(`50741ad4b4b0934d.ParamNode` 与 `50741ad4b4b0934d.LLVMType` 前缀相同,同出
includes/llvmlite/__types.vp)。结构体分支迭代 Gen.structs 时先命中 ParamNode
(它在 __types.vp 里声明更早)⇒ `_get_member_offset('Pointee','ParamNode')`
直接 fast-fail,报出与真因无关的类名。判据收紧为:**同一对象** 或 **IR 全名逐字相同**
(前缀标识的是模块,不是类)。同短名不同前缀(同一类的另一模块实例)仍由调用点的
`_UniqShortCN`(同短名候选唯一)放行,不受影响。
2) REnum 的变体字段在旧路径上无处可取:'Pointee' 只属变体键 'LLVMType_Ptr',
而主类 'LLVMType' 的成员表为空(HandlesClassDef:716)⇒ 即使接收者判对类名也必 fail-fast。
新增 `FuncGen._RenumVariantFieldOwner`,在 `_get_member_offset` 里当"主类不声明该字段"时改按
变体键取偏移。判据(逐条可查):① 主类经 `_IsRenumLayoutClass`(唯一判据源)判为 REnum;
② 变体键固定 `<主类短名>_<变体名>` 且成员表**首项恒为 `__tag`**(唯一产源:
HandlesClassDef._EmitREnumLlvm 与 HandlesImports 的 REnum 跨模块注册);
③ 恰有**一个**变体声明该字段(多个 ⇒ 不猜,照常 fail-fast);④ 变体 struct 已注册。
槽位正确的依据:变体 struct 的 body 是**主 REnum 镜像** `{i32 __tag, <max_variant_payload>}`,
变体成员表 `[__tag] + 载荷` 与主 REnum 槽位一一对应。
3) 载荷槽按**最大变体**宽度物化('Pointee' 槽是 i64),而字段本身声明为指针 ⇒
新增 `HandlesExprAttr._RenumVariantPayloadType` / `_CoerceVariantPayload`,在
`_gep_and_Load_member` 载入后按字段声明类型收口(int→ptr: inttoptr;ptr→ptr: bitcast;
int→int: trunc/zext;不可转换原样返回,交下游照常 fail-fast)。非 REnum 变体键逐字不变。
冒烟(临时脚本已删):_PointeeMatchesStruct 5 例、_RenumVariantFieldOwner 7 例、
载荷类型与转换 6 例、_get_member_offset 钩子(绑定真实方法)5 例 —— 全部通过。
|
2026-09-30 12:46:37 +08:00 |
|
TermiNexus
|
64ddabbe17
|
feat(HandlesAnnAssign/HandlesExprAttr): 左值注解靶型提示(target-typing)——属性链读错类名时按注解类兜回
现场:CallGenDispatch.vp:787 `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee`
报 "[fast-fail] 属性 'Pointee' 在类 'ParamNode' 中找不到(class_members=2 个: ['Next','Ty'])"。
'Pointee' 的真实归属是 LLVMType 的 REnum 变体镜像字段(LLVMType.Ptr.Pointee,
includes/llvmlite/__types.vp:33-34);ParamNode 只有 Next/Ty。更隐蔽的是**两边都有 Ty**
(Value.Ty = rawptr[LLVMType]、ParamNode.Ty = 裸 rawptr = i8*)⇒ 读错类不会当场报错,
只静默把类型变成 i8*,再往下 `.Pointee` 才炸。
机制(与 TPV translate_value 的 target_ty「使用处真实站点种类」同思路):
① HandlesAnnAssign._EvalRhsWithTargetHint:注解赋值求值 RHS 前,把**注解指向的结构体类**
作为 `Gen._TargetStructHint` 传给属性链解析;求值结束(含异常)复位,不跨语句泄漏。
两个 RHS 求值点(_reg_values 提升路径 + 常规路径)都改走该入口。
② HandlesExprAttr._apply_target_struct_hint:**仅当两条件同时成立**才改判——
Ⅰ 当前解析出的类(或解析不出)**不声明**该字段(此刻原本会 fail-fast);
Ⅱ 靶型类**确实声明**该字段。
任一不满足即原样返回(不猜、不篡改本来正确的链);改判时 `_vlog().warning` 留痕。
接入点:char*(i8*)分支与单级 Name 分支(含"原键解析不出"的情形)。
验证:py_compile 通过;`_apply_target_struct_hint` 冒烟 7 例全部符合预期——
无提示原样 / 原类不声明且靶型声明⇒改判(本次现场)/ 原类声明该字段⇒不改判 /
原键即靶型⇒原样 / 原键解析不出且靶型声明⇒改判 / 靶型也不声明⇒原样 / 靶型类不存在⇒原样。
|
2026-09-30 12:28:39 +08:00 |
|
TermiNexus
|
a1c43a7b3b
|
chore(诊断): MemberLookupFailFast 补取证——字段所有者、类键/实例名、登记为该类的变量与实际 pointee
现场:CallGenDispatch.vp:787 `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee`
报 "[fast-fail] 属性 'Pointee' 在类 'ParamNode' 中找不到(class_members=2 个:
['Next','Ty'])"。'Pointee' 实际属 LLVMType 的 REnum 变体镜像字段(LLVMType.Ptr.Pointee),
而 ParamNode 只有 Next/Ty ⇒ 说明**读取时的接收者类名被判成了 ParamNode**
(`len_val` 的注解与 RHS 都写的是 `llvmlite.Value*`,Value.Ty 才是 rawptr[LLVMType];
ParamNode.Ty 是裸 rawptr = i8*)。
本轮只补取证、不改行为,让下一轮能一次定位"是谁把接收者记成了 ParamNode":
① 本类键 → struct 实例 IR 名(键/实例名不一致 ⇒ 同一类名两份实例);
② 其它**声明了该字段**的类键列表(唯一者即"本该用的类");
③ var_struct_class 中登记为**本类**的变量(封顶 6 个)及其实际 pointee IR 名
—— 记录类名与实际 IR 类型不一致时,直接圈定记录点。
验证:py_compile 通过。
|
2026-09-30 12:26:05 +08:00 |
|